diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 636b029e1..b936e33bb 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -19,6 +19,7 @@ // Look for BEGINs // BEGIN(VAR...) -> VAR ... {renamed} // FOR -> WHILEs +// Move static function variables and their AstInitialStatic blocks before a function // // There are two scopes; named BEGINs change %m and variable scopes. // Unnamed BEGINs change only variable, not $display("%m") scope. @@ -64,13 +65,14 @@ private: // STATE BeginState* const m_statep; // Current global state AstNodeModule* m_modp = nullptr; // Current module - const AstNodeFTask* m_ftaskp = nullptr; // Current function/task + AstNodeFTask* m_ftaskp = nullptr; // Current function/task AstNode* m_liftedp = nullptr; // Local nodes we are lifting into m_ftaskp string m_displayScope; // Name of %m in $display/AstScopeName string m_namedScope; // Name of begin blocks above us string m_unnamedScope; // Name of begin blocks, including unnamed blocks int m_ifDepth = 0; // Current if depth bool m_keepBegins = false; // True if begins should not be inlined + std::set m_staticFuncVars; // Static variables from m_ftaskp // METHODS @@ -121,6 +123,25 @@ private: } } + void renameAndStaticsRecurse(AstNode* const nodep) { + // Rename references and move InitialStatic items + if (AstVarRef* const varrefp = VN_CAST(nodep, VarRef)) { + const auto it = m_staticFuncVars.find(varrefp->varp()); + if (it != m_staticFuncVars.end()) varrefp->name((*it)->name()); + } + + if (nodep->op1p()) renameAndStaticsRecurse(nodep->op1p()); + if (nodep->op2p()) renameAndStaticsRecurse(nodep->op2p()); + if (nodep->op3p()) renameAndStaticsRecurse(nodep->op3p()); + if (nodep->op4p()) renameAndStaticsRecurse(nodep->op4p()); + if (nodep->nextp()) renameAndStaticsRecurse(nodep->nextp()); + + if (VN_IS(nodep, InitialStatic)) { + nodep->unlinkFrBack(); + m_ftaskp->addHereThisAsNext(nodep); + } + } + // VISITORS void visit(AstFork* nodep) override { // Keep this begin to group its statements together @@ -165,6 +186,7 @@ private: m_ftaskp = nodep; m_liftedp = nullptr; iterateChildren(nodep); + renameAndStaticsRecurse(nodep); if (m_liftedp) { // Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced if (AstNode* const stmtsp = nodep->stmtsp()) { @@ -174,6 +196,7 @@ private: nodep->addStmtsp(m_liftedp); m_liftedp = nullptr; } + m_staticFuncVars.clear(); m_ftaskp = nullptr; } } @@ -210,7 +233,15 @@ private: } } void visit(AstVar* nodep) override { - if (m_unnamedScope != "") { + // If static variable, move it outside a function. + if (nodep->lifetime().isStatic() && m_ftaskp) { + const std::string newName = m_ftaskp->name() + "__Vstatic__" + nodep->name(); + nodep->name(newName); + nodep->unlinkFrBack(); + m_ftaskp->addHereThisAsNext(nodep); + m_staticFuncVars.insert(nodep); + nodep->funcLocal(false); + } else if (m_unnamedScope != "") { // Rename it nodep->name(dot(m_unnamedScope, nodep->name())); m_statep->userMarkChanged(nodep); diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index c87fd5198..03b8abbef 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1176,9 +1176,6 @@ class LinkDotFindVisitor final : public VNVisitor { // Var: Remember its name for later resolution UASSERT_OBJ(m_curSymp && m_modSymp, nodep, "Var not under module?"); iterateChildren(nodep); - if (nodep->isFuncLocal() && nodep->lifetime().isStatic()) { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: 'static' function/task variables"); - } if (!m_statep->forScopeCreation()) { // Find under either a task or the module's vars const VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name()); diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 8aa45b271..7368edb62 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -148,8 +148,8 @@ private: if (classPkgRefp && VN_IS(classPkgRefp->classOrPackageNodep(), Class)) { // Class methods are automatic by default m_lifetime = VLifetime::AUTOMATIC; - } else if (nodep->dpiImport()) { - // DPI-imported function don't have lifetime specifiers + } else if (nodep->dpiImport() || VN_IS(nodep, Property)) { + // DPI-imported functions and properties don't have lifetime specifiers m_lifetime = VLifetime::NONE; } if (m_lifetime.isStatic() && hasStaticDeclAssignments(nodep)) { @@ -222,12 +222,8 @@ private: void visit(AstVar* nodep) override { cleanFileline(nodep); - if (nodep->lifetime().isNone()) { - if (m_ftaskp) { - nodep->lifetime(VLifetime::AUTOMATIC); - } else { - nodep->lifetime(m_lifetime); - } + if (nodep->lifetime().isNone() && nodep->varType() != VVarType::PORT) { + nodep->lifetime(m_lifetime); } if (nodep->isParam() && !nodep->valuep() && nodep->fileline()->language() < V3LangCode::L1800_2009) { diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 0fda5bec2..b77d89840 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -503,6 +503,15 @@ private: checkNodeInfo(nodep); iterateChildren(nodep); } + void visit(AstInitialStatic* nodep) override { + if (jumpingOver(nodep)) return; + if (!m_params) { + badNodeType(nodep); + return; + } + checkNodeInfo(nodep); + iterateChildren(nodep); + } void visit(AstNodeIf* nodep) override { if (jumpingOver(nodep)) return; UINFO(5, " IF " << nodep << endl); diff --git a/src/verilog.y b/src/verilog.y index bdbd218af..7dcfb0258 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3788,12 +3788,16 @@ for_initializationItem: // IEEE: variable_assignment + for_varia // // IEEE: for_variable_declaration data_type idAny/*new*/ '=' expr { VARRESET_NONLIST(VAR); VARDTYPE($1); - $$ = VARDONEA($2, *$2, nullptr, nullptr); + AstVar* const varp = VARDONEA($2, *$2, nullptr, nullptr); + varp->lifetime(VLifetime::AUTOMATIC); + $$ = varp; $$->addNext(new AstAssign{$3, new AstVarRef{$2, *$2, VAccess::WRITE}, $4}); } // // IEEE-2012: | yVAR data_type idAny/*new*/ '=' expr { VARRESET_NONLIST(VAR); VARDTYPE($2); - $$ = VARDONEA($3, *$3, nullptr, nullptr); + AstVar* const varp = VARDONEA($3, *$3, nullptr, nullptr); + varp->lifetime(VLifetime::AUTOMATIC); + $$ = varp; $$->addNext(new AstAssign{$4, new AstVarRef{$3, *$3, VAccess::WRITE}, $5}); } // // IEEE: variable_assignment // // UNSUP variable_lvalue below diff --git a/test_regress/t/t_case_write1_tasks.v b/test_regress/t/t_case_write1_tasks.v index be868734c..07ce93190 100644 --- a/test_regress/t/t_case_write1_tasks.v +++ b/test_regress/t/t_case_write1_tasks.v @@ -2066,7 +2066,7 @@ module t_case_write1_tasks (); endcase end endtask - task ozonef3; + task automatic ozonef3; input [ 31:0] foo; inout [STRLEN*8: 1] foobar; reg nacho; @@ -2475,7 +2475,7 @@ module t_case_write1_tasks (); endcase end endtask - task dude; + task automatic dude; inout [STRLEN*8: 1] foobar; reg [ 7:0] temp; integer i; @@ -2497,7 +2497,7 @@ module t_case_write1_tasks (); end endtask - task big_case; + task automatic big_case; input [ 31:0] fd; input [ 31:0] foo; reg [STRLEN*8: 1] foobar; diff --git a/test_regress/t/t_case_write2_tasks.v b/test_regress/t/t_case_write2_tasks.v index cae09726e..ebd12bdb3 100644 --- a/test_regress/t/t_case_write2_tasks.v +++ b/test_regress/t/t_case_write2_tasks.v @@ -2067,7 +2067,7 @@ module t_case_write2_tasks (); endcase end endtask - task ozonef3; + task automatic ozonef3; input [ 31:0] foo; input [`FD_BITS] fd; reg nacho; @@ -2482,7 +2482,7 @@ module t_case_write2_tasks (); $fwrite(fd," dude"); endtask - task big_case; + task automatic big_case; input [ `FD_BITS] fd; input [ 31:0] foo; // verilator no_inline_task diff --git a/test_regress/t/t_class_static.out b/test_regress/t/t_class_static.out deleted file mode 100644 index 927221b52..000000000 --- a/test_regress/t/t_class_static.out +++ /dev/null @@ -1,6 +0,0 @@ -%Error-UNSUPPORTED: t/t_class_static.v:25:18: Unsupported: 'static' function/task variables - : ... In instance t - 25 | static int st = 2; st++; return st; - | ^~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to diff --git a/test_regress/t/t_class_static.pl b/test_regress/t/t_class_static.pl index 8d48ddb75..b46d46042 100755 --- a/test_regress/t/t_class_static.pl +++ b/test_regress/t/t_class_static.pl @@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( - fails => $Self->{vlt_all}, # Verilator unsupported, bug546 - expect_filename => $Self->{golden_filename}, ); execute( check_finished => 1, - ) if !$Self->{vlt_all}; + ); ok(1); 1; diff --git a/test_regress/t/t_func_noinl.v b/test_regress/t/t_func_noinl.v index 5631f30ab..2b280291b 100644 --- a/test_regress/t/t_func_noinl.v +++ b/test_regress/t/t_func_noinl.v @@ -76,7 +76,7 @@ module Test (/*AUTOARG*/ input [31:0] inp; output [31:0] outp; - function [31:0] no_inline_function; + function automatic [31:0] no_inline_function; input [31:0] var1; input [31:0] var2; /*verilator no_inline_task*/ diff --git a/test_regress/t/t_hier_task.v b/test_regress/t/t_hier_task.v index dfd3f7948..42fe3eff2 100644 --- a/test_regress/t/t_hier_task.v +++ b/test_regress/t/t_hier_task.v @@ -39,7 +39,7 @@ endmodule : mod_inner module mod_a_mon; bit y; - function void accessor; + function automatic void accessor; begin : accessor_block bit read_x = mod_a.u_inner.x; y = read_x; diff --git a/test_regress/t/t_param_array3.v b/test_regress/t/t_param_array3.v index 3d9d98fb1..667276578 100644 --- a/test_regress/t/t_param_array3.v +++ b/test_regress/t/t_param_array3.v @@ -18,12 +18,18 @@ module t; endfunction parameter int SUMS[3:0] = calc_sums(); + parameter int SUMS1[3:0] = calc_sums(); initial begin if (SUMS[0] != 4) $stop; if (SUMS[1] != 4+3) $stop; if (SUMS[2] != 4+3+2) $stop; if (SUMS[3] != 4+3+2+1) $stop; + // According to section 13.4.3 of IEEE Std 1800-2017, + // execution at elaboration has no effect on the initial values + // of the variables used either at simulation time or among + // multiple invocations of a function at elaboration time + if (SUMS1 != SUMS) $stop; $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_stacktrace.v b/test_regress/t/t_stacktrace.v index c4662bf82..b8ad7e34d 100644 --- a/test_regress/t/t_stacktrace.v +++ b/test_regress/t/t_stacktrace.v @@ -6,7 +6,7 @@ module t; - task t; + task automatic t; // verilator no_inline_task string trace; diff --git a/test_regress/t/t_var_nonamebegin.v b/test_regress/t/t_var_nonamebegin.v index 164e5a3b8..2c366c5dd 100644 --- a/test_regress/t/t_var_nonamebegin.v +++ b/test_regress/t/t_var_nonamebegin.v @@ -67,7 +67,7 @@ module t (/*AUTOARG*/ $finish; end - task tsk; + task automatic tsk; integer t1; $display("t1 {mod}.tsk %m"); begin diff --git a/test_regress/t/t_var_static.out b/test_regress/t/t_var_static.out deleted file mode 100644 index 01e0c2fa9..000000000 --- a/test_regress/t/t_var_static.out +++ /dev/null @@ -1,14 +0,0 @@ -%Error-UNSUPPORTED: t/t_var_static.v:20:18: Unsupported: 'static' function/task variables - : ... In instance t - 20 | static int st = 2; st++; return st; - | ^~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_var_static.v:30:18: Unsupported: 'static' function/task variables - : ... In instance t - 30 | static int st = 2; st++; return st; - | ^~ -%Error-UNSUPPORTED: t/t_var_static.v:40:18: Unsupported: 'static' function/task variables - : ... In instance t - 40 | static int st = 2; st++; return st; - | ^~ -%Error: Exiting due to diff --git a/test_regress/t/t_var_static.pl b/test_regress/t/t_var_static.pl index dc589151c..93a3a1be3 100755 --- a/test_regress/t/t_var_static.pl +++ b/test_regress/t/t_var_static.pl @@ -12,13 +12,11 @@ scenarios(simulator => 1); compile( verilator_flags2 => ['-Wno-IMPLICITSTATIC'], - fails => $Self->{vlt_all}, # Verilator unsupported, bug546 - expect_filename => $Self->{golden_filename}, ); execute( check_finished => 1, - ) if !$Self->{vlt_all}; + ); ok(1); 1; diff --git a/test_regress/t/t_var_static.v b/test_regress/t/t_var_static.v index 8dbc698ff..cb54f6a0c 100644 --- a/test_regress/t/t_var_static.v +++ b/test_regress/t/t_var_static.v @@ -6,6 +6,16 @@ `define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0); +function automatic int f_au_st_global (); + static int st = 0; st++; return st; +endfunction + +package my_pkg; + function int f_no_st_pkg (); + static int st = 0; st++; return st; + endfunction +endpackage + module t (/*AUTOARG*/ // Inputs clk @@ -67,6 +77,11 @@ module t (/*AUTOARG*/ v = f_au_au(); `checkh(v, 3); v = f_au_au(); `checkh(v, 3); // + v = f_au_st_global(); `checkh(v, 1); + v = f_au_st_global(); `checkh(v, 2); + v = my_pkg::f_no_st_pkg(); `checkh(v, 1); + v = my_pkg::f_no_st_pkg(); `checkh(v, 2); + // end int cyc = 0; diff --git a/test_regress/t/t_var_static_param.out b/test_regress/t/t_var_static_param.out deleted file mode 100644 index 2a08df190..000000000 --- a/test_regress/t/t_var_static_param.out +++ /dev/null @@ -1,6 +0,0 @@ -%Error-UNSUPPORTED: t/t_var_static_param.v:33:18: Unsupported: 'static' function/task variables - : ... In instance t.subb - 33 | static int st = 2; st += P; return st; - | ^~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to diff --git a/test_regress/t/t_var_static_param.pl b/test_regress/t/t_var_static_param.pl index 8d48ddb75..b46d46042 100755 --- a/test_regress/t/t_var_static_param.pl +++ b/test_regress/t/t_var_static_param.pl @@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( - fails => $Self->{vlt_all}, # Verilator unsupported, bug546 - expect_filename => $Self->{golden_filename}, ); execute( check_finished => 1, - ) if !$Self->{vlt_all}; + ); ok(1); 1;