forked from github/verilator
Fix mis-optimizing gate assignments in unopt blocks, bug881.
This commit is contained in:
parent
a001babad2
commit
27ccaffb37
2
Changes
2
Changes
@ -23,6 +23,8 @@ indicates the contributor was also the author of the fix; Thanks!
|
||||
|
||||
**** Fix slice connections of arrays to ports, bug880. [Varun Koyyalagunta]
|
||||
|
||||
**** Fix mis-optimizing gate assignments in unopt blocks, bug881. [Mike Thyer]
|
||||
|
||||
**** Fix clang compile warnings.
|
||||
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Gate.h"
|
||||
@ -346,6 +347,7 @@ private:
|
||||
}
|
||||
|
||||
void optimizeSignals(bool allowMultiIn);
|
||||
bool elimLogicOkOutputs(GateLogicVertex* consumeVertexp, const GateOkVisitor& okVisitor);
|
||||
void optimizeElimVar(AstVarScope* varscp, AstNode* logicp, AstNode* consumerp);
|
||||
void warnSignals();
|
||||
void consumedMark();
|
||||
@ -591,40 +593,51 @@ void GateVisitor::optimizeSignals(bool allowMultiIn) {
|
||||
if (debug()>=5) logicp->dumpTree(cout,"\telimVar: ");
|
||||
if (debug()>=5) substp->dumpTree(cout,"\t subst: ");
|
||||
++m_statSigs;
|
||||
while (V3GraphEdge* edgep = vvertexp->outBeginp()) {
|
||||
bool removedAllUsages = true;
|
||||
for (V3GraphEdge* edgep = vvertexp->outBeginp();
|
||||
edgep; ) {
|
||||
GateLogicVertex* consumeVertexp = dynamic_cast<GateLogicVertex*>(edgep->top());
|
||||
AstNode* consumerp = consumeVertexp->nodep();
|
||||
optimizeElimVar(vvertexp->varScp(), substp, consumerp);
|
||||
// If the new replacement referred to a signal,
|
||||
// Correct the graph to point to this new generating variable
|
||||
const GateVarRefList& rhsVarRefs = okVisitor.rhsVarRefs();
|
||||
for (GateVarRefList::const_iterator it = rhsVarRefs.begin();
|
||||
it != rhsVarRefs.end(); ++it) {
|
||||
AstVarScope* newvarscp = (*it)->varScopep();
|
||||
UINFO(9," Point-to-new vertex "<<newvarscp<<endl);
|
||||
GateVarVertex* varvertexp = makeVarVertex(newvarscp);
|
||||
new V3GraphEdge(&m_graph, varvertexp, consumeVertexp, 1);
|
||||
// Propagate clock attribute onto generating node
|
||||
varvertexp->propagateAttrClocksFrom(vvertexp);
|
||||
if (!elimLogicOkOutputs(consumeVertexp, okVisitor/*ref*/)) {
|
||||
// Cannot optimize this replacement
|
||||
removedAllUsages = false;
|
||||
edgep = edgep->outNextp();
|
||||
} else {
|
||||
optimizeElimVar(vvertexp->varScp(), substp, consumerp);
|
||||
// If the new replacement referred to a signal,
|
||||
// Correct the graph to point to this new generating variable
|
||||
const GateVarRefList& rhsVarRefs = okVisitor.rhsVarRefs();
|
||||
for (GateVarRefList::const_iterator it = rhsVarRefs.begin();
|
||||
it != rhsVarRefs.end(); ++it) {
|
||||
AstVarScope* newvarscp = (*it)->varScopep();
|
||||
UINFO(9," Point-to-new vertex "<<newvarscp<<endl);
|
||||
GateVarVertex* varvertexp = makeVarVertex(newvarscp);
|
||||
new V3GraphEdge(&m_graph, varvertexp, consumeVertexp, 1);
|
||||
// Propagate clock attribute onto generating node
|
||||
varvertexp->propagateAttrClocksFrom(vvertexp);
|
||||
}
|
||||
// Remove the edge
|
||||
edgep->unlinkDelete(); edgep=NULL;
|
||||
++m_statRefs;
|
||||
edgep = vvertexp->outBeginp();
|
||||
}
|
||||
// Remove the edge
|
||||
edgep->unlinkDelete(); edgep=NULL;
|
||||
++m_statRefs;
|
||||
}
|
||||
// Remove input links
|
||||
while (V3GraphEdge* edgep = vvertexp->inBeginp()) {
|
||||
edgep->unlinkDelete(); edgep=NULL;
|
||||
if (removedAllUsages) {
|
||||
// Remove input links
|
||||
while (V3GraphEdge* edgep = vvertexp->inBeginp()) {
|
||||
edgep->unlinkDelete(); edgep=NULL;
|
||||
}
|
||||
// Clone tree so we remember it for tracing, and keep the pointer
|
||||
// to the "ALWAYS" part of the tree as part of this statement
|
||||
// That way if a later signal optimization that retained a pointer to the always
|
||||
// can optimize it further
|
||||
logicp->unlinkFrBack();
|
||||
vvertexp->varScp()->valuep(logicp);
|
||||
logicp = NULL;
|
||||
// Mark the vertex so we don't mark it as being unconsumed in the next step
|
||||
vvertexp->user(true);
|
||||
logicVertexp->user(true);
|
||||
}
|
||||
// Clone tree so we remember it for tracing, and keep the pointer
|
||||
// to the "ALWAYS" part of the tree as part of this statement
|
||||
// That way if a later signal optimization that retained a pointer to the always
|
||||
// can optimize it further
|
||||
logicp->unlinkFrBack();
|
||||
vvertexp->varScp()->valuep(logicp);
|
||||
logicp = NULL;
|
||||
// Mark the vertex so we don't mark it as being unconsumed in the next step
|
||||
vvertexp->user(true);
|
||||
logicVertexp->user(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -632,6 +645,30 @@ void GateVisitor::optimizeSignals(bool allowMultiIn) {
|
||||
}
|
||||
}
|
||||
|
||||
bool GateVisitor::elimLogicOkOutputs(GateLogicVertex* consumeVertexp, const GateOkVisitor& okVisitor) {
|
||||
// Return true if can optimize
|
||||
// Return false if the consuming logic has an output signal that the replacement logic has as an input
|
||||
typedef set<AstVarScope*> VarScopeSet;
|
||||
// Use map to find duplicates between two lists
|
||||
VarScopeSet varscopes;
|
||||
// Replacement logic usually has shorter input list, so faster to build list based on it
|
||||
const GateVarRefList& rhsVarRefs = okVisitor.rhsVarRefs();
|
||||
for (GateVarRefList::const_iterator it = rhsVarRefs.begin();
|
||||
it != rhsVarRefs.end(); ++it) {
|
||||
AstVarScope* vscp = (*it)->varScopep();
|
||||
if (varscopes.find(vscp) == varscopes.end()) varscopes.insert(vscp);
|
||||
}
|
||||
for (V3GraphEdge* edgep = consumeVertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
GateVarVertex* consVVertexp = dynamic_cast<GateVarVertex*>(edgep->top());
|
||||
AstVarScope* vscp = consVVertexp->varScp();
|
||||
if (varscopes.find(vscp) != varscopes.end()) {
|
||||
UINFO(9," Block-unopt, insertion generates input vscp "<<vscp<<endl);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GateVisitor::replaceAssigns() {
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
||||
|
19
test_regress/t/t_assign_inline.pl
Executable file
19
test_regress/t/t_assign_inline.pl
Executable file
@ -0,0 +1,19 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
compile (
|
||||
verilator_flags2 => ["-O0 -OG"],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
52
test_regress/t/t_assign_inline.v
Normal file
52
test_regress/t/t_assign_inline.v
Normal file
@ -0,0 +1,52 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2015 by Mike Thyer.
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
|
||||
int cycle=0;
|
||||
|
||||
// verilator lint_off UNOPTFLAT
|
||||
reg [7:0] a_r;
|
||||
wire [7:0] a_w;
|
||||
reg [7:0] b_r;
|
||||
reg [7:0] c_d_r, c_q_r;
|
||||
|
||||
assign a_w = a_r;
|
||||
|
||||
always @(*) begin
|
||||
a_r = 0;
|
||||
b_r = a_w; // Substituting the a_w assignment to get b_r = 0 is wrong, as a_r is not "complete"
|
||||
a_r = c_q_r;
|
||||
c_d_r = c_q_r;
|
||||
end
|
||||
|
||||
// stimulus + checks
|
||||
always @(posedge clk) begin
|
||||
cycle <= cycle+1;
|
||||
if (cycle==0) begin
|
||||
c_q_r <= 8'b0;
|
||||
end
|
||||
else begin
|
||||
c_q_r <= c_d_r+1;
|
||||
`ifdef TEST_VERBOSE
|
||||
$display("[%0t] a_r=%0d, b_r=%0d", $time, a_r, b_r); // a_r and b_r should always be the same
|
||||
`endif
|
||||
end
|
||||
if (cycle >= 10) begin
|
||||
if (b_r==9) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
else begin
|
||||
$stop;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
@ -8,9 +8,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
||||
# Version 2.0.
|
||||
|
||||
compile (
|
||||
fails=>$Self->{v3},
|
||||
fails=>$Self->{v3},
|
||||
# Used to be %Error: t/t_order_wireloop.v:\d+: Wire inputs its own output, creating circular logic .wire x=x.
|
||||
# However we no longer gate optimize this
|
||||
expect=>
|
||||
'%Error: t/t_order_wireloop.v:\d+: Wire inputs its own output, creating circular logic .wire x=x.
|
||||
'%Warning-UNOPT: t/t_order_wireloop.v:\d+: Signal unoptimizable: Feedback to public clock or circular logic: bar
|
||||
',
|
||||
);
|
||||
|
||||
|
@ -3,10 +3,13 @@
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2005 by Wilson Snyder.
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
module t (/*AUTOARG*/
|
||||
// Outputs
|
||||
bar
|
||||
);
|
||||
|
||||
wire foo;
|
||||
wire bar;
|
||||
output bar;
|
||||
|
||||
// Oh dear.
|
||||
assign foo = bar;
|
||||
|
Loading…
Reference in New Issue
Block a user