Fix false LATCH warning on 'unique if' (#3088).

This commit is contained in:
Wilson Snyder 2022-10-21 19:10:06 -04:00
parent 203993b018
commit a57a3579c0
4 changed files with 30 additions and 16 deletions

View File

@ -25,7 +25,7 @@ Verilator 5.001 devel
* Introduce a new combinational logic optimizer (DFG), that can yield * Introduce a new combinational logic optimizer (DFG), that can yield
significant performance improvements on some designs. [Geza Lore, Shunyao CAD] significant performance improvements on some designs. [Geza Lore, Shunyao CAD]
* Add --binary option as alias of --main --exe --build --timing (#3625). * Add --binary option as alias of --main --exe --build --timing (#3625).
For designs where C++ was only used to make a simple testbench we For designs where C++ was only used to make a simple no-I/O testbench, we
recommend abandoning that C++, and instead letting Verilator build it recommend abandoning that C++, and instead letting Verilator build it
with --binary (or --main). with --binary (or --main).
@ -38,6 +38,7 @@ Verilator 5.001 devel
* Add --dump-tree-dot to enable dumping Ast Tree .dot files (#3636). [Marcel Chang] * Add --dump-tree-dot to enable dumping Ast Tree .dot files (#3636). [Marcel Chang]
* Add --get-supported to determine what features are in Verilator. * Add --get-supported to determine what features are in Verilator.
* Add error on real edge event control. * Add error on real edge event control.
* Fix false LATCH warning on 'unique if' (#3088). [Rachit Nigam]
* Fix cell assigning integer array parameters (#3299). [Michael Platzer] * Fix cell assigning integer array parameters (#3299). [Michael Platzer]
* Fix LSB error on --hierarchical submodules (#3539). [danbone] * Fix LSB error on --hierarchical submodules (#3539). [danbone]
* Fix $display of fixed-width numbers (#3565). [Iztok Jeras] * Fix $display of fixed-width numbers (#3565). [Iztok Jeras]

View File

@ -98,7 +98,7 @@ private:
// Add a internal if to check assertions are on. // Add a internal if to check assertions are on.
// Don't make this a AND term, as it's unlikely to need to test this. // Don't make this a AND term, as it's unlikely to need to test this.
FileLine* const fl = nodep->fileline(); FileLine* const fl = nodep->fileline();
AstNode* const newp = new AstIf{ AstNodeIf* const newp = new AstIf{
fl, fl,
(force ? new AstConst{fl, AstConst::BitTrue{}} (force ? new AstConst{fl, AstConst::BitTrue{}}
: // If assertions are off, have constant propagation rip them out later : // If assertions are off, have constant propagation rip them out later
@ -108,6 +108,7 @@ private:
new AstCMath{fl, "vlSymsp->_vm_contextp__->assertOn()", 1}) new AstCMath{fl, "vlSymsp->_vm_contextp__->assertOn()", 1})
: static_cast<AstNode*>(new AstConst{fl, AstConst::BitFalse{}}))), : static_cast<AstNode*>(new AstConst{fl, AstConst::BitFalse{}}))),
nodep}; nodep};
newp->isBoundsCheck(true); // To avoid LATCH warning
newp->user1(true); // Don't assert/cover this if newp->user1(true); // Don't assert/cover this if
return newp; return newp;
} }
@ -164,6 +165,7 @@ private:
if (bodysp && passsp) bodysp = bodysp->addNext(passsp); if (bodysp && passsp) bodysp = bodysp->addNext(passsp);
ifp = new AstIf{nodep->fileline(), propp, bodysp}; ifp = new AstIf{nodep->fileline(), propp, bodysp};
ifp->isBoundsCheck(true); // To avoid LATCH warning
bodysp = ifp; bodysp = ifp;
} else if (VN_IS(nodep, Assert) || VN_IS(nodep, AssertIntrinsic)) { } else if (VN_IS(nodep, Assert) || VN_IS(nodep, AssertIntrinsic)) {
if (nodep->immediate()) { if (nodep->immediate()) {
@ -176,6 +178,7 @@ private:
if (failsp) failsp = newIfAssertOn(failsp, force); if (failsp) failsp = newIfAssertOn(failsp, force);
if (!failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed."); if (!failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed.");
ifp = new AstIf{nodep->fileline(), propp, passsp, failsp}; ifp = new AstIf{nodep->fileline(), propp, passsp, failsp};
ifp->isBoundsCheck(true); // To avoid LATCH warning
// It's more LIKELY that we'll take the nullptr if clause // It's more LIKELY that we'll take the nullptr if clause
// than the sim-killing else clause: // than the sim-killing else clause:
ifp->branchPred(VBranchPred::BP_LIKELY); ifp->branchPred(VBranchPred::BP_LIKELY);
@ -253,6 +256,7 @@ private:
AstIf* const checkifp AstIf* const checkifp
= new AstIf{nodep->fileline(), new AstLogNot{nodep->fileline(), ohot}, = new AstIf{nodep->fileline(), new AstLogNot{nodep->fileline(), ohot},
newFireAssert(nodep, "'unique if' statement violated"), newifp}; newFireAssert(nodep, "'unique if' statement violated"), newifp};
checkifp->isBoundsCheck(true); // To avoid LATCH warning
checkifp->branchPred(VBranchPred::BP_UNLIKELY); checkifp->branchPred(VBranchPred::BP_UNLIKELY);
nodep->replaceWith(checkifp); nodep->replaceWith(checkifp);
pushDeletep(nodep); pushDeletep(nodep);
@ -323,6 +327,7 @@ private:
nodep->fileline(), new AstLogNot{nodep->fileline(), ohot}, nodep->fileline(), new AstLogNot{nodep->fileline(), ohot},
newFireAssert(nodep, newFireAssert(nodep,
"synthesis parallel_case, but multiple matches found")}; "synthesis parallel_case, but multiple matches found")};
ifp->isBoundsCheck(true); // To avoid LATCH warning
ifp->branchPred(VBranchPred::BP_UNLIKELY); ifp->branchPred(VBranchPred::BP_UNLIKELY);
nodep->addNotParallelp(ifp); nodep->addNotParallelp(ifp);
} }
@ -425,6 +430,7 @@ private:
new AstEq{fl, new AstConst{fl, monNum}, new AstEq{fl, new AstConst{fl, monNum},
newMonitorNumVarRefp(nodep, VAccess::READ)}}, newMonitorNumVarRefp(nodep, VAccess::READ)}},
stmtsp}; stmtsp};
ifp->isBoundsCheck(true); // To avoid LATCH warning
ifp->branchPred(VBranchPred::BP_UNLIKELY); ifp->branchPred(VBranchPred::BP_UNLIKELY);
AstNode* const newp = new AstAlways{fl, VAlwaysKwd::ALWAYS, nullptr, ifp}; AstNode* const newp = new AstAlways{fl, VAlwaysKwd::ALWAYS, nullptr, ifp};
m_modp->addStmtsp(newp); m_modp->addStmtsp(newp);
@ -443,6 +449,7 @@ private:
// Add "always_comb if (__Vstrobe) begin $display(...); __Vstrobe = '0; end" // Add "always_comb if (__Vstrobe) begin $display(...); __Vstrobe = '0; end"
AstNode* const stmtsp = nodep; AstNode* const stmtsp = nodep;
AstIf* const ifp = new AstIf{fl, new AstVarRef{fl, varp, VAccess::READ}, stmtsp}; AstIf* const ifp = new AstIf{fl, new AstVarRef{fl, varp, VAccess::READ}, stmtsp};
ifp->isBoundsCheck(true); // To avoid LATCH warning
ifp->branchPred(VBranchPred::BP_UNLIKELY); ifp->branchPred(VBranchPred::BP_UNLIKELY);
AstNode* const newp = new AstAlwaysPostponed{fl, ifp}; AstNode* const newp = new AstAlwaysPostponed{fl, ifp};
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},

View File

@ -504,7 +504,7 @@ class AstNodeIf VL_NOT_FINAL : public AstNodeStmt {
// @astgen op3 := elsesp : List[AstNode] // @astgen op3 := elsesp : List[AstNode]
private: private:
VBranchPred m_branchPred; // Branch prediction as taken/untaken? VBranchPred m_branchPred; // Branch prediction as taken/untaken?
bool m_isBoundsCheck; // True if this if node was inserted for array bounds checking bool m_isBoundsCheck; // True if this if node is for assertion/bounds checking
protected: protected:
AstNodeIf(VNType t, FileLine* fl, AstNode* condp, AstNode* thensp, AstNode* elsesp) AstNodeIf(VNType t, FileLine* fl, AstNode* condp, AstNode* thensp, AstNode* elsesp)
: AstNodeStmt{t, fl} { : AstNodeStmt{t, fl} {

View File

@ -7,21 +7,27 @@ module test (
input [2:0] a, input [2:0] a,
input [3:0] c, input [3:0] c,
output reg [7:0] b output reg [7:0] o1,
output reg [7:0] o2
); );
integer i; integer i;
always @ (*) always @ (*) begin
begin
case(a) case(a)
{3'b000}: b = 8'd1; {3'b000}: o1 = 8'd1;
{3'b001}: {3'b001}:
for(i=0;i<4;i=i+1) b[i*2+:2] = 2'(c[i]); for(i=0;i<4;i=i+1) o1[i*2+:2] = 2'(c[i]);
{3'b010}: b = 8'd3; {3'b010}: o1 = 8'd3;
{3'b011}: b = 8'd4; {3'b011}: o1 = 8'd4;
default : b = 0; default : o1 = 0;
endcase endcase
end end
always_comb begin
unique if (a[0]) o2 = 1;
else if (a[1]) o2 = 2;
else o2 = 3;
end
endmodule endmodule