diff --git a/src/V3AstNodeStmt.h b/src/V3AstNodeStmt.h index ef96db6f3..5efe38ccc 100644 --- a/src/V3AstNodeStmt.h +++ b/src/V3AstNodeStmt.h @@ -619,6 +619,20 @@ public: int instrCount() const override { return 0; } // Rarely executes bool sameNode(const AstNode* samep) const override { return fileline() == samep->fileline(); } }; +class AstFinishFork final : public AstNodeStmt { + // $finish in fork +public: + explicit AstFinishFork(FileLine* fl) + : ASTGEN_SUPER_FinishFork(fl) {} + ASTGEN_MEMBERS_AstFinishFork; + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering + bool isOutputter() override { return true; } // SPECIAL: $display makes output + bool isUnlikely() const override { return true; } + int instrCount() const override { return 0; } // Rarely executes + bool sameNode(const AstNode* samep) const override { return fileline() == samep->fileline(); } +}; class AstFireEvent final : public AstNodeStmt { // '-> _' and '->> _' event trigger statements // @astgen op1 := operandp : AstNodeExpr diff --git a/src/V3CfgBuilder.cpp b/src/V3CfgBuilder.cpp index 4373a305a..1c8c225e0 100644 --- a/src/V3CfgBuilder.cpp +++ b/src/V3CfgBuilder.cpp @@ -90,6 +90,7 @@ class CfgBuilder final : public VNVisitorConst { void visit(AstComment*) override {} // ignore entirely void visit(AstDisplay* nodep) override { simpleStatement(nodep); } void visit(AstFinish* nodep) override { simpleStatement(nodep); } + void visit(AstFinishFork* nodep) override { simpleStatement(nodep); } void visit(AstStmtExpr* nodep) override { simpleStatement(nodep); } void visit(AstStop* nodep) override { simpleStatement(nodep); } diff --git a/src/V3CfgLiveVariables.cpp b/src/V3CfgLiveVariables.cpp index cf0d8a5a9..313734421 100644 --- a/src/V3CfgLiveVariables.cpp +++ b/src/V3CfgLiveVariables.cpp @@ -165,6 +165,7 @@ class CfgLiveVariables final : VNVisitorConst { void visit(AstAssignW* nodep) override { single(nodep); } void visit(AstDisplay* nodep) override { single(nodep); } void visit(AstFinish* nodep) override { single(nodep); } + void visit(AstFinishFork* nodep) override { single(nodep); } void visit(AstStmtExpr* nodep) override { single(nodep); } void visit(AstStop* nodep) override { single(nodep); } diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index a6f1a6361..328bbba58 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1253,6 +1253,18 @@ public: puts(cvtToStr(nodep->fileline()->lineno())); puts(", \"\");\n"); } + void visit(AstFinishFork* nodep) override { + putns(nodep, "VL_FINISH_MT("); + putsQuoted(protect(nodep->fileline()->filename())); + puts(", "); + puts(cvtToStr(nodep->fileline()->lineno())); + puts(", \"\");\n"); + if (m_cfuncp->isCoroutine()) { + putns(nodep, "co_return;\n"); + } else { + putns(nodep, "return;\n"); + } + } void visit(AstPrintTimeScale* nodep) override { putns(nodep, "VL_PRINTTIMESCALE("); putsQuoted(protect(nodep->prettyName())); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 0f7602bd3..da66640a5 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -556,6 +556,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { puts(";\n"); } void visit(AstFinish* nodep) override { putfs(nodep, "$finish;\n"); } + void visit(AstFinishFork* nodep) override { putfs(nodep, "$finish;\n"); } void visit(AstStmtExpr* nodep) override { iterateConst(nodep->exprp()); puts(";\n"); diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index 1ac3cd51f..428cca95f 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -441,7 +441,10 @@ class LinkJumpVisitor final : public VNVisitor { void visit(AstFinish* nodep) override { if (nodep->user1SetOnce()) return; // Process once iterateChildren(nodep); - if (m_loopp) { + if (m_inFork) { + nodep->replaceWith(new AstFinishFork{nodep->fileline()}); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + } else if (m_loopp) { // Jump to the end of the loop (post-finish) AstJumpBlock* const blockp = getJumpBlock(m_loopp, false); nodep->addNextHere(new AstJumpGo{nodep->fileline(), blockp}); diff --git a/test_regress/t/t_fork_finish.py b/test_regress/t/t_fork_finish.py new file mode 100755 index 000000000..1c061c8c9 --- /dev/null +++ b/test_regress/t/t_fork_finish.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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(verilator_flags2=["--timing"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_fork_finish.v b/test_regress/t/t_fork_finish.v new file mode 100644 index 000000000..3bb0a1193 --- /dev/null +++ b/test_regress/t/t_fork_finish.v @@ -0,0 +1,21 @@ +// 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; + initial begin + forever begin + fork + begin + assert ($c(1)) begin + $write("*-* All Finished *-*\n"); + $finish; + end + wait ($c(1)); + end + join_any + end + end +endmodule