diff --git a/Changes b/Changes index b942c54ac..528fc7bf7 100644 --- a/Changes +++ b/Changes @@ -25,7 +25,7 @@ Verilator 5.001 devel * Introduce a new combinational logic optimizer (DFG), that can yield significant performance improvements on some designs. [Geza Lore, Shunyao CAD] * 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 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 --get-supported to determine what features are in Verilator. * 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 LSB error on --hierarchical submodules (#3539). [danbone] * Fix $display of fixed-width numbers (#3565). [Iztok Jeras] diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index bc49f8834..40d9f609f 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -98,7 +98,7 @@ private: // 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. FileLine* const fl = nodep->fileline(); - AstNode* const newp = new AstIf{ + AstNodeIf* const newp = new AstIf{ fl, (force ? new AstConst{fl, AstConst::BitTrue{}} : // If assertions are off, have constant propagation rip them out later @@ -108,6 +108,7 @@ private: new AstCMath{fl, "vlSymsp->_vm_contextp__->assertOn()", 1}) : static_cast(new AstConst{fl, AstConst::BitFalse{}}))), nodep}; + newp->isBoundsCheck(true); // To avoid LATCH warning newp->user1(true); // Don't assert/cover this if return newp; } @@ -164,6 +165,7 @@ private: if (bodysp && passsp) bodysp = bodysp->addNext(passsp); ifp = new AstIf{nodep->fileline(), propp, bodysp}; + ifp->isBoundsCheck(true); // To avoid LATCH warning bodysp = ifp; } else if (VN_IS(nodep, Assert) || VN_IS(nodep, AssertIntrinsic)) { if (nodep->immediate()) { @@ -176,6 +178,7 @@ private: if (failsp) failsp = newIfAssertOn(failsp, force); if (!failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed."); 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 // than the sim-killing else clause: ifp->branchPred(VBranchPred::BP_LIKELY); @@ -253,6 +256,7 @@ private: AstIf* const checkifp = new AstIf{nodep->fileline(), new AstLogNot{nodep->fileline(), ohot}, newFireAssert(nodep, "'unique if' statement violated"), newifp}; + checkifp->isBoundsCheck(true); // To avoid LATCH warning checkifp->branchPred(VBranchPred::BP_UNLIKELY); nodep->replaceWith(checkifp); pushDeletep(nodep); @@ -323,6 +327,7 @@ private: nodep->fileline(), new AstLogNot{nodep->fileline(), ohot}, newFireAssert(nodep, "synthesis parallel_case, but multiple matches found")}; + ifp->isBoundsCheck(true); // To avoid LATCH warning ifp->branchPred(VBranchPred::BP_UNLIKELY); nodep->addNotParallelp(ifp); } @@ -425,6 +430,7 @@ private: new AstEq{fl, new AstConst{fl, monNum}, newMonitorNumVarRefp(nodep, VAccess::READ)}}, stmtsp}; + ifp->isBoundsCheck(true); // To avoid LATCH warning ifp->branchPred(VBranchPred::BP_UNLIKELY); AstNode* const newp = new AstAlways{fl, VAlwaysKwd::ALWAYS, nullptr, ifp}; m_modp->addStmtsp(newp); @@ -443,6 +449,7 @@ private: // Add "always_comb if (__Vstrobe) begin $display(...); __Vstrobe = '0; end" AstNode* const stmtsp = nodep; 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); AstNode* const newp = new AstAlwaysPostponed{fl, ifp}; stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 764dcfaaf..3e2fd6430 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -504,7 +504,7 @@ class AstNodeIf VL_NOT_FINAL : public AstNodeStmt { // @astgen op3 := elsesp : List[AstNode] private: 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: AstNodeIf(VNType t, FileLine* fl, AstNode* condp, AstNode* thensp, AstNode* elsesp) : AstNodeStmt{t, fl} { diff --git a/test_regress/t/t_lint_latch_4.v b/test_regress/t/t_lint_latch_4.v index e7182f4ca..9f5504dc0 100644 --- a/test_regress/t/t_lint_latch_4.v +++ b/test_regress/t/t_lint_latch_4.v @@ -7,21 +7,27 @@ module test ( input [2:0] a, input [3:0] c, - output reg [7:0] b + output reg [7:0] o1, + output reg [7:0] o2 ); - integer i; + integer i; - always @ (*) - begin - case(a) - {3'b000}: b = 8'd1; - {3'b001}: - for(i=0;i<4;i=i+1) b[i*2+:2] = 2'(c[i]); - {3'b010}: b = 8'd3; - {3'b011}: b = 8'd4; - default : b = 0; - endcase - end + always @ (*) begin + case(a) + {3'b000}: o1 = 8'd1; + {3'b001}: + for(i=0;i<4;i=i+1) o1[i*2+:2] = 2'(c[i]); + {3'b010}: o1 = 8'd3; + {3'b011}: o1 = 8'd4; + default : o1 = 0; + endcase + end + + always_comb begin + unique if (a[0]) o2 = 1; + else if (a[1]) o2 = 2; + else o2 = 3; + end endmodule