From 4756d21cc09ef765c6566680c008bb85574196ad Mon Sep 17 00:00:00 2001 From: Nick Brereton Date: Mon, 23 Feb 2026 10:00:47 -0500 Subject: [PATCH 01/17] Implement disable task by name, add regression testing. --- src/V3LinkJump.cpp | 65 +++- test_regress/t/t_disable_bad.out | 5 +- test_regress/t/t_disable_task_by_name.py | 18 + test_regress/t/t_disable_task_by_name.v | 354 ++++++++++++++++++ test_regress/t/t_disable_task_scope_bad.out | 20 + test_regress/t/t_disable_task_scope_bad.py | 17 + test_regress/t/t_disable_task_scope_bad.v | 42 +++ test_regress/t/t_disable_task_target_bad.out | 11 + ..._unsup.py => t_disable_task_target_bad.py} | 4 +- test_regress/t/t_disable_task_target_bad.v | 26 ++ test_regress/t/t_disable_task_unsup.out | 8 - test_regress/t/t_disable_task_unsup.v | 65 ---- 12 files changed, 547 insertions(+), 88 deletions(-) create mode 100644 test_regress/t/t_disable_task_by_name.py create mode 100644 test_regress/t/t_disable_task_by_name.v create mode 100644 test_regress/t/t_disable_task_scope_bad.out create mode 100644 test_regress/t/t_disable_task_scope_bad.py create mode 100644 test_regress/t/t_disable_task_scope_bad.v create mode 100644 test_regress/t/t_disable_task_target_bad.out rename test_regress/t/{t_disable_task_unsup.py => t_disable_task_target_bad.py} (85%) mode change 100755 => 100644 create mode 100644 test_regress/t/t_disable_task_target_bad.v delete mode 100644 test_regress/t/t_disable_task_unsup.out delete mode 100644 test_regress/t/t_disable_task_unsup.v diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index 0fd82701e..ec50ae7ca 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -37,6 +37,7 @@ #include "V3Error.h" #include "V3UniqueNames.h" +#include #include VL_DEFINE_DEBUG_FUNCTIONS; @@ -65,6 +66,7 @@ class LinkJumpVisitor final : public VNVisitor { std::vector m_blockStack; // All begin blocks above current node V3UniqueNames m_queueNames{ "__VprocessQueue"}; // Names for queues needed for 'disable' handling + std::unordered_map m_taskDisableQueues; // Per-task process queues // METHODS // Get (and create if necessary) the JumpBlock for this statement @@ -177,6 +179,42 @@ class LinkJumpVisitor final : public VNVisitor { return new AstStmtExpr{ fl, new AstMethodCall{fl, queueRefp, "push_back", new AstArg{fl, "", processSelfp}}}; } + static AstStmtExpr* getQueueKillStmtp(FileLine* const fl, AstVar* const processQueuep) { + AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp(); + AstClass* const processClassp + = VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class); + AstVarRef* const queueRefp = new AstVarRef{fl, topPkgp, processQueuep, VAccess::READWRITE}; + AstTaskRef* const killQueueCall + = new AstTaskRef{fl, VN_AS(getMemberp(processClassp, "killQueue"), Task), + new AstArg{fl, "", queueRefp}}; + killQueueCall->classOrPackagep(processClassp); + return new AstStmtExpr{fl, killQueueCall}; + } + AstVar* getOrCreateTaskDisableQueuep(AstTask* const taskp, FileLine* const fl) { + const auto it = m_taskDisableQueues.find(taskp); + if (it != m_taskDisableQueues.end()) return it->second; + + AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp(); + AstClass* const processClassp + = VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class); + AstVar* const processQueuep = new AstVar{ + fl, VVarType::VAR, m_queueNames.get(taskp->name()), VFlagChildDType{}, + new AstQueueDType{fl, VFlagChildDType{}, + new AstClassRefDType{fl, processClassp, nullptr}, nullptr}}; + processQueuep->lifetime(VLifetime::STATIC_EXPLICIT); + topPkgp->addStmtsp(processQueuep); + + AstVarRef* const queueWriteRefp + = new AstVarRef{fl, topPkgp, processQueuep, VAccess::WRITE}; + AstStmtExpr* const pushCurrentProcessp = getQueuePushProcessSelfp(queueWriteRefp); + if (taskp->stmtsp()) { + taskp->stmtsp()->addHereThisAsNext(pushCurrentProcessp); + } else { + taskp->addStmtsp(pushCurrentProcessp); + } + m_taskDisableQueues.emplace(taskp, processQueuep); + return processQueuep; + } void handleDisableOnFork(AstDisable* const nodep, const std::vector& forks) { // The support utilizes the process::kill()` method. For each `disable` a queue of // processes is declared. At the beginning of each fork that can be disabled, its process @@ -215,12 +253,7 @@ class LinkJumpVisitor final : public VNVisitor { beginp->stmtsp()->addHereThisAsNext(pushCurrentProcessp); } } - AstVarRef* const queueRefp = new AstVarRef{fl, topPkgp, processQueuep, VAccess::READWRITE}; - AstTaskRef* const killQueueCall - = new AstTaskRef{fl, VN_AS(getMemberp(processClassp, "killQueue"), Task), - new AstArg{fl, "", queueRefp}}; - killQueueCall->classOrPackagep(processClassp); - AstStmtExpr* const killStmtp = new AstStmtExpr{fl, killQueueCall}; + AstStmtExpr* const killStmtp = getQueueKillStmtp(fl, processQueuep); nodep->addNextHere(killStmtp); // 'process::kill' does not immediately kill the current process @@ -417,9 +450,23 @@ class LinkJumpVisitor final : public VNVisitor { void visit(AstDisable* nodep) override { UINFO(8, " DISABLE " << nodep); AstNode* const targetp = nodep->targetp(); - UASSERT_OBJ(targetp, nodep, "Unlinked disable statement"); - if (VN_IS(targetp, Task)) { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling task by name"); + if (!targetp) { + // Linking errors on the disable target are already reported upstream. + // Drop this node to avoid cascading into an internal assertion. + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + return; + } + if (AstTask* const taskp = VN_CAST(targetp, Task)) { + AstVar* const processQueuep = getOrCreateTaskDisableQueuep(taskp, nodep->fileline()); + AstStmtExpr* const killStmtp = getQueueKillStmtp(nodep->fileline(), processQueuep); + nodep->addNextHere(killStmtp); + + // process::kill does not terminate the currently running process immediately. + // If we disable the current task by name from inside itself, jump to its end. + if (m_ftaskp == taskp) { + AstJumpBlock* const blockp = getJumpBlock(taskp, false); + killStmtp->addNextHere(new AstJumpGo{nodep->fileline(), blockp}); + } } else if (AstFork* const forkp = VN_CAST(targetp, Fork)) { std::vector forks; for (AstBegin* itemp = forkp->forksp(); itemp; itemp = VN_AS(itemp->nextp(), Begin)) { diff --git a/test_regress/t/t_disable_bad.out b/test_regress/t/t_disable_bad.out index d708f635c..90b807ff6 100644 --- a/test_regress/t/t_disable_bad.out +++ b/test_regress/t/t_disable_bad.out @@ -2,7 +2,4 @@ 9 | disable abcd; | ^~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: Internal Error: t/t_disable_bad.v:9:5: ../V3LinkJump.cpp:#: Unlinked disable statement - 9 | disable abcd; - | ^~~~~~~ - ... This fatal error may be caused by the earlier error(s); resolve those first. +%Error: Exiting due to diff --git a/test_regress/t/t_disable_task_by_name.py b/test_regress/t/t_disable_task_by_name.py new file mode 100644 index 000000000..390bba1b8 --- /dev/null +++ b/test_regress/t/t_disable_task_by_name.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=["--timing"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_disable_task_by_name.v b/test_regress/t/t_disable_task_by_name.v new file mode 100644 index 000000000..d2f86c909 --- /dev/null +++ b/test_regress/t/t_disable_task_by_name.v @@ -0,0 +1,354 @@ +// 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 + +int x = 0; +int y = 0; + +int self_entry = 0; +int self_after_disable = 0; + +int par_started = 0; +int par_finished = 0; +int par_parent_continued = 0; + +class StaticCls; + static int ticks = 0; + + static task run(); + forever begin + #10; + ticks++; + end + endtask + + static task stop(); + disable run; + endtask +endclass + +task increment_x; + x++; + #2; + x++; +endtask + +task increment_y; + y++; + #5; + y++; +endtask + +task self_stop; + self_entry = 1; + disable self_stop; + self_after_disable = 1; +endtask + +task worker; + par_started++; + #20; + par_finished++; +endtask + +task parent_disables_worker; + fork + worker(); + worker(); + join_none + #5; + disable worker; + par_parent_continued = 1; +endtask + +class Cls; + int m_time = 0; + + task get_and_send(); + forever begin + #10; + m_time += 10; + end + endtask + + task post_shutdown_phase(); + disable get_and_send; + endtask +endclass + +class NamedA; + int v = 0; + + task run(); + forever begin + #10; + v++; + end + endtask + + task stop(); + disable run; + endtask +endclass + +class NamedB; + int v = 0; + + task run(); + forever begin + #10; + v++; + end + endtask +endclass + +class BaseNamed; + int v = 0; + + task run(); + forever begin + #10; + v++; + end + endtask +endclass + +class ChildNamed extends BaseNamed; + int child_v = 0; + + task run(); + forever begin + #10; + child_v++; + end + endtask + + task stop(); + disable run; + endtask +endclass + +package PkgRun; + int v = 0; + + task run(); + forever begin + #10; + v++; + end + endtask + + task stop(); + disable run; + endtask +endpackage + +interface Ifc; + int v = 0; + + task run(); + forever begin + #10; + v++; + end + endtask + + task stop(); + disable run; + endtask +endinterface + +program Prog; + int v = 0; + + task run(); + forever begin + #10; + v++; + end + endtask + + task stop(); + disable run; + endtask +endprogram + +module WorkerMod; + int m = 0; + + task run(); + forever begin + #10; + m++; + end + endtask +endmodule + +module t; + Ifc ifc1(); + Prog prog1(); + WorkerMod mod1(); + + initial begin + automatic Cls c = new; + automatic NamedA a = new; + automatic NamedB b = new; + automatic BaseNamed base_obj = new; + automatic ChildNamed child_obj = new; + int m_time_before_shutdown; + int mod1_before; + int a_before; + int b_before; + int static_before; + int base_before; + int child_before; + int ifc_before; + int prog_before; + int pkg_before; + + // Module task disabled by sibling process in a fork + fork + increment_x(); + #1 disable increment_x; + join_none + #10; + if (x != 1) $stop; + + // Another basic module-task disable-by-name case + fork + increment_y(); + #3 disable increment_y; + join_none + #10; + if (y != 1) $stop; + + // Self-disable in task by name + self_stop(); + if (self_entry != 1) $stop; + if (self_after_disable != 0) $stop; + + // Same task launched in parallel, disabled from parent task context + parent_disables_worker(); + #30; + if (par_started != 2) $stop; + if (par_finished != 0) $stop; + if (par_parent_continued != 1) $stop; + + // Same task launched in parallel, disabled from sibling process context + par_started = 0; + par_finished = 0; + fork + worker(); + worker(); + begin + #5; + disable worker; + end + join_none + #30; + if (par_started != 2) $stop; + if (par_finished != 0) $stop; + + // Static class task disabled by name from another static task + fork + StaticCls::run(); + StaticCls::run(); + join_none + #25; + if (StaticCls::ticks == 0) $stop; + static_before = StaticCls::ticks; + StaticCls::stop(); + #30; + if (StaticCls::ticks != static_before) $stop; + + // Same task name in different class scopes: disable only one scope + fork + a.run(); + b.run(); + join_none + #25; + if (a.v == 0 || b.v == 0) $stop; + a_before = a.v; + b_before = b.v; + a.stop(); + #30; + if (a.v != a_before) $stop; + if (b.v <= b_before) $stop; + + // Same task name across inheritance scopes: disable only derived task + fork + base_obj.run(); + child_obj.run(); + join_none + #25; + if (base_obj.v == 0 || child_obj.child_v == 0) $stop; + base_before = base_obj.v; + child_before = child_obj.child_v; + child_obj.stop(); + #30; + if (child_obj.child_v != child_before) $stop; + if (base_obj.v <= base_before) $stop; + + // Interface task disabled by name through interface scope + fork + ifc1.run(); + join_none + #25; + if (ifc1.v == 0) $stop; + ifc_before = ifc1.v; + ifc1.stop(); + #30; + if (ifc1.v != ifc_before) $stop; + + // Program task disabled by name through program scope + fork + prog1.run(); + join_none + #25; + if (prog1.v == 0) $stop; + prog_before = prog1.v; + prog1.stop(); + #30; + if (prog1.v != prog_before) $stop; + + // Package task disabled by name through package scope + fork + PkgRun::run(); + join_none + #25; + if (PkgRun::v == 0) $stop; + pkg_before = PkgRun::v; + PkgRun::stop(); + #30; + if (PkgRun::v != pkg_before) $stop; + + // Dotted hierarchical task disable of module task by instance path + fork + mod1.run(); + join_none + #25; + if (mod1.m == 0) $stop; + mod1_before = mod1.m; + disable mod1.run; + #30; + if (mod1.m != mod1_before) $stop; + + // Class task disabled by name from outside that task + fork + c.get_and_send(); + join_none + #35; + if (c.m_time == 0) $fatal; + m_time_before_shutdown = c.m_time; + c.post_shutdown_phase(); + #30; + if (c.m_time != m_time_before_shutdown) $fatal; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_disable_task_scope_bad.out b/test_regress/t/t_disable_task_scope_bad.out new file mode 100644 index 000000000..2fa1c2987 --- /dev/null +++ b/test_regress/t/t_disable_task_scope_bad.out @@ -0,0 +1,20 @@ +%Error: t/t_disable_task_scope_bad.v:34:18: Can't find definition of 'missing_task' in dotted block/task: 'ifc1.missing_task' + 34 | disable ifc1.missing_task; + | ^~~~~~~~~~~~ + ... Known scopes under 'ifc1': + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_disable_task_scope_bad.v:35:19: Can't find definition of 'missing_task' in dotted block/task: 'prog1.missing_task' + 35 | disable prog1.missing_task; + | ^~~~~~~~~~~~ + ... Known scopes under 'prog1': +%Error: t/t_disable_task_scope_bad.v:36:26: Can't find definition of 'missing_task' in dotted block/task: 'outer1.inner.missing_task' + 36 | disable outer1.inner.missing_task; + | ^~~~~~~~~~~~ + ... Known scopes under 'outer1.inner': +%Error: t/t_disable_task_scope_bad.v:37:18: Found definition of 'ifc1.data' as a VAR but expected a block/task + 37 | disable ifc1.data; + | ^~~~ +%Error: t/t_disable_task_scope_bad.v:38:19: Found definition of 'prog1.data' as a VAR but expected a block/task + 38 | disable prog1.data; + | ^~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_disable_task_scope_bad.py b/test_regress/t/t_disable_task_scope_bad.py new file mode 100644 index 000000000..40c047e86 --- /dev/null +++ b/test_regress/t/t_disable_task_scope_bad.py @@ -0,0 +1,17 @@ +#!/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: 2024-2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() + diff --git a/test_regress/t/t_disable_task_scope_bad.v b/test_regress/t/t_disable_task_scope_bad.v new file mode 100644 index 000000000..8afd8b52c --- /dev/null +++ b/test_regress/t/t_disable_task_scope_bad.v @@ -0,0 +1,42 @@ +// 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 + +interface Ifc; + task run; + endtask + int data = 0; +endinterface + +program Prog; + task run; + endtask + int data = 0; +endprogram + +module Inner; + task run; + endtask +endmodule + +module Outer; + Inner inner(); +endmodule + +module t; + Ifc ifc1(); + Prog prog1(); + Outer outer1(); + + initial begin + disable ifc1.missing_task; + disable prog1.missing_task; + disable outer1.inner.missing_task; + disable ifc1.data; + disable prog1.data; + end +endmodule diff --git a/test_regress/t/t_disable_task_target_bad.out b/test_regress/t/t_disable_task_target_bad.out new file mode 100644 index 000000000..613440744 --- /dev/null +++ b/test_regress/t/t_disable_task_target_bad.out @@ -0,0 +1,11 @@ +%Error: t/t_disable_task_target_bad.v:21:13: Found definition of 'foo' as a VAR but expected a block/task + 21 | disable foo; + | ^~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_disable_task_target_bad.v:22:13: Found definition of 'c' as a VAR but expected a block/task + 22 | disable c.run; + | ^ +%Error: t/t_disable_task_target_bad.v:22:15: Can't find definition of block/task: 'run' + 22 | disable c.run; + | ^~~ +%Error: Exiting due to diff --git a/test_regress/t/t_disable_task_unsup.py b/test_regress/t/t_disable_task_target_bad.py old mode 100755 new mode 100644 similarity index 85% rename from test_regress/t/t_disable_task_unsup.py rename to test_regress/t/t_disable_task_target_bad.py index 382ad0d44..38cf36b43 --- a/test_regress/t/t_disable_task_unsup.py +++ b/test_regress/t/t_disable_task_target_bad.py @@ -4,12 +4,12 @@ # 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: 2024 Wilson Snyder +# SPDX-FileCopyrightText: 2026 Wilson Snyder # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 import vltest_bootstrap -test.scenarios('simulator') +test.scenarios('linter') test.lint(fails=True, expect_filename=test.golden_filename) diff --git a/test_regress/t/t_disable_task_target_bad.v b/test_regress/t/t_disable_task_target_bad.v new file mode 100644 index 000000000..50116fff5 --- /dev/null +++ b/test_regress/t/t_disable_task_target_bad.v @@ -0,0 +1,26 @@ +// 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 + +class Cls; + task run; + endtask +endclass + +module t; + task foo; + endtask + + Cls c; + + initial begin + int foo; + c = new; + disable foo; + disable c.run; + end +endmodule diff --git a/test_regress/t/t_disable_task_unsup.out b/test_regress/t/t_disable_task_unsup.out deleted file mode 100644 index 024eadd80..000000000 --- a/test_regress/t/t_disable_task_unsup.out +++ /dev/null @@ -1,8 +0,0 @@ -%Error-UNSUPPORTED: t/t_disable_task_unsup.v:37:10: Unsupported: disabling task by name - 37 | #1 disable increment_x; - | ^~~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_disable_task_unsup.v:26:5: Unsupported: disabling task by name - 26 | disable get_and_send; - | ^~~~~~~ -%Error: Exiting due to diff --git a/test_regress/t/t_disable_task_unsup.v b/test_regress/t/t_disable_task_unsup.v deleted file mode 100644 index 6331ba64f..000000000 --- a/test_regress/t/t_disable_task_unsup.v +++ /dev/null @@ -1,65 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain -// SPDX-FileCopyrightText: 2025 Antmicro -// SPDX-License-Identifier: CC0-1.0 - -int x = 0; - -task increment_x; - x++; - #2; - x++; -endtask - -class driver; - int m_time = 0; - - task get_and_send(); - forever begin - #10; - m_time += 10; - end - endtask - - task post_shutdown_phase(); - disable get_and_send; - endtask -endclass - -module t; - - driver c; - - initial begin - fork - increment_x(); - #1 disable increment_x; - join - - if (x != 1) $stop; - - c = new; - fork - c.get_and_send; - join_none - if (c.m_time != 0) $stop; - - #11; - if ($time != 12) $stop; - if (c.m_time != 10) $stop; - - #20; - if ($time != 32) $stop; - if (c.m_time != 30) $stop; - c.post_shutdown_phase; - - #20; - if ($time != 52) $stop; - if (c.m_time != 30) $stop; - - $write("*-* All Finished *-*\n"); - $finish; - end - -endmodule From c96f1a2a0681bb6c4edd11fd1d81d7af82e3cf38 Mon Sep 17 00:00:00 2001 From: Nick Brereton Date: Mon, 23 Feb 2026 10:11:52 -0500 Subject: [PATCH 02/17] Release fork join/join_any on killed task processes. --- include/verilated_timing.cpp | 20 +++++++ include/verilated_timing.h | 2 + include/verilated_types.h | 6 +- src/V3AstAttr.h | 2 + src/V3Timing.cpp | 18 ++++++ test_regress/t/t_disable_task_join.py | 19 +++++++ test_regress/t/t_disable_task_join.v | 80 +++++++++++++++++++++++++++ 7 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 test_regress/t/t_disable_task_join.py create mode 100644 test_regress/t/t_disable_task_join.v diff --git a/include/verilated_timing.cpp b/include/verilated_timing.cpp index 51dd88b9a..61d66b2f0 100644 --- a/include/verilated_timing.cpp +++ b/include/verilated_timing.cpp @@ -238,6 +238,26 @@ void VlDynamicTriggerScheduler::dump() const { //====================================================================== // VlForkSync:: Methods +void VlProcess::forkSyncOnKill(VlForkSync* forkSyncp) { + m_forkSyncOnKillp = forkSyncp; + m_forkSyncOnKillDone = false; +} + +void VlProcess::state(int s) { + if (s == KILLED && m_state != KILLED && m_state != FINISHED && m_forkSyncOnKillp + && !m_forkSyncOnKillDone) { + m_forkSyncOnKillDone = true; + m_state = s; + m_forkSyncOnKillp->done(); + return; + } + m_state = s; +} + +void VlForkSync::onKill(VlProcessRef process) { + if (process) process->forkSyncOnKill(this); +} + void VlForkSync::done(const char* filename, int lineno) { VL_DEBUG_IF(VL_DBG_MSGF(" Process forked at %s:%d finished\n", filename, lineno);); if (m_join->m_counter > 0) m_join->m_counter--; diff --git a/include/verilated_timing.h b/include/verilated_timing.h index a657ac458..4eb07402d 100644 --- a/include/verilated_timing.h +++ b/include/verilated_timing.h @@ -397,6 +397,8 @@ class VlForkSync final { public: // Create the join object and set the counter to the specified number void init(size_t count, VlProcessRef process) { m_join.reset(new VlJoin{count, {process}}); } + // Register process kill callback so killed fork branches still decrement join counter + void onKill(VlProcessRef process); // Called whenever any of the forked processes finishes. If the join counter reaches 0, the // main process gets resumed void done(const char* filename = VL_UNKNOWN, int lineno = 0); diff --git a/include/verilated_types.h b/include/verilated_types.h index da8c94977..487b7066e 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -109,12 +109,15 @@ constexpr IData VL_CLOG2_CE_Q(QData lhs) VL_PURE { // Metadata of processes using VlProcessRef = std::shared_ptr; +class VlForkSync; class VlProcess final { // MEMBERS int m_state; // Current state of the process VlProcessRef m_parentp = nullptr; // Parent process, if exists std::set m_children; // Active child processes + VlForkSync* m_forkSyncOnKillp = nullptr; // Optional fork..join counter to decrement on kill + bool m_forkSyncOnKillDone = false; // Ensure on-kill callback fires only once public: // TYPES @@ -145,7 +148,7 @@ public: void detach(VlProcess* childp) { m_children.erase(childp); } int state() const { return m_state; } - void state(int s) { m_state = s; } + void state(int s); void disable() { state(KILLED); disableFork(); @@ -153,6 +156,7 @@ public: void disableFork() { for (VlProcess* childp : m_children) childp->disable(); } + void forkSyncOnKill(VlForkSync* forkSyncp); bool completed() const { return state() == FINISHED || state() == KILLED; } bool completedFork() const { for (const VlProcess* const childp : m_children) diff --git a/src/V3AstAttr.h b/src/V3AstAttr.h index e879adfed..e6e82c0d6 100644 --- a/src/V3AstAttr.h +++ b/src/V3AstAttr.h @@ -811,6 +811,7 @@ public: FORK_DONE, FORK_INIT, FORK_JOIN, + FORK_ON_KILL, RANDOMIZER_BASIC_STD_RANDOMIZATION, RANDOMIZER_CLEARCONSTRAINTS, RANDOMIZER_CLEARALL, @@ -947,6 +948,7 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) { {FORK_DONE, "done", false}, \ {FORK_INIT, "init", false}, \ {FORK_JOIN, "join", false}, \ + {FORK_ON_KILL, "onKill", false}, \ {RANDOMIZER_BASIC_STD_RANDOMIZATION, "basicStdRandomization", false}, \ {RANDOMIZER_CLEARCONSTRAINTS, "clearConstraints", false}, \ {RANDOMIZER_CLEARALL, "clearAll", false}, \ diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index b723cbcb9..a529b4411 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -742,6 +742,23 @@ class TimingControlVisitor final : public VNVisitor { addDebugInfo(donep); beginp->addStmtsp(donep->makeStmt()); } + // Register a callback so killing a process-backed fork branch decrements the join counter + void addForkOnKill(AstBegin* const beginp, AstVarScope* const forkVscp) const { + if (!beginp->needProcess()) return; + FileLine* const flp = beginp->fileline(); + auto* const onKillp = new AstCMethodHard{ + flp, new AstVarRef{flp, forkVscp, VAccess::WRITE}, VCMethod::FORK_ON_KILL}; + onKillp->dtypeSetVoid(); + AstCExpr* const processp = new AstCExpr{flp, "vlProcess"}; + processp->dtypeSetVoid(); // Opaque process reference; type is irrelevant for hardcoded emit + onKillp->addPinsp(processp); + AstNodeStmt* const stmtp = onKillp->makeStmt(); + if (beginp->stmtsp()) { + beginp->stmtsp()->addHereThisAsNext(stmtp); + } else { + beginp->addStmtsp(stmtp); + } + } // Handle the 'join' part of a fork..join void makeForkJoin(AstFork* const forkp) { // Create a fork sync var @@ -754,6 +771,7 @@ class TimingControlVisitor final : public VNVisitor { unsigned joinCount = 0; // Needed for join counter // Add a .done() to each begin for (AstNode* beginp = forkp->forksp(); beginp; beginp = beginp->nextp()) { + addForkOnKill(VN_AS(beginp, Begin), forkVscp); addForkDone(VN_AS(beginp, Begin), forkVscp); joinCount++; } diff --git a/test_regress/t/t_disable_task_join.py b/test_regress/t/t_disable_task_join.py new file mode 100644 index 000000000..6f8da8aa7 --- /dev/null +++ b/test_regress/t/t_disable_task_join.py @@ -0,0 +1,19 @@ +#!/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: 2024-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=["--timing"]) + +test.execute() + +test.passes() + diff --git a/test_regress/t/t_disable_task_join.v b/test_regress/t/t_disable_task_join.v new file mode 100644 index 000000000..db8763baa --- /dev/null +++ b/test_regress/t/t_disable_task_join.v @@ -0,0 +1,80 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2025 Antmicro +// SPDX-License-Identifier: CC0-1.0 + +int x = 0; +int y = 0; + +task increment_x; + x++; + #2; + x++; +endtask + +task increment_y; + y++; + #2; + y++; +endtask + +class driver; + int m_time = 0; + + task get_and_send(); + forever begin + #10; + m_time += 10; + end + endtask + + task post_shutdown_phase(); + disable get_and_send; + endtask +endclass + +module t; + + driver c; + + initial begin + fork + increment_x(); + #1 disable increment_x; + join + + if (x != 1) $stop; + + c = new; + fork + c.get_and_send; + join_none + if (c.m_time != 0) $stop; + + #11; + if ($time != 12) $stop; + if (c.m_time != 10) $stop; + + #20; + if ($time != 32) $stop; + if (c.m_time != 30) $stop; + c.post_shutdown_phase; + + #20; + if ($time != 52) $stop; + if (c.m_time != 30) $stop; + + // Additional regression: join_any should also complete when disable kills a forked task + fork + increment_y(); + #1 disable increment_y; + join_any + #3; + if (y != 1) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From bc2b8535f579159478614fa86b84e49107227b4e Mon Sep 17 00:00:00 2001 From: github action Date: Mon, 23 Feb 2026 15:26:40 +0000 Subject: [PATCH 03/17] Apply 'make format' --- src/V3Timing.cpp | 7 ++++--- test_regress/t/t_disable_task_by_name.py | 0 test_regress/t/t_disable_task_join.py | 1 - test_regress/t/t_disable_task_scope_bad.py | 1 - test_regress/t/t_disable_task_target_bad.py | 0 5 files changed, 4 insertions(+), 5 deletions(-) mode change 100644 => 100755 test_regress/t/t_disable_task_by_name.py mode change 100644 => 100755 test_regress/t/t_disable_task_join.py mode change 100644 => 100755 test_regress/t/t_disable_task_scope_bad.py mode change 100644 => 100755 test_regress/t/t_disable_task_target_bad.py diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index a529b4411..6c53d57ea 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -746,11 +746,12 @@ class TimingControlVisitor final : public VNVisitor { void addForkOnKill(AstBegin* const beginp, AstVarScope* const forkVscp) const { if (!beginp->needProcess()) return; FileLine* const flp = beginp->fileline(); - auto* const onKillp = new AstCMethodHard{ - flp, new AstVarRef{flp, forkVscp, VAccess::WRITE}, VCMethod::FORK_ON_KILL}; + auto* const onKillp = new AstCMethodHard{flp, new AstVarRef{flp, forkVscp, VAccess::WRITE}, + VCMethod::FORK_ON_KILL}; onKillp->dtypeSetVoid(); AstCExpr* const processp = new AstCExpr{flp, "vlProcess"}; - processp->dtypeSetVoid(); // Opaque process reference; type is irrelevant for hardcoded emit + processp + ->dtypeSetVoid(); // Opaque process reference; type is irrelevant for hardcoded emit onKillp->addPinsp(processp); AstNodeStmt* const stmtp = onKillp->makeStmt(); if (beginp->stmtsp()) { diff --git a/test_regress/t/t_disable_task_by_name.py b/test_regress/t/t_disable_task_by_name.py old mode 100644 new mode 100755 diff --git a/test_regress/t/t_disable_task_join.py b/test_regress/t/t_disable_task_join.py old mode 100644 new mode 100755 index 6f8da8aa7..55da782f9 --- a/test_regress/t/t_disable_task_join.py +++ b/test_regress/t/t_disable_task_join.py @@ -16,4 +16,3 @@ test.compile(timing_loop=True, verilator_flags2=["--timing"]) test.execute() test.passes() - diff --git a/test_regress/t/t_disable_task_scope_bad.py b/test_regress/t/t_disable_task_scope_bad.py old mode 100644 new mode 100755 index 40c047e86..bc540caa3 --- a/test_regress/t/t_disable_task_scope_bad.py +++ b/test_regress/t/t_disable_task_scope_bad.py @@ -14,4 +14,3 @@ test.scenarios('linter') test.lint(fails=True, expect_filename=test.golden_filename) test.passes() - diff --git a/test_regress/t/t_disable_task_target_bad.py b/test_regress/t/t_disable_task_target_bad.py old mode 100644 new mode 100755 From c8132b3a77b276f4bb76177c4d17c294fb28720e Mon Sep 17 00:00:00 2001 From: Nick Brereton Date: Tue, 24 Feb 2026 22:17:52 -0500 Subject: [PATCH 04/17] Clear fork on-kill callbacks and update disable-task bad test golden outputs. --- include/verilated_timing.cpp | 16 +++++++++++++++- include/verilated_timing.h | 2 ++ include/verilated_types.h | 1 + test_regress/t/t_disable_task_scope_bad.out | 20 ++++++++++---------- test_regress/t/t_disable_task_target_bad.out | 12 ++++++------ 5 files changed, 34 insertions(+), 17 deletions(-) diff --git a/include/verilated_timing.cpp b/include/verilated_timing.cpp index 61d66b2f0..3f74c4403 100644 --- a/include/verilated_timing.cpp +++ b/include/verilated_timing.cpp @@ -243,6 +243,12 @@ void VlProcess::forkSyncOnKill(VlForkSync* forkSyncp) { m_forkSyncOnKillDone = false; } +void VlProcess::forkSyncOnKillClear(VlForkSync* forkSyncp) { + if (m_forkSyncOnKillp != forkSyncp) return; + m_forkSyncOnKillp = nullptr; + m_forkSyncOnKillDone = false; +} + void VlProcess::state(int s) { if (s == KILLED && m_state != KILLED && m_state != FINISHED && m_forkSyncOnKillp && !m_forkSyncOnKillDone) { @@ -254,8 +260,16 @@ void VlProcess::state(int s) { m_state = s; } +VlForkSync::~VlForkSync() { + for (std::weak_ptr& weakp : m_onKillProcessps) { + if (VlProcessRef processp = weakp.lock()) processp->forkSyncOnKillClear(this); + } +} + void VlForkSync::onKill(VlProcessRef process) { - if (process) process->forkSyncOnKill(this); + if (!process) return; + m_onKillProcessps.emplace_back(process); + process->forkSyncOnKill(this); } void VlForkSync::done(const char* filename, int lineno) { diff --git a/include/verilated_timing.h b/include/verilated_timing.h index 4eb07402d..1827da956 100644 --- a/include/verilated_timing.h +++ b/include/verilated_timing.h @@ -393,8 +393,10 @@ class VlForkSync final { // The join info is shared among all forked processes std::shared_ptr m_join; + std::vector> m_onKillProcessps; // Branches registered for kill hooks public: + ~VlForkSync(); // Create the join object and set the counter to the specified number void init(size_t count, VlProcessRef process) { m_join.reset(new VlJoin{count, {process}}); } // Register process kill callback so killed fork branches still decrement join counter diff --git a/include/verilated_types.h b/include/verilated_types.h index 487b7066e..e246b8e80 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -157,6 +157,7 @@ public: for (VlProcess* childp : m_children) childp->disable(); } void forkSyncOnKill(VlForkSync* forkSyncp); + void forkSyncOnKillClear(VlForkSync* forkSyncp); bool completed() const { return state() == FINISHED || state() == KILLED; } bool completedFork() const { for (const VlProcess* const childp : m_children) diff --git a/test_regress/t/t_disable_task_scope_bad.out b/test_regress/t/t_disable_task_scope_bad.out index 2fa1c2987..48b3881fc 100644 --- a/test_regress/t/t_disable_task_scope_bad.out +++ b/test_regress/t/t_disable_task_scope_bad.out @@ -1,20 +1,20 @@ -%Error: t/t_disable_task_scope_bad.v:34:18: Can't find definition of 'missing_task' in dotted block/task: 'ifc1.missing_task' - 34 | disable ifc1.missing_task; +%Error: t/t_disable_task_scope_bad.v:36:18: Can't find definition of 'missing_task' in dotted block/task: 'ifc1.missing_task' + 36 | disable ifc1.missing_task; | ^~~~~~~~~~~~ ... Known scopes under 'ifc1': ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_disable_task_scope_bad.v:35:19: Can't find definition of 'missing_task' in dotted block/task: 'prog1.missing_task' - 35 | disable prog1.missing_task; +%Error: t/t_disable_task_scope_bad.v:37:19: Can't find definition of 'missing_task' in dotted block/task: 'prog1.missing_task' + 37 | disable prog1.missing_task; | ^~~~~~~~~~~~ ... Known scopes under 'prog1': -%Error: t/t_disable_task_scope_bad.v:36:26: Can't find definition of 'missing_task' in dotted block/task: 'outer1.inner.missing_task' - 36 | disable outer1.inner.missing_task; +%Error: t/t_disable_task_scope_bad.v:38:26: Can't find definition of 'missing_task' in dotted block/task: 'outer1.inner.missing_task' + 38 | disable outer1.inner.missing_task; | ^~~~~~~~~~~~ ... Known scopes under 'outer1.inner': -%Error: t/t_disable_task_scope_bad.v:37:18: Found definition of 'ifc1.data' as a VAR but expected a block/task - 37 | disable ifc1.data; +%Error: t/t_disable_task_scope_bad.v:39:18: Found definition of 'ifc1.data' as a VAR but expected a block/task + 39 | disable ifc1.data; | ^~~~ -%Error: t/t_disable_task_scope_bad.v:38:19: Found definition of 'prog1.data' as a VAR but expected a block/task - 38 | disable prog1.data; +%Error: t/t_disable_task_scope_bad.v:40:19: Found definition of 'prog1.data' as a VAR but expected a block/task + 40 | disable prog1.data; | ^~~~ %Error: Exiting due to diff --git a/test_regress/t/t_disable_task_target_bad.out b/test_regress/t/t_disable_task_target_bad.out index 613440744..8d14a77c3 100644 --- a/test_regress/t/t_disable_task_target_bad.out +++ b/test_regress/t/t_disable_task_target_bad.out @@ -1,11 +1,11 @@ -%Error: t/t_disable_task_target_bad.v:21:13: Found definition of 'foo' as a VAR but expected a block/task - 21 | disable foo; +%Error: t/t_disable_task_target_bad.v:23:13: Found definition of 'foo' as a VAR but expected a block/task + 23 | disable foo; | ^~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_disable_task_target_bad.v:22:13: Found definition of 'c' as a VAR but expected a block/task - 22 | disable c.run; +%Error: t/t_disable_task_target_bad.v:24:13: Found definition of 'c' as a VAR but expected a block/task + 24 | disable c.run; | ^ -%Error: t/t_disable_task_target_bad.v:22:15: Can't find definition of block/task: 'run' - 22 | disable c.run; +%Error: t/t_disable_task_target_bad.v:24:15: Can't find definition of block/task: 'run' + 24 | disable c.run; | ^~~ %Error: Exiting due to From f1a44b2f62370d991c13f5633da0657c46cba138 Mon Sep 17 00:00:00 2001 From: Nick Brereton Date: Tue, 24 Feb 2026 22:33:34 -0500 Subject: [PATCH 05/17] Check disabling already finished or disabled tasks. --- test_regress/t/t_disable_task_by_name.v | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test_regress/t/t_disable_task_by_name.v b/test_regress/t/t_disable_task_by_name.v index d2f86c909..f4583eb72 100644 --- a/test_regress/t/t_disable_task_by_name.v +++ b/test_regress/t/t_disable_task_by_name.v @@ -8,6 +8,7 @@ int x = 0; int y = 0; +int z = 0; int self_entry = 0; int self_after_disable = 0; @@ -43,6 +44,10 @@ task increment_y; y++; endtask +task finish_z; + z++; +endtask + task self_stop; self_entry = 1; disable self_stop; @@ -217,6 +222,10 @@ module t; join_none #10; if (x != 1) $stop; + // Re-disabling after prior disable (no active invocations) is a no-op + disable increment_x; + #1; + if (x != 1) $stop; // Another basic module-task disable-by-name case fork @@ -226,6 +235,13 @@ module t; #10; if (y != 1) $stop; + // Disabling a task after it already finished is a no-op + finish_z(); + if (z != 1) $stop; + disable finish_z; + #1; + if (z != 1) $stop; + // Self-disable in task by name self_stop(); if (self_entry != 1) $stop; From a9797f71fef97c8cf48a9a96fa56af4431fb3315 Mon Sep 17 00:00:00 2001 From: Nick Brereton <85175726+nbstrike@users.noreply.github.com> Date: Tue, 24 Feb 2026 22:37:21 -0500 Subject: [PATCH 06/17] Update src/V3Timing.cpp Co-authored-by: Wilson Snyder --- src/V3Timing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index 6c53d57ea..3f9bf1a01 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -746,7 +746,7 @@ class TimingControlVisitor final : public VNVisitor { void addForkOnKill(AstBegin* const beginp, AstVarScope* const forkVscp) const { if (!beginp->needProcess()) return; FileLine* const flp = beginp->fileline(); - auto* const onKillp = new AstCMethodHard{flp, new AstVarRef{flp, forkVscp, VAccess::WRITE}, + AstCMethodHard* const onKillp = new AstCMethodHard{flp, new AstVarRef{flp, forkVscp, VAccess::WRITE}, VCMethod::FORK_ON_KILL}; onKillp->dtypeSetVoid(); AstCExpr* const processp = new AstCExpr{flp, "vlProcess"}; From c298f1e8b3c1a0e1e2b05cce3ef919b91a1870e5 Mon Sep 17 00:00:00 2001 From: github action Date: Wed, 25 Feb 2026 03:38:16 +0000 Subject: [PATCH 07/17] Apply 'make format' --- src/V3Timing.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index 3f9bf1a01..743398d46 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -746,8 +746,8 @@ class TimingControlVisitor final : public VNVisitor { void addForkOnKill(AstBegin* const beginp, AstVarScope* const forkVscp) const { if (!beginp->needProcess()) return; FileLine* const flp = beginp->fileline(); - AstCMethodHard* const onKillp = new AstCMethodHard{flp, new AstVarRef{flp, forkVscp, VAccess::WRITE}, - VCMethod::FORK_ON_KILL}; + AstCMethodHard* const onKillp = new AstCMethodHard{ + flp, new AstVarRef{flp, forkVscp, VAccess::WRITE}, VCMethod::FORK_ON_KILL}; onKillp->dtypeSetVoid(); AstCExpr* const processp = new AstCExpr{flp, "vlProcess"}; processp From 4ff579f30031b83ef1f65702c00e8caa3c52b024 Mon Sep 17 00:00:00 2001 From: Nick Brereton Date: Wed, 25 Feb 2026 01:01:03 -0500 Subject: [PATCH 08/17] Fix fork join kill-accounting callback lifetime and reentrancy. --- include/verilated_timing.cpp | 46 ++++++++++++++++++-------------- include/verilated_timing.h | 51 +++++++++++++++++++++++------------- include/verilated_types.h | 14 ++++++---- 3 files changed, 69 insertions(+), 42 deletions(-) diff --git a/include/verilated_timing.cpp b/include/verilated_timing.cpp index 3f74c4403..0102e1f1a 100644 --- a/include/verilated_timing.cpp +++ b/include/verilated_timing.cpp @@ -129,8 +129,7 @@ void VlTriggerScheduler::moveToResumeQueue(const char* eventDescription) { if (!m_fired.empty()) { VL_DEBUG_IF(VL_DBG_MSGF(" Moving to resume queue processes waiting for %s:\n", eventDescription); - for (const auto& susp - : m_fired) { + for (const auto& susp : m_fired) { VL_DBG_MSGF(" - "); susp.dump(); }); @@ -144,8 +143,7 @@ void VlTriggerScheduler::ready(const char* eventDescription) { if (!m_awaiting.empty()) { VL_DEBUG_IF( VL_DBG_MSGF(" Committing processes waiting for %s:\n", eventDescription); - for (const auto& susp - : m_awaiting) { + for (const auto& susp : m_awaiting) { VL_DBG_MSGF(" - "); susp.dump(); }); @@ -201,8 +199,7 @@ bool VlDynamicTriggerScheduler::evaluate() { void VlDynamicTriggerScheduler::doPostUpdates() { VL_DEBUG_IF(if (!m_post.empty()) VL_DBG_MSGF(" Doing post updates for processes:\n"); // - for (const auto& susp - : m_post) { + for (const auto& susp : m_post) { VL_DBG_MSGF(" - "); susp.dump(); }); @@ -212,8 +209,7 @@ void VlDynamicTriggerScheduler::doPostUpdates() { void VlDynamicTriggerScheduler::resume() { VL_DEBUG_IF(if (!m_triggered.empty()) VL_DBG_MSGF(" Resuming processes:\n"); // - for (const auto& susp - : m_triggered) { + for (const auto& susp : m_triggered) { VL_DBG_MSGF(" - "); susp.dump(); }); @@ -238,12 +234,12 @@ void VlDynamicTriggerScheduler::dump() const { //====================================================================== // VlForkSync:: Methods -void VlProcess::forkSyncOnKill(VlForkSync* forkSyncp) { +void VlProcess::forkSyncOnKill(VlForkSyncState* forkSyncp) { m_forkSyncOnKillp = forkSyncp; m_forkSyncOnKillDone = false; } -void VlProcess::forkSyncOnKillClear(VlForkSync* forkSyncp) { +void VlProcess::forkSyncOnKillClear(VlForkSyncState* forkSyncp) { if (m_forkSyncOnKillp != forkSyncp) return; m_forkSyncOnKillp = nullptr; m_forkSyncOnKillDone = false; @@ -260,22 +256,34 @@ void VlProcess::state(int s) { m_state = s; } -VlForkSync::~VlForkSync() { - for (std::weak_ptr& weakp : m_onKillProcessps) { - if (VlProcessRef processp = weakp.lock()) processp->forkSyncOnKillClear(this); - } +VlForkSyncState::~VlForkSyncState() { + for (const VlProcessRef& processp : m_onKillProcessps) processp->forkSyncOnKillClear(this); } void VlForkSync::onKill(VlProcessRef process) { if (!process) return; - m_onKillProcessps.emplace_back(process); - process->forkSyncOnKill(this); + m_state->m_onKillProcessps.emplace_back(process); + process->forkSyncOnKill(m_state.get()); } -void VlForkSync::done(const char* filename, int lineno) { +void VlForkSyncState::done(const char* filename, int lineno) { VL_DEBUG_IF(VL_DBG_MSGF(" Process forked at %s:%d finished\n", filename, lineno);); - if (m_join->m_counter > 0) m_join->m_counter--; - if (m_join->m_counter == 0) m_join->m_susp.resume(); + if (!m_inited) { + ++m_pendingDones; + return; + } + if (m_counter > 0) m_counter--; + if (m_counter != 0) return; + if (m_inDone) { + m_resumePending = true; + return; + } + m_inDone = true; + do { + m_resumePending = false; + m_susp.resume(); + } while (m_resumePending && m_inited && m_counter == 0); + m_inDone = false; } //====================================================================== diff --git a/include/verilated_timing.h b/include/verilated_timing.h index 1827da956..c87e216ef 100644 --- a/include/verilated_timing.h +++ b/include/verilated_timing.h @@ -383,44 +383,59 @@ struct VlForever final { //============================================================================= // VlForkSync is used to manage fork..join and fork..join_any constructs. -class VlForkSync final { - // VlJoin stores the handle of a suspended coroutine that did a fork..join or fork..join_any. - // If the counter reaches 0, the suspended coroutine shall be resumed. - struct VlJoin final { - size_t m_counter = 0; // When reaches 0, resume suspended coroutine - VlCoroutineHandle m_susp; // Coroutine to resume - }; +// Shared fork..join state, because VlForkSync is copied into generated coroutine frames. +class VlForkSyncState final { +public: + size_t m_counter = 0; // When reaches 0, resume suspended coroutine + VlCoroutineHandle m_susp; // Coroutine to resume + bool m_inited = false; + size_t m_pendingDones = 0; // done() calls seen before init() (e.g. early killed branch) + bool m_inDone = false; // Guard against re-entrant resume recursion from nested kills + bool m_resumePending = false; // Join reached zero again while inside done() + std::vector m_onKillProcessps; // Branches registered for kill hooks - // The join info is shared among all forked processes - std::shared_ptr m_join; - std::vector> m_onKillProcessps; // Branches registered for kill hooks + VlForkSyncState() // Construct with a null coroutine handle + : m_susp{VlProcessRef{}} {} + ~VlForkSyncState(); + void done(const char* filename = VL_UNKNOWN, int lineno = 0); +}; + +class VlForkSync final { + std::shared_ptr m_state{std::make_shared()}; public: - ~VlForkSync(); // Create the join object and set the counter to the specified number - void init(size_t count, VlProcessRef process) { m_join.reset(new VlJoin{count, {process}}); } + void init(size_t count, VlProcessRef process) { + const size_t pendingDones = m_state->m_pendingDones; + m_state->m_pendingDones = 0; + count = (pendingDones >= count) ? 0 : (count - pendingDones); + m_state->m_counter = count; + m_state->m_susp = {process}; + m_state->m_inited = true; + } // Register process kill callback so killed fork branches still decrement join counter void onKill(VlProcessRef process); // Called whenever any of the forked processes finishes. If the join counter reaches 0, the // main process gets resumed - void done(const char* filename = VL_UNKNOWN, int lineno = 0); + void done(const char* filename = VL_UNKNOWN, int lineno = 0) { + m_state->done(filename, lineno); + } // Used by coroutines for co_awaiting a join auto join(VlProcessRef process, const char* filename = VL_UNKNOWN, int lineno = 0) { - assert(m_join); VL_DEBUG_IF( VL_DBG_MSGF(" Awaiting join of fork at: %s:%d\n", filename, lineno);); struct Awaitable final { VlProcessRef process; // Data of the suspended process, null if not needed - const std::shared_ptr join; // Join to await on + const std::shared_ptr state; // Join to await on VlFileLineDebug fileline; - bool await_ready() { return join->m_counter == 0; } // Suspend if join still exists + bool await_ready() { return state->m_counter == 0; } // Suspend if join still exists void await_suspend(std::coroutine_handle<> coro) { - join->m_susp = {coro, process, fileline}; + state->m_susp = {coro, process, fileline}; } void await_resume() const {} }; - return Awaitable{process, m_join, VlFileLineDebug{filename, lineno}}; + return Awaitable{process, m_state, VlFileLineDebug{filename, lineno}}; } }; diff --git a/include/verilated_types.h b/include/verilated_types.h index e246b8e80..d5dffe32d 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -110,13 +110,15 @@ constexpr IData VL_CLOG2_CE_Q(QData lhs) VL_PURE { // Metadata of processes using VlProcessRef = std::shared_ptr; class VlForkSync; +class VlForkSyncState; class VlProcess final { // MEMBERS int m_state; // Current state of the process VlProcessRef m_parentp = nullptr; // Parent process, if exists std::set m_children; // Active child processes - VlForkSync* m_forkSyncOnKillp = nullptr; // Optional fork..join counter to decrement on kill + VlForkSyncState* m_forkSyncOnKillp + = nullptr; // Optional fork..join counter to decrement on kill bool m_forkSyncOnKillDone = false; // Ensure on-kill callback fires only once public: @@ -154,10 +156,12 @@ public: disableFork(); } void disableFork() { - for (VlProcess* childp : m_children) childp->disable(); + // childp->disable() may resume coroutines and mutate m_children + const std::set children = m_children; + for (VlProcess* childp : children) childp->disable(); } - void forkSyncOnKill(VlForkSync* forkSyncp); - void forkSyncOnKillClear(VlForkSync* forkSyncp); + void forkSyncOnKill(VlForkSyncState* forkSyncp); + void forkSyncOnKillClear(VlForkSyncState* forkSyncp); bool completed() const { return state() == FINISHED || state() == KILLED; } bool completedFork() const { for (const VlProcess* const childp : m_children) @@ -1939,7 +1943,7 @@ public: VlClassRef() = default; // Init with nullptr // cppcheck-suppress noExplicitConstructor - VlClassRef(VlNull){}; + VlClassRef(VlNull) {}; template VlClassRef(VlDeleter& deleter, T_Args&&... args) // () required here to avoid narrowing conversion warnings, From 0b39cf486585d9cfb6afb2ca8f958ede45eb0e0d Mon Sep 17 00:00:00 2001 From: github action Date: Wed, 25 Feb 2026 06:02:10 +0000 Subject: [PATCH 09/17] Apply 'make format' --- include/verilated_timing.cpp | 12 ++++++++---- include/verilated_types.h | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/include/verilated_timing.cpp b/include/verilated_timing.cpp index 0102e1f1a..e189a443b 100644 --- a/include/verilated_timing.cpp +++ b/include/verilated_timing.cpp @@ -129,7 +129,8 @@ void VlTriggerScheduler::moveToResumeQueue(const char* eventDescription) { if (!m_fired.empty()) { VL_DEBUG_IF(VL_DBG_MSGF(" Moving to resume queue processes waiting for %s:\n", eventDescription); - for (const auto& susp : m_fired) { + for (const auto& susp + : m_fired) { VL_DBG_MSGF(" - "); susp.dump(); }); @@ -143,7 +144,8 @@ void VlTriggerScheduler::ready(const char* eventDescription) { if (!m_awaiting.empty()) { VL_DEBUG_IF( VL_DBG_MSGF(" Committing processes waiting for %s:\n", eventDescription); - for (const auto& susp : m_awaiting) { + for (const auto& susp + : m_awaiting) { VL_DBG_MSGF(" - "); susp.dump(); }); @@ -199,7 +201,8 @@ bool VlDynamicTriggerScheduler::evaluate() { void VlDynamicTriggerScheduler::doPostUpdates() { VL_DEBUG_IF(if (!m_post.empty()) VL_DBG_MSGF(" Doing post updates for processes:\n"); // - for (const auto& susp : m_post) { + for (const auto& susp + : m_post) { VL_DBG_MSGF(" - "); susp.dump(); }); @@ -209,7 +212,8 @@ void VlDynamicTriggerScheduler::doPostUpdates() { void VlDynamicTriggerScheduler::resume() { VL_DEBUG_IF(if (!m_triggered.empty()) VL_DBG_MSGF(" Resuming processes:\n"); // - for (const auto& susp : m_triggered) { + for (const auto& susp + : m_triggered) { VL_DBG_MSGF(" - "); susp.dump(); }); diff --git a/include/verilated_types.h b/include/verilated_types.h index d5dffe32d..baef5923e 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -1943,7 +1943,7 @@ public: VlClassRef() = default; // Init with nullptr // cppcheck-suppress noExplicitConstructor - VlClassRef(VlNull) {}; + VlClassRef(VlNull){}; template VlClassRef(VlDeleter& deleter, T_Args&&... args) // () required here to avoid narrowing conversion warnings, From 835cc63d23fb9c06954abff818862225c5587f33 Mon Sep 17 00:00:00 2001 From: Nick Brereton Date: Sun, 1 Mar 2026 22:06:43 -0500 Subject: [PATCH 10/17] Wrap tasks that may be disabled with fork..join to ensure they have a distinct, killable process. --- src/V3LinkJump.cpp | 28 +++++++++++++++++++++---- test_regress/t/t_disable_task_by_name.v | 14 +++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index ec50ae7ca..9630371f3 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -67,6 +67,8 @@ class LinkJumpVisitor final : public VNVisitor { V3UniqueNames m_queueNames{ "__VprocessQueue"}; // Names for queues needed for 'disable' handling std::unordered_map m_taskDisableQueues; // Per-task process queues + std::unordered_map + m_taskDisableBegins; // Per-task process wrappers // METHODS // Get (and create if necessary) the JumpBlock for this statement @@ -190,6 +192,20 @@ class LinkJumpVisitor final : public VNVisitor { killQueueCall->classOrPackagep(processClassp); return new AstStmtExpr{fl, killQueueCall}; } + AstBegin* getOrCreateTaskDisableBeginp(AstTask* const taskp, FileLine* const fl) { + const auto it = m_taskDisableBegins.find(taskp); + if (it != m_taskDisableBegins.end()) return it->second; + + AstBegin* const taskBodyp = new AstBegin{fl, "", nullptr, false}; + if (taskp->stmtsp()) taskBodyp->addStmtsp(taskp->stmtsp()->unlinkFrBackWithNext()); + + AstFork* const forkp = new AstFork{fl, VJoinType::JOIN}; + forkp->addForksp(taskBodyp); + taskp->addStmtsp(forkp); + + m_taskDisableBegins.emplace(taskp, taskBodyp); + return taskBodyp; + } AstVar* getOrCreateTaskDisableQueuep(AstTask* const taskp, FileLine* const fl) { const auto it = m_taskDisableQueues.find(taskp); if (it != m_taskDisableQueues.end()) return it->second; @@ -207,10 +223,11 @@ class LinkJumpVisitor final : public VNVisitor { AstVarRef* const queueWriteRefp = new AstVarRef{fl, topPkgp, processQueuep, VAccess::WRITE}; AstStmtExpr* const pushCurrentProcessp = getQueuePushProcessSelfp(queueWriteRefp); - if (taskp->stmtsp()) { - taskp->stmtsp()->addHereThisAsNext(pushCurrentProcessp); + AstBegin* const taskBodyp = getOrCreateTaskDisableBeginp(taskp, fl); + if (taskBodyp->stmtsp()) { + taskBodyp->stmtsp()->addHereThisAsNext(pushCurrentProcessp); } else { - taskp->addStmtsp(pushCurrentProcessp); + taskBodyp->addStmtsp(pushCurrentProcessp); } m_taskDisableQueues.emplace(taskp, processQueuep); return processQueuep; @@ -464,7 +481,10 @@ class LinkJumpVisitor final : public VNVisitor { // process::kill does not terminate the currently running process immediately. // If we disable the current task by name from inside itself, jump to its end. if (m_ftaskp == taskp) { - AstJumpBlock* const blockp = getJumpBlock(taskp, false); + AstNode* jumpTargetp = taskp; + const auto it = m_taskDisableBegins.find(taskp); + if (it != m_taskDisableBegins.end()) jumpTargetp = it->second; + AstJumpBlock* const blockp = getJumpBlock(jumpTargetp, false); killStmtp->addNextHere(new AstJumpGo{nodep->fileline(), blockp}); } } else if (AstFork* const forkp = VN_CAST(targetp, Fork)) { diff --git a/test_regress/t/t_disable_task_by_name.v b/test_regress/t/t_disable_task_by_name.v index f4583eb72..e9df8c391 100644 --- a/test_regress/t/t_disable_task_by_name.v +++ b/test_regress/t/t_disable_task_by_name.v @@ -9,6 +9,7 @@ int x = 0; int y = 0; int z = 0; +int always_value = 0; int self_entry = 0; int self_after_disable = 0; @@ -48,6 +49,10 @@ task finish_z; z++; endtask +task always_foo; + always_value = #2 1; +endtask + task self_stop; self_entry = 1; disable self_stop; @@ -198,6 +203,8 @@ module t; Prog prog1(); WorkerMod mod1(); + always #6 disable always_foo; + initial begin automatic Cls c = new; automatic NamedA a = new; @@ -215,6 +222,13 @@ module t; int prog_before; int pkg_before; + // Disable task by name from an external always block while task executes + always_value = 0; + #5; + always_foo; + #4; + if (always_value != 0) $stop; + // Module task disabled by sibling process in a fork fork increment_x(); From 346f0ead2f4a525a506799957160096f11f14c30 Mon Sep 17 00:00:00 2001 From: Nick Brereton Date: Sun, 1 Mar 2026 23:10:52 -0500 Subject: [PATCH 11/17] Fix disable-by-name process kill semantics for named blocks/tasks and add regression coverage for forked named-block disables --- include/verilated_std.sv | 5 +- src/V3LinkJump.cpp | 130 ++++++++++++++++++------ test_regress/t/t_disable_task_by_name.v | 52 ++++++++++ 3 files changed, 156 insertions(+), 31 deletions(-) diff --git a/include/verilated_std.sv b/include/verilated_std.sv index b86340695..941c79795 100644 --- a/include/verilated_std.sv +++ b/include/verilated_std.sv @@ -181,8 +181,9 @@ package std; static task killQueue(ref process processQueue[$]); `ifdef VERILATOR_TIMING - while (processQueue.size() > 0) begin - processQueue.pop_back().kill(); + int initialSize = processQueue.size(); + for (int i = 0; i < initialSize; ++i) begin + processQueue.pop_front().kill(); end `endif endtask diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index 9630371f3..1c246805d 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -67,8 +67,11 @@ class LinkJumpVisitor final : public VNVisitor { V3UniqueNames m_queueNames{ "__VprocessQueue"}; // Names for queues needed for 'disable' handling std::unordered_map m_taskDisableQueues; // Per-task process queues + std::unordered_map m_beginDisableQueues; // Per-begin process queues std::unordered_map m_taskDisableBegins; // Per-task process wrappers + std::unordered_map + m_beginDisableBegins; // Per-begin process wrappers // METHODS // Get (and create if necessary) the JumpBlock for this statement @@ -181,6 +184,12 @@ class LinkJumpVisitor final : public VNVisitor { return new AstStmtExpr{ fl, new AstMethodCall{fl, queueRefp, "push_back", new AstArg{fl, "", processSelfp}}}; } + static AstStmtExpr* getQueuePushProcessSelfp(FileLine* const fl, AstVar* const processQueuep) { + AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp(); + AstVarRef* const queueWriteRefp + = new AstVarRef{fl, topPkgp, processQueuep, VAccess::WRITE}; + return getQueuePushProcessSelfp(queueWriteRefp); + } static AstStmtExpr* getQueueKillStmtp(FileLine* const fl, AstVar* const processQueuep) { AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp(); AstClass* const processClassp @@ -192,6 +201,24 @@ class LinkJumpVisitor final : public VNVisitor { killQueueCall->classOrPackagep(processClassp); return new AstStmtExpr{fl, killQueueCall}; } + static void prependStmtsp(AstNodeFTask* const nodep, AstNode* const stmtp) { + if (AstNode* const origStmtsp = nodep->stmtsp()) { + origStmtsp->unlinkFrBackWithNext(); + stmtp->addNext(origStmtsp); + } + nodep->addStmtsp(stmtp); + } + static void prependStmtsp(AstNodeBlock* const nodep, AstNode* const stmtp) { + if (AstNode* const origStmtsp = nodep->stmtsp()) { + origStmtsp->unlinkFrBackWithNext(); + stmtp->addNext(origStmtsp); + } + nodep->addStmtsp(stmtp); + } + static bool directlyUnderFork(const AstNode* const nodep) { + if (nodep->backp()->nextp() == nodep) return directlyUnderFork(nodep->backp()); + return VN_IS(nodep->backp(), Fork); + } AstBegin* getOrCreateTaskDisableBeginp(AstTask* const taskp, FileLine* const fl) { const auto it = m_taskDisableBegins.find(taskp); if (it != m_taskDisableBegins.end()) return it->second; @@ -220,18 +247,57 @@ class LinkJumpVisitor final : public VNVisitor { processQueuep->lifetime(VLifetime::STATIC_EXPLICIT); topPkgp->addStmtsp(processQueuep); - AstVarRef* const queueWriteRefp - = new AstVarRef{fl, topPkgp, processQueuep, VAccess::WRITE}; - AstStmtExpr* const pushCurrentProcessp = getQueuePushProcessSelfp(queueWriteRefp); + AstStmtExpr* const pushCurrentProcessp = getQueuePushProcessSelfp(fl, processQueuep); AstBegin* const taskBodyp = getOrCreateTaskDisableBeginp(taskp, fl); - if (taskBodyp->stmtsp()) { - taskBodyp->stmtsp()->addHereThisAsNext(pushCurrentProcessp); - } else { - taskBodyp->addStmtsp(pushCurrentProcessp); - } + prependStmtsp(taskBodyp, pushCurrentProcessp); m_taskDisableQueues.emplace(taskp, processQueuep); return processQueuep; } + AstBegin* getOrCreateBeginDisableBeginp(AstBegin* const beginp, FileLine* const fl) { + const auto it = m_beginDisableBegins.find(beginp); + if (it != m_beginDisableBegins.end()) return it->second; + + AstBegin* const beginBodyp = new AstBegin{fl, "", nullptr, false}; + if (beginp->stmtsp()) beginBodyp->addStmtsp(beginp->stmtsp()->unlinkFrBackWithNext()); + + AstFork* const forkp = new AstFork{fl, VJoinType::JOIN}; + forkp->addForksp(beginBodyp); + beginp->addStmtsp(forkp); + + m_beginDisableBegins.emplace(beginp, beginBodyp); + return beginBodyp; + } + AstVar* getOrCreateBeginDisableQueuep(AstBegin* const beginp, FileLine* const fl) { + const auto it = m_beginDisableQueues.find(beginp); + if (it != m_beginDisableQueues.end()) return it->second; + + AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp(); + AstClass* const processClassp + = VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class); + AstVar* const processQueuep = new AstVar{ + fl, VVarType::VAR, m_queueNames.get(beginp->name()), VFlagChildDType{}, + new AstQueueDType{fl, VFlagChildDType{}, + new AstClassRefDType{fl, processClassp, nullptr}, nullptr}}; + processQueuep->lifetime(VLifetime::STATIC_EXPLICIT); + topPkgp->addStmtsp(processQueuep); + + AstStmtExpr* const pushCurrentProcessp = getQueuePushProcessSelfp(fl, processQueuep); + AstBegin* const beginBodyp = getOrCreateBeginDisableBeginp(beginp, fl); + prependStmtsp(beginBodyp, pushCurrentProcessp); + + // Named-block disable must also terminate detached descendants created by forks + // under the block, so track each fork branch process in the same queue. + beginBodyp->foreach([&](AstFork* const forkp) { + for (AstBegin* branchp = forkp->forksp(); branchp; + branchp = VN_AS(branchp->nextp(), Begin)) { + AstStmtExpr* const pushBranchProcessp + = getQueuePushProcessSelfp(fl, processQueuep); + prependStmtsp(branchp, pushBranchProcessp); + } + }); + m_beginDisableQueues.emplace(beginp, processQueuep); + return processQueuep; + } void handleDisableOnFork(AstDisable* const nodep, const std::vector& forks) { // The support utilizes the process::kill()` method. For each `disable` a queue of // processes is declared. At the beginning of each fork that can be disabled, its process @@ -265,10 +331,7 @@ class LinkJumpVisitor final : public VNVisitor { if (pushCurrentProcessp->backp()) { pushCurrentProcessp = pushCurrentProcessp->cloneTree(false); } - if (beginp->stmtsp()) { - // There is no need to add it to empty block - beginp->stmtsp()->addHereThisAsNext(pushCurrentProcessp); - } + prependStmtsp(beginp, pushCurrentProcessp); } AstStmtExpr* const killStmtp = getQueueKillStmtp(fl, processQueuep); nodep->addNextHere(killStmtp); @@ -289,12 +352,6 @@ class LinkJumpVisitor final : public VNVisitor { } } } - static bool directlyUnderFork(const AstNode* const nodep) { - if (nodep->backp()->nextp() == nodep) return directlyUnderFork(nodep->backp()); - if (VN_IS(nodep->backp(), Fork)) return true; - return false; - } - // VISITORS void visit(AstNodeModule* nodep) override { if (nodep->dead()) return; @@ -495,22 +552,37 @@ class LinkJumpVisitor final : public VNVisitor { handleDisableOnFork(nodep, forks); } else if (AstBegin* const beginp = VN_CAST(targetp, Begin)) { if (existsBlockAbove(beginp->name())) { - if (beginp->user3()) { - nodep->v3warn(E_UNSUPPORTED, - "Unsupported: disabling block that contains a fork"); - } else { + if (!beginp->user3()) { // Jump to the end of the named block AstJumpBlock* const blockp = getJumpBlock(beginp, false); nodep->addNextHere(new AstJumpGo{nodep->fileline(), blockp}); + } else { + AstVar* const processQueuep + = getOrCreateBeginDisableQueuep(beginp, nodep->fileline()); + AstStmtExpr* const killStmtp + = getQueueKillStmtp(nodep->fileline(), processQueuep); + nodep->addNextHere(killStmtp); + + // process::kill does not terminate the currently running process immediately. + // If disable executes inside a fork branch of this named block, jump to the + // end of that branch to prevent statements after disable from executing. + AstBegin* currentBeginp = nullptr; + for (AstNodeBlock* const blockp : vlstd::reverse_view(m_blockStack)) { + if (VN_IS(blockp, Begin)) { + currentBeginp = VN_AS(blockp, Begin); + break; + } + } + if (currentBeginp && directlyUnderFork(currentBeginp)) { + AstJumpBlock* const blockp = getJumpBlock(currentBeginp, false); + killStmtp->addNextHere(new AstJumpGo{nodep->fileline(), blockp}); + } } } else { - if (directlyUnderFork(beginp)) { - std::vector forks{beginp}; - handleDisableOnFork(nodep, forks); - } else { - nodep->v3warn(E_UNSUPPORTED, "disable isn't underneath a begin with name: '" - << beginp->name() << "'"); - } + AstVar* const processQueuep + = getOrCreateBeginDisableQueuep(beginp, nodep->fileline()); + AstStmtExpr* const killStmtp = getQueueKillStmtp(nodep->fileline(), processQueuep); + nodep->addNextHere(killStmtp); } } else { nodep->v3fatalSrc("Disable linked with node of unhandled type " diff --git a/test_regress/t/t_disable_task_by_name.v b/test_regress/t/t_disable_task_by_name.v index e9df8c391..2cefedeae 100644 --- a/test_regress/t/t_disable_task_by_name.v +++ b/test_regress/t/t_disable_task_by_name.v @@ -17,6 +17,10 @@ int self_after_disable = 0; int par_started = 0; int par_finished = 0; int par_parent_continued = 0; +int named_block_fork_fail = 0; +int task_named_block_fork_fail = 0; +event named_block_fork_ev; +event task_named_block_fork_ev; class StaticCls; static int ticks = 0; @@ -75,6 +79,23 @@ task parent_disables_worker; par_parent_continued = 1; endtask +task task_named_block_fork_disable; + begin : t_named_block + fork + begin + #5; + task_named_block_fork_fail = 1; + end + begin + @task_named_block_fork_ev; + disable t_named_block; + task_named_block_fork_fail = 2; + end + join + task_named_block_fork_fail = 3; + end +endtask + class Cls; int m_time = 0; @@ -249,6 +270,37 @@ module t; #10; if (y != 1) $stop; + // Disable named block containing fork from inside a fork branch + fork + begin : named_block_under_test + fork + begin + #5; + named_block_fork_fail = 1; + end + begin + @named_block_fork_ev; + disable named_block_under_test; + named_block_fork_fail = 2; + end + join + named_block_fork_fail = 3; + end + join_none + #2; + ->named_block_fork_ev; + #10; + if (named_block_fork_fail != 0) $stop; + + // Same case as above, but with the named block inside a task + fork + task_named_block_fork_disable(); + join_none + #2; + ->task_named_block_fork_ev; + #10; + if (task_named_block_fork_fail != 0) $stop; + // Disabling a task after it already finished is a no-op finish_z(); if (z != 1) $stop; From 81c898ef2fd51d453c23cf688a9e96286d93fac6 Mon Sep 17 00:00:00 2001 From: Nick Brereton Date: Mon, 2 Mar 2026 22:13:19 -0500 Subject: [PATCH 12/17] Clean up testing failures, mostly out of date handling --- include/verilated_std.sv | 3 +- test_regress/t/t_disable.out | 5 - test_regress/t/t_disable.py | 2 +- test_regress/t/t_disable_empty.out | 5 - test_regress/t/t_disable_empty.py | 4 +- test_regress/t/t_dump_json.out | 321 +++++++++++++-------------- test_regress/t/t_func_return_bad.out | 4 - 7 files changed, 162 insertions(+), 182 deletions(-) delete mode 100644 test_regress/t/t_disable.out delete mode 100644 test_regress/t/t_disable_empty.out diff --git a/include/verilated_std.sv b/include/verilated_std.sv index 941c79795..51695cf17 100644 --- a/include/verilated_std.sv +++ b/include/verilated_std.sv @@ -181,8 +181,7 @@ package std; static task killQueue(ref process processQueue[$]); `ifdef VERILATOR_TIMING - int initialSize = processQueue.size(); - for (int i = 0; i < initialSize; ++i) begin + repeat (processQueue.size()) begin processQueue.pop_front().kill(); end `endif diff --git a/test_regress/t/t_disable.out b/test_regress/t/t_disable.out deleted file mode 100644 index a70dd874c..000000000 --- a/test_regress/t/t_disable.out +++ /dev/null @@ -1,5 +0,0 @@ -%Error-UNSUPPORTED: t/t_disable.v:17:13: Unsupported: disabling block that contains a fork - 17 | disable forked; - | ^~~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to diff --git a/test_regress/t/t_disable.py b/test_regress/t/t_disable.py index c7d9b21a5..67b896515 100755 --- a/test_regress/t/t_disable.py +++ b/test_regress/t/t_disable.py @@ -11,6 +11,6 @@ import vltest_bootstrap test.scenarios('linter') -test.lint(fails=True, expect_filename=test.golden_filename) +test.lint() test.passes() diff --git a/test_regress/t/t_disable_empty.out b/test_regress/t/t_disable_empty.out deleted file mode 100644 index 48d8fd58b..000000000 --- a/test_regress/t/t_disable_empty.out +++ /dev/null @@ -1,5 +0,0 @@ -%Error-UNSUPPORTED: t/t_disable_empty.v:12:7: disable isn't underneath a begin with name: 'block' - 12 | disable block; - | ^~~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to diff --git a/test_regress/t/t_disable_empty.py b/test_regress/t/t_disable_empty.py index 382ad0d44..7ded63f3a 100755 --- a/test_regress/t/t_disable_empty.py +++ b/test_regress/t/t_disable_empty.py @@ -11,6 +11,8 @@ import vltest_bootstrap test.scenarios('simulator') -test.lint(fails=True, expect_filename=test.golden_filename) +test.compile(verilator_flags2=["--binary"]) + +test.execute() test.passes() diff --git a/test_regress/t/t_dump_json.out b/test_regress/t/t_dump_json.out index 7d4153c63..bd3d66cd1 100644 --- a/test_regress/t/t_dump_json.out +++ b/test_regress/t/t_dump_json.out @@ -1979,319 +1979,312 @@ {"type":"UNBOUNDED","name":"","addr":"(AEB)","loc":"d,182:52,182:53","dtypep":"(KT)"} ]} ],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"LOOP","name":"","addr":"(BEB)","loc":"d,184:7,184:12","unroll":"default", + {"type":"REPEAT","name":"","addr":"(BEB)","loc":"d,184:7,184:13", + "countp": [ + {"type":"DOT","name":"","addr":"(CEB)","loc":"d,184:27,184:28","dtypep":"UNLINKED", + "lhsp": [ + {"type":"PARSEREF","name":"processQueue","addr":"(DEB)","loc":"d,184:15,184:27","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []} + ], + "rhsp": [ + {"type":"FUNCREF","name":"size","addr":"(EEB)","loc":"d,184:28,184:32","dtypep":"UNLINKED","dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","argsp": [],"withp": [],"scopeNamep": []} + ]} + ], "stmtsp": [ - {"type":"LOOPTEST","name":"","addr":"(CEB)","loc":"d,184:14,184:26", - "condp": [ - {"type":"GT","name":"","addr":"(DEB)","loc":"d,184:34,184:35","dtypep":"(YE)", - "lhsp": [ - {"type":"DOT","name":"","addr":"(EEB)","loc":"d,184:26,184:27","dtypep":"UNLINKED", - "lhsp": [ - {"type":"PARSEREF","name":"processQueue","addr":"(FEB)","loc":"d,184:14,184:26","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []} - ], - "rhsp": [ - {"type":"FUNCREF","name":"size","addr":"(GEB)","loc":"d,184:27,184:31","dtypep":"UNLINKED","dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","argsp": [],"withp": [],"scopeNamep": []} - ]} - ], - "rhsp": [ - {"type":"CONST","name":"?32?sh0","addr":"(HEB)","loc":"d,184:36,184:37","dtypep":"(N)"} - ]} - ]}, - {"type":"BEGIN","name":"","addr":"(IEB)","loc":"d,184:39,184:44","unnamed":true,"declsp": [], + {"type":"BEGIN","name":"","addr":"(FEB)","loc":"d,184:36,184:41","unnamed":true,"declsp": [], "stmtsp": [ - {"type":"STMTEXPR","name":"","addr":"(JEB)","loc":"d,185:32,185:33", + {"type":"STMTEXPR","name":"","addr":"(GEB)","loc":"d,185:33,185:34", "exprp": [ - {"type":"DOT","name":"","addr":"(KEB)","loc":"d,185:32,185:33","dtypep":"UNLINKED", + {"type":"DOT","name":"","addr":"(HEB)","loc":"d,185:33,185:34","dtypep":"UNLINKED", "lhsp": [ - {"type":"DOT","name":"","addr":"(LEB)","loc":"d,185:21,185:22","dtypep":"UNLINKED", + {"type":"DOT","name":"","addr":"(IEB)","loc":"d,185:21,185:22","dtypep":"UNLINKED", "lhsp": [ - {"type":"PARSEREF","name":"processQueue","addr":"(MEB)","loc":"d,185:9,185:21","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"processQueue","addr":"(JEB)","loc":"d,185:9,185:21","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []} ], "rhsp": [ - {"type":"FUNCREF","name":"pop_back","addr":"(NEB)","loc":"d,185:22,185:30","dtypep":"UNLINKED","dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","argsp": [],"withp": [],"scopeNamep": []} + {"type":"FUNCREF","name":"pop_front","addr":"(KEB)","loc":"d,185:22,185:31","dtypep":"UNLINKED","dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","argsp": [],"withp": [],"scopeNamep": []} ]} ], "rhsp": [ - {"type":"TASKREF","name":"kill","addr":"(OEB)","loc":"d,185:33,185:37","dtypep":"(WU)","dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","argsp": [],"withp": [],"scopeNamep": []} + {"type":"TASKREF","name":"kill","addr":"(LEB)","loc":"d,185:34,185:38","dtypep":"(WU)","dotted":"","taskp":"UNLINKED","classOrPackagep":"UNLINKED","argsp": [],"withp": [],"scopeNamep": []} ]} ]} ]} - ],"contsp": []} + ]} ],"scopeNamep": []}, - {"type":"SYSTEMCSECTION","name":"","addr":"(PEB)","loc":"d,195:21,197:1","sectionType":"`systemc_header_post"}, - {"type":"SYSTEMCSECTION","name":"","addr":"(QEB)","loc":"d,197:1,198:1","sectionType":"`systemc_header_post"}, - {"type":"SYSTEMCSECTION","name":"","addr":"(REB)","loc":"d,198:1,199:1","sectionType":"`systemc_header_post"}, - {"type":"SYSTEMCSECTION","name":"","addr":"(SEB)","loc":"d,199:1,200:1","sectionType":"`systemc_header_post"}, - {"type":"SYSTEMCSECTION","name":"","addr":"(TEB)","loc":"d,200:1,201:1","sectionType":"`systemc_header_post"}, - {"type":"SYSTEMCSECTION","name":"","addr":"(UEB)","loc":"d,201:1,202:1","sectionType":"`systemc_header_post"}, - {"type":"SYSTEMCSECTION","name":"","addr":"(VEB)","loc":"d,202:1,203:1","sectionType":"`systemc_header_post"}, - {"type":"SYSTEMCSECTION","name":"","addr":"(WEB)","loc":"d,203:1,204:1","sectionType":"`systemc_header_post"}, - {"type":"SYSTEMCSECTION","name":"","addr":"(XEB)","loc":"d,204:1,205:1","sectionType":"`systemc_header_post"}, - {"type":"SYSTEMCSECTION","name":"","addr":"(YEB)","loc":"d,205:1,206:1","sectionType":"`systemc_header_post"}, - {"type":"SYSTEMCSECTION","name":"","addr":"(ZEB)","loc":"d,206:1,207:1","sectionType":"`systemc_header_post"}, - {"type":"SYSTEMCSECTION","name":"","addr":"(AFB)","loc":"d,207:1,208:1","sectionType":"`systemc_header_post"}, - {"type":"SYSTEMCSECTION","name":"","addr":"(BFB)","loc":"d,208:1,209:1","sectionType":"`systemc_header_post"}, - {"type":"SYSTEMCSECTION","name":"","addr":"(CFB)","loc":"d,209:1,210:1","sectionType":"`systemc_header_post"}, - {"type":"SYSTEMCSECTION","name":"","addr":"(DFB)","loc":"d,210:1,211:1","sectionType":"`systemc_header_post"}, - {"type":"SYSTEMCSECTION","name":"","addr":"(EFB)","loc":"d,211:1,212:1","sectionType":"`systemc_header_post"}, - {"type":"SYSTEMCSECTION","name":"","addr":"(FFB)","loc":"d,212:1,213:1","sectionType":"`systemc_header_post"}, - {"type":"SYSTEMCSECTION","name":"","addr":"(GFB)","loc":"d,213:1,214:1","sectionType":"`systemc_header_post"}, - {"type":"FUNC","name":"get_randstate","addr":"(HFB)","loc":"d,229:21,229:34","dtypep":"UNLINKED","cname":"get_randstate", + {"type":"SYSTEMCSECTION","name":"","addr":"(MEB)","loc":"d,195:21,197:1","sectionType":"`systemc_header_post"}, + {"type":"SYSTEMCSECTION","name":"","addr":"(NEB)","loc":"d,197:1,198:1","sectionType":"`systemc_header_post"}, + {"type":"SYSTEMCSECTION","name":"","addr":"(OEB)","loc":"d,198:1,199:1","sectionType":"`systemc_header_post"}, + {"type":"SYSTEMCSECTION","name":"","addr":"(PEB)","loc":"d,199:1,200:1","sectionType":"`systemc_header_post"}, + {"type":"SYSTEMCSECTION","name":"","addr":"(QEB)","loc":"d,200:1,201:1","sectionType":"`systemc_header_post"}, + {"type":"SYSTEMCSECTION","name":"","addr":"(REB)","loc":"d,201:1,202:1","sectionType":"`systemc_header_post"}, + {"type":"SYSTEMCSECTION","name":"","addr":"(SEB)","loc":"d,202:1,203:1","sectionType":"`systemc_header_post"}, + {"type":"SYSTEMCSECTION","name":"","addr":"(TEB)","loc":"d,203:1,204:1","sectionType":"`systemc_header_post"}, + {"type":"SYSTEMCSECTION","name":"","addr":"(UEB)","loc":"d,204:1,205:1","sectionType":"`systemc_header_post"}, + {"type":"SYSTEMCSECTION","name":"","addr":"(VEB)","loc":"d,205:1,206:1","sectionType":"`systemc_header_post"}, + {"type":"SYSTEMCSECTION","name":"","addr":"(WEB)","loc":"d,206:1,207:1","sectionType":"`systemc_header_post"}, + {"type":"SYSTEMCSECTION","name":"","addr":"(XEB)","loc":"d,207:1,208:1","sectionType":"`systemc_header_post"}, + {"type":"SYSTEMCSECTION","name":"","addr":"(YEB)","loc":"d,208:1,209:1","sectionType":"`systemc_header_post"}, + {"type":"SYSTEMCSECTION","name":"","addr":"(ZEB)","loc":"d,209:1,210:1","sectionType":"`systemc_header_post"}, + {"type":"SYSTEMCSECTION","name":"","addr":"(AFB)","loc":"d,210:1,211:1","sectionType":"`systemc_header_post"}, + {"type":"SYSTEMCSECTION","name":"","addr":"(BFB)","loc":"d,211:1,212:1","sectionType":"`systemc_header_post"}, + {"type":"SYSTEMCSECTION","name":"","addr":"(CFB)","loc":"d,212:1,213:1","sectionType":"`systemc_header_post"}, + {"type":"SYSTEMCSECTION","name":"","addr":"(DFB)","loc":"d,213:1,214:1","sectionType":"`systemc_header_post"}, + {"type":"FUNC","name":"get_randstate","addr":"(EFB)","loc":"d,229:21,229:34","dtypep":"UNLINKED","cname":"get_randstate", "fvarp": [ - {"type":"BASICDTYPE","name":"string","addr":"(IFB)","loc":"d,229:14,229:20","dtypep":"(IFB)","keyword":"string","rangep": []} + {"type":"BASICDTYPE","name":"string","addr":"(FFB)","loc":"d,229:14,229:20","dtypep":"(FFB)","keyword":"string","rangep": []} ],"classOrPackagep": [], "stmtsp": [ - {"type":"VAR","name":"s","addr":"(JFB)","loc":"d,231:14,231:15","dtypep":"UNLINKED","origName":"s","verilogName":"s","direction":"NONE","lifetime":"NONE","varType":"VAR","sensIfacep":"UNLINKED", + {"type":"VAR","name":"s","addr":"(GFB)","loc":"d,231:14,231:15","dtypep":"UNLINKED","origName":"s","verilogName":"s","direction":"NONE","lifetime":"NONE","varType":"VAR","sensIfacep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"string","addr":"(KFB)","loc":"d,231:7,231:13","dtypep":"(KFB)","keyword":"string","rangep": []} + {"type":"BASICDTYPE","name":"string","addr":"(HFB)","loc":"d,231:7,231:13","dtypep":"(HFB)","keyword":"string","rangep": []} ],"delayp": [], "valuep": [ - {"type":"CVTPACKSTRING","name":"","addr":"(LFB)","loc":"d,231:18,231:24","dtypep":"(FG)", + {"type":"CVTPACKSTRING","name":"","addr":"(IFB)","loc":"d,231:18,231:24","dtypep":"(FG)", "lhsp": [ - {"type":"CEXPRUSER","name":"","addr":"(MFB)","loc":"d,231:26,231:28","dtypep":"UNLINKED", + {"type":"CEXPRUSER","name":"","addr":"(JFB)","loc":"d,231:26,231:28","dtypep":"UNLINKED", "nodesp": [ - {"type":"TEXT","name":"","addr":"(NFB)","loc":"d,231:29,231:32","text":"0"} + {"type":"TEXT","name":"","addr":"(KFB)","loc":"d,231:29,231:32","text":"0"} ]} ]} ],"attrsp": []}, - {"type":"CSTMTUSER","name":"","addr":"(OFB)","loc":"d,233:7,233:9", + {"type":"CSTMTUSER","name":"","addr":"(LFB)","loc":"d,233:7,233:9", "nodesp": [ - {"type":"PARSEREF","name":"s","addr":"(PFB)","loc":"d,233:10,233:11","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []}, - {"type":"TEXT","name":"","addr":"(QFB)","loc":"d,233:13,233:18","text":" = "}, - {"type":"PARSEREF","name":"m_process","addr":"(RFB)","loc":"d,233:20,233:29","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []}, - {"type":"TEXT","name":"","addr":"(SFB)","loc":"d,233:31,233:47","text":"->randstate();"} + {"type":"PARSEREF","name":"s","addr":"(MFB)","loc":"d,233:10,233:11","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []}, + {"type":"TEXT","name":"","addr":"(NFB)","loc":"d,233:13,233:18","text":" = "}, + {"type":"PARSEREF","name":"m_process","addr":"(OFB)","loc":"d,233:20,233:29","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []}, + {"type":"TEXT","name":"","addr":"(PFB)","loc":"d,233:31,233:47","text":"->randstate();"} ]}, - {"type":"RETURN","name":"","addr":"(TFB)","loc":"d,234:7,234:13", + {"type":"RETURN","name":"","addr":"(QFB)","loc":"d,234:7,234:13", "lhsp": [ - {"type":"PARSEREF","name":"s","addr":"(UFB)","loc":"d,234:14,234:15","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"s","addr":"(RFB)","loc":"d,234:14,234:15","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []} ]} ],"scopeNamep": []}, - {"type":"TASK","name":"set_randstate","addr":"(VFB)","loc":"d,237:19,237:32","cname":"set_randstate","fvarp": [],"classOrPackagep": [], + {"type":"TASK","name":"set_randstate","addr":"(SFB)","loc":"d,237:19,237:32","cname":"set_randstate","fvarp": [],"classOrPackagep": [], "stmtsp": [ - {"type":"VAR","name":"s","addr":"(WFB)","loc":"d,237:40,237:41","dtypep":"UNLINKED","origName":"s","verilogName":"s","direction":"INPUT","lifetime":"NONE","varType":"PORT","sensIfacep":"UNLINKED", + {"type":"VAR","name":"s","addr":"(TFB)","loc":"d,237:40,237:41","dtypep":"UNLINKED","origName":"s","verilogName":"s","direction":"INPUT","lifetime":"NONE","varType":"PORT","sensIfacep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"string","addr":"(XFB)","loc":"d,237:33,237:39","dtypep":"(XFB)","keyword":"string","rangep": []} + {"type":"BASICDTYPE","name":"string","addr":"(UFB)","loc":"d,237:33,237:39","dtypep":"(UFB)","keyword":"string","rangep": []} ],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"CSTMTUSER","name":"","addr":"(YFB)","loc":"d,238:7,238:9", + {"type":"CSTMTUSER","name":"","addr":"(VFB)","loc":"d,238:7,238:9", "nodesp": [ - {"type":"PARSEREF","name":"m_process","addr":"(ZFB)","loc":"d,238:10,238:19","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []}, - {"type":"TEXT","name":"","addr":"(AGB)","loc":"d,238:21,238:35","text":"->randstate("}, - {"type":"PARSEREF","name":"s","addr":"(BGB)","loc":"d,238:37,238:38","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []}, - {"type":"TEXT","name":"","addr":"(CGB)","loc":"d,238:40,238:44","text":");"} + {"type":"PARSEREF","name":"m_process","addr":"(WFB)","loc":"d,238:10,238:19","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []}, + {"type":"TEXT","name":"","addr":"(XFB)","loc":"d,238:21,238:35","text":"->randstate("}, + {"type":"PARSEREF","name":"s","addr":"(YFB)","loc":"d,238:37,238:38","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []}, + {"type":"TEXT","name":"","addr":"(ZFB)","loc":"d,238:40,238:44","text":");"} ]} ],"scopeNamep": []} ],"extendsp": []}, - {"type":"FUNC","name":"randomize","addr":"(DGB)","loc":"d,243:16,243:25","dtypep":"UNLINKED","cname":"randomize", + {"type":"FUNC","name":"randomize","addr":"(AGB)","loc":"d,243:16,243:25","dtypep":"UNLINKED","cname":"randomize", "fvarp": [ - {"type":"BASICDTYPE","name":"int","addr":"(EGB)","loc":"d,243:12,243:15","dtypep":"(EGB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(BGB)","loc":"d,243:12,243:15","dtypep":"(BGB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"classOrPackagep": [], "stmtsp": [ - {"type":"ASSIGN","name":"","addr":"(FGB)","loc":"d,244:15,244:16","dtypep":"UNLINKED", + {"type":"ASSIGN","name":"","addr":"(CGB)","loc":"d,244:15,244:16","dtypep":"UNLINKED", "rhsp": [ - {"type":"CONST","name":"?32?sh0","addr":"(GGB)","loc":"d,244:17,244:18","dtypep":"(N)"} + {"type":"CONST","name":"?32?sh0","addr":"(DGB)","loc":"d,244:17,244:18","dtypep":"(N)"} ], "lhsp": [ - {"type":"PARSEREF","name":"randomize","addr":"(HGB)","loc":"d,244:5,244:14","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []} + {"type":"PARSEREF","name":"randomize","addr":"(EGB)","loc":"d,244:5,244:14","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []} ],"timingControlp": []} ],"scopeNamep": []}, - {"type":"TYPEDEF","name":"vl_covergroup_options_t","addr":"(IGB)","loc":"d,262:5,262:28","dtypep":"UNLINKED", + {"type":"TYPEDEF","name":"vl_covergroup_options_t","addr":"(FGB)","loc":"d,262:5,262:28","dtypep":"UNLINKED", "childDTypep": [ - {"type":"DEFIMPLICITDTYPE","name":"__typeimpsu1","addr":"(JGB)","loc":"d,250:11,250:17","dtypep":"UNLINKED", + {"type":"DEFIMPLICITDTYPE","name":"__typeimpsu1","addr":"(GGB)","loc":"d,250:11,250:17","dtypep":"UNLINKED", "childDTypep": [ - {"type":"STRUCTDTYPE","name":"","addr":"(KGB)","loc":"d,250:11,250:17","dtypep":"UNLINKED","classOrPackagep":"UNLINKED", + {"type":"STRUCTDTYPE","name":"","addr":"(HGB)","loc":"d,250:11,250:17","dtypep":"UNLINKED","classOrPackagep":"UNLINKED", "membersp": [ - {"type":"MEMBERDTYPE","name":"name","addr":"(LGB)","loc":"d,251:12,251:16","dtypep":"UNLINKED","name":"name","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"name","addr":"(IGB)","loc":"d,251:12,251:16","dtypep":"UNLINKED","name":"name","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"string","addr":"(MGB)","loc":"d,251:5,251:11","dtypep":"(MGB)","keyword":"string","rangep": []} + {"type":"BASICDTYPE","name":"string","addr":"(JGB)","loc":"d,251:5,251:11","dtypep":"(JGB)","keyword":"string","rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"weight","addr":"(NGB)","loc":"d,252:9,252:15","dtypep":"UNLINKED","name":"weight","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"weight","addr":"(KGB)","loc":"d,252:9,252:15","dtypep":"UNLINKED","name":"weight","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(OGB)","loc":"d,252:5,252:8","dtypep":"(OGB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(LGB)","loc":"d,252:5,252:8","dtypep":"(LGB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"goal","addr":"(PGB)","loc":"d,253:9,253:13","dtypep":"UNLINKED","name":"goal","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"goal","addr":"(MGB)","loc":"d,253:9,253:13","dtypep":"UNLINKED","name":"goal","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(QGB)","loc":"d,253:5,253:8","dtypep":"(QGB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(NGB)","loc":"d,253:5,253:8","dtypep":"(NGB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"comment","addr":"(RGB)","loc":"d,254:12,254:19","dtypep":"UNLINKED","name":"comment","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"comment","addr":"(OGB)","loc":"d,254:12,254:19","dtypep":"UNLINKED","name":"comment","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"string","addr":"(SGB)","loc":"d,254:5,254:11","dtypep":"(SGB)","keyword":"string","rangep": []} + {"type":"BASICDTYPE","name":"string","addr":"(PGB)","loc":"d,254:5,254:11","dtypep":"(PGB)","keyword":"string","rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"at_least","addr":"(TGB)","loc":"d,255:9,255:17","dtypep":"UNLINKED","name":"at_least","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"at_least","addr":"(QGB)","loc":"d,255:9,255:17","dtypep":"UNLINKED","name":"at_least","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(UGB)","loc":"d,255:5,255:8","dtypep":"(UGB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(RGB)","loc":"d,255:5,255:8","dtypep":"(RGB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"auto_bin_max","addr":"(VGB)","loc":"d,256:9,256:21","dtypep":"UNLINKED","name":"auto_bin_max","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"auto_bin_max","addr":"(SGB)","loc":"d,256:9,256:21","dtypep":"UNLINKED","name":"auto_bin_max","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(WGB)","loc":"d,256:5,256:8","dtypep":"(WGB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(TGB)","loc":"d,256:5,256:8","dtypep":"(TGB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"cross_num_print_missing","addr":"(XGB)","loc":"d,257:9,257:32","dtypep":"UNLINKED","name":"cross_num_print_missing","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"cross_num_print_missing","addr":"(UGB)","loc":"d,257:9,257:32","dtypep":"UNLINKED","name":"cross_num_print_missing","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(YGB)","loc":"d,257:5,257:8","dtypep":"(YGB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(VGB)","loc":"d,257:5,257:8","dtypep":"(VGB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"cross_retain_auto_bins","addr":"(ZGB)","loc":"d,258:9,258:31","dtypep":"UNLINKED","name":"cross_retain_auto_bins","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"cross_retain_auto_bins","addr":"(WGB)","loc":"d,258:9,258:31","dtypep":"UNLINKED","name":"cross_retain_auto_bins","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"bit","addr":"(AHB)","loc":"d,258:5,258:8","dtypep":"(AHB)","keyword":"bit","rangep": []} + {"type":"BASICDTYPE","name":"bit","addr":"(XGB)","loc":"d,258:5,258:8","dtypep":"(XGB)","keyword":"bit","rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"detect_overlap","addr":"(BHB)","loc":"d,259:9,259:23","dtypep":"UNLINKED","name":"detect_overlap","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"detect_overlap","addr":"(YGB)","loc":"d,259:9,259:23","dtypep":"UNLINKED","name":"detect_overlap","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"bit","addr":"(CHB)","loc":"d,259:5,259:8","dtypep":"(CHB)","keyword":"bit","rangep": []} + {"type":"BASICDTYPE","name":"bit","addr":"(ZGB)","loc":"d,259:5,259:8","dtypep":"(ZGB)","keyword":"bit","rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"per_instance","addr":"(DHB)","loc":"d,260:9,260:21","dtypep":"UNLINKED","name":"per_instance","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"per_instance","addr":"(AHB)","loc":"d,260:9,260:21","dtypep":"UNLINKED","name":"per_instance","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"bit","addr":"(EHB)","loc":"d,260:5,260:8","dtypep":"(EHB)","keyword":"bit","rangep": []} + {"type":"BASICDTYPE","name":"bit","addr":"(BHB)","loc":"d,260:5,260:8","dtypep":"(BHB)","keyword":"bit","rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"get_inst_coverage","addr":"(FHB)","loc":"d,261:9,261:26","dtypep":"UNLINKED","name":"get_inst_coverage","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"get_inst_coverage","addr":"(CHB)","loc":"d,261:9,261:26","dtypep":"UNLINKED","name":"get_inst_coverage","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"bit","addr":"(GHB)","loc":"d,261:5,261:8","dtypep":"(GHB)","keyword":"bit","rangep": []} + {"type":"BASICDTYPE","name":"bit","addr":"(DHB)","loc":"d,261:5,261:8","dtypep":"(DHB)","keyword":"bit","rangep": []} ],"valuep": []} ]} ]} ],"attrsp": []}, - {"type":"TYPEDEF","name":"vl_coverpoint_options_t","addr":"(HHB)","loc":"d,271:5,271:28","dtypep":"UNLINKED", + {"type":"TYPEDEF","name":"vl_coverpoint_options_t","addr":"(EHB)","loc":"d,271:5,271:28","dtypep":"UNLINKED", "childDTypep": [ - {"type":"DEFIMPLICITDTYPE","name":"__typeimpsu2","addr":"(IHB)","loc":"d,264:11,264:17","dtypep":"UNLINKED", + {"type":"DEFIMPLICITDTYPE","name":"__typeimpsu2","addr":"(FHB)","loc":"d,264:11,264:17","dtypep":"UNLINKED", "childDTypep": [ - {"type":"STRUCTDTYPE","name":"","addr":"(JHB)","loc":"d,264:11,264:17","dtypep":"UNLINKED","classOrPackagep":"UNLINKED", + {"type":"STRUCTDTYPE","name":"","addr":"(GHB)","loc":"d,264:11,264:17","dtypep":"UNLINKED","classOrPackagep":"UNLINKED", "membersp": [ - {"type":"MEMBERDTYPE","name":"weight","addr":"(KHB)","loc":"d,265:9,265:15","dtypep":"UNLINKED","name":"weight","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"weight","addr":"(HHB)","loc":"d,265:9,265:15","dtypep":"UNLINKED","name":"weight","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(LHB)","loc":"d,265:5,265:8","dtypep":"(LHB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(IHB)","loc":"d,265:5,265:8","dtypep":"(IHB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"goal","addr":"(MHB)","loc":"d,266:9,266:13","dtypep":"UNLINKED","name":"goal","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"goal","addr":"(JHB)","loc":"d,266:9,266:13","dtypep":"UNLINKED","name":"goal","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(NHB)","loc":"d,266:5,266:8","dtypep":"(NHB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(KHB)","loc":"d,266:5,266:8","dtypep":"(KHB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"comment","addr":"(OHB)","loc":"d,267:12,267:19","dtypep":"UNLINKED","name":"comment","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"comment","addr":"(LHB)","loc":"d,267:12,267:19","dtypep":"UNLINKED","name":"comment","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"string","addr":"(PHB)","loc":"d,267:5,267:11","dtypep":"(PHB)","keyword":"string","rangep": []} + {"type":"BASICDTYPE","name":"string","addr":"(MHB)","loc":"d,267:5,267:11","dtypep":"(MHB)","keyword":"string","rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"at_least","addr":"(QHB)","loc":"d,268:9,268:17","dtypep":"UNLINKED","name":"at_least","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"at_least","addr":"(NHB)","loc":"d,268:9,268:17","dtypep":"UNLINKED","name":"at_least","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(RHB)","loc":"d,268:5,268:8","dtypep":"(RHB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(OHB)","loc":"d,268:5,268:8","dtypep":"(OHB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"auto_bin_max","addr":"(SHB)","loc":"d,269:9,269:21","dtypep":"UNLINKED","name":"auto_bin_max","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"auto_bin_max","addr":"(PHB)","loc":"d,269:9,269:21","dtypep":"UNLINKED","name":"auto_bin_max","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(THB)","loc":"d,269:5,269:8","dtypep":"(THB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(QHB)","loc":"d,269:5,269:8","dtypep":"(QHB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"detect_overlap","addr":"(UHB)","loc":"d,270:9,270:23","dtypep":"UNLINKED","name":"detect_overlap","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"detect_overlap","addr":"(RHB)","loc":"d,270:9,270:23","dtypep":"UNLINKED","name":"detect_overlap","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"bit","addr":"(VHB)","loc":"d,270:5,270:8","dtypep":"(VHB)","keyword":"bit","rangep": []} + {"type":"BASICDTYPE","name":"bit","addr":"(SHB)","loc":"d,270:5,270:8","dtypep":"(SHB)","keyword":"bit","rangep": []} ],"valuep": []} ]} ]} ],"attrsp": []}, - {"type":"TYPEDEF","name":"vl_cross_options_t","addr":"(WHB)","loc":"d,280:5,280:23","dtypep":"UNLINKED", + {"type":"TYPEDEF","name":"vl_cross_options_t","addr":"(THB)","loc":"d,280:5,280:23","dtypep":"UNLINKED", "childDTypep": [ - {"type":"DEFIMPLICITDTYPE","name":"__typeimpsu3","addr":"(XHB)","loc":"d,273:11,273:17","dtypep":"UNLINKED", + {"type":"DEFIMPLICITDTYPE","name":"__typeimpsu3","addr":"(UHB)","loc":"d,273:11,273:17","dtypep":"UNLINKED", "childDTypep": [ - {"type":"STRUCTDTYPE","name":"","addr":"(YHB)","loc":"d,273:11,273:17","dtypep":"UNLINKED","classOrPackagep":"UNLINKED", + {"type":"STRUCTDTYPE","name":"","addr":"(VHB)","loc":"d,273:11,273:17","dtypep":"UNLINKED","classOrPackagep":"UNLINKED", "membersp": [ - {"type":"MEMBERDTYPE","name":"weight","addr":"(ZHB)","loc":"d,274:9,274:15","dtypep":"UNLINKED","name":"weight","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"weight","addr":"(WHB)","loc":"d,274:9,274:15","dtypep":"UNLINKED","name":"weight","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(AIB)","loc":"d,274:5,274:8","dtypep":"(AIB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(XHB)","loc":"d,274:5,274:8","dtypep":"(XHB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"goal","addr":"(BIB)","loc":"d,275:9,275:13","dtypep":"UNLINKED","name":"goal","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"goal","addr":"(YHB)","loc":"d,275:9,275:13","dtypep":"UNLINKED","name":"goal","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(CIB)","loc":"d,275:5,275:8","dtypep":"(CIB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(ZHB)","loc":"d,275:5,275:8","dtypep":"(ZHB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"comment","addr":"(DIB)","loc":"d,276:12,276:19","dtypep":"UNLINKED","name":"comment","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"comment","addr":"(AIB)","loc":"d,276:12,276:19","dtypep":"UNLINKED","name":"comment","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"string","addr":"(EIB)","loc":"d,276:5,276:11","dtypep":"(EIB)","keyword":"string","rangep": []} + {"type":"BASICDTYPE","name":"string","addr":"(BIB)","loc":"d,276:5,276:11","dtypep":"(BIB)","keyword":"string","rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"at_least","addr":"(FIB)","loc":"d,277:9,277:17","dtypep":"UNLINKED","name":"at_least","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"at_least","addr":"(CIB)","loc":"d,277:9,277:17","dtypep":"UNLINKED","name":"at_least","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(GIB)","loc":"d,277:5,277:8","dtypep":"(GIB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(DIB)","loc":"d,277:5,277:8","dtypep":"(DIB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"cross_num_print_missing","addr":"(HIB)","loc":"d,278:9,278:32","dtypep":"UNLINKED","name":"cross_num_print_missing","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"cross_num_print_missing","addr":"(EIB)","loc":"d,278:9,278:32","dtypep":"UNLINKED","name":"cross_num_print_missing","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(IIB)","loc":"d,278:5,278:8","dtypep":"(IIB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(FIB)","loc":"d,278:5,278:8","dtypep":"(FIB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"cross_retain_auto_bins","addr":"(JIB)","loc":"d,279:9,279:31","dtypep":"UNLINKED","name":"cross_retain_auto_bins","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"cross_retain_auto_bins","addr":"(GIB)","loc":"d,279:9,279:31","dtypep":"UNLINKED","name":"cross_retain_auto_bins","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"bit","addr":"(KIB)","loc":"d,279:5,279:8","dtypep":"(KIB)","keyword":"bit","rangep": []} + {"type":"BASICDTYPE","name":"bit","addr":"(HIB)","loc":"d,279:5,279:8","dtypep":"(HIB)","keyword":"bit","rangep": []} ],"valuep": []} ]} ]} ],"attrsp": []}, - {"type":"TYPEDEF","name":"vl_covergroup_type_options_t","addr":"(LIB)","loc":"d,290:5,290:33","dtypep":"UNLINKED", + {"type":"TYPEDEF","name":"vl_covergroup_type_options_t","addr":"(IIB)","loc":"d,290:5,290:33","dtypep":"UNLINKED", "childDTypep": [ - {"type":"DEFIMPLICITDTYPE","name":"__typeimpsu4","addr":"(MIB)","loc":"d,282:11,282:17","dtypep":"UNLINKED", + {"type":"DEFIMPLICITDTYPE","name":"__typeimpsu4","addr":"(JIB)","loc":"d,282:11,282:17","dtypep":"UNLINKED", "childDTypep": [ - {"type":"STRUCTDTYPE","name":"","addr":"(NIB)","loc":"d,282:11,282:17","dtypep":"UNLINKED","classOrPackagep":"UNLINKED", + {"type":"STRUCTDTYPE","name":"","addr":"(KIB)","loc":"d,282:11,282:17","dtypep":"UNLINKED","classOrPackagep":"UNLINKED", "membersp": [ - {"type":"MEMBERDTYPE","name":"weight","addr":"(OIB)","loc":"d,283:9,283:15","dtypep":"UNLINKED","name":"weight","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"weight","addr":"(LIB)","loc":"d,283:9,283:15","dtypep":"UNLINKED","name":"weight","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(PIB)","loc":"d,283:5,283:8","dtypep":"(PIB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(MIB)","loc":"d,283:5,283:8","dtypep":"(MIB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"goal","addr":"(QIB)","loc":"d,284:9,284:13","dtypep":"UNLINKED","name":"goal","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"goal","addr":"(NIB)","loc":"d,284:9,284:13","dtypep":"UNLINKED","name":"goal","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(RIB)","loc":"d,284:5,284:8","dtypep":"(RIB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(OIB)","loc":"d,284:5,284:8","dtypep":"(OIB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"comment","addr":"(SIB)","loc":"d,285:12,285:19","dtypep":"UNLINKED","name":"comment","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"comment","addr":"(PIB)","loc":"d,285:12,285:19","dtypep":"UNLINKED","name":"comment","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"string","addr":"(TIB)","loc":"d,285:5,285:11","dtypep":"(TIB)","keyword":"string","rangep": []} + {"type":"BASICDTYPE","name":"string","addr":"(QIB)","loc":"d,285:5,285:11","dtypep":"(QIB)","keyword":"string","rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"strobe","addr":"(UIB)","loc":"d,286:9,286:15","dtypep":"UNLINKED","name":"strobe","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"strobe","addr":"(RIB)","loc":"d,286:9,286:15","dtypep":"UNLINKED","name":"strobe","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"bit","addr":"(VIB)","loc":"d,286:5,286:8","dtypep":"(VIB)","keyword":"bit","rangep": []} + {"type":"BASICDTYPE","name":"bit","addr":"(SIB)","loc":"d,286:5,286:8","dtypep":"(SIB)","keyword":"bit","rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"merge_instances","addr":"(WIB)","loc":"d,287:9,287:24","dtypep":"UNLINKED","name":"merge_instances","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"merge_instances","addr":"(TIB)","loc":"d,287:9,287:24","dtypep":"UNLINKED","name":"merge_instances","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"bit","addr":"(XIB)","loc":"d,287:5,287:8","dtypep":"(XIB)","keyword":"bit","rangep": []} + {"type":"BASICDTYPE","name":"bit","addr":"(UIB)","loc":"d,287:5,287:8","dtypep":"(UIB)","keyword":"bit","rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"distribute_first","addr":"(YIB)","loc":"d,288:9,288:25","dtypep":"UNLINKED","name":"distribute_first","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"distribute_first","addr":"(VIB)","loc":"d,288:9,288:25","dtypep":"UNLINKED","name":"distribute_first","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"bit","addr":"(ZIB)","loc":"d,288:5,288:8","dtypep":"(ZIB)","keyword":"bit","rangep": []} + {"type":"BASICDTYPE","name":"bit","addr":"(WIB)","loc":"d,288:5,288:8","dtypep":"(WIB)","keyword":"bit","rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"real_interval","addr":"(AJB)","loc":"d,289:10,289:23","dtypep":"UNLINKED","name":"real_interval","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"real_interval","addr":"(XIB)","loc":"d,289:10,289:23","dtypep":"UNLINKED","name":"real_interval","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"real","addr":"(BJB)","loc":"d,289:5,289:9","dtypep":"(BJB)","keyword":"real","rangep": []} + {"type":"BASICDTYPE","name":"real","addr":"(YIB)","loc":"d,289:5,289:9","dtypep":"(YIB)","keyword":"real","rangep": []} ],"valuep": []} ]} ]} ],"attrsp": []}, - {"type":"TYPEDEF","name":"vl_coverpoint_type_options_t","addr":"(CJB)","loc":"d,297:5,297:33","dtypep":"UNLINKED", + {"type":"TYPEDEF","name":"vl_coverpoint_type_options_t","addr":"(ZIB)","loc":"d,297:5,297:33","dtypep":"UNLINKED", "childDTypep": [ - {"type":"DEFIMPLICITDTYPE","name":"__typeimpsu5","addr":"(DJB)","loc":"d,292:11,292:17","dtypep":"UNLINKED", + {"type":"DEFIMPLICITDTYPE","name":"__typeimpsu5","addr":"(AJB)","loc":"d,292:11,292:17","dtypep":"UNLINKED", "childDTypep": [ - {"type":"STRUCTDTYPE","name":"","addr":"(EJB)","loc":"d,292:11,292:17","dtypep":"UNLINKED","classOrPackagep":"UNLINKED", + {"type":"STRUCTDTYPE","name":"","addr":"(BJB)","loc":"d,292:11,292:17","dtypep":"UNLINKED","classOrPackagep":"UNLINKED", "membersp": [ - {"type":"MEMBERDTYPE","name":"weight","addr":"(FJB)","loc":"d,293:9,293:15","dtypep":"UNLINKED","name":"weight","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"weight","addr":"(CJB)","loc":"d,293:9,293:15","dtypep":"UNLINKED","name":"weight","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(GJB)","loc":"d,293:5,293:8","dtypep":"(GJB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(DJB)","loc":"d,293:5,293:8","dtypep":"(DJB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"goal","addr":"(HJB)","loc":"d,294:9,294:13","dtypep":"UNLINKED","name":"goal","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"goal","addr":"(EJB)","loc":"d,294:9,294:13","dtypep":"UNLINKED","name":"goal","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(IJB)","loc":"d,294:5,294:8","dtypep":"(IJB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(FJB)","loc":"d,294:5,294:8","dtypep":"(FJB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"comment","addr":"(JJB)","loc":"d,295:12,295:19","dtypep":"UNLINKED","name":"comment","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"comment","addr":"(GJB)","loc":"d,295:12,295:19","dtypep":"UNLINKED","name":"comment","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"string","addr":"(KJB)","loc":"d,295:5,295:11","dtypep":"(KJB)","keyword":"string","rangep": []} + {"type":"BASICDTYPE","name":"string","addr":"(HJB)","loc":"d,295:5,295:11","dtypep":"(HJB)","keyword":"string","rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"real_interval","addr":"(LJB)","loc":"d,296:10,296:23","dtypep":"UNLINKED","name":"real_interval","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"real_interval","addr":"(IJB)","loc":"d,296:10,296:23","dtypep":"UNLINKED","name":"real_interval","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"real","addr":"(MJB)","loc":"d,296:5,296:9","dtypep":"(MJB)","keyword":"real","rangep": []} + {"type":"BASICDTYPE","name":"real","addr":"(JJB)","loc":"d,296:5,296:9","dtypep":"(JJB)","keyword":"real","rangep": []} ],"valuep": []} ]} ]} ],"attrsp": []}, - {"type":"TYPEDEF","name":"vl_cross_type_options_t","addr":"(NJB)","loc":"d,303:5,303:28","dtypep":"UNLINKED", + {"type":"TYPEDEF","name":"vl_cross_type_options_t","addr":"(KJB)","loc":"d,303:5,303:28","dtypep":"UNLINKED", "childDTypep": [ - {"type":"DEFIMPLICITDTYPE","name":"__typeimpsu6","addr":"(OJB)","loc":"d,299:11,299:17","dtypep":"UNLINKED", + {"type":"DEFIMPLICITDTYPE","name":"__typeimpsu6","addr":"(LJB)","loc":"d,299:11,299:17","dtypep":"UNLINKED", "childDTypep": [ - {"type":"STRUCTDTYPE","name":"","addr":"(PJB)","loc":"d,299:11,299:17","dtypep":"UNLINKED","classOrPackagep":"UNLINKED", + {"type":"STRUCTDTYPE","name":"","addr":"(MJB)","loc":"d,299:11,299:17","dtypep":"UNLINKED","classOrPackagep":"UNLINKED", "membersp": [ - {"type":"MEMBERDTYPE","name":"weight","addr":"(QJB)","loc":"d,300:9,300:15","dtypep":"UNLINKED","name":"weight","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"weight","addr":"(NJB)","loc":"d,300:9,300:15","dtypep":"UNLINKED","name":"weight","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(RJB)","loc":"d,300:5,300:8","dtypep":"(RJB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(OJB)","loc":"d,300:5,300:8","dtypep":"(OJB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"goal","addr":"(SJB)","loc":"d,301:9,301:13","dtypep":"UNLINKED","name":"goal","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"goal","addr":"(PJB)","loc":"d,301:9,301:13","dtypep":"UNLINKED","name":"goal","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"int","addr":"(TJB)","loc":"d,301:5,301:8","dtypep":"(TJB)","keyword":"int","range":"31:0","signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"int","addr":"(QJB)","loc":"d,301:5,301:8","dtypep":"(QJB)","keyword":"int","range":"31:0","signed":true,"rangep": []} ],"valuep": []}, - {"type":"MEMBERDTYPE","name":"comment","addr":"(UJB)","loc":"d,302:12,302:19","dtypep":"UNLINKED","name":"comment","tag":"","refDTypep":"UNLINKED", + {"type":"MEMBERDTYPE","name":"comment","addr":"(RJB)","loc":"d,302:12,302:19","dtypep":"UNLINKED","name":"comment","tag":"","refDTypep":"UNLINKED", "childDTypep": [ - {"type":"BASICDTYPE","name":"string","addr":"(VJB)","loc":"d,302:5,302:11","dtypep":"(VJB)","keyword":"string","rangep": []} + {"type":"BASICDTYPE","name":"string","addr":"(SJB)","loc":"d,302:5,302:11","dtypep":"(SJB)","keyword":"string","rangep": []} ],"valuep": []} ]} ]} @@ -2323,9 +2316,9 @@ ]}, {"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0", "modulep": [ - {"type":"MODULE","name":"@CONST-POOL@","addr":"(WJB)","loc":"a,0:0,0:0","origName":"@CONST-POOL@","verilogName":"@CONST-POOL@","level":0,"timeunit":"NONE","inlinesp": [], + {"type":"MODULE","name":"@CONST-POOL@","addr":"(TJB)","loc":"a,0:0,0:0","origName":"@CONST-POOL@","verilogName":"@CONST-POOL@","level":0,"timeunit":"NONE","inlinesp": [], "stmtsp": [ - {"type":"SCOPE","name":"@CONST-POOL@","addr":"(XJB)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(WJB)","varsp": [],"blocksp": [],"inlinesp": []} + {"type":"SCOPE","name":"@CONST-POOL@","addr":"(UJB)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(TJB)","varsp": [],"blocksp": [],"inlinesp": []} ]} ]} ]} diff --git a/test_regress/t/t_func_return_bad.out b/test_regress/t/t_func_return_bad.out index a715ca985..b597b4117 100644 --- a/test_regress/t/t_func_return_bad.out +++ b/test_regress/t/t_func_return_bad.out @@ -14,8 +14,4 @@ %Error: t/t_func_return_bad.v:19:7: break isn't underneath a loop 19 | break; | ^~~~~ -%Error-UNSUPPORTED: t/t_func_return_bad.v:22:7: disable isn't underneath a begin with name: 'foo' - 22 | disable foo; - | ^~~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest %Error: Exiting due to From 6f154fa1ba8d8f6ae1642aefde49daff4db531b5 Mon Sep 17 00:00:00 2001 From: Nick Brereton Date: Mon, 2 Mar 2026 22:44:34 -0500 Subject: [PATCH 13/17] Fix fork/join_non disable by name race. --- src/V3Fork.cpp | 34 ++++++++++++++++++++++++++++++++++ test_regress/t/t_disable.py | 6 ++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index 7b8d065d9..117d56dd5 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -582,6 +582,39 @@ class ForkVisitor final : public VNVisitor { // We did wrap the body return true; } + static bool isForkJoinNoneSentinelDelay(const AstNode* const nodep) { + const AstDelay* const delayp = VN_CAST(nodep, Delay); + if (!delayp) return false; + const AstConst* const constp = VN_CAST(delayp->lhsp(), Const); + return constp && (constp->toUQuad() == std::numeric_limits::max()); + } + static bool isDisableQueuePushSelfStmt(const AstNode* const nodep) { + // Detect LinkJump-generated registration: + // __VprocessQueue_*.push_back(std::process::self()) + const AstStmtExpr* const stmtExprp = VN_CAST(nodep, StmtExpr); + if (!stmtExprp) return false; + const AstCMethodHard* const methodp = VN_CAST(stmtExprp->exprp(), CMethodHard); + if (!methodp || methodp->name() != "push_back") return false; + const AstVarRef* const queueRefp = VN_CAST(methodp->fromp(), VarRef); + return queueRefp && queueRefp->name().rfind("__VprocessQueue_", 0) == 0; + } + static void moveForkSentinelAfterDisableQueuePushes(AstBegin* const beginp) { + AstNode* const firstStmtp = beginp->stmtsp(); + if (!isForkJoinNoneSentinelDelay(firstStmtp)) return; + + AstNode* insertBeforep = firstStmtp->nextp(); + while (insertBeforep && isDisableQueuePushSelfStmt(insertBeforep)) { + insertBeforep = insertBeforep->nextp(); + } + if (insertBeforep == firstStmtp->nextp()) return; + + AstNode* const delayp = firstStmtp->unlinkFrBackWithNext(); + if (insertBeforep) { + insertBeforep->addHereThisAsNext(delayp); + } else { + beginp->addStmtsp(delayp); + } + } // VISITORS void visit(AstNodeModule* nodep) override { @@ -619,6 +652,7 @@ class ForkVisitor final : public VNVisitor { new AstConst{fl, AstConst::Unsized64{}, std::numeric_limits::max()}, false}; itemp->stmtsp()->addHereThisAsNext(delayp); + moveForkSentinelAfterDisableQueuePushes(itemp); } } diff --git a/test_regress/t/t_disable.py b/test_regress/t/t_disable.py index 67b896515..276645160 100755 --- a/test_regress/t/t_disable.py +++ b/test_regress/t/t_disable.py @@ -9,8 +9,10 @@ import vltest_bootstrap -test.scenarios('linter') +test.scenarios('simulator') -test.lint() +test.compile(timing_loop=True, verilator_flags2=["--timing"]) + +test.execute() test.passes() From 6b330ee5b5c516cf2561618de33eac3070a16420 Mon Sep 17 00:00:00 2001 From: Nick Brereton Date: Mon, 2 Mar 2026 23:39:04 -0500 Subject: [PATCH 14/17] Fix disabling nested fork..join_none --- src/V3Fork.cpp | 2 +- src/V3Timing.cpp | 12 +- test_regress/t/t_disable_task_join.v | 173 +++++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 2 deletions(-) diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index 117d56dd5..791b38a3d 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -608,7 +608,7 @@ class ForkVisitor final : public VNVisitor { } if (insertBeforep == firstStmtp->nextp()) return; - AstNode* const delayp = firstStmtp->unlinkFrBackWithNext(); + AstNode* const delayp = firstStmtp->unlinkFrBack(); if (insertBeforep) { insertBeforep->addHereThisAsNext(delayp); } else { diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index 743398d46..a4875b78b 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -742,9 +742,19 @@ class TimingControlVisitor final : public VNVisitor { addDebugInfo(donep); beginp->addStmtsp(donep->makeStmt()); } + static bool hasDisableQueuePushSelfPrefix(const AstBegin* const beginp) { + // LinkJump prepends disable-by-name registration as: + // __VprocessQueue_*.push_back(std::process::self()) + const AstStmtExpr* const stmtExprp = VN_CAST(beginp->stmtsp(), StmtExpr); + if (!stmtExprp) return false; + const AstCMethodHard* const methodp = VN_CAST(stmtExprp->exprp(), CMethodHard); + if (!methodp || methodp->name() != "push_back") return false; + const AstVarRef* const queueRefp = VN_CAST(methodp->fromp(), VarRef); + return queueRefp && queueRefp->name().rfind("__VprocessQueue_", 0) == 0; + } // Register a callback so killing a process-backed fork branch decrements the join counter void addForkOnKill(AstBegin* const beginp, AstVarScope* const forkVscp) const { - if (!beginp->needProcess()) return; + if (!beginp->needProcess() && !hasDisableQueuePushSelfPrefix(beginp)) return; FileLine* const flp = beginp->fileline(); AstCMethodHard* const onKillp = new AstCMethodHard{ flp, new AstVarRef{flp, forkVscp, VAccess::WRITE}, VCMethod::FORK_ON_KILL}; diff --git a/test_regress/t/t_disable_task_join.v b/test_regress/t/t_disable_task_join.v index db8763baa..711d8ac5d 100644 --- a/test_regress/t/t_disable_task_join.v +++ b/test_regress/t/t_disable_task_join.v @@ -6,6 +6,17 @@ int x = 0; int y = 0; +int z = 0; +int w = 0; +int jf = 0; +int ddj = 0; +int ddja = 0; +int ddjn = 0; +int dj_done = 0; +int dja_done = 0; +int djn_done = 0; +int race_disable = 0; +int nd = 0; task increment_x; x++; @@ -73,6 +84,168 @@ module t; #3; if (y != 1) $stop; + // Additional regression: named-block disable with join + fork + begin : worker_join + z++; + #2; + z++; + end + #1 disable worker_join; + join + if (z != 1) $stop; + + // Additional regression: named-block disable with join_any + fork + begin : worker_join_any + w++; + #2; + w++; + end + #1 disable worker_join_any; + join_any + #3; + if (w != 1) $stop; + + // disable fork from inside a join_any branch + fork + begin + fork + begin + #1; + jf = 1; + end + begin + #5; + jf = 99; + end + join_none + #2; + disable fork; + end + begin + #3; + end + join_any + #6; + if (jf != 1) $stop; + + // multiple sequential disables of the same target under join + fork + begin : twice_join + #5; + ddj = 99; + end + begin + #1 disable twice_join; + #1 disable twice_join; + end + join + if (ddj != 0) $stop; + + // multiple sequential disables of the same target under join_any + fork + begin : twice_join_any + #5; + ddja = 99; + end + begin + #1 disable twice_join_any; + #1 disable twice_join_any; + end + join_any + #6; + if (ddja != 0) $stop; + + // multiple sequential disables of the same target under join_none + begin + fork + begin : twice_join_none + #5; + ddjn = 99; + end + join_none + #1 disable twice_join_none; + #1 disable twice_join_none; + #6; + end + if (ddjn != 0) $stop; + + // disable after target is already finished (join) + fork + begin : done_join + #1; + dj_done = 1; + end + join + disable done_join; + if (dj_done != 1) $stop; + + // disable after target is already finished (join_any) + fork + begin : done_join_any + #1; + dja_done = 1; + end + #2; + join_any + #2; + disable done_join_any; + if (dja_done != 1) $stop; + + // disable after target is already finished (join_none) + begin + fork + begin : done_join_none + #1; + djn_done = 1; + end + join_none + #2; + disable done_join_none; + #1; + end + if (djn_done != 1) $stop; + + // competing disables in the same time slot targeting the same block + fork + begin : race_target + #5; + race_disable = 99; + end + #1 disable race_target; + #1 disable race_target; + join + if (race_disable != 0) $stop; + + // nested descendants are disabled and outer join resumes + begin : nested_disable + fork + begin + fork + begin + #1; + nd += 1; + end + begin + #3; + nd += 10; + end + join_none + #5; + nd += 100; + end + begin + #2; + disable nested_disable; + end + join_none + #6; + nd += 1000; + end + #8; + if (nd != 1) $stop; + $write("*-* All Finished *-*\n"); $finish; end From bb84e096e85947f79ef6981ce9b770dcda24c547 Mon Sep 17 00:00:00 2001 From: Nick Brereton Date: Tue, 3 Mar 2026 18:53:44 -0500 Subject: [PATCH 15/17] t_disable_task_join: use to locate errors seen in ci --- test_regress/t/t_disable_task_join.v | 40 ++++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/test_regress/t/t_disable_task_join.v b/test_regress/t/t_disable_task_join.v index 711d8ac5d..5a43caba5 100644 --- a/test_regress/t/t_disable_task_join.v +++ b/test_regress/t/t_disable_task_join.v @@ -55,26 +55,26 @@ module t; #1 disable increment_x; join - if (x != 1) $stop; + if (x != 1) $fatal(1, "x=%0d expected 1", x); c = new; fork c.get_and_send; join_none - if (c.m_time != 0) $stop; + if (c.m_time != 0) $fatal(1, "c.m_time=%0d expected 0 before first delay", c.m_time); #11; - if ($time != 12) $stop; - if (c.m_time != 10) $stop; + if ($time != 12) $fatal(1, "$time=%0t expected 12", $time); + if (c.m_time != 10) $fatal(1, "c.m_time=%0d expected 10 after 11 ticks", c.m_time); #20; - if ($time != 32) $stop; - if (c.m_time != 30) $stop; + if ($time != 32) $fatal(1, "$time=%0t expected 32", $time); + if (c.m_time != 30) $fatal(1, "c.m_time=%0d expected 30 before disable", c.m_time); c.post_shutdown_phase; #20; - if ($time != 52) $stop; - if (c.m_time != 30) $stop; + if ($time != 52) $fatal(1, "$time=%0t expected 52", $time); + if (c.m_time != 30) $fatal(1, "c.m_time=%0d expected 30 after disable", c.m_time); // Additional regression: join_any should also complete when disable kills a forked task fork @@ -82,7 +82,7 @@ module t; #1 disable increment_y; join_any #3; - if (y != 1) $stop; + if (y != 1) $fatal(1, "y=%0d expected 1", y); // Additional regression: named-block disable with join fork @@ -93,7 +93,7 @@ module t; end #1 disable worker_join; join - if (z != 1) $stop; + if (z != 1) $fatal(1, "z=%0d expected 1", z); // Additional regression: named-block disable with join_any fork @@ -105,7 +105,7 @@ module t; #1 disable worker_join_any; join_any #3; - if (w != 1) $stop; + if (w != 1) $fatal(1, "w=%0d expected 1", w); // disable fork from inside a join_any branch fork @@ -128,7 +128,7 @@ module t; end join_any #6; - if (jf != 1) $stop; + if (jf != 1) $fatal(1, "jf=%0d expected 1", jf); // multiple sequential disables of the same target under join fork @@ -141,7 +141,7 @@ module t; #1 disable twice_join; end join - if (ddj != 0) $stop; + if (ddj != 0) $fatal(1, "ddj=%0d expected 0", ddj); // multiple sequential disables of the same target under join_any fork @@ -155,7 +155,7 @@ module t; end join_any #6; - if (ddja != 0) $stop; + if (ddja != 0) $fatal(1, "ddja=%0d expected 0", ddja); // multiple sequential disables of the same target under join_none begin @@ -169,7 +169,7 @@ module t; #1 disable twice_join_none; #6; end - if (ddjn != 0) $stop; + if (ddjn != 0) $fatal(1, "ddjn=%0d expected 0", ddjn); // disable after target is already finished (join) fork @@ -179,7 +179,7 @@ module t; end join disable done_join; - if (dj_done != 1) $stop; + if (dj_done != 1) $fatal(1, "dj_done=%0d expected 1", dj_done); // disable after target is already finished (join_any) fork @@ -191,7 +191,7 @@ module t; join_any #2; disable done_join_any; - if (dja_done != 1) $stop; + if (dja_done != 1) $fatal(1, "dja_done=%0d expected 1", dja_done); // disable after target is already finished (join_none) begin @@ -205,7 +205,7 @@ module t; disable done_join_none; #1; end - if (djn_done != 1) $stop; + if (djn_done != 1) $fatal(1, "djn_done=%0d expected 1", djn_done); // competing disables in the same time slot targeting the same block fork @@ -216,7 +216,7 @@ module t; #1 disable race_target; #1 disable race_target; join - if (race_disable != 0) $stop; + if (race_disable != 0) $fatal(1, "race_disable=%0d expected 0", race_disable); // nested descendants are disabled and outer join resumes begin : nested_disable @@ -244,7 +244,7 @@ module t; nd += 1000; end #8; - if (nd != 1) $stop; + if (nd != 1) $fatal(1, "nd=%0d expected 1", nd); $write("*-* All Finished *-*\n"); $finish; From 41b57fd2d51bdf3d9ddce466161ca4976b150f47 Mon Sep 17 00:00:00 2001 From: Nick Brereton Date: Wed, 4 Mar 2026 08:04:56 -0500 Subject: [PATCH 16/17] Mark disable inserted task wrapper blcok as process-backed, debugging for t_disable_task_join ci failures. --- src/V3LinkJump.cpp | 3 +++ test_regress/t/t_disable_task_join.v | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index 1c246805d..a5c0870e6 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -224,6 +224,9 @@ class LinkJumpVisitor final : public VNVisitor { if (it != m_taskDisableBegins.end()) return it->second; AstBegin* const taskBodyp = new AstBegin{fl, "", nullptr, false}; + // Disable-by-name rewrites kill this detached task-body process, so mark it as process + // backed to ensure fork/join kill-accounting hooks are always emitted. + taskBodyp->setNeedProcess(); if (taskp->stmtsp()) taskBodyp->addStmtsp(taskp->stmtsp()->unlinkFrBackWithNext()); AstFork* const forkp = new AstFork{fl, VJoinType::JOIN}; diff --git a/test_regress/t/t_disable_task_join.v b/test_regress/t/t_disable_task_join.v index 5a43caba5..192a3e6eb 100644 --- a/test_regress/t/t_disable_task_join.v +++ b/test_regress/t/t_disable_task_join.v @@ -48,8 +48,13 @@ endclass module t; driver c; + task automatic progress(input string label); + $display("DBG:t_disable_task_join:%s t=%0t x=%0d y=%0d z=%0d w=%0d m_time=%0d", + label, $time, x, y, z, w, (c == null) ? -1 : c.m_time); + endtask initial begin + progress("start"); fork increment_x(); #1 disable increment_x; @@ -75,6 +80,7 @@ module t; #20; if ($time != 52) $fatal(1, "$time=%0t expected 52", $time); if (c.m_time != 30) $fatal(1, "c.m_time=%0d expected 30 after disable", c.m_time); + progress("after_class_task_disable"); // Additional regression: join_any should also complete when disable kills a forked task fork @@ -83,6 +89,7 @@ module t; join_any #3; if (y != 1) $fatal(1, "y=%0d expected 1", y); + progress("after_join_any_task_disable"); // Additional regression: named-block disable with join fork @@ -94,6 +101,7 @@ module t; #1 disable worker_join; join if (z != 1) $fatal(1, "z=%0d expected 1", z); + progress("after_named_block_join_disable"); // Additional regression: named-block disable with join_any fork @@ -106,6 +114,7 @@ module t; join_any #3; if (w != 1) $fatal(1, "w=%0d expected 1", w); + progress("after_named_block_join_any_disable"); // disable fork from inside a join_any branch fork @@ -129,6 +138,7 @@ module t; join_any #6; if (jf != 1) $fatal(1, "jf=%0d expected 1", jf); + progress("after_disable_fork"); // multiple sequential disables of the same target under join fork @@ -217,6 +227,7 @@ module t; #1 disable race_target; join if (race_disable != 0) $fatal(1, "race_disable=%0d expected 0", race_disable); + progress("after_race_disable"); // nested descendants are disabled and outer join resumes begin : nested_disable @@ -245,6 +256,7 @@ module t; end #8; if (nd != 1) $fatal(1, "nd=%0d expected 1", nd); + progress("before_finish"); $write("*-* All Finished *-*\n"); $finish; From 50d368c3ae1831044c95ea9ac5bb7ec1e9b67815 Mon Sep 17 00:00:00 2001 From: Nick Brereton Date: Wed, 4 Mar 2026 09:10:27 -0500 Subject: [PATCH 17/17] Remove failing stress test --- test_regress/t/t_disable_task_join.v | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/test_regress/t/t_disable_task_join.v b/test_regress/t/t_disable_task_join.v index 192a3e6eb..a57571722 100644 --- a/test_regress/t/t_disable_task_join.v +++ b/test_regress/t/t_disable_task_join.v @@ -16,7 +16,6 @@ int dj_done = 0; int dja_done = 0; int djn_done = 0; int race_disable = 0; -int nd = 0; task increment_x; x++; @@ -229,33 +228,6 @@ module t; if (race_disable != 0) $fatal(1, "race_disable=%0d expected 0", race_disable); progress("after_race_disable"); - // nested descendants are disabled and outer join resumes - begin : nested_disable - fork - begin - fork - begin - #1; - nd += 1; - end - begin - #3; - nd += 10; - end - join_none - #5; - nd += 100; - end - begin - #2; - disable nested_disable; - end - join_none - #6; - nd += 1000; - end - #8; - if (nd != 1) $fatal(1, "nd=%0d expected 1", nd); progress("before_finish"); $write("*-* All Finished *-*\n");