diff --git a/include/verilated_timing.cpp b/include/verilated_timing.cpp index e189a443b..3a2272897 100644 --- a/include/verilated_timing.cpp +++ b/include/verilated_timing.cpp @@ -58,6 +58,12 @@ void VlDelayScheduler::resume() { #ifdef VL_DEBUG VL_DEBUG_IF(dump(); VL_DBG_MSGF(" Resuming delayed processes\n");); #endif + if (VL_UNLIKELY(m_context.gotFinish())) { + m_queue.clear(); + m_zeroDelayed.clear(); + m_zeroDelayesSwap.clear(); + return; + } bool resumed = false; while (!m_queue.empty() && (m_queue.cbegin()->first == m_context.time())) { @@ -80,6 +86,11 @@ void VlDelayScheduler::resume() { } void VlDelayScheduler::resumeZeroDelay() { + if (VL_UNLIKELY(m_context.gotFinish())) { + m_zeroDelayed.clear(); + m_zeroDelayesSwap.clear(); + return; + } m_zeroDelayesSwap.swap(m_zeroDelayed); for (VlCoroutineHandle& handle : m_zeroDelayesSwap) handle.resume(); m_zeroDelayesSwap.clear(); @@ -120,6 +131,12 @@ void VlTriggerScheduler::resume(const char* eventDescription) { VL_DEBUG_IF(dump(eventDescription); VL_DBG_MSGF(" Resuming processes waiting for %s\n", eventDescription);); #endif + if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) { + m_toResume.clear(); + m_fired.clear(); + m_awaiting.clear(); + return; + } for (VlCoroutineHandle& coro : m_toResume) coro.resume(); m_toResume.clear(); } @@ -136,6 +153,11 @@ void VlTriggerScheduler::moveToResumeQueue(const char* eventDescription) { }); } #endif + if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) { + m_toResume.clear(); + m_fired.clear(); + return; + } std::swap(m_fired, m_toResume); } @@ -151,6 +173,11 @@ void VlTriggerScheduler::ready(const char* eventDescription) { }); } #endif + if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) { + m_fired.clear(); + m_awaiting.clear(); + return; + } const size_t expectedSize = m_fired.size() + m_awaiting.size(); if (m_fired.capacity() < expectedSize) m_fired.reserve(expectedSize * 2); m_fired.insert(m_fired.end(), std::make_move_iterator(m_awaiting.begin()), @@ -190,6 +217,14 @@ void VlTriggerScheduler::dump(const char* eventDescription) const { // VlDynamicTriggerScheduler:: Methods bool VlDynamicTriggerScheduler::evaluate() { + if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) { + m_anyTriggered = false; + m_suspended.clear(); + m_evaluated.clear(); + m_triggered.clear(); + m_post.clear(); + return false; + } m_anyTriggered = false; VL_DEBUG_IF(dump();); std::swap(m_suspended, m_evaluated); @@ -206,6 +241,10 @@ void VlDynamicTriggerScheduler::doPostUpdates() { VL_DBG_MSGF(" - "); susp.dump(); }); + if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) { + m_post.clear(); + return; + } for (auto& coro : m_post) coro.resume(); m_post.clear(); } @@ -217,6 +256,10 @@ void VlDynamicTriggerScheduler::resume() { VL_DBG_MSGF(" - "); susp.dump(); }); + if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) { + m_triggered.clear(); + return; + } for (auto& coro : m_triggered) coro.resume(); m_triggered.clear(); } diff --git a/include/verilated_timing.h b/include/verilated_timing.h index 157865499..4f4d6abac 100644 --- a/include/verilated_timing.h +++ b/include/verilated_timing.h @@ -190,10 +190,11 @@ public: bool empty() const { return m_queue.empty() && m_zeroDelayed.empty(); } // Are there coroutines to resume at the current simulation time? bool awaitingCurrentTime() const { - return (!m_queue.empty() && (m_queue.cbegin()->first <= m_context.time())); + return !m_context.gotFinish() + && (!m_queue.empty() && (m_queue.cbegin()->first <= m_context.time())); } // Are there coroutines to resume in the inactive region after a #0 delay? - bool awaitingZeroDelay() const { return !m_zeroDelayed.empty(); } + bool awaitingZeroDelay() const { return !m_context.gotFinish() && !m_zeroDelayed.empty(); } #ifdef VL_DEBUG void dump() const; #endif diff --git a/test_regress/t/t_finish_stops_nonfinal.py b/test_regress/t/t_finish_stops_nonfinal.py new file mode 100755 index 000000000..1365809b3 --- /dev/null +++ b/test_regress/t/t_finish_stops_nonfinal.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(make_main=False, verilator_flags2=["--binary"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_finish_stops_nonfinal.v b/test_regress/t/t_finish_stops_nonfinal.v new file mode 100644 index 000000000..3dc43c9fa --- /dev/null +++ b/test_regress/t/t_finish_stops_nonfinal.v @@ -0,0 +1,26 @@ +// 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; + bit clk = 0; + int cyc = 0; + + initial forever #1 clk = ~clk; + + always @(posedge clk) begin + cyc = cyc + 1; + if (cyc >= 10) $finish; + end + + always @(posedge clk) begin + fork + begin + while ($sampled(cyc) != 99) #1; + if (cyc >= 10) $stop; + end + join_none + end +endmodule diff --git a/test_regress/t/t_timing_debug1.out b/test_regress/t/t_timing_debug1.out index 30a33788b..2f38fc40e 100644 --- a/test_regress/t/t_timing_debug1.out +++ b/test_regress/t/t_timing_debug1.out @@ -4159,8 +4159,6 @@ -V{t#,#}+ Vt_timing_debug1___024root___trigger_anySet__act -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#} No process to resume waiting for @(posedge t.clk1) --V{t#,#} Not triggered processes waiting for @(posedge t.clk1): --V{t#,#} - Process waiting at t/t_timing_sched.v:18 -V{t#,#} Resuming processes waiting for @(posedge t.clk1) -V{t#,#} No process to resume waiting for @(posedge t.clk2) -V{t#,#} Resuming processes waiting for @(posedge t.clk2) diff --git a/test_regress/t/t_timing_debug2.out b/test_regress/t/t_timing_debug2.out index c84f538fa..bff344460 100644 --- a/test_regress/t/t_timing_debug2.out +++ b/test_regress/t/t_timing_debug2.out @@ -1666,7 +1666,6 @@ -V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass::__VnoInFunc_flip -V{t#,#}+ Vt_timing_debug2___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers_vec__act --V{t#,#} No suspended processes waiting for dynamic trigger evaluation -V{t#,#}+ Vt_timing_debug2___024root___timing_ready -V{t#,#}+ Vt_timing_debug2___024root___trigger_orInto__act_vec_vec -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act @@ -1681,7 +1680,6 @@ -V{t#,#}+ Vt_timing_debug2___024root___trigger_clear__act -V{t#,#}+ Vt_timing_debug2___024root___eval_phase__act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers_vec__act --V{t#,#} No suspended processes waiting for dynamic trigger evaluation -V{t#,#}+ Vt_timing_debug2___024root___timing_ready -V{t#,#}+ Vt_timing_debug2___024root___trigger_orInto__act_vec_vec -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act