diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index 774434ec2..e09fb5af7 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -37,6 +37,8 @@ class LinkLValueVisitor final : public VNVisitor { bool m_setStrengthSpecified = false; // Set that var has assignment with strength specified. bool m_setForcedByCode = false; // Set that var is the target of an AstAssignForce/AstRelease bool m_setIfRand = false; // Update VarRefs if var declared as rand + bool m_inInitialStatic = false; // Set if inside AstInitialStatic + bool m_inFunc = false; // Set if inside AstNodeFTask VAccess m_setRefLvalue; // Set VarRefs to lvalues for pin assignments // VISITs @@ -98,6 +100,35 @@ class LinkLValueVisitor final : public VNVisitor { m_setStrengthSpecified = false; iterateAndNextNull(nodep->rhsp()); } + + if (m_inInitialStatic && m_inFunc) { + const bool rhsHasIO = nodep->rhsp()->exists([](const AstNodeVarRef* const refp) { + // Exclude module I/O referenced from a function/task. + return refp->varp() && refp->varp()->isIO() + && refp->varp()->lifetime() != VLifetime::NONE; + }); + if (rhsHasIO) { + nodep->rhsp()->v3warn(E_UNSUPPORTED, + "Static variable initializer\n" + << nodep->rhsp()->warnMore() + << "is dependent on function/task I/O variable"); + } else { + const bool rhsHasAutomatic + = nodep->rhsp()->exists([](const AstNodeVarRef* const refp) { + return refp->varp() && refp->varp()->lifetime() == VLifetime::AUTOMATIC; + }); + if (rhsHasAutomatic) { + nodep->rhsp()->v3error("Static variable initializer\n" + << nodep->rhsp()->warnMore() + << "is dependent on automatic variable"); + } + } + } + } + void visit(AstInitialStatic* nodep) override { + VL_RESTORER(m_inInitialStatic); + m_inInitialStatic = true; + iterateChildren(nodep); } void visit(AstRelease* nodep) override { VL_RESTORER(m_setRefLvalue); @@ -328,6 +359,11 @@ class LinkLValueVisitor final : public VNVisitor { m_setIfRand = true; iterateChildren(nodep); } + void visit(AstNodeFTask* nodep) override { + VL_RESTORER(m_inFunc); + m_inFunc = true; + iterateChildren(nodep); + } void visit(AstNode* nodep) override { iterateChildren(nodep); } diff --git a/test_regress/t/t_var_static_assign_decl_bad.out b/test_regress/t/t_var_static_assign_decl_bad.out new file mode 100644 index 000000000..7ca2970b1 --- /dev/null +++ b/test_regress/t/t_var_static_assign_decl_bad.out @@ -0,0 +1,66 @@ +%Error-UNSUPPORTED: t/t_var_static_assign_decl_bad.v:101:17: Static variable initializer + : is dependent on function/task I/O variable + 101 | logic tmp = in; + | ^~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_var_static_assign_decl_bad.v:106:17: Static variable initializer + : is dependent on function/task I/O variable + 106 | logic tmp = in; + | ^~ +%Error-UNSUPPORTED: t/t_var_static_assign_decl_bad.v:111:24: Static variable initializer + : is dependent on function/task I/O variable + 111 | static logic tmp = in; + | ^~ +%Error-UNSUPPORTED: t/t_var_static_assign_decl_bad.v:116:17: Static variable initializer + : is dependent on function/task I/O variable + 116 | logic tmp = out; + | ^~~ +%Error-UNSUPPORTED: t/t_var_static_assign_decl_bad.v:121:20: Static variable initializer + : is dependent on function/task I/O variable + 121 | logic tmp = in + 1; + | ^ +%Error: t/t_var_static_assign_decl_bad.v:126:26: Static variable initializer + : is dependent on automatic variable + 126 | static int foo = tmp + 1; + | ^ +%Error: t/t_var_static_assign_decl_bad.v:132:26: Static variable initializer + : is dependent on automatic variable + 132 | static int foo = tmp + 1; + | ^ +%Error: t/t_var_static_assign_decl_bad.v:138:29: Static variable initializer + : is dependent on automatic variable + 138 | static logic func_var = loc; + | ^~~ +%Error-UNSUPPORTED: t/t_var_static_assign_decl_bad.v:9:15: Static variable initializer + : is dependent on function/task I/O variable + 9 | logic tmp = in; + | ^~ +%Error-UNSUPPORTED: t/t_var_static_assign_decl_bad.v:14:15: Static variable initializer + : is dependent on function/task I/O variable + 14 | logic tmp = in; + | ^~ +%Error-UNSUPPORTED: t/t_var_static_assign_decl_bad.v:20:17: Static variable initializer + : is dependent on function/task I/O variable + 20 | logic tmp = in; + | ^~ +%Error-UNSUPPORTED: t/t_var_static_assign_decl_bad.v:25:17: Static variable initializer + : is dependent on function/task I/O variable + 25 | logic tmp = in; + | ^~ +%Error-UNSUPPORTED: t/t_var_static_assign_decl_bad.v:32:17: Static variable initializer + : is dependent on function/task I/O variable + 32 | logic tmp = in; + | ^~ +%Error-UNSUPPORTED: t/t_var_static_assign_decl_bad.v:37:17: Static variable initializer + : is dependent on function/task I/O variable + 37 | logic tmp = in; + | ^~ +%Error-UNSUPPORTED: t/t_var_static_assign_decl_bad.v:44:17: Static variable initializer + : is dependent on function/task I/O variable + 44 | logic tmp = in; + | ^~ +%Error-UNSUPPORTED: t/t_var_static_assign_decl_bad.v:49:17: Static variable initializer + : is dependent on function/task I/O variable + 49 | logic tmp = in; + | ^~ +%Error: Exiting due to diff --git a/test_regress/t/t_var_static_assign_decl_bad.pl b/test_regress/t/t_var_static_assign_decl_bad.pl new file mode 100755 index 000000000..dbc037440 --- /dev/null +++ b/test_regress/t/t_var_static_assign_decl_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_var_static_assign_decl_bad.v b/test_regress/t/t_var_static_assign_decl_bad.v new file mode 100644 index 000000000..2c4217372 --- /dev/null +++ b/test_regress/t/t_var_static_assign_decl_bad.v @@ -0,0 +1,151 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +function static func_stat; + input logic in; + logic tmp = in; +endfunction + +task static task_stat; + input logic in; + logic tmp = in; +endtask + +package pkg; + function static func_stat; + input logic in; + logic tmp = in; + endfunction + + task static task_stat; + input logic in; + logic tmp = in; + endtask +endpackage + +interface iface; + function static func_stat; + input logic in; + logic tmp = in; + endfunction + + task static task_stat; + input logic in; + logic tmp = in; + endtask +endinterface + +program prog; + function static func_stat; + input logic in; + logic tmp = in; + endfunction + + task static task_stat; + input logic in; + logic tmp = in; + endtask +endprogram + +module no_warn#(PARAM = 1)(input in, input clk); + typedef enum {A, B} enum_t; + + // Do not warn on variables under modules. + logic tmp = in; + + // Do not warn on assignment with module var. + function static func; + static logic func_var = tmp; + endfunction + + // Do not warn on constant assignments. + function static func_param; + static logic func_var = PARAM; + static logic func_enum = A; + endfunction + + // Do not warn on assignment referencing module I/O. + function static func_module_input; + logic tmp = in; + endfunction + + // Do not warn on automatic assignment. + function automatic func_auto; + input logic in; + logic tmp = in; + endfunction + + // Do not warn on assignment separate from declaration. + function static func_decl_and_assign; + input logic in; + logic tmp; + tmp = in; + endfunction + + // Do not warn on variables under blocks. + initial begin + logic init_tmp = in; + end + + always @(posedge clk) begin + logic always_tmp = in; + end +endmodule + +module t(input clk); + function static func_stat; + input logic in; + logic tmp = in; + endfunction + + task static task_stat; + input logic in; + logic tmp = in; + endtask + + function automatic func_auto_with_static; + input logic in; + static logic tmp = in; + endfunction + + function static func_assign_out; + output logic out; + logic tmp = out; + endfunction + + function static func_assign_expr; + input logic in; + logic tmp = in + 1; + endfunction + + function static int func_assign_static_in_to_auto(input int i); + automatic int tmp = i; + static int foo = tmp + 1; + return foo; + endfunction + + function static int func_assign_auto_to_static(); + automatic int tmp = 0; + static int foo = tmp + 1; + return foo; + endfunction + + function static func_local; + automatic logic loc; + static logic func_var = loc; + endfunction + + iface iface(); + prog prog; + + logic in; + no_warn no_warn(.in(in), .clk(clk)); + + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule