diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 9787f13d1..d84a3c986 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -2293,20 +2293,21 @@ class ParamVisitor final : public VNVisitor { nodep->v3error("Parameter without default value is never given value" << " (IEEE 1800-2023 6.20.1): " << nodep->prettyNameQ()); } else if (nodep->valuep()) { - // In visit(AstVar*) for localparams, check if expression contains VARXREF - // to another localparam (not parameter). Parameters are already const, - // but localparams may not be evaluated yet. - bool hasVarXRefToLparam = false; + // If the value expression contains a VarXRef to an interface + // localparam whose value is not yet constant, defer constification + // to avoid premature widthing with unresolved values (see + // t_lparam_dep_iface tests). When the referenced localparam is + // already const, proceed normally so FUNCREFs with resolved iface + // param args get folded. + bool hasUnresolvedLparamXRef = false; nodep->valuep()->foreach([&](const AstVarXRef* xrefp) { - if (xrefp->varp() && xrefp->varp()->varType() == VVarType::LPARAM) { - hasVarXRefToLparam = true; + if (const AstVar* const varp = xrefp->varp()) { + if (varp->varType() == VVarType::LPARAM && !VN_IS(varp->valuep(), Const)) { + hasUnresolvedLparamXRef = true; + } } }); - if (hasVarXRefToLparam) { - // Don't constify - let it be evaluated later - return; - } - + if (hasUnresolvedLparamXRef) return; V3Const::constifyParamsEdit(nodep); } } @@ -2328,6 +2329,13 @@ class ParamVisitor final : public VNVisitor { if (nodep->name() == candp->name()) { if (AstVar* const varp = VN_CAST(candp, Var)) { UINFO(9, "Found interface parameter: " << varp); + // The interface may not have been visited yet (it is at a + // deeper level in the work queue), so its localparams may + // not be constified. Eagerly constify here so that the + // caller's hasUnresolvedLparamXRef check sees a Const. + if (varp->isParam() && varp->valuep() && !VN_IS(varp->valuep(), Const)) { + V3Const::constifyParamsEdit(varp); + } nodep->varp(varp); return true; } else if (const AstPin* const pinp = VN_CAST(candp, Pin)) { diff --git a/test_regress/t/t_lparam_pkg_assign.py b/test_regress/t/t_lparam_pkg_assign.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_lparam_pkg_assign.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: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=["--binary"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_lparam_pkg_assign.v b/test_regress/t/t_lparam_pkg_assign.v new file mode 100644 index 000000000..d1190c23f --- /dev/null +++ b/test_regress/t/t_lparam_pkg_assign.v @@ -0,0 +1,37 @@ +// DESCRIPTION: Verilator: Localparam with package function call using interface param +// +// 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: 2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +package pkg; + function automatic bit fn(int value); + return (value > 0) ? 1'b1 : 1'b0; + endfunction +endpackage + +interface ifc(); + localparam int PARAM = 1; +endinterface + +module mod(ifc i); + localparam bit lpbit = pkg::fn(i.PARAM); +endmodule + +module t; + ifc i(); + mod m(.i); + + initial begin + `checkd(m.lpbit, 1'b1); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_pkg_assign2.py b/test_regress/t/t_lparam_pkg_assign2.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_lparam_pkg_assign2.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: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=["--binary"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_lparam_pkg_assign2.v b/test_regress/t/t_lparam_pkg_assign2.v new file mode 100644 index 000000000..4a7ee7dfb --- /dev/null +++ b/test_regress/t/t_lparam_pkg_assign2.v @@ -0,0 +1,42 @@ +// DESCRIPTION: Verilator: Localparam with package function call using computed interface param +// +// 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: 2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +package pkg; + function automatic bit fn(int value); + return (value > 0) ? 1'b1 : 1'b0; + endfunction +endpackage + +interface ifc #(parameter int WIDTH = 8); + localparam int DEPTH = $clog2(WIDTH); + localparam int COMPUTED = DEPTH * 2; +endinterface + +module mod(ifc i); + // LPARAM references i.COMPUTED which depends on i.DEPTH which depends on WIDTH + localparam bit lpbit = pkg::fn(i.COMPUTED); + localparam int lpval = i.COMPUTED + 1; +endmodule + +module t; + ifc #(.WIDTH(64)) i(); + mod m(.i); + + initial begin + // DEPTH = $clog2(64) = 6, COMPUTED = 6*2 = 12 + `checkd(m.lpbit, 1'b1); // fn(12) returns 1 since 12 > 0 + `checkd(m.lpval, 13); // lpval = 12 + 1 = 13 + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_pkg_assign3.py b/test_regress/t/t_lparam_pkg_assign3.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_lparam_pkg_assign3.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: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=["--binary"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_lparam_pkg_assign3.v b/test_regress/t/t_lparam_pkg_assign3.v new file mode 100644 index 000000000..89640a45e --- /dev/null +++ b/test_regress/t/t_lparam_pkg_assign3.v @@ -0,0 +1,45 @@ +// DESCRIPTION: Verilator: Localparam with function call using type-parameterized interface param +// +// 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: 2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +package pkg; + function automatic bit fn(int value); + return (value > 0) ? 1'b1 : 1'b0; + endfunction +endpackage + +interface ifc #(parameter type some_type) (); + localparam int PARAM = 1; + localparam int TYPE_WIDTH = $bits(some_type); +endinterface + +function automatic bit assert_func(bit value); + if (!value) $fatal(2, "DEAD"); + return value; +endfunction + +module mod(ifc i); + localparam bit lpbit = pkg::fn(i.PARAM); + localparam bit test = assert_func(i.TYPE_WIDTH == 32); +endmodule + +module t; + ifc #(.some_type(int)) i(); + mod m(.i); + + initial begin + `checkd(m.lpbit, 1'b1); // fn(1) returns 1 since 1 > 0 + `checkd(m.test, 1'b1); // $bits(int) == 32, so assert_func(1) returns 1 + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_pkg_assign4.py b/test_regress/t/t_lparam_pkg_assign4.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_lparam_pkg_assign4.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: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=["--binary"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_lparam_pkg_assign4.v b/test_regress/t/t_lparam_pkg_assign4.v new file mode 100644 index 000000000..751991ee4 --- /dev/null +++ b/test_regress/t/t_lparam_pkg_assign4.v @@ -0,0 +1,49 @@ +// DESCRIPTION: Verilator: Interface array with multiple consumer modules using FUNCREF LPARAMs +// +// 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: 2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +package pkg; + function automatic int decode_width(int value); + return (value > 0) ? value * 2 : 0; + endfunction +endpackage + +interface ifc #(parameter int WIDTH = 8); + localparam int DEPTH = $clog2(WIDTH); + localparam int DECODED = pkg::decode_width(DEPTH); +endinterface + +module producer(ifc i); + localparam int BUF_SIZE = pkg::decode_width(i.DEPTH); + localparam int OUT_W = i.DECODED + 1; +endmodule + +module consumer(ifc i); + localparam int HALF = i.DECODED / 2; + localparam int TAG_W = pkg::decode_width(i.DEPTH) + i.DECODED; +endmodule + +module t; + ifc #(.WIDTH(64)) bus[2](); + producer p(.i(bus[0])); + consumer c(.i(bus[1])); + + initial begin + // WIDTH=64, DEPTH=$clog2(64)=6, DECODED=decode_width(6)=12 + `checkd(p.BUF_SIZE, 12); // decode_width(6) = 12 + `checkd(p.OUT_W, 13); // 12 + 1 = 13 + `checkd(c.HALF, 6); // 12 / 2 = 6 + `checkd(c.TAG_W, 24); // decode_width(6) + 12 = 12 + 12 = 24 + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_pkg_assign5.py b/test_regress/t/t_lparam_pkg_assign5.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_lparam_pkg_assign5.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: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=["--binary"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_lparam_pkg_assign5.v b/test_regress/t/t_lparam_pkg_assign5.v new file mode 100644 index 000000000..783e32245 --- /dev/null +++ b/test_regress/t/t_lparam_pkg_assign5.v @@ -0,0 +1,52 @@ +// DESCRIPTION: Verilator: Hierarchical interface pass-through with FUNCREF LPARAMs +// +// 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: 2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +package pkg; + function automatic int decode_width(int value); + return (value > 0) ? value * 2 : 0; + endfunction +endpackage + +interface ifc #(parameter int WIDTH = 8); + localparam int DEPTH = $clog2(WIDTH); + localparam int DECODED = pkg::decode_width(DEPTH); +endinterface + +// Leaf module: uses interface LPARAM in a FUNCREF-based localparam +module leaf(ifc i); + localparam int BUF_SIZE = pkg::decode_width(i.DEPTH); + localparam int OUT_W = i.DECODED + 1; +endmodule + +// Intermediate wrapper: passes interface through to leaf +module wrapper(ifc i); + leaf u_leaf(.i); +endmodule + +// Second level wrapper: adds another layer of hierarchy +module subsystem(ifc bus); + wrapper u_wrap(.i(bus)); +endmodule + +module t; + ifc #(.WIDTH(64)) bus(); + subsystem u_sub(.bus); + + initial begin + // WIDTH=64, DEPTH=$clog2(64)=6, DECODED=decode_width(6)=12 + `checkd(u_sub.u_wrap.u_leaf.BUF_SIZE, 12); // decode_width(6) = 12 + `checkd(u_sub.u_wrap.u_leaf.OUT_W, 13); // 12 + 1 = 13 + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule