From f66df9e70ebdd9b54a7af667eb7c904047e35532 Mon Sep 17 00:00:00 2001 From: Nick Brereton <85175726+nbstrike@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:31:03 -0400 Subject: [PATCH] Fix `disable iff` imply-delay statement linking (#7337) --- src/V3AssertPre.cpp | 11 +++- .../t/t_property_disable_iff_midreset.py | 18 ++++++ .../t/t_property_disable_iff_midreset.v | 56 +++++++++++++++++++ 3 files changed, 82 insertions(+), 3 deletions(-) create mode 100755 test_regress/t/t_property_disable_iff_midreset.py create mode 100644 test_regress/t/t_property_disable_iff_midreset.v diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index cd8868632..f65ca8b24 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -493,7 +493,9 @@ private: } if (m_disableSeqIfp) { AstIf* const disableSeqIfp = m_disableSeqIfp->cloneTree(false); - disableSeqIfp->addThensp(nodep->nextp()->unlinkFrBackWithNext()); + AstNode* const continuationsp = nodep->nextp()->unlinkFrBackWithNext(); + // Keep continuation statements in a proper statement-list container. + disableSeqIfp->addThensp(new AstBegin{flp, "", continuationsp, true}); nodep->addNextHere(disableSeqIfp); } nodep->replaceWith(beginp); @@ -931,7 +933,7 @@ private: // */ } AstBegin* const bodyp = pexprp->bodyp(); AstNode* const origStmtsp = bodyp->stmtsp()->unlinkFrBackWithNext(); - AstIf* const guardp = new AstIf{flp, condp, origStmtsp}; + AstIf* const guardp = new AstIf{flp, condp, new AstBegin{flp, "", origStmtsp, true}}; bodyp->addStmtsp(guardp); nodep->replaceWith(pexprp); // Don't iterate pexprp here -- it was already iterated when created @@ -1043,11 +1045,14 @@ private: AstVar* const initialCntp = new AstVar{flp, VVarType::BLOCKTEMP, "__VinitialCnt", nodep->findBasicDType(VBasicDTypeKwd::UINT32)}; initialCntp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); - bodyp->stmtsp()->addHereThisAsNext(initialCntp); AstAssign* const assignp = new AstAssign{flp, new AstVarRef{flp, initialCntp, VAccess::WRITE}, readCntRefp->cloneTree(false)}; + // Prepend to the sequence body to keep statement list structure valid. + AstNode* const origStmtsp = bodyp->stmtsp()->unlinkFrBackWithNext(); + bodyp->addStmtsp(initialCntp); initialCntp->addNextHere(assignp); + assignp->addNextHere(origStmtsp); m_disableSeqIfp = new AstIf{flp, new AstEq{flp, new AstVarRef{flp, initialCntp, VAccess::READ}, diff --git a/test_regress/t/t_property_disable_iff_midreset.py b/test_regress/t/t_property_disable_iff_midreset.py new file mode 100755 index 000000000..35e44000c --- /dev/null +++ b/test_regress/t/t_property_disable_iff_midreset.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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. +# SPDX-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(timing_loop=True, verilator_flags2=['--assert', '--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_property_disable_iff_midreset.v b/test_regress/t/t_property_disable_iff_midreset.v new file mode 100644 index 000000000..e82e9ea46 --- /dev/null +++ b/test_regress/t/t_property_disable_iff_midreset.v @@ -0,0 +1,56 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// 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. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +module t ( + input clk +); + + int cyc = 0; + bit rst = 0; + bit start = 0; + bit done = 0; + + int fails_a = 0; + int fails_b = 0; + + // First launch at cyc==2 should be canceled by reset pulse in the middle. + assert property (@(posedge clk) disable iff (rst) (cyc == 2) |-> ##2 done) + else fails_a++; + + // Second launch at cyc==8 has no reset pulse in flight and should fail once. + assert property (@(posedge clk) disable iff (rst) (cyc == 8) |-> ##2 done) + else fails_b++; + + always @(posedge clk) begin + cyc <= cyc + 1; + + // Defaults + start <= 0; + done <= 0; + + if (cyc == 2) start <= 1; + if (cyc == 8) start <= 1; + + // Mid-window reset pulse for first launch. + if (cyc == 3) rst <= 1; + if (cyc == 4) rst <= 0; + + if (cyc == 16) begin + `checkd(fails_a, 0); + `checkd(fails_b, 1); + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule