diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index fdfe2abe7..79878428a 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -298,12 +298,39 @@ 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) { 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); + nodep->replaceWith(portp); + + if (nodep->isInput() || nodep->isInout()) { + 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}, + new AstVarRef{nodep->fileline(), nodep, VAccess::READ}}; + m_ftaskp->addStmtsp(endAssignp); + } + } else { + nodep->unlinkFrBack(); + } nodep->name(newName); - nodep->unlinkFrBack(); m_ftaskp->addHereThisAsNext(nodep); nodep->funcLocal(false); } else if (m_unnamedScope != "") { diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index 2e40faa24..451c2c4a2 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -443,20 +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 - if (m_inFunc) { - nodep->v3error( - "Writing to an " - << 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"); - } + 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 36c7136c2..0cc410c91 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,9 @@ 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 +386,15 @@ class LinkParseVisitor final : public VNVisitor { } } } else if (m_ftaskp) { - if (!nodep->lifetime().isAutomatic()) nodep->lifetime(VLifetime::AUTOMATIC_IMPLICIT); + // 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); + } } else if (nodep->lifetime().isNone()) { // lifetime shouldn't be unknown, set static if none nodep->lifetime(VLifetime::STATIC_IMPLICIT); 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_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_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_dynscope_unsup.py b/test_regress/t/t_fork_write_after_timing.py similarity index 84% rename from test_regress/t/t_fork_dynscope_unsup.py rename to test_regress/t/t_fork_write_after_timing.py index 382ad0d44..044191f3f 100755 --- a/test_regress/t/t_fork_dynscope_unsup.py +++ b/test_regress/t/t_fork_write_after_timing.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", "-Wno-IMPLICITSTATIC"]) + +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..93b778ad6 --- /dev/null +++ b/test_regress/t/t_fork_write_after_timing.v @@ -0,0 +1,118 @@ +// 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 + +// 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; + + initial begin + x1 = -1; + x2 = -1; + x3 = -1; + x4 = -1; + + #1; + t1(x1); + 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) + + `checkd(x5, 3) + `checkd(x6, 0) + `checkd(x7, 4) + + #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); + always #1 t6(x6); + always #1 t7(x7); + + task t1(output int x); + fork + x = #1 1; + join_none + if ($time < 10) begin + `checkd(x, 0) + end + else begin + `checkd(x, 1) + end + endtask + + task t2(inout int x); + fork + x = #1 2; + join_none + `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); + 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; + `checkd(x, 4) + endtask +endmodule 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..b22afa40a --- /dev/null +++ b/test_regress/t/t_fork_write_after_timing_bad.out @@ -0,0 +1,11 @@ +%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); + | ^ + ... 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_dynscope_unsup.v b/test_regress/t/t_fork_write_after_timing_bad.v similarity index 55% rename from test_regress/t/t_fork_dynscope_unsup.v rename to test_regress/t/t_fork_write_after_timing_bad.v index 893c44a8b..27c133140 100644 --- a/test_regress/t/t_fork_dynscope_unsup.v +++ b/test_regress/t/t_fork_write_after_timing_bad.v @@ -1,24 +1,17 @@ // DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain -// SPDX-FileCopyrightText: 2024 Antmicro +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Antmicro // SPDX-License-Identifier: CC0-1.0 module t; - bit p = 0, q = 0; - + int x1; initial begin - t1(p); - t2(q); + t1(x1); + $finish; end - - task t1(inout p); + task t1(ref int x); fork - p = #1 1; + x = #1 2; join_none endtask - - task t2(output q); - q <= #1 1; - endtask endmodule diff --git a/test_regress/t/t_timing_func_fork_bad.out b/test_regress/t/t_timing_func_fork_bad.out index b9fa6bbce..8e594bf3d 100644 --- a/test_regress/t/t_timing_func_fork_bad.out +++ b/test_regress/t/t_timing_func_fork_bad.out @@ -1,42 +1,46 @@ -%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 (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 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: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: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 | 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 (IEEE 1800-2023 13.2.2) + : ... note: In instance 't' + 29 | 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: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' - 24 | io2 = 0; + 31 | 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: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' - 34 | f3 = 0; + 41 | 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: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' - 35 | o3 = 0; + 42 | 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: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' - 43 | f4 = @e 0; + 50 | 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: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' - 45 | io4 = 0; + 52 | 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: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' - 56 | f5 = 0; + 63 | 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: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' - 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 d3319513a..ff480632f 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,14 @@ module t; join_none endfunction - function int f2(inout io2); + task automatic t1(output int o1); + fork begin + #1 $stop; + o1 = 0; + end join_none + endtask + + function automatic int f2(inout io2); fork begin f2 = #5 0; @@ -27,7 +34,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 +44,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 +56,7 @@ module t; int i; - function int f5(output int o5); + function automatic int f5(output int o5); fork begin wait (i == 0) $stop; 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..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", 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 288cde74e..c7efa5325 100755 --- a/test_regress/t/t_uvm_dpi_v2020_3_1.py +++ b/test_regress/t/t_uvm_dpi_v2020_3_1.py @@ -18,7 +18,7 @@ if re.search(r'clang', test.cxx_version): test.compile(verilator_flags2=[ "--binary", test.build_jobs, "--vpi", "+define+T_V2020_3_1", "+incdir+t/uvm/v2020_3_1", - test.pli_filename + "-Wno-IMPLICITSTATIC", test.pli_filename ]) test.execute(expect_filename=test.golden_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()