diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index e572b87fb..fe7e640c8 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -180,16 +180,12 @@ class LinkJumpVisitor final : public VNVisitor { fl, new AstMethodCall{fl, queueRefp, "push_back", new AstArg{fl, "", processSelfp}}}; } void handleDisableOnFork(AstDisable* const nodep, const std::vector& forks) { - // The support is limited only to disabling a fork from outside that fork. - // It 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 handle is - // pushed to the queue. `disable` statement is replaced with calling `kill()` method on - // each element of the queue. + // 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 + // handle is pushed to the queue. `disable` statement is replaced with calling `kill()` + // method on each element of the queue. FileLine* const fl = nodep->fileline(); const std::string targetName = nodep->targetp()->name(); - if (existsBlockAbove(targetName)) { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork from within same fork"); - } if (m_ftaskp) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork from task / function"); } @@ -222,7 +218,15 @@ class LinkJumpVisitor final : public VNVisitor { = new AstTaskRef{fl, VN_AS(getMemberp(processClassp, "killQueue"), Task), new AstArg{fl, "", queueRefp}}; killQueueCall->classOrPackagep(processClassp); - nodep->addNextHere(new AstStmtExpr{fl, killQueueCall}); + AstStmtExpr* const killStmtp = new AstStmtExpr{fl, killQueueCall}; + nodep->addNextHere(killStmtp); + if (existsBlockAbove(targetName)) { + // process::kill doesn't kill the current process immediately, because it is in the + // running state. Since the current process has to be terminated immediately, we jump + // at the end of the fork that is being disabled + AstJumpBlock* const jumpBlockp = getJumpBlock(nodep->targetp(), false); + killStmtp->addNextHere(new AstJumpGo{fl, jumpBlockp}); + } } static bool directlyUnderFork(const AstNode* const nodep) { if (nodep->backp()->nextp() == nodep) return directlyUnderFork(nodep->backp()); diff --git a/test_regress/t/t_disable.out b/test_regress/t/t_disable.out index a42b835d4..a70dd874c 100644 --- a/test_regress/t/t_disable.out +++ b/test_regress/t/t_disable.out @@ -1,8 +1,5 @@ -%Error-UNSUPPORTED: t/t_disable.v:11:10: Unsupported: disabling fork from within same fork - 11 | disable foo; - | ^~~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest %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_inside.py b/test_regress/t/t_disable_inside.py new file mode 100755 index 000000000..3476171ff --- /dev/null +++ b/test_regress/t/t_disable_inside.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 by Wilson Snyder. 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-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_inside.v b/test_regress/t/t_disable_inside.v new file mode 100644 index 000000000..04b74b3bf --- /dev/null +++ b/test_regress/t/t_disable_inside.v @@ -0,0 +1,24 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module t ( /*AUTOARG*/); + initial begin + int x = 0; + fork : fork_blk + begin + x = 1; + #2; + x = 2; + end + join_none + #1; + disable fork_blk; + #2; + if (x != 1) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule