From b70696fc0ac9a03d9dd8712e0d0fc0f2ce8465cc Mon Sep 17 00:00:00 2001 From: Artur Bieniek Date: Fri, 31 Jul 2026 03:39:21 +0200 Subject: [PATCH] Fix $finish continuing event loop (#7267) (#7950) Fixes #7267, --- include/verilated.cpp | 3 +++ include/verilated.h | 10 ++++++++ src/V3SenExprBuilder.h | 13 +++++++++- test_regress/t/t_finish_stops_nonfinal2.py | 18 +++++++++++++ test_regress/t/t_finish_stops_nonfinal2.v | 30 ++++++++++++++++++++++ 5 files changed, 73 insertions(+), 1 deletion(-) create mode 100755 test_regress/t/t_finish_stops_nonfinal2.py create mode 100644 test_regress/t/t_finish_stops_nonfinal2.v diff --git a/include/verilated.cpp b/include/verilated.cpp index 92de3fd29..540f8ee22 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -243,8 +243,11 @@ void vl_warn(const char* filename, int linenum, const char* hier, const char* ms // Wrapper to call certain functions via messages when multithreaded void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE { + VerilatedContext* const contextp = Verilated::threadContextp(); + contextp->finishPendingInc(); VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { // vl_finish(filename, linenum, hier); + contextp->finishPendingDec(); }}); } diff --git a/include/verilated.h b/include/verilated.h index b3ce78068..b8fc95e76 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -445,6 +445,8 @@ protected: struct NonSerialized final { // Non-serialized information // These are reloaded from on command-line settings, so do not need to persist // Fast path + // A worker queues $finish before the main thread callback can set m_gotFinish. + std::atomic m_finishPending{0}; // Number of queued $finish callbacks bool m_executingFinal = false; // Running generated final() code uint64_t m_profExecStart = 1; // +prof+exec+start time uint32_t m_profExecWindow = 2; // +prof+exec+window size @@ -684,6 +686,14 @@ public: // METHODS - public but for internal use only + // Internal: Track $finish callbacks queued by worker threads + bool finishPending() const VL_MT_SAFE { return m_ns.m_finishPending.load() != 0; } + void finishPendingInc() VL_MT_SAFE { ++m_ns.m_finishPending; } + void finishPendingDec() VL_MT_SAFE { + const uint32_t previous = m_ns.m_finishPending.fetch_sub(1); + assert(previous > 0); + } + // Internal: access to implementation class VerilatedContextImp* impp() VL_MT_SAFE { return reinterpret_cast(this); } const VerilatedContextImp* impp() const VL_MT_SAFE { diff --git a/src/V3SenExprBuilder.h b/src/V3SenExprBuilder.h index ac9abf196..a0271fcfd 100644 --- a/src/V3SenExprBuilder.h +++ b/src/V3SenExprBuilder.h @@ -290,7 +290,18 @@ private: AstCMethodHard* const callp = new AstCMethodHard{flp, currp(), VCMethod::EVENT_IS_FIRED}; callp->dtypeSetBit(); - return {wrapExprWithNullCheck(flp, callp, baseClassRefp), false}; + AstNodeExpr* const firedp = wrapExprWithNullCheck(flp, callp, baseClassRefp); + if (const AstVarRef* const refp = VN_CAST(senp, VarRef)) { + // The private NBA event must run so pending assignments finish settling. + if (refp->varScopep() == v3Global.rootp()->nbaEventp()) return {firedp, false}; + } + // Do not trigger user named-event processes after $finish. + AstNodeExpr* const notFinishp + = new AstCExpr{flp, + "VL_LIKELY(!vlSymsp->_vm_contextp__->gotFinish() && " + "!vlSymsp->_vm_contextp__->finishPending())", + 1}; + return {new AstLogAnd{flp, notFinishp, firedp}, false}; } case VEdgeType::ET_TRUE: // return {currp(), false}; diff --git a/test_regress/t/t_finish_stops_nonfinal2.py b/test_regress/t/t_finish_stops_nonfinal2.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_finish_stops_nonfinal2.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(verilator_flags2=["--binary"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_finish_stops_nonfinal2.v b/test_regress/t/t_finish_stops_nonfinal2.v new file mode 100644 index 000000000..350311476 --- /dev/null +++ b/test_regress/t/t_finish_stops_nonfinal2.v @@ -0,0 +1,30 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2026 Antmicro +// SPDX-License-Identifier: CC0-1.0 + +module t; + int cyc; + event e; + + always @(e) begin + $display("e=%0d", e.triggered); + ->e; + cyc = cyc + 1; + if (cyc >= 10) begin + $display("Fin"); + $finish; + end + end + + initial begin + #1; + ->e; + #1; + end + + final begin + if (cyc != 10) $stop; + end +endmodule