From add4ec6e806ce622e1be0e92f66df8b92ab5bb33 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Fri, 6 Mar 2026 20:46:38 +0100 Subject: [PATCH 01/42] Set lifetime to static Signed-off-by: Kamil Danecki --- src/V3Fork.cpp | 3 ++- src/V3LinkParse.cpp | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index 2e40faa24..f50210308 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -434,6 +434,7 @@ class DynScopeVisitor final : public VNVisitor { void visit(AstVarRef* nodep) override { ForkDynScopeFrame* const framep = frameOf(nodep->varp()); if (!framep) return; + UINFO(0, "IS AUTO " << nodep->varp()->lifetime()); if (needsDynScope(nodep)) { bool isEvent = false; if (AstBasicDType* const dtypep = VN_CAST(nodep->dtypep()->skipRefp(), BasicDType)) { @@ -448,7 +449,7 @@ class DynScopeVisitor final : public VNVisitor { "Writing to an " << nodep->varp()->verilogKwd() << " variable of a function after a timing control is not allowed"); - } else { + } else if (nodep->varp()->lifetime().isAutomatic()) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: Writing to a captured " << nodep->varp()->verilogKwd() << " variable in a " diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 4e238c75f..f0451a042 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -382,7 +382,12 @@ class LinkParseVisitor final : public VNVisitor { } } } else if (m_ftaskp) { - if (!nodep->lifetime().isAutomatic()) nodep->lifetime(VLifetime::AUTOMATIC_IMPLICIT); + UINFO(0, "TASK LIFE " << m_ftaskp->lifetime()); + if (m_ftaskp->lifetime().isStatic()) { + nodep->lifetime(VLifetime::STATIC_IMPLICIT); + } else { + if (!nodep->lifetime().isAutomatic()) nodep->lifetime(VLifetime::AUTOMATIC_IMPLICIT); + } } else if (nodep->lifetime().isNone()) { // lifetime shouldn't be unknown, set static if none nodep->lifetime(VLifetime::STATIC_IMPLICIT); From 22cad93083c8bad9de642ca46931561f4f5ae4a8 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Tue, 17 Mar 2026 19:20:24 +0100 Subject: [PATCH 02/42] Non functioning split static parameter into port and static var Signed-off-by: Kamil Danecki --- src/V3Begin.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index b1b02ca0a..bbc8b0305 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -300,10 +300,14 @@ class BeginVisitor final : public VNVisitor { void visit(AstVar* nodep) override { // If static variable, move it outside a function. if (nodep->lifetime().isStatic() && m_ftaskp) { + UINFO(0, "TASK IS STATIC " << m_ftaskp->lifetime().isStatic()); const std::string newName = m_ftaskp->name() + "__Vstatic__" + dot(m_unnamedScope, nodep->name()); + AstVar* newVarp = nodep->cloneTreePure(false); + nodep->replaceWith(newVarp); + // AstNode::addNext(nodep, newVarp); nodep->name(newName); - nodep->unlinkFrBack(); + // nodep->unlinkFrBack(); m_ftaskp->addHereThisAsNext(nodep); nodep->funcLocal(false); } else if (m_unnamedScope != "") { From bf586ce95ff4080dac9807a1e772b324edda1bdf Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 19 Mar 2026 11:20:43 +0100 Subject: [PATCH 03/42] Fix looping Signed-off-by: Kamil Danecki --- src/V3Begin.cpp | 11 ++++++++--- src/V3Fork.cpp | 14 +++++++------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index bbc8b0305..44b49ce3c 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -300,16 +300,21 @@ class BeginVisitor final : public VNVisitor { void visit(AstVar* nodep) override { // If static variable, move it outside a function. if (nodep->lifetime().isStatic() && m_ftaskp) { + if (nodep->user1()) { + UINFO(0, "RETURN"); + return; + } UINFO(0, "TASK IS STATIC " << m_ftaskp->lifetime().isStatic()); const std::string newName = m_ftaskp->name() + "__Vstatic__" + dot(m_unnamedScope, nodep->name()); - AstVar* newVarp = nodep->cloneTreePure(false); - nodep->replaceWith(newVarp); - // AstNode::addNext(nodep, newVarp); + AstVar* keepAsPort = nodep->cloneTreePure(false); + nodep->replaceWith(keepAsPort); nodep->name(newName); // nodep->unlinkFrBack(); m_ftaskp->addHereThisAsNext(nodep); nodep->funcLocal(false); + m_statep->userMarkChanged(keepAsPort); + // m_statep->userMarkChanged(nodep); } else if (m_unnamedScope != "") { // Rename it nodep->name(dot(m_unnamedScope, nodep->name())); diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index f50210308..02850580e 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -450,13 +450,13 @@ class DynScopeVisitor final : public VNVisitor { << nodep->varp()->verilogKwd() << " variable of a function after a timing control is not allowed"); } else if (nodep->varp()->lifetime().isAutomatic()) { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: Writing to a captured " - << nodep->varp()->verilogKwd() - << " variable in a " - << (VN_IS(nodep->backp(), AssignDly) - ? "non-blocking assignment" - : "fork") - << " after a timing control"); + // nodep->v3warn(E_UNSUPPORTED, "Unsupported: Writing to a captured " + // << nodep->varp()->verilogKwd() + // << " variable in a " + // << (VN_IS(nodep->backp(), AssignDly) + // ? "non-blocking assignment" + // : "fork") + // << " after a timing control"); } } if (!framep->instance().initialized()) framep->createInstancePrototype(); From 590b34701d0b15c8bca4c96f574bd458c78ce682 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 19 Mar 2026 11:23:32 +0100 Subject: [PATCH 04/42] other check Signed-off-by: Kamil Danecki --- src/V3Begin.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 44b49ce3c..95d1fde7b 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -301,19 +301,21 @@ class BeginVisitor final : public VNVisitor { // If static variable, move it outside a function. if (nodep->lifetime().isStatic() && m_ftaskp) { if (nodep->user1()) { - UINFO(0, "RETURN"); + // UINFO(0, "RETURN"); return; } - UINFO(0, "TASK IS STATIC " << m_ftaskp->lifetime().isStatic()); + // UINFO(0, "TASK IS STATIC " << m_ftaskp->lifetime().isStatic()); const std::string newName = m_ftaskp->name() + "__Vstatic__" + dot(m_unnamedScope, nodep->name()); - AstVar* keepAsPort = nodep->cloneTreePure(false); - nodep->replaceWith(keepAsPort); + if (m_ftaskp->lifetime().isStatic()) { + AstVar* keepAsPort = nodep->cloneTreePure(false); + nodep->replaceWith(keepAsPort); + m_statep->userMarkChanged(keepAsPort); + } nodep->name(newName); // nodep->unlinkFrBack(); m_ftaskp->addHereThisAsNext(nodep); nodep->funcLocal(false); - m_statep->userMarkChanged(keepAsPort); // m_statep->userMarkChanged(nodep); } else if (m_unnamedScope != "") { // Rename it From 1b77100b6c6063b48c0624d4e689e4a568ddfbb8 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 19 Mar 2026 11:35:36 +0100 Subject: [PATCH 05/42] Remove print Signed-off-by: Kamil Danecki --- src/V3Fork.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index 02850580e..2770589ad 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -434,7 +434,7 @@ class DynScopeVisitor final : public VNVisitor { void visit(AstVarRef* nodep) override { ForkDynScopeFrame* const framep = frameOf(nodep->varp()); if (!framep) return; - UINFO(0, "IS AUTO " << nodep->varp()->lifetime()); + // UINFO(0, "IS AUTO " << nodep->varp()->lifetime()); if (needsDynScope(nodep)) { bool isEvent = false; if (AstBasicDType* const dtypep = VN_CAST(nodep->dtypep()->skipRefp(), BasicDType)) { From 739c84202c3ef9c355f02703470462400f9a64a2 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 19 Mar 2026 14:30:48 +0100 Subject: [PATCH 06/42] Add assigns between static variable and argument ports Signed-off-by: Kamil Danecki --- src/V3Begin.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 95d1fde7b..1c8dab4cd 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -311,6 +311,24 @@ class BeginVisitor final : public VNVisitor { AstVar* keepAsPort = nodep->cloneTreePure(false); nodep->replaceWith(keepAsPort); m_statep->userMarkChanged(keepAsPort); + + if (nodep->isInput() || nodep->isInout()) { + AstAssign* initAssignp = new AstAssign{nodep->fileline(), + new AstVarRef{nodep->fileline(), nodep, VAccess::WRITE}, + new AstVarRef{keepAsPort->fileline(), keepAsPort, VAccess::READ}}; + keepAsPort->addNextHere(initAssignp); + } + + if (nodep->isWritable()) { + AstAssign* endAssignp = new AstAssign{nodep->fileline(), + new AstVarRef{keepAsPort->fileline(), keepAsPort, VAccess::WRITE}, + new AstVarRef{nodep->fileline(), nodep, VAccess::READ}}; + AstNode* endp = keepAsPort; + while (endp->nextp()) { + endp = endp->nextp(); + } + endp->addNextHere(endAssignp); + } } nodep->name(newName); // nodep->unlinkFrBack(); From 510347125ce382f64f49faf8b934ad961d7fca12 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 19 Mar 2026 14:36:15 +0100 Subject: [PATCH 07/42] Remove debug print Signed-off-by: Kamil Danecki --- src/V3LinkParse.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index f0451a042..727d60af4 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -382,7 +382,6 @@ class LinkParseVisitor final : public VNVisitor { } } } else if (m_ftaskp) { - UINFO(0, "TASK LIFE " << m_ftaskp->lifetime()); if (m_ftaskp->lifetime().isStatic()) { nodep->lifetime(VLifetime::STATIC_IMPLICIT); } else { From c18262523da355a10a8f91927ad98b434c18e2e1 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 19 Mar 2026 15:34:57 +0100 Subject: [PATCH 08/42] Fix unlink Signed-off-by: Kamil Danecki --- src/V3Begin.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 1c8dab4cd..c8052c5e4 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -329,9 +329,10 @@ class BeginVisitor final : public VNVisitor { } endp->addNextHere(endAssignp); } + } else { + nodep->unlinkFrBack(); } nodep->name(newName); - // nodep->unlinkFrBack(); m_ftaskp->addHereThisAsNext(nodep); nodep->funcLocal(false); // m_statep->userMarkChanged(nodep); From 4bb3c55f0657ff7737045cd993ac03b41f3a7a8d Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 19 Mar 2026 17:43:38 +0100 Subject: [PATCH 09/42] Only apply to IO nodes Signed-off-by: Kamil Danecki --- src/V3Begin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index c8052c5e4..06fa88fbe 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -307,7 +307,7 @@ class BeginVisitor final : public VNVisitor { // UINFO(0, "TASK IS STATIC " << m_ftaskp->lifetime().isStatic()); const std::string newName = m_ftaskp->name() + "__Vstatic__" + dot(m_unnamedScope, nodep->name()); - if (m_ftaskp->lifetime().isStatic()) { + if (m_ftaskp->lifetime().isStatic() && nodep->isIO()) { AstVar* keepAsPort = nodep->cloneTreePure(false); nodep->replaceWith(keepAsPort); m_statep->userMarkChanged(keepAsPort); From c37d1fd92530eaf1f7f41d0760ce27d1f0aedab2 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Fri, 20 Mar 2026 10:14:17 +0100 Subject: [PATCH 10/42] Revert "Set lifetime to static" This reverts commit 8dc3b2b1ded9326ea9ed5a7bdf2a40b893a7dcd0. --- src/V3Fork.cpp | 17 ++++++++--------- src/V3LinkParse.cpp | 6 +----- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index 2770589ad..2e40faa24 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -434,7 +434,6 @@ class DynScopeVisitor final : public VNVisitor { void visit(AstVarRef* nodep) override { ForkDynScopeFrame* const framep = frameOf(nodep->varp()); if (!framep) return; - // UINFO(0, "IS AUTO " << nodep->varp()->lifetime()); if (needsDynScope(nodep)) { bool isEvent = false; if (AstBasicDType* const dtypep = VN_CAST(nodep->dtypep()->skipRefp(), BasicDType)) { @@ -449,14 +448,14 @@ class DynScopeVisitor final : public VNVisitor { "Writing to an " << nodep->varp()->verilogKwd() << " variable of a function after a timing control is not allowed"); - } else if (nodep->varp()->lifetime().isAutomatic()) { - // nodep->v3warn(E_UNSUPPORTED, "Unsupported: Writing to a captured " - // << nodep->varp()->verilogKwd() - // << " variable in a " - // << (VN_IS(nodep->backp(), AssignDly) - // ? "non-blocking assignment" - // : "fork") - // << " after a timing control"); + } else { + nodep->v3warn(E_UNSUPPORTED, "Unsupported: Writing to a captured " + << nodep->varp()->verilogKwd() + << " variable in a " + << (VN_IS(nodep->backp(), AssignDly) + ? "non-blocking assignment" + : "fork") + << " after a timing control"); } } if (!framep->instance().initialized()) framep->createInstancePrototype(); diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 727d60af4..4e238c75f 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -382,11 +382,7 @@ class LinkParseVisitor final : public VNVisitor { } } } else if (m_ftaskp) { - if (m_ftaskp->lifetime().isStatic()) { - nodep->lifetime(VLifetime::STATIC_IMPLICIT); - } else { - if (!nodep->lifetime().isAutomatic()) nodep->lifetime(VLifetime::AUTOMATIC_IMPLICIT); - } + if (!nodep->lifetime().isAutomatic()) nodep->lifetime(VLifetime::AUTOMATIC_IMPLICIT); } else if (nodep->lifetime().isNone()) { // lifetime shouldn't be unknown, set static if none nodep->lifetime(VLifetime::STATIC_IMPLICIT); From 1e6fb7ae23e7f91da7391433ae8c927220af738a Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Fri, 20 Mar 2026 10:16:31 +0100 Subject: [PATCH 11/42] Change var lifetime in v3fork Signed-off-by: Kamil Danecki --- src/V3Fork.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index 2e40faa24..e657a0671 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -449,13 +449,17 @@ class DynScopeVisitor final : public VNVisitor { << nodep->varp()->verilogKwd() << " variable of a function after a timing control is not allowed"); } else { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: Writing to a captured " + if (nodep->varp()->lifetime() == VLifetime::AUTOMATIC_IMPLICIT) { + nodep->varp()->lifetime(VLifetime::STATIC_IMPLICIT); + } else { + nodep->v3warn(E_UNSUPPORTED, "Unsupported: Writing to a captured " << nodep->varp()->verilogKwd() << " variable in a " << (VN_IS(nodep->backp(), AssignDly) ? "non-blocking assignment" : "fork") << " after a timing control"); + } } } if (!framep->instance().initialized()) framep->createInstancePrototype(); From d2f34b997428d08d4284079954e0ed0fc79d38e5 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Fri, 20 Mar 2026 14:00:09 +0100 Subject: [PATCH 12/42] Revert "Change var lifetime in v3fork" This reverts commit 77597cda8c0c22491ca2fbbe135ccdd7ff538132. --- src/V3Fork.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index e657a0671..2e40faa24 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -449,17 +449,13 @@ class DynScopeVisitor final : public VNVisitor { << nodep->varp()->verilogKwd() << " variable of a function after a timing control is not allowed"); } else { - if (nodep->varp()->lifetime() == VLifetime::AUTOMATIC_IMPLICIT) { - nodep->varp()->lifetime(VLifetime::STATIC_IMPLICIT); - } else { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: Writing to a captured " + nodep->v3warn(E_UNSUPPORTED, "Unsupported: Writing to a captured " << nodep->varp()->verilogKwd() << " variable in a " << (VN_IS(nodep->backp(), AssignDly) ? "non-blocking assignment" : "fork") << " after a timing control"); - } } } if (!framep->instance().initialized()) framep->createInstancePrototype(); From adb298dce1a2f37f25b63df4d0475aa31674b171 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Fri, 20 Mar 2026 14:32:20 +0100 Subject: [PATCH 13/42] Set static when task has timing control Signed-off-by: Kamil Danecki --- src/V3Fork.cpp | 8 +------- src/V3LinkParse.cpp | 6 +++++- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index 2e40faa24..c6f6b09d2 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -449,13 +449,7 @@ class DynScopeVisitor final : public VNVisitor { << nodep->varp()->verilogKwd() << " variable of a function after a timing control is not allowed"); } else { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: Writing to a captured " - << nodep->varp()->verilogKwd() - << " variable in a " - << (VN_IS(nodep->backp(), AssignDly) - ? "non-blocking assignment" - : "fork") - << " after a timing control"); + UASSERT_OBJ(nodep->varp()->lifetime().isAutomatic(), nodep, "writing to an automatic variable after a timing control"); } } if (!framep->instance().initialized()) framep->createInstancePrototype(); diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 4e238c75f..ee2351a24 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -382,7 +382,11 @@ class LinkParseVisitor final : public VNVisitor { } } } else if (m_ftaskp) { - if (!nodep->lifetime().isAutomatic()) nodep->lifetime(VLifetime::AUTOMATIC_IMPLICIT); + if (m_ftaskp->existsAndNext([&](const AstNode* nodep) {return nodep->isTimingControl();})) { + nodep->lifetime(VLifetime::STATIC_IMPLICIT); + } else { + nodep->lifetime(VLifetime::AUTOMATIC_IMPLICIT); + } } else if (nodep->lifetime().isNone()) { // lifetime shouldn't be unknown, set static if none nodep->lifetime(VLifetime::STATIC_IMPLICIT); From e8f596ed909f14240c6f0435771a5d2f6593e696 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Fri, 20 Mar 2026 14:33:08 +0100 Subject: [PATCH 14/42] Remove unsup test Signed-off-by: Kamil Danecki --- test_regress/t/t_fork_dynscope_unsup.out | 10 ---------- test_regress/t/t_fork_dynscope_unsup.py | 16 ---------------- test_regress/t/t_fork_dynscope_unsup.v | 24 ------------------------ 3 files changed, 50 deletions(-) delete mode 100644 test_regress/t/t_fork_dynscope_unsup.out delete mode 100755 test_regress/t/t_fork_dynscope_unsup.py delete mode 100644 test_regress/t/t_fork_dynscope_unsup.v diff --git a/test_regress/t/t_fork_dynscope_unsup.out b/test_regress/t/t_fork_dynscope_unsup.out deleted file mode 100644 index c87c7fd3f..000000000 --- a/test_regress/t/t_fork_dynscope_unsup.out +++ /dev/null @@ -1,10 +0,0 @@ -%Error-UNSUPPORTED: t/t_fork_dynscope_unsup.v:17:7: Unsupported: Writing to a captured inout variable in a fork after a timing control - : ... note: In instance 't' - 17 | p = #1 1; - | ^ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_fork_dynscope_unsup.v:22:5: Unsupported: Writing to a captured output variable in a non-blocking assignment after a timing control - : ... note: In instance 't' - 22 | q <= #1 1; - | ^ -%Error: Exiting due to diff --git a/test_regress/t/t_fork_dynscope_unsup.py b/test_regress/t/t_fork_dynscope_unsup.py deleted file mode 100755 index 382ad0d44..000000000 --- a/test_regress/t/t_fork_dynscope_unsup.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/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 Wilson Snyder -# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 - -import vltest_bootstrap - -test.scenarios('simulator') - -test.lint(fails=True, expect_filename=test.golden_filename) - -test.passes() diff --git a/test_regress/t/t_fork_dynscope_unsup.v b/test_regress/t/t_fork_dynscope_unsup.v deleted file mode 100644 index 893c44a8b..000000000 --- a/test_regress/t/t_fork_dynscope_unsup.v +++ /dev/null @@ -1,24 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain -// SPDX-FileCopyrightText: 2024 Antmicro -// SPDX-License-Identifier: CC0-1.0 - -module t; - bit p = 0, q = 0; - - initial begin - t1(p); - t2(q); - end - - task t1(inout p); - fork - p = #1 1; - join_none - endtask - - task t2(output q); - q <= #1 1; - endtask -endmodule From 88a0b7df68230931d1c9bc4dff69a4aef7937ecb Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Fri, 20 Mar 2026 15:20:04 +0100 Subject: [PATCH 15/42] Add test Signed-off-by: Kamil Danecki --- test_regress/t/t_fork_write_after_timing.py | 18 ++++++ test_regress/t/t_fork_write_after_timing.v | 67 +++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100755 test_regress/t/t_fork_write_after_timing.py create mode 100644 test_regress/t/t_fork_write_after_timing.v diff --git a/test_regress/t/t_fork_write_after_timing.py b/test_regress/t/t_fork_write_after_timing.py new file mode 100755 index 000000000..3cc73805c --- /dev/null +++ b/test_regress/t/t_fork_write_after_timing.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: 2024 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_fork_write_after_timing.v b/test_regress/t/t_fork_write_after_timing.v new file mode 100644 index 000000000..de70ffb58 --- /dev/null +++ b/test_regress/t/t_fork_write_after_timing.v @@ -0,0 +1,67 @@ +// DESCRIPTION: Verilator: Verilog Test module +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Antmicro +// SPDX-License-Identifier: CC0-1.0 + +module t; + int x1, x2, x3, x4; + + initial begin + x1 = -1; + x2 = -1; + x3 = -1; + x4 = -1; + #1 t1(x1); + t2(x2); + t3(x3); + t4(x4); + #10 t1(x1); + t2(x1); + t3(x1); + t4(x1); + + t5(x2); + t6(x2); + end + + task t1(output int x); + $display("t1 start %d", x); + fork + x = #1 1; + join_none + $display("t1 end %d", x); + endtask + + task t2(inout int xa); + $display("t2 start %d", xa); + fork + xa = #1 2; + join_none + $display("t2 end %d", xa); + endtask + + task t3(output int x); + $display("t3 start %d", x); + fork + x = #1 1; + join_none + #2 $display("t3 end %d", x); + endtask + + task t4(inout int x); + $display("t4 start %d", x); + fork + x = #1 2; + join_none + #2 $display("t4 end %d", x); + endtask + + task t5(output int x); + if (x != 0) $stop; + x <= #1 3; + endtask + + task t6(inout int x); + x <= #1 4; + endtask +endmodule From 24a50a39bffa19a66c027615775c4b5744723845 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Tue, 24 Mar 2026 09:29:59 +0100 Subject: [PATCH 16/42] fixup! Set static when task has timing control --- src/V3LinkParse.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index ee2351a24..61c95beb2 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -382,7 +382,7 @@ class LinkParseVisitor final : public VNVisitor { } } } else if (m_ftaskp) { - if (m_ftaskp->existsAndNext([&](const AstNode* nodep) {return nodep->isTimingControl();})) { + if (m_ftaskp->exists([&](const AstNode* nodep) {return nodep->isTimingControl();})) { nodep->lifetime(VLifetime::STATIC_IMPLICIT); } else { nodep->lifetime(VLifetime::AUTOMATIC_IMPLICIT); From 3a2c29c04b7b932f80e1ccb1c03573e8e803bf48 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Tue, 24 Mar 2026 10:11:01 +0100 Subject: [PATCH 17/42] Only in static tasks Signed-off-by: Kamil Danecki --- src/V3LinkParse.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 61c95beb2..23811b13d 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -382,7 +382,7 @@ class LinkParseVisitor final : public VNVisitor { } } } else if (m_ftaskp) { - if (m_ftaskp->exists([&](const AstNode* nodep) {return nodep->isTimingControl();})) { + if (m_ftaskp->lifetime().isStatic() && m_ftaskp->exists([&](const AstNode* nodep) {return nodep->isTimingControl();})) { nodep->lifetime(VLifetime::STATIC_IMPLICIT); } else { nodep->lifetime(VLifetime::AUTOMATIC_IMPLICIT); From 911128cd7f510e8db000b755c7ac1d4d7ec05047 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Tue, 24 Mar 2026 11:04:37 +0100 Subject: [PATCH 18/42] Update tests Signed-off-by: Kamil Danecki --- test_regress/t/t_fork_write_after_timing.out | 17 +++++++++++++++++ test_regress/t/t_fork_write_after_timing.py | 4 ++-- test_regress/t/t_fork_write_after_timing.v | 6 +++++- test_regress/t/t_timing_func_fork_bad.v | 10 +++++----- 4 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 test_regress/t/t_fork_write_after_timing.out diff --git a/test_regress/t/t_fork_write_after_timing.out b/test_regress/t/t_fork_write_after_timing.out new file mode 100644 index 000000000..08d8e0da2 --- /dev/null +++ b/test_regress/t/t_fork_write_after_timing.out @@ -0,0 +1,17 @@ +t1 start 0 +t1 end 0 +t2 start -1 +t2 end -1 +t3 start 0 +t3 end 1 +t4 start -1 +t4 end 2 +t1 start 1 +t1 end 1 +t2 start 1 +t2 end 1 +t3 start 1 +t3 end 1 +t4 start 1 +t4 end 2 +*-* All Finished *-* diff --git a/test_regress/t/t_fork_write_after_timing.py b/test_regress/t/t_fork_write_after_timing.py index 3cc73805c..6f2f7c7d7 100755 --- a/test_regress/t/t_fork_write_after_timing.py +++ b/test_regress/t/t_fork_write_after_timing.py @@ -11,8 +11,8 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile() +test.compile(verilator_flags2=["--Wno-initialdly", "--binary"]) -test.execute() +test.execute(expect_filename=test.golden_filename) test.passes() diff --git a/test_regress/t/t_fork_write_after_timing.v b/test_regress/t/t_fork_write_after_timing.v index de70ffb58..08d9c4228 100644 --- a/test_regress/t/t_fork_write_after_timing.v +++ b/test_regress/t/t_fork_write_after_timing.v @@ -11,17 +11,21 @@ module t; x2 = -1; x3 = -1; x4 = -1; + #1 t1(x1); t2(x2); t3(x3); t4(x4); + #10 t1(x1); t2(x1); t3(x1); t4(x1); - t5(x2); t6(x2); + + #5 $write("*-* All Finished *-*\n"); + $finish; end task t1(output int x); diff --git a/test_regress/t/t_timing_func_fork_bad.v b/test_regress/t/t_timing_func_fork_bad.v index d3319513a..d0e35c8b5 100644 --- a/test_regress/t/t_timing_func_fork_bad.v +++ b/test_regress/t/t_timing_func_fork_bad.v @@ -6,7 +6,7 @@ module t; - function int f1(output int o1); + function automatic int f1(output int o1); fork begin #1 $stop; @@ -16,7 +16,7 @@ module t; join_none endfunction - function int f2(inout io2); + function automatic int f2(inout io2); fork begin f2 = #5 0; @@ -27,7 +27,7 @@ module t; endfunction event e; - function int f3(output int o3); + function automatic int f3(output int o3); fork begin @e $stop; @@ -37,7 +37,7 @@ module t; join_none endfunction - function int f4(inout int io4); + function automatic int f4(inout int io4); fork begin f4 = @e 0; @@ -49,7 +49,7 @@ module t; int i; - function int f5(output int o5); + function automatic int f5(output int o5); fork begin wait (i == 0) $stop; From e16db343ab4b5ec016ae5004a6fbbcc9aa44966c Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Tue, 24 Mar 2026 12:45:09 +0100 Subject: [PATCH 19/42] Cleanup Signed-off-by: Kamil Danecki --- src/V3Begin.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 06fa88fbe..5c4361294 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -301,10 +301,8 @@ class BeginVisitor final : public VNVisitor { // If static variable, move it outside a function. if (nodep->lifetime().isStatic() && m_ftaskp) { if (nodep->user1()) { - // UINFO(0, "RETURN"); return; } - // UINFO(0, "TASK IS STATIC " << m_ftaskp->lifetime().isStatic()); const std::string newName = m_ftaskp->name() + "__Vstatic__" + dot(m_unnamedScope, nodep->name()); if (m_ftaskp->lifetime().isStatic() && nodep->isIO()) { From 935b3bf9ea9d01dfc19e4aef8b426d4c28a34c4e Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 26 Mar 2026 08:51:07 +0100 Subject: [PATCH 20/42] Check for timing control only once Signed-off-by: Kamil Danecki --- src/V3LinkParse.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 23811b13d..9ae1493fa 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -69,6 +69,7 @@ class LinkParseVisitor final : public VNVisitor { int m_randSequenceNum = 0; // RandSequence uniqify number VLifetime m_lifetime = VLifetime::STATIC_IMPLICIT; // Propagating lifetime bool m_lifetimeAllowed = false; // True to allow lifetime settings + bool m_hasTimingControl = false; // If current task has timing control bool m_moduleWithGenericIface = false; // If current module contains generic interface std::set m_portDups; // Non-ANSI port datatype duplicating input/output decls @@ -248,6 +249,8 @@ class LinkParseVisitor final : public VNVisitor { VL_RESTORER(m_lifetime); VL_RESTORER(m_lifetimeAllowed); m_lifetimeAllowed = true; + VL_RESTORER(m_hasTimingControl); + m_hasTimingControl = nodep->exists([&](const AstNode* const nodep) {return nodep->isTimingControl();}); if (!nodep->lifetime().isNone()) { m_lifetime = nodep->lifetime().makeImplicit(); } else { @@ -382,7 +385,7 @@ class LinkParseVisitor final : public VNVisitor { } } } else if (m_ftaskp) { - if (m_ftaskp->lifetime().isStatic() && m_ftaskp->exists([&](const AstNode* nodep) {return nodep->isTimingControl();})) { + if (m_ftaskp->lifetime().isStatic() && m_hasTimingControl) { nodep->lifetime(VLifetime::STATIC_IMPLICIT); } else { nodep->lifetime(VLifetime::AUTOMATIC_IMPLICIT); From 4439f17cd593a5c36623d13d6e07ba46c616297b Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 26 Mar 2026 08:59:03 +0100 Subject: [PATCH 21/42] Remove task lifetime check Signed-off-by: Kamil Danecki --- src/V3Begin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 5c4361294..4f7859c86 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -305,7 +305,7 @@ class BeginVisitor final : public VNVisitor { } const std::string newName = m_ftaskp->name() + "__Vstatic__" + dot(m_unnamedScope, nodep->name()); - if (m_ftaskp->lifetime().isStatic() && nodep->isIO()) { + if (nodep->isIO()) { AstVar* keepAsPort = nodep->cloneTreePure(false); nodep->replaceWith(keepAsPort); m_statep->userMarkChanged(keepAsPort); From 4235c938c66b5dc4c48a9d3eed4006087fbf0b3a Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 26 Mar 2026 09:03:51 +0100 Subject: [PATCH 22/42] Replace with addStmtsp Signed-off-by: Kamil Danecki --- src/V3Begin.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 4f7859c86..d8f55a51c 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -321,11 +321,7 @@ class BeginVisitor final : public VNVisitor { AstAssign* endAssignp = new AstAssign{nodep->fileline(), new AstVarRef{keepAsPort->fileline(), keepAsPort, VAccess::WRITE}, new AstVarRef{nodep->fileline(), nodep, VAccess::READ}}; - AstNode* endp = keepAsPort; - while (endp->nextp()) { - endp = endp->nextp(); - } - endp->addNextHere(endAssignp); + m_ftaskp->addStmtsp(endAssignp); } } else { nodep->unlinkFrBack(); From 4926fbd36961b739726e33ff67b35591cccbb16b Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 26 Mar 2026 09:04:02 +0100 Subject: [PATCH 23/42] Cleanup Signed-off-by: Kamil Danecki --- src/V3Begin.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index d8f55a51c..f76107c8b 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -329,7 +329,6 @@ class BeginVisitor final : public VNVisitor { nodep->name(newName); m_ftaskp->addHereThisAsNext(nodep); nodep->funcLocal(false); - // m_statep->userMarkChanged(nodep); } else if (m_unnamedScope != "") { // Rename it nodep->name(dot(m_unnamedScope, nodep->name())); From 8eaf171135bda098b2ec6e79d32c374367392464 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 26 Mar 2026 09:22:34 +0100 Subject: [PATCH 24/42] Rename port variable Signed-off-by: Kamil Danecki --- src/V3Begin.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index f76107c8b..ad60df60e 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -306,20 +306,21 @@ class BeginVisitor final : public VNVisitor { const std::string newName = m_ftaskp->name() + "__Vstatic__" + dot(m_unnamedScope, nodep->name()); if (nodep->isIO()) { - AstVar* keepAsPort = nodep->cloneTreePure(false); - nodep->replaceWith(keepAsPort); - m_statep->userMarkChanged(keepAsPort); + // Create a port that is used for passing value between argument and static variable + AstVar* const portp = nodep->cloneTreePure(false); + portp->user1(true); + nodep->replaceWith(portp); if (nodep->isInput() || nodep->isInout()) { AstAssign* initAssignp = new AstAssign{nodep->fileline(), new AstVarRef{nodep->fileline(), nodep, VAccess::WRITE}, - new AstVarRef{keepAsPort->fileline(), keepAsPort, VAccess::READ}}; - keepAsPort->addNextHere(initAssignp); + new AstVarRef{portp->fileline(), portp, VAccess::READ}}; + portp->addNextHere(initAssignp); } if (nodep->isWritable()) { AstAssign* endAssignp = new AstAssign{nodep->fileline(), - new AstVarRef{keepAsPort->fileline(), keepAsPort, VAccess::WRITE}, + new AstVarRef{portp->fileline(), portp, VAccess::WRITE}, new AstVarRef{nodep->fileline(), nodep, VAccess::READ}}; m_ftaskp->addStmtsp(endAssignp); } From c816addc6085eb50ec10c4babb89c05bae0ebaf9 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 26 Mar 2026 09:22:47 +0100 Subject: [PATCH 25/42] Add comment Signed-off-by: Kamil Danecki --- src/V3LinkParse.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 9ae1493fa..ec5fd35f1 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -385,6 +385,8 @@ class LinkParseVisitor final : public VNVisitor { } } } else if (m_ftaskp) { + // Variables in static tasks with timing control can be used after the task has ended + // with use of join..fork_none, so they need to be static if (m_ftaskp->lifetime().isStatic() && m_hasTimingControl) { nodep->lifetime(VLifetime::STATIC_IMPLICIT); } else { From 7982361aaffbbbfdc4897adb872b4838426a9b3c Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 26 Mar 2026 12:50:09 +0100 Subject: [PATCH 26/42] Remove assert Signed-off-by: Kamil Danecki --- src/V3Fork.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index c6f6b09d2..605abdd2d 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -443,14 +443,12 @@ class DynScopeVisitor final : public VNVisitor { if (!isEvent && m_afterTimingControl && nodep->varp()->isWritable() && nodep->access().isWriteOrRW()) { // The output variable may not exist after a delay, so we can't just write to it - if (m_inFunc) { nodep->v3error( "Writing to an " << nodep->varp()->verilogKwd() - << " variable of a function after a timing control is not allowed"); - } else { - UASSERT_OBJ(nodep->varp()->lifetime().isAutomatic(), nodep, "writing to an automatic variable after a timing control"); - } + << " variable of a " + << (m_inFunc ? "function" : "task") + << " after a timing control is not allowed"); } if (!framep->instance().initialized()) framep->createInstancePrototype(); framep->captureVarInsert(nodep->varp()); From e7cc286decc24e2730f54a0eed816f7d4782ed48 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 26 Mar 2026 13:00:27 +0100 Subject: [PATCH 27/42] Format error message Signed-off-by: Kamil Danecki --- src/V3Fork.cpp | 12 ++++++------ test_regress/t/t_timing_func_fork_bad.out | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index 605abdd2d..993924a09 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -443,12 +443,12 @@ class DynScopeVisitor final : public VNVisitor { if (!isEvent && m_afterTimingControl && nodep->varp()->isWritable() && nodep->access().isWriteOrRW()) { // The output variable may not exist after a delay, so we can't just write to it - nodep->v3error( - "Writing to an " - << nodep->varp()->verilogKwd() - << " variable of a " - << (m_inFunc ? "function" : "task") - << " after a timing control is not allowed"); + nodep->v3error( + "Writing to an " + << nodep->varp()->verilogKwd() + << " automatic variable of a " + << (m_inFunc ? "function" : "task") + << " after a timing control is not allowed"); } if (!framep->instance().initialized()) framep->createInstancePrototype(); framep->captureVarInsert(nodep->varp()); diff --git a/test_regress/t/t_timing_func_fork_bad.out b/test_regress/t/t_timing_func_fork_bad.out index b9fa6bbce..f6cd4adb5 100644 --- a/test_regress/t/t_timing_func_fork_bad.out +++ b/test_regress/t/t_timing_func_fork_bad.out @@ -1,41 +1,41 @@ -%Error: t/t_timing_func_fork_bad.v:13:9: Writing to an output variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:13:9: Writing to an output automatic variable of a function after a timing control is not allowed : ... note: In instance 't' 13 | f1 = 0; | ^~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_timing_func_fork_bad.v:14:9: Writing to an output variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:14:9: Writing to an output automatic variable of a function after a timing control is not allowed : ... note: In instance 't' 14 | o1 = 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:22:9: Writing to an output variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:22:9: Writing to an output automatic variable of a function after a timing control is not allowed : ... note: In instance 't' 22 | f2 = #5 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:24:9: Writing to an inout variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:24:9: Writing to an inout automatic variable of a function after a timing control is not allowed : ... note: In instance 't' 24 | io2 = 0; | ^~~ -%Error: t/t_timing_func_fork_bad.v:34:9: Writing to an output variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:34:9: Writing to an output automatic variable of a function after a timing control is not allowed : ... note: In instance 't' 34 | f3 = 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:35:9: Writing to an output variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:35:9: Writing to an output automatic variable of a function after a timing control is not allowed : ... note: In instance 't' 35 | o3 = 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:43:9: Writing to an output variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:43:9: Writing to an output automatic variable of a function after a timing control is not allowed : ... note: In instance 't' 43 | f4 = @e 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:45:9: Writing to an inout variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:45:9: Writing to an inout automatic variable of a function after a timing control is not allowed : ... note: In instance 't' 45 | io4 = 0; | ^~~ -%Error: t/t_timing_func_fork_bad.v:56:9: Writing to an output variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:56:9: Writing to an output automatic variable of a function after a timing control is not allowed : ... note: In instance 't' 56 | f5 = 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:57:9: Writing to an output variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:57:9: Writing to an output automatic variable of a function after a timing control is not allowed : ... note: In instance 't' 57 | o5 = 0; | ^~ From 486551ef3edb75d05524dcee33fe8f7ecbc4b99e Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 26 Mar 2026 15:14:23 +0100 Subject: [PATCH 28/42] Add test case Signed-off-by: Kamil Danecki --- test_regress/t/t_timing_func_fork_bad.out | 36 +++++++++++++---------- test_regress/t/t_timing_func_fork_bad.v | 7 +++++ 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/test_regress/t/t_timing_func_fork_bad.out b/test_regress/t/t_timing_func_fork_bad.out index f6cd4adb5..a605bf784 100644 --- a/test_regress/t/t_timing_func_fork_bad.out +++ b/test_regress/t/t_timing_func_fork_bad.out @@ -7,36 +7,40 @@ : ... note: In instance 't' 14 | o1 = 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:22:9: Writing to an output automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:22:7: Writing to an output automatic variable of a task after a timing control is not allowed : ... note: In instance 't' - 22 | f2 = #5 0; + 22 | o1 = 0; + | ^~ +%Error: t/t_timing_func_fork_bad.v:29:9: Writing to an output automatic variable of a function after a timing control is not allowed + : ... note: In instance 't' + 29 | f2 = #5 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:24:9: Writing to an inout automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:31:9: Writing to an inout automatic variable of a function after a timing control is not allowed : ... note: In instance 't' - 24 | io2 = 0; + 31 | io2 = 0; | ^~~ -%Error: t/t_timing_func_fork_bad.v:34:9: Writing to an output automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:41:9: Writing to an output automatic variable of a function after a timing control is not allowed : ... note: In instance 't' - 34 | f3 = 0; + 41 | f3 = 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:35:9: Writing to an output automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:42:9: Writing to an output automatic variable of a function after a timing control is not allowed : ... note: In instance 't' - 35 | o3 = 0; + 42 | o3 = 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:43:9: Writing to an output automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:50:9: Writing to an output automatic variable of a function after a timing control is not allowed : ... note: In instance 't' - 43 | f4 = @e 0; + 50 | f4 = @e 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:45:9: Writing to an inout automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:52:9: Writing to an inout automatic variable of a function after a timing control is not allowed : ... note: In instance 't' - 45 | io4 = 0; + 52 | io4 = 0; | ^~~ -%Error: t/t_timing_func_fork_bad.v:56:9: Writing to an output automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:63:9: Writing to an output automatic variable of a function after a timing control is not allowed : ... note: In instance 't' - 56 | f5 = 0; + 63 | f5 = 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:57:9: Writing to an output automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:64:9: Writing to an output automatic variable of a function after a timing control is not allowed : ... note: In instance 't' - 57 | o5 = 0; + 64 | o5 = 0; | ^~ %Error: Exiting due to diff --git a/test_regress/t/t_timing_func_fork_bad.v b/test_regress/t/t_timing_func_fork_bad.v index d0e35c8b5..ff480632f 100644 --- a/test_regress/t/t_timing_func_fork_bad.v +++ b/test_regress/t/t_timing_func_fork_bad.v @@ -16,6 +16,13 @@ module t; join_none endfunction + task automatic t1(output int o1); + fork begin + #1 $stop; + o1 = 0; + end join_none + endtask + function automatic int f2(inout io2); fork begin From 0e9551ece1c2b4cf0be86eb2a6ee2216191cbc2e Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Fri, 27 Mar 2026 12:50:19 +0100 Subject: [PATCH 29/42] Update test Signed-off-by: Kamil Danecki --- test_regress/t/t_fork_write_after_timing.py | 2 +- test_regress/t/t_fork_write_after_timing.v | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/test_regress/t/t_fork_write_after_timing.py b/test_regress/t/t_fork_write_after_timing.py index 6f2f7c7d7..55248e18d 100755 --- a/test_regress/t/t_fork_write_after_timing.py +++ b/test_regress/t/t_fork_write_after_timing.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--Wno-initialdly", "--binary"]) +test.compile(verilator_flags2=["--binary"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_fork_write_after_timing.v b/test_regress/t/t_fork_write_after_timing.v index 08d9c4228..bac418fac 100644 --- a/test_regress/t/t_fork_write_after_timing.v +++ b/test_regress/t/t_fork_write_after_timing.v @@ -21,8 +21,6 @@ module t; t2(x1); t3(x1); t4(x1); - t5(x2); - t6(x2); #5 $write("*-* All Finished *-*\n"); $finish; @@ -59,13 +57,4 @@ module t; join_none #2 $display("t4 end %d", x); endtask - - task t5(output int x); - if (x != 0) $stop; - x <= #1 3; - endtask - - task t6(inout int x); - x <= #1 4; - endtask endmodule From 011ca59c0e03f32112e12cb86b5b75bccdb6527c Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Fri, 27 Mar 2026 12:57:05 +0100 Subject: [PATCH 30/42] Apply suggestion Signed-off-by: Kamil Danecki --- src/V3Begin.cpp | 4 ++-- src/V3LinkParse.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index ad60df60e..745058e99 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -312,14 +312,14 @@ class BeginVisitor final : public VNVisitor { nodep->replaceWith(portp); if (nodep->isInput() || nodep->isInout()) { - AstAssign* initAssignp = new AstAssign{nodep->fileline(), + AstAssign* const initAssignp = new AstAssign{nodep->fileline(), new AstVarRef{nodep->fileline(), nodep, VAccess::WRITE}, new AstVarRef{portp->fileline(), portp, VAccess::READ}}; portp->addNextHere(initAssignp); } if (nodep->isWritable()) { - AstAssign* endAssignp = new AstAssign{nodep->fileline(), + AstAssign* const endAssignp = new AstAssign{nodep->fileline(), new AstVarRef{portp->fileline(), portp, VAccess::WRITE}, new AstVarRef{nodep->fileline(), nodep, VAccess::READ}}; m_ftaskp->addStmtsp(endAssignp); diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index ec5fd35f1..8d1c652e7 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -250,7 +250,7 @@ class LinkParseVisitor final : public VNVisitor { VL_RESTORER(m_lifetimeAllowed); m_lifetimeAllowed = true; VL_RESTORER(m_hasTimingControl); - m_hasTimingControl = nodep->exists([&](const AstNode* const nodep) {return nodep->isTimingControl();}); + m_hasTimingControl = nodep->exists([](const AstNode* const nodep) {return nodep->isTimingControl();}); if (!nodep->lifetime().isNone()) { m_lifetime = nodep->lifetime().makeImplicit(); } else { From 75097a083e712d28fe975bac98f598654430d80a Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Mon, 30 Mar 2026 13:02:01 +0200 Subject: [PATCH 31/42] Update test Signed-off-by: Kamil Danecki --- test_regress/t/t_fork_write_after_timing.out | 6 ++-- test_regress/t/t_fork_write_after_timing.v | 30 +++++++++++++++++--- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/test_regress/t/t_fork_write_after_timing.out b/test_regress/t/t_fork_write_after_timing.out index 08d8e0da2..03a4d55ce 100644 --- a/test_regress/t/t_fork_write_after_timing.out +++ b/test_regress/t/t_fork_write_after_timing.out @@ -8,10 +8,10 @@ t4 start -1 t4 end 2 t1 start 1 t1 end 1 -t2 start 1 -t2 end 1 +t2 start -1 +t2 end -1 t3 start 1 t3 end 1 -t4 start 1 +t4 start 2 t4 end 2 *-* All Finished *-* diff --git a/test_regress/t/t_fork_write_after_timing.v b/test_regress/t/t_fork_write_after_timing.v index bac418fac..60cbe44cb 100644 --- a/test_regress/t/t_fork_write_after_timing.v +++ b/test_regress/t/t_fork_write_after_timing.v @@ -4,7 +4,7 @@ // SPDX-License-Identifier: CC0-1.0 module t; - int x1, x2, x3, x4; + int x1, x2, x3, x4, x5, x6, x7; initial begin x1 = -1; @@ -18,13 +18,20 @@ module t; t4(x4); #10 t1(x1); - t2(x1); - t3(x1); - t4(x1); + t2(x2); + t3(x3); + t4(x4); + + if (x5 != 3) $stop; + if (x6 != 0) $stop; + if (x7 != 4) $stop; #5 $write("*-* All Finished *-*\n"); $finish; end + always #1 t5(x5); + always #1 t6(x6); + always #1 t7(x7); task t1(output int x); $display("t1 start %d", x); @@ -57,4 +64,19 @@ module t; join_none #2 $display("t4 end %d", x); endtask + + task t5(output int x); + x <= #1 3; + endtask + + task t6(inout int x); + x <= #1 4; + endtask + + task static t7(inout int x); + int y = 0; + x <= #1 4; + #2 y = x; + if (y != 4) $stop; + endtask endmodule From d9dfe983ae561e16357b690fe0af499d885b1122 Mon Sep 17 00:00:00 2001 From: github action Date: Mon, 30 Mar 2026 11:23:21 +0000 Subject: [PATCH 32/42] Apply 'make format' --- src/V3Begin.cpp | 15 +++++++-------- src/V3Fork.cpp | 10 ++++------ src/V3LinkParse.cpp | 3 ++- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 745058e99..7ec8b2511 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -300,27 +300,26 @@ class BeginVisitor final : public VNVisitor { void visit(AstVar* nodep) override { // If static variable, move it outside a function. if (nodep->lifetime().isStatic() && m_ftaskp) { - if (nodep->user1()) { - return; - } + if (nodep->user1()) { return; } const std::string newName = m_ftaskp->name() + "__Vstatic__" + dot(m_unnamedScope, nodep->name()); if (nodep->isIO()) { - // Create a port that is used for passing value between argument and static variable + // Create a port that is used for passing value between argument and static + // variable AstVar* const portp = nodep->cloneTreePure(false); portp->user1(true); nodep->replaceWith(portp); if (nodep->isInput() || nodep->isInout()) { - AstAssign* const initAssignp = new AstAssign{nodep->fileline(), - new AstVarRef{nodep->fileline(), nodep, VAccess::WRITE}, + AstAssign* const initAssignp = new AstAssign{ + nodep->fileline(), new AstVarRef{nodep->fileline(), nodep, VAccess::WRITE}, new AstVarRef{portp->fileline(), portp, VAccess::READ}}; portp->addNextHere(initAssignp); } if (nodep->isWritable()) { - AstAssign* const endAssignp = new AstAssign{nodep->fileline(), - new AstVarRef{portp->fileline(), portp, VAccess::WRITE}, + AstAssign* const endAssignp = new AstAssign{ + nodep->fileline(), new AstVarRef{portp->fileline(), portp, VAccess::WRITE}, new AstVarRef{nodep->fileline(), nodep, VAccess::READ}}; m_ftaskp->addStmtsp(endAssignp); } diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index 993924a09..9d9f7dcdb 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -443,12 +443,10 @@ class DynScopeVisitor final : public VNVisitor { if (!isEvent && m_afterTimingControl && nodep->varp()->isWritable() && nodep->access().isWriteOrRW()) { // The output variable may not exist after a delay, so we can't just write to it - nodep->v3error( - "Writing to an " - << nodep->varp()->verilogKwd() - << " automatic variable of a " - << (m_inFunc ? "function" : "task") - << " after a timing control is not allowed"); + nodep->v3error("Writing to an " << nodep->varp()->verilogKwd() + << " automatic variable of a " + << (m_inFunc ? "function" : "task") + << " after a timing control is not allowed"); } if (!framep->instance().initialized()) framep->createInstancePrototype(); framep->captureVarInsert(nodep->varp()); diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 8d1c652e7..8e8089f9a 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -250,7 +250,8 @@ class LinkParseVisitor final : public VNVisitor { VL_RESTORER(m_lifetimeAllowed); m_lifetimeAllowed = true; VL_RESTORER(m_hasTimingControl); - m_hasTimingControl = nodep->exists([](const AstNode* const nodep) {return nodep->isTimingControl();}); + m_hasTimingControl + = nodep->exists([](const AstNode* const nodep) { return nodep->isTimingControl(); }); if (!nodep->lifetime().isNone()) { m_lifetime = nodep->lifetime().makeImplicit(); } else { From 3e596e7f55cd1de42901c10291a832d3f0817f59 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Tue, 31 Mar 2026 11:51:52 +0200 Subject: [PATCH 33/42] Update test Signed-off-by: Kamil Danecki --- test_regress/t/t_fork_write_after_timing.out | 17 ----- test_regress/t/t_fork_write_after_timing.py | 2 +- test_regress/t/t_fork_write_after_timing.v | 76 +++++++++++++------- 3 files changed, 52 insertions(+), 43 deletions(-) delete mode 100644 test_regress/t/t_fork_write_after_timing.out diff --git a/test_regress/t/t_fork_write_after_timing.out b/test_regress/t/t_fork_write_after_timing.out deleted file mode 100644 index 03a4d55ce..000000000 --- a/test_regress/t/t_fork_write_after_timing.out +++ /dev/null @@ -1,17 +0,0 @@ -t1 start 0 -t1 end 0 -t2 start -1 -t2 end -1 -t3 start 0 -t3 end 1 -t4 start -1 -t4 end 2 -t1 start 1 -t1 end 1 -t2 start -1 -t2 end -1 -t3 start 1 -t3 end 1 -t4 start 2 -t4 end 2 -*-* All Finished *-* diff --git a/test_regress/t/t_fork_write_after_timing.py b/test_regress/t/t_fork_write_after_timing.py index 55248e18d..7ded63f3a 100755 --- a/test_regress/t/t_fork_write_after_timing.py +++ b/test_regress/t/t_fork_write_after_timing.py @@ -13,6 +13,6 @@ test.scenarios('simulator') test.compile(verilator_flags2=["--binary"]) -test.execute(expect_filename=test.golden_filename) +test.execute() test.passes() diff --git a/test_regress/t/t_fork_write_after_timing.v b/test_regress/t/t_fork_write_after_timing.v index 60cbe44cb..aef9e34d0 100644 --- a/test_regress/t/t_fork_write_after_timing.v +++ b/test_regress/t/t_fork_write_after_timing.v @@ -3,6 +3,11 @@ // SPDX-FileCopyrightText: 2026 Antmicro // SPDX-License-Identifier: CC0-1.0 +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + module t; int x1, x2, x3, x4, x5, x6, x7; @@ -16,15 +21,23 @@ module t; t2(x2); t3(x3); t4(x4); + `checkd(x1, 0) + `checkd(x2, -1) + `checkd(x3, 1) + `checkd(x4, 2) #10 t1(x1); t2(x2); t3(x3); t4(x4); + `checkd(x1, 1) + `checkd(x2, -1) + `checkd(x3, 1) + `checkd(x4, 2) - if (x5 != 3) $stop; - if (x6 != 0) $stop; - if (x7 != 4) $stop; + `checkd(x5, 3) + `checkd(x6, 0) + `checkd(x7, 4) #5 $write("*-* All Finished *-*\n"); $finish; @@ -34,35 +47,48 @@ module t; always #1 t7(x7); task t1(output int x); - $display("t1 start %d", x); fork x = #1 1; join_none - $display("t1 end %d", x); + if ($time < 10) begin + `checkd(x, 0) + end + else begin + `checkd(x, 1) + end endtask - task t2(inout int xa); - $display("t2 start %d", xa); - fork - xa = #1 2; - join_none - $display("t2 end %d", xa); - endtask - - task t3(output int x); - $display("t3 start %d", x); - fork - x = #1 1; - join_none - #2 $display("t3 end %d", x); - endtask - - task t4(inout int x); - $display("t4 start %d", x); + task t2(inout int x); fork x = #1 2; join_none - #2 $display("t4 end %d", x); + `checkd(x, -1) + endtask + + task t3(output int x); + if ($time < 10) begin + `checkd(x, 0) + end + else begin + `checkd(x, 1) + end + fork + x = #1 1; + join_none + #2 `checkd(x, 1); + endtask + + task t4(inout int x); + if ($time < 10) begin + `checkd(x, -1) + end + else begin + `checkd(x, 2) + end + fork + x = #1 2; + join_none + #2 `checkd(x, 2); endtask task t5(output int x); @@ -77,6 +103,6 @@ module t; int y = 0; x <= #1 4; #2 y = x; - if (y != 4) $stop; + `checkd(x, 4) endtask endmodule From fbf110cd060d21d141d3a218c7f3a35357005426 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Tue, 31 Mar 2026 12:20:50 +0200 Subject: [PATCH 34/42] Apply suggestion Signed-off-by: Kamil Danecki --- src/V3Begin.cpp | 3 +-- src/V3Fork.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 7ec8b2511..fa339c062 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -298,16 +298,15 @@ class BeginVisitor final : public VNVisitor { VL_DO_DANGLING(pushDeletep(nodep), nodep); } void visit(AstVar* nodep) override { + if (nodep->user1SetOnce()) { return; } // If static variable, move it outside a function. if (nodep->lifetime().isStatic() && m_ftaskp) { - if (nodep->user1()) { return; } const std::string newName = m_ftaskp->name() + "__Vstatic__" + dot(m_unnamedScope, nodep->name()); if (nodep->isIO()) { // Create a port that is used for passing value between argument and static // variable AstVar* const portp = nodep->cloneTreePure(false); - portp->user1(true); nodep->replaceWith(portp); if (nodep->isInput() || nodep->isInout()) { diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index 9d9f7dcdb..e0329c2c2 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -446,7 +446,7 @@ class DynScopeVisitor final : public VNVisitor { nodep->v3error("Writing to an " << nodep->varp()->verilogKwd() << " automatic variable of a " << (m_inFunc ? "function" : "task") - << " after a timing control is not allowed"); + << " after a timing control is not allowed (IEEE 1800-2023 13.2.2)"); } if (!framep->instance().initialized()) framep->createInstancePrototype(); framep->captureVarInsert(nodep->varp()); From 1e581ba5fa16f207f376acd7cb26da92b9cd6dbe Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Tue, 31 Mar 2026 12:21:45 +0200 Subject: [PATCH 35/42] Make refs illegal inside static tasks Signed-off-by: Kamil Danecki --- src/V3Begin.cpp | 3 +++ .../t/t_fork_write_after_timing_bad.out | 6 ++++++ test_regress/t/t_fork_write_after_timing_bad.py | 16 ++++++++++++++++ test_regress/t/t_fork_write_after_timing_bad.v | 17 +++++++++++++++++ 4 files changed, 42 insertions(+) create mode 100644 test_regress/t/t_fork_write_after_timing_bad.out create mode 100755 test_regress/t/t_fork_write_after_timing_bad.py create mode 100644 test_regress/t/t_fork_write_after_timing_bad.v diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index fa339c062..d296bb70f 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -304,6 +304,9 @@ class BeginVisitor final : public VNVisitor { const std::string newName = m_ftaskp->name() + "__Vstatic__" + dot(m_unnamedScope, nodep->name()); if (nodep->isIO()) { + if (nodep->direction().isRef()) { + nodep->v3error("It is illegal to use argument passing by reference for subroutines with a lifetime of static (IEEE 1800-2023 13.5.2)"); + } // Create a port that is used for passing value between argument and static // variable AstVar* const portp = nodep->cloneTreePure(false); diff --git a/test_regress/t/t_fork_write_after_timing_bad.out b/test_regress/t/t_fork_write_after_timing_bad.out new file mode 100644 index 000000000..99c600148 --- /dev/null +++ b/test_regress/t/t_fork_write_after_timing_bad.out @@ -0,0 +1,6 @@ +%Error: t/t_fork_write_after_timing_bad.v:12:19: It is illegal to use argument passing by reference for subroutines with a lifetime of static (IEEE 1800-2023 13.5.2) + : ... note: In instance 't' + 12 | task t1(ref int x); + | ^ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: Exiting due to diff --git a/test_regress/t/t_fork_write_after_timing_bad.py b/test_regress/t/t_fork_write_after_timing_bad.py new file mode 100755 index 000000000..d77726df8 --- /dev/null +++ b/test_regress/t/t_fork_write_after_timing_bad.py @@ -0,0 +1,16 @@ +#!/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 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.lint(verilator_flags2=["--timing"], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_fork_write_after_timing_bad.v b/test_regress/t/t_fork_write_after_timing_bad.v new file mode 100644 index 000000000..27c133140 --- /dev/null +++ b/test_regress/t/t_fork_write_after_timing_bad.v @@ -0,0 +1,17 @@ +// DESCRIPTION: Verilator: Verilog Test module +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Antmicro +// SPDX-License-Identifier: CC0-1.0 + +module t; + int x1; + initial begin + t1(x1); + $finish; + end + task t1(ref int x); + fork + x = #1 2; + join_none + endtask +endmodule From e36564ff7332033a0ec650e2b8783097e7af73f0 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Tue, 31 Mar 2026 12:27:37 +0200 Subject: [PATCH 36/42] Add warning about setting lifetime to static Signed-off-by: Kamil Danecki --- src/V3LinkParse.cpp | 1 + test_regress/t/t_fork_write_after_timing.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 8e8089f9a..6ac2855ed 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -389,6 +389,7 @@ class LinkParseVisitor final : public VNVisitor { // Variables in static tasks with timing control can be used after the task has ended // with use of join..fork_none, so they need to be static if (m_ftaskp->lifetime().isStatic() && m_hasTimingControl) { + nodep->v3warn(IMPLICITSTATIC, "Variable's lifetime implicitly set to static because in static task with timing control."); nodep->lifetime(VLifetime::STATIC_IMPLICIT); } else { nodep->lifetime(VLifetime::AUTOMATIC_IMPLICIT); diff --git a/test_regress/t/t_fork_write_after_timing.py b/test_regress/t/t_fork_write_after_timing.py index 7ded63f3a..f3eeea08e 100755 --- a/test_regress/t/t_fork_write_after_timing.py +++ b/test_regress/t/t_fork_write_after_timing.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--binary"]) +test.compile(verilator_flags2=["--binary", "-Wno-implicitstatic"]) test.execute() From 22e566a1c06ebeb5569c07041d337d32e0aa6063 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 2 Apr 2026 15:09:56 +0200 Subject: [PATCH 37/42] Allow warning in tests Signed-off-by: Kamil Danecki --- test_regress/t/t_event_array_fire.py | 2 +- test_regress/t/t_fork_dynscope_interface.py | 2 +- test_regress/t/t_fork_dynscope_out.py | 2 +- test_regress/t/t_fork_repeat_reset.py | 2 +- test_regress/t/t_fork_write_after_timing.py | 2 +- .../t/t_fork_write_after_timing_bad.out | 5 +++++ test_regress/t/t_timing_func_fork_bad.out | 22 +++++++++---------- test_regress/t/t_uvm_dpi_v2017_1_0.py | 2 +- test_regress/t/t_uvm_dpi_v2020_3_1.py | 2 +- .../t/t_uvm_hello_all_v2017_1_0_dpi.py | 1 + .../t/t_uvm_hello_all_v2020_3_1_dpi.py | 1 + test_regress/t/t_virtual_interface_method.py | 2 +- 12 files changed, 26 insertions(+), 19 deletions(-) diff --git a/test_regress/t/t_event_array_fire.py b/test_regress/t/t_event_array_fire.py index 4ee7f9e14..a6946df89 100755 --- a/test_regress/t/t_event_array_fire.py +++ b/test_regress/t/t_event_array_fire.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=['--binary']) +test.compile(verilator_flags2=['--binary', '-Wno-IMPLICITSTATIC']) test.execute() diff --git a/test_regress/t/t_fork_dynscope_interface.py b/test_regress/t/t_fork_dynscope_interface.py index 6ed2bc5c7..afb502505 100755 --- a/test_regress/t/t_fork_dynscope_interface.py +++ b/test_regress/t/t_fork_dynscope_interface.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=['--binary --trace-fst']) +test.compile(verilator_flags2=['--binary --trace-fst', '-Wno-IMPLICITSTATIC']) test.execute() diff --git a/test_regress/t/t_fork_dynscope_out.py b/test_regress/t/t_fork_dynscope_out.py index f478cb764..e423547dd 100755 --- a/test_regress/t/t_fork_dynscope_out.py +++ b/test_regress/t/t_fork_dynscope_out.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--binary -Wno-INITIALDLY"]) +test.compile(verilator_flags2=["--binary -Wno-INITIALDLY", '-Wno-IMPLICITSTATIC']) test.execute() diff --git a/test_regress/t/t_fork_repeat_reset.py b/test_regress/t/t_fork_repeat_reset.py index 31b1f0e53..b3b2ce95d 100755 --- a/test_regress/t/t_fork_repeat_reset.py +++ b/test_regress/t/t_fork_repeat_reset.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--binary"]) +test.compile(verilator_flags2=["--binary", '-Wno-IMPLICITSTATIC']) test.execute() diff --git a/test_regress/t/t_fork_write_after_timing.py b/test_regress/t/t_fork_write_after_timing.py index f3eeea08e..044191f3f 100755 --- a/test_regress/t/t_fork_write_after_timing.py +++ b/test_regress/t/t_fork_write_after_timing.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--binary", "-Wno-implicitstatic"]) +test.compile(verilator_flags2=["--binary", "-Wno-IMPLICITSTATIC"]) test.execute() diff --git a/test_regress/t/t_fork_write_after_timing_bad.out b/test_regress/t/t_fork_write_after_timing_bad.out index 99c600148..b22afa40a 100644 --- a/test_regress/t/t_fork_write_after_timing_bad.out +++ b/test_regress/t/t_fork_write_after_timing_bad.out @@ -1,3 +1,8 @@ +%Warning-IMPLICITSTATIC: t/t_fork_write_after_timing_bad.v:12:19: Variable's lifetime implicitly set to static because in static task with timing control. + 12 | task t1(ref int x); + | ^ + ... For warning description see https://verilator.org/warn/IMPLICITSTATIC?v=latest + ... Use "/* verilator lint_off IMPLICITSTATIC */" and lint_on around source to disable this message. %Error: t/t_fork_write_after_timing_bad.v:12:19: It is illegal to use argument passing by reference for subroutines with a lifetime of static (IEEE 1800-2023 13.5.2) : ... note: In instance 't' 12 | task t1(ref int x); diff --git a/test_regress/t/t_timing_func_fork_bad.out b/test_regress/t/t_timing_func_fork_bad.out index a605bf784..8e594bf3d 100644 --- a/test_regress/t/t_timing_func_fork_bad.out +++ b/test_regress/t/t_timing_func_fork_bad.out @@ -1,45 +1,45 @@ -%Error: t/t_timing_func_fork_bad.v:13:9: Writing to an output automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:13:9: Writing to an output automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2) : ... note: In instance 't' 13 | f1 = 0; | ^~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_timing_func_fork_bad.v:14:9: Writing to an output automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:14:9: Writing to an output automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2) : ... note: In instance 't' 14 | o1 = 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:22:7: Writing to an output automatic variable of a task after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:22:7: Writing to an output automatic variable of a task after a timing control is not allowed (IEEE 1800-2023 13.2.2) : ... note: In instance 't' 22 | o1 = 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:29:9: Writing to an output automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:29:9: Writing to an output automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2) : ... note: In instance 't' 29 | f2 = #5 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:31:9: Writing to an inout automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:31:9: Writing to an inout automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2) : ... note: In instance 't' 31 | io2 = 0; | ^~~ -%Error: t/t_timing_func_fork_bad.v:41:9: Writing to an output automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:41:9: Writing to an output automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2) : ... note: In instance 't' 41 | f3 = 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:42:9: Writing to an output automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:42:9: Writing to an output automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2) : ... note: In instance 't' 42 | o3 = 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:50:9: Writing to an output automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:50:9: Writing to an output automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2) : ... note: In instance 't' 50 | f4 = @e 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:52:9: Writing to an inout automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:52:9: Writing to an inout automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2) : ... note: In instance 't' 52 | io4 = 0; | ^~~ -%Error: t/t_timing_func_fork_bad.v:63:9: Writing to an output automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:63:9: Writing to an output automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2) : ... note: In instance 't' 63 | f5 = 0; | ^~ -%Error: t/t_timing_func_fork_bad.v:64:9: Writing to an output automatic variable of a function after a timing control is not allowed +%Error: t/t_timing_func_fork_bad.v:64:9: Writing to an output automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2) : ... note: In instance 't' 64 | o5 = 0; | ^~ diff --git a/test_regress/t/t_uvm_dpi_v2017_1_0.py b/test_regress/t/t_uvm_dpi_v2017_1_0.py index 42e81f4b6..0f0153143 100755 --- a/test_regress/t/t_uvm_dpi_v2017_1_0.py +++ b/test_regress/t/t_uvm_dpi_v2017_1_0.py @@ -17,7 +17,7 @@ if re.search(r'clang', test.cxx_version): test.skip("uvm_regex.cc from upstream has clang warnings") test.compile(verilator_flags2=[ - "--binary", test.build_jobs, "--vpi", "+define+T_V2017_1_0", "+incdir+t/uvm/v2017_1_0", + "--binary", "-Wno-IMPLICITSTATIC", test.build_jobs, "--vpi", "+define+T_V2017_1_0", "+incdir+t/uvm/v2017_1_0", test.pli_filename ]) diff --git a/test_regress/t/t_uvm_dpi_v2020_3_1.py b/test_regress/t/t_uvm_dpi_v2020_3_1.py index 288cde74e..cbe3158a2 100755 --- a/test_regress/t/t_uvm_dpi_v2020_3_1.py +++ b/test_regress/t/t_uvm_dpi_v2020_3_1.py @@ -17,7 +17,7 @@ if re.search(r'clang', test.cxx_version): test.skip("uvm_regex.cc from upstream has clang warnings") test.compile(verilator_flags2=[ - "--binary", test.build_jobs, "--vpi", "+define+T_V2020_3_1", "+incdir+t/uvm/v2020_3_1", + "--binary", test.build_jobs, "--vpi", "+define+T_V2020_3_1", "+incdir+t/uvm/v2020_3_1", "-Wno-IMPLICITSTATIC", test.pli_filename ]) diff --git a/test_regress/t/t_uvm_hello_all_v2017_1_0_dpi.py b/test_regress/t/t_uvm_hello_all_v2017_1_0_dpi.py index c410f628b..e27e4eb85 100755 --- a/test_regress/t/t_uvm_hello_all_v2017_1_0_dpi.py +++ b/test_regress/t/t_uvm_hello_all_v2017_1_0_dpi.py @@ -23,6 +23,7 @@ test.compile(v_flags2=[ "--vpi", "--CFLAGS -O0", "-Wall", + "-Wno-IMPLICITSTATIC", "+incdir+t/uvm", # "t/uvm/uvm_pkg_all_v2017_1_0_dpi.svh", test.pli_filename diff --git a/test_regress/t/t_uvm_hello_all_v2020_3_1_dpi.py b/test_regress/t/t_uvm_hello_all_v2020_3_1_dpi.py index b6ffb1f15..97d73bd14 100755 --- a/test_regress/t/t_uvm_hello_all_v2020_3_1_dpi.py +++ b/test_regress/t/t_uvm_hello_all_v2020_3_1_dpi.py @@ -23,6 +23,7 @@ test.compile(v_flags2=[ "--vpi", "--CFLAGS -O0", "-Wall", + '-Wno-IMPLICITSTATIC', "+incdir+t/uvm", # "t/uvm/uvm_pkg_all_v2020_3_1_dpi.svh", test.pli_filename diff --git a/test_regress/t/t_virtual_interface_method.py b/test_regress/t/t_virtual_interface_method.py index 61fae108a..1ca098664 100755 --- a/test_regress/t/t_virtual_interface_method.py +++ b/test_regress/t/t_virtual_interface_method.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(v_flags2=["--binary"], verilator_make_gmake=False) +test.compile(v_flags2=["--binary", '-Wno-IMPLICITSTATIC'], verilator_make_gmake=False) test.execute() From 8f99f3e0bccda7c59027dd1dfb150b00dc99ce0d Mon Sep 17 00:00:00 2001 From: github action Date: Thu, 2 Apr 2026 14:08:45 +0000 Subject: [PATCH 38/42] Apply 'make format' --- src/V3Begin.cpp | 4 +++- src/V3Fork.cpp | 9 +++++---- src/V3LinkParse.cpp | 3 ++- test_regress/t/t_uvm_dpi_v2017_1_0.py | 4 ++-- test_regress/t/t_uvm_dpi_v2020_3_1.py | 4 ++-- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index d296bb70f..43ad40c1e 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -305,7 +305,9 @@ class BeginVisitor final : public VNVisitor { = m_ftaskp->name() + "__Vstatic__" + dot(m_unnamedScope, nodep->name()); if (nodep->isIO()) { if (nodep->direction().isRef()) { - nodep->v3error("It is illegal to use argument passing by reference for subroutines with a lifetime of static (IEEE 1800-2023 13.5.2)"); + nodep->v3error( + "It is illegal to use argument passing by reference for subroutines with " + "a lifetime of static (IEEE 1800-2023 13.5.2)"); } // Create a port that is used for passing value between argument and static // variable diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index e0329c2c2..451c2c4a2 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -443,10 +443,11 @@ class DynScopeVisitor final : public VNVisitor { if (!isEvent && m_afterTimingControl && nodep->varp()->isWritable() && nodep->access().isWriteOrRW()) { // The output variable may not exist after a delay, so we can't just write to it - nodep->v3error("Writing to an " << nodep->varp()->verilogKwd() - << " automatic variable of a " - << (m_inFunc ? "function" : "task") - << " after a timing control is not allowed (IEEE 1800-2023 13.2.2)"); + nodep->v3error( + "Writing to an " + << nodep->varp()->verilogKwd() << " automatic variable of a " + << (m_inFunc ? "function" : "task") + << " after a timing control is not allowed (IEEE 1800-2023 13.2.2)"); } if (!framep->instance().initialized()) framep->createInstancePrototype(); framep->captureVarInsert(nodep->varp()); diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 6ac2855ed..6569d210c 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -389,7 +389,8 @@ class LinkParseVisitor final : public VNVisitor { // Variables in static tasks with timing control can be used after the task has ended // with use of join..fork_none, so they need to be static if (m_ftaskp->lifetime().isStatic() && m_hasTimingControl) { - nodep->v3warn(IMPLICITSTATIC, "Variable's lifetime implicitly set to static because in static task with timing control."); + nodep->v3warn(IMPLICITSTATIC, "Variable's lifetime implicitly set to static " + "because in static task with timing control."); nodep->lifetime(VLifetime::STATIC_IMPLICIT); } else { nodep->lifetime(VLifetime::AUTOMATIC_IMPLICIT); diff --git a/test_regress/t/t_uvm_dpi_v2017_1_0.py b/test_regress/t/t_uvm_dpi_v2017_1_0.py index 0f0153143..f2df8b869 100755 --- a/test_regress/t/t_uvm_dpi_v2017_1_0.py +++ b/test_regress/t/t_uvm_dpi_v2017_1_0.py @@ -17,8 +17,8 @@ if re.search(r'clang', test.cxx_version): test.skip("uvm_regex.cc from upstream has clang warnings") test.compile(verilator_flags2=[ - "--binary", "-Wno-IMPLICITSTATIC", test.build_jobs, "--vpi", "+define+T_V2017_1_0", "+incdir+t/uvm/v2017_1_0", - test.pli_filename + "--binary", "-Wno-IMPLICITSTATIC", test.build_jobs, "--vpi", "+define+T_V2017_1_0", + "+incdir+t/uvm/v2017_1_0", test.pli_filename ]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_uvm_dpi_v2020_3_1.py b/test_regress/t/t_uvm_dpi_v2020_3_1.py index cbe3158a2..c7efa5325 100755 --- a/test_regress/t/t_uvm_dpi_v2020_3_1.py +++ b/test_regress/t/t_uvm_dpi_v2020_3_1.py @@ -17,8 +17,8 @@ if re.search(r'clang', test.cxx_version): test.skip("uvm_regex.cc from upstream has clang warnings") test.compile(verilator_flags2=[ - "--binary", test.build_jobs, "--vpi", "+define+T_V2020_3_1", "+incdir+t/uvm/v2020_3_1", "-Wno-IMPLICITSTATIC", - test.pli_filename + "--binary", test.build_jobs, "--vpi", "+define+T_V2020_3_1", "+incdir+t/uvm/v2020_3_1", + "-Wno-IMPLICITSTATIC", test.pli_filename ]) test.execute(expect_filename=test.golden_filename) From 16362deb2ef916d13c8ccf93dc855454813a06f7 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Fri, 3 Apr 2026 10:51:55 +0200 Subject: [PATCH 39/42] Update test_regress/t/t_fork_write_after_timing.v Co-authored-by: Wilson Snyder --- test_regress/t/t_fork_write_after_timing.v | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test_regress/t/t_fork_write_after_timing.v b/test_regress/t/t_fork_write_after_timing.v index aef9e34d0..0da2d9b0e 100644 --- a/test_regress/t/t_fork_write_after_timing.v +++ b/test_regress/t/t_fork_write_after_timing.v @@ -17,7 +17,8 @@ module t; x3 = -1; x4 = -1; - #1 t1(x1); + #1; + t1(x1); t2(x2); t3(x3); t4(x4); From 14dfd8124eacde3e74641e919add5322fae7c7cb Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Fri, 3 Apr 2026 10:54:43 +0200 Subject: [PATCH 40/42] Apply suggestion from @wsnyder Co-authored-by: Wilson Snyder --- test_regress/t/t_fork_write_after_timing.v | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test_regress/t/t_fork_write_after_timing.v b/test_regress/t/t_fork_write_after_timing.v index 0da2d9b0e..318a0f8d9 100644 --- a/test_regress/t/t_fork_write_after_timing.v +++ b/test_regress/t/t_fork_write_after_timing.v @@ -40,7 +40,16 @@ module t; `checkd(x6, 0) `checkd(x7, 4) - #5 $write("*-* All Finished *-*\n"); + #5; + `checkd(x1, 1) + `checkd(x2, -1) + `checkd(x3, 1) + `checkd(x4, 2) + `checkd(x5, 3) + `checkd(x6, 0) + `checkd(x7, 4) + + $write("*-* All Finished *-*\n"); $finish; end always #1 t5(x5); From e181a86d6e069857ab01511be7205a3565c71eac Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Fri, 3 Apr 2026 11:15:05 +0200 Subject: [PATCH 41/42] Fix whitespace Signed-off-by: Kamil Danecki --- test_regress/t/t_fork_write_after_timing.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_regress/t/t_fork_write_after_timing.v b/test_regress/t/t_fork_write_after_timing.v index 318a0f8d9..027884880 100644 --- a/test_regress/t/t_fork_write_after_timing.v +++ b/test_regress/t/t_fork_write_after_timing.v @@ -48,7 +48,7 @@ module t; `checkd(x5, 3) `checkd(x6, 0) `checkd(x7, 4) - + $write("*-* All Finished *-*\n"); $finish; end From c66d4729c2a0426d09961b44ce464ca0af156e1c Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Fri, 3 Apr 2026 11:41:08 +0200 Subject: [PATCH 42/42] Fix whitespace 2 Signed-off-by: Kamil Danecki --- test_regress/t/t_fork_write_after_timing.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_regress/t/t_fork_write_after_timing.v b/test_regress/t/t_fork_write_after_timing.v index 027884880..93b778ad6 100644 --- a/test_regress/t/t_fork_write_after_timing.v +++ b/test_regress/t/t_fork_write_after_timing.v @@ -17,7 +17,7 @@ module t; x3 = -1; x4 = -1; - #1; + #1; t1(x1); t2(x2); t3(x3);