From 121fd3f6139c82611bbfc8c8ea27653d771a6091 Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Thu, 23 Apr 2026 12:30:42 -0400 Subject: [PATCH] Fix module parameters not re-evaluated upon instantiation (#7463) (#7477) --- src/V3Param.cpp | 42 +++++--- test_regress/t/t_param_type_dep_chain.py | 18 ++++ test_regress/t/t_param_type_dep_chain.v | 77 +++++++++++++++ test_regress/t/t_param_type_dep_cross_inst.py | 18 ++++ test_regress/t/t_param_type_dep_cross_inst.v | 47 +++++++++ test_regress/t/t_param_type_dep_deep.py | 18 ++++ test_regress/t/t_param_type_dep_deep.v | 61 ++++++++++++ test_regress/t/t_param_type_dep_hash_norm.py | 18 ++++ test_regress/t/t_param_type_dep_hash_norm.v | 65 +++++++++++++ test_regress/t/t_param_type_dep_hier.py | 18 ++++ test_regress/t/t_param_type_dep_hier.v | 62 ++++++++++++ test_regress/t/t_param_type_dep_recompute.py | 18 ++++ test_regress/t/t_param_type_dep_recompute.v | 96 +++++++++++++++++++ test_regress/t/t_param_type_dep_struct.py | 18 ++++ test_regress/t/t_param_type_dep_struct.v | 59 ++++++++++++ 15 files changed, 621 insertions(+), 14 deletions(-) create mode 100755 test_regress/t/t_param_type_dep_chain.py create mode 100644 test_regress/t/t_param_type_dep_chain.v create mode 100755 test_regress/t/t_param_type_dep_cross_inst.py create mode 100644 test_regress/t/t_param_type_dep_cross_inst.v create mode 100755 test_regress/t/t_param_type_dep_deep.py create mode 100644 test_regress/t/t_param_type_dep_deep.v create mode 100755 test_regress/t/t_param_type_dep_hash_norm.py create mode 100644 test_regress/t/t_param_type_dep_hash_norm.v create mode 100755 test_regress/t/t_param_type_dep_hier.py create mode 100644 test_regress/t/t_param_type_dep_hier.v create mode 100755 test_regress/t/t_param_type_dep_recompute.py create mode 100644 test_regress/t/t_param_type_dep_recompute.v create mode 100755 test_regress/t/t_param_type_dep_struct.py create mode 100644 test_regress/t/t_param_type_dep_struct.v diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 0bc8a68a8..480432dae 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -1406,6 +1406,7 @@ class ParamProcessor final { AstConst* normedNamep = nullptr; if (exprp && !exprp->num().isDouble() && !exprp->num().isString()) { AstVar* cloneVarp = modvarp->cloneTree(false); + bool cloneVarpUnresolved = false; if (AstNode* const oldValuep = cloneVarp->valuep()) { oldValuep->unlinkFrBack(); VL_DO_DANGLING(oldValuep->deleteTree(), oldValuep); @@ -1441,24 +1442,26 @@ class ParamProcessor final { any = true; } }); - // Substitute RefDType to an overridden paramtype. RefDType is - // not a foreach leaf, so collect matches and replace after the - // walk. Reverse order so descendants are replaced before - // ancestors -- replacing an ancestor would free its descendants. + // Replace RefDType to a ParamTypeDType with pin override + // or the paramtype's default so constify below does not + // reach into the template. Collect then replace in + // reverse so descendants aren't freed early. std::vector> toReplace; cloneVarp->foreach([&](AstRefDType* refp) { AstParamTypeDType* const ptdp = VN_CAST(refp->refDTypep(), ParamTypeDType); if (!ptdp) return; + AstPin* overridePinp = nullptr; for (AstPin* pp = paramsp; pp; pp = VN_AS(pp->nextp(), Pin)) { if (pp->modPTypep() == ptdp) { - if (AstNodeDType* const overDtp - = VN_CAST(pp->exprp(), NodeDType)) { - toReplace.emplace_back(refp, overDtp); - } + overridePinp = pp; break; } } + AstNodeDType* const substp + = overridePinp ? VN_CAST(overridePinp->exprp(), NodeDType) + : ptdp->subDTypep(); + if (substp) toReplace.emplace_back(refp, substp); }); for (auto it = toReplace.rbegin(); it != toReplace.rend(); ++it) { AstRefDType* const refp = it->first; @@ -1475,13 +1478,24 @@ class ParamProcessor final { << varrefp->prettyName() << "' in pin dtype clone. Pin: " << pinp->prettyNameQ() << " of " << nodep->prettyNameQ()); }); + // Skip the widthing constify if any RefDType is unresolved. + cloneVarp->foreach([&](AstRefDType* refp) { + if (VN_IS(refp->refDTypep(), ParamTypeDType)) { + UINFO(5, " cellPinCleanup: skip normedNamep " + "(unresolved RefDType->ParamTypeDType) pin=" + << pinp->prettyNameQ()); + cloneVarpUnresolved = true; + } + }); } - V3Const::constifyParamsEdit(cloneVarp); - if (AstConst* const widthedp = VN_CAST(cloneVarp->valuep(), Const)) { - // Stamp the port's type on the const so equal values hash the same. - if (cloneVarp->dtypep()) widthedp->dtypep(cloneVarp->dtypep()); - widthedp->unlinkFrBack(); - normedNamep = widthedp; + if (!cloneVarpUnresolved) { + V3Const::constifyParamsEdit(cloneVarp); + if (AstConst* const widthedp = VN_CAST(cloneVarp->valuep(), Const)) { + // Stamp the port's type so equal values hash the same. + if (cloneVarp->dtypep()) widthedp->dtypep(cloneVarp->dtypep()); + widthedp->unlinkFrBack(); + normedNamep = widthedp; + } } VL_DO_DANGLING(cloneVarp->deleteTree(), cloneVarp); } diff --git a/test_regress/t/t_param_type_dep_chain.py b/test_regress/t/t_param_type_dep_chain.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_param_type_dep_chain.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_param_type_dep_chain.v b/test_regress/t/t_param_type_dep_chain.v new file mode 100644 index 000000000..ca8377a3a --- /dev/null +++ b/test_regress/t/t_param_type_dep_chain.v @@ -0,0 +1,77 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Chained paramtypes: type B = A where A = logic[W-1:0]. The iterative +// RefDType substitution in cellPinCleanup must unwind the chain over +// multiple passes: B -> REFDTYPE(A) -> A's body -> VARREF(W) -> override. +// Each instance overrides .val (paramtype-typed, spec-matched width) so +// cellPinCleanup processes a RefDType pin that must be resolved per-spec. +// Pre-fix, pin values are checked against the template's B (8 bits), +// producing WIDTHTRUNC warnings on i16/i32. Post-fix, they check +// against each spec's resolved B and pass cleanly. + +module m #( + parameter int W = 8, + parameter type A = logic [W-1:0], + parameter type B = A, + parameter B val = '0 +) (); + A a_sig; + B b_sig; + initial a_sig = '1; + initial b_sig = '1; +endmodule + +module t; + m #(.W(8), .val(8'hA5)) i8 (); + m #(.W(16), .val(16'hBEEF)) i16 (); + m #(.W(32), .val(32'hDEADBEEF)) i32 (); + + initial begin + #1; + if ($bits(i8.val) !== 8) begin + $write("%%Error $bits(i8.val)=%0d\n", $bits(i8.val)); $stop; + end + if ($bits(i8.a_sig) !== 8) begin + $write("%%Error $bits(i8.a_sig)=%0d\n", $bits(i8.a_sig)); $stop; + end + if ($bits(i8.b_sig) !== 8) begin + $write("%%Error $bits(i8.b_sig)=%0d\n", $bits(i8.b_sig)); $stop; + end + if (i8.val !== 8'hA5) begin $write("%%Error i8.val=%h\n", i8.val); $stop; end + if (i8.a_sig !== 8'hFF) begin $write("%%Error i8.a_sig=%h\n", i8.a_sig); $stop; end + if (i8.b_sig !== 8'hFF) begin $write("%%Error i8.b_sig=%h\n", i8.b_sig); $stop; end + + if ($bits(i16.val) !== 16) begin + $write("%%Error $bits(i16.val)=%0d\n", $bits(i16.val)); $stop; + end + if ($bits(i16.a_sig) !== 16) begin + $write("%%Error $bits(i16.a_sig)=%0d\n", $bits(i16.a_sig)); $stop; + end + if ($bits(i16.b_sig) !== 16) begin + $write("%%Error $bits(i16.b_sig)=%0d\n", $bits(i16.b_sig)); $stop; + end + if (i16.val !== 16'hBEEF) begin $write("%%Error i16.val=%h\n", i16.val); $stop; end + if (i16.a_sig !== 16'hFFFF) begin $write("%%Error i16.a_sig=%h\n", i16.a_sig); $stop; end + if (i16.b_sig !== 16'hFFFF) begin $write("%%Error i16.b_sig=%h\n", i16.b_sig); $stop; end + + if ($bits(i32.val) !== 32) begin + $write("%%Error $bits(i32.val)=%0d\n", $bits(i32.val)); $stop; + end + if ($bits(i32.a_sig) !== 32) begin + $write("%%Error $bits(i32.a_sig)=%0d\n", $bits(i32.a_sig)); $stop; + end + if ($bits(i32.b_sig) !== 32) begin + $write("%%Error $bits(i32.b_sig)=%0d\n", $bits(i32.b_sig)); $stop; + end + if (i32.val !== 32'hDEADBEEF) begin $write("%%Error i32.val=%h\n", i32.val); $stop; end + if (i32.a_sig !== 32'hFFFFFFFF) begin $write("%%Error i32.a_sig=%h\n", i32.a_sig); $stop; end + if (i32.b_sig !== 32'hFFFFFFFF) begin $write("%%Error i32.b_sig=%h\n", i32.b_sig); $stop; end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_param_type_dep_cross_inst.py b/test_regress/t/t_param_type_dep_cross_inst.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_param_type_dep_cross_inst.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_param_type_dep_cross_inst.v b/test_regress/t/t_param_type_dep_cross_inst.v new file mode 100644 index 000000000..7a61d1a50 --- /dev/null +++ b/test_regress/t/t_param_type_dep_cross_inst.v @@ -0,0 +1,47 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Interleaved specs (A B A C B A). Any cross-instance leakage from +// template poisoning causes a later instance of a repeated tuple to +// mismatch its expected value. + +module m #( + parameter int W = 8, + parameter type T = logic [W-1:0], + parameter T VAL = '0 +) (); + logic [W-1:0] observed; + assign observed = VAL; +endmodule + +module t; + m #(.W(8), .VAL(8'h11)) ia1 (); // A + m #(.W(16), .VAL(16'h2222)) ib1 (); // B + m #(.W(8), .VAL(8'h11)) ia2 (); // A + m #(.W(32), .VAL(32'h33333333)) ic1 (); // C + m #(.W(16), .VAL(16'h2222)) ib2 (); // B + m #(.W(8), .VAL(8'h11)) ia3 (); // A + + initial begin + #1; + if ($bits(ia1.observed) !== 8) begin $write("%%Error ia1 bits\n"); $stop; end + if ($bits(ib1.observed) !== 16) begin $write("%%Error ib1 bits\n"); $stop; end + if ($bits(ia2.observed) !== 8) begin $write("%%Error ia2 bits\n"); $stop; end + if ($bits(ic1.observed) !== 32) begin $write("%%Error ic1 bits\n"); $stop; end + if ($bits(ib2.observed) !== 16) begin $write("%%Error ib2 bits\n"); $stop; end + if ($bits(ia3.observed) !== 8) begin $write("%%Error ia3 bits\n"); $stop; end + + if (ia1.observed !== 8'h11) begin $write("%%Error ia1=%h\n", ia1.observed); $stop; end + if (ib1.observed !== 16'h2222) begin $write("%%Error ib1=%h\n", ib1.observed); $stop; end + if (ia2.observed !== 8'h11) begin $write("%%Error ia2=%h\n", ia2.observed); $stop; end + if (ic1.observed !== 32'h33333333) begin $write("%%Error ic1=%h\n", ic1.observed); $stop; end + if (ib2.observed !== 16'h2222) begin $write("%%Error ib2=%h\n", ib2.observed); $stop; end + if (ia3.observed !== 8'h11) begin $write("%%Error ia3=%h\n", ia3.observed); $stop; end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_param_type_dep_deep.py b/test_regress/t/t_param_type_dep_deep.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_param_type_dep_deep.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_param_type_dep_deep.v b/test_regress/t/t_param_type_dep_deep.v new file mode 100644 index 000000000..88972c69e --- /dev/null +++ b/test_regress/t/t_param_type_dep_deep.v @@ -0,0 +1,61 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Deep value-param chain under a type param. Each intermediate must +// recompute per spec; template poisoning at any level fails a specific +// assertion pointing at the leaking level. Each instance overrides +// .val (paramtype-typed) with a spec-matched width so cellPinCleanup +// processes a RefDType pin and the full W1->W2->W3->W4 chain must +// unwind. + +module m #( + parameter int W1 = 4, + parameter int W2 = W1 + 4, + parameter int W3 = W2 * 2, + parameter int W4 = W3 + 1, + parameter type T = logic [W4-1:0], + parameter T val = '0 +) (); +endmodule + +module t; + m #(.W1(8), .val(25'h1234567)) iw8 (); // W4 = 25 + m iwd (); // default W4 = 17 + m #(.W1(16), .val(41'h123456789AB)) iw16 (); // W4 = 41 + + initial begin + if (iw8.W1 !== 8) begin $write("%%Error iw8.W1=%0d\n", iw8.W1); $stop; end + if (iw8.W2 !== 12) begin $write("%%Error iw8.W2=%0d\n", iw8.W2); $stop; end + if (iw8.W3 !== 24) begin $write("%%Error iw8.W3=%0d\n", iw8.W3); $stop; end + if (iw8.W4 !== 25) begin $write("%%Error iw8.W4=%0d\n", iw8.W4); $stop; end + if ($bits(iw8.val) !== 25) begin + $write("%%Error $bits(iw8.val)=%0d\n", $bits(iw8.val)); $stop; + end + if (iw8.val !== 25'h1234567) begin $write("%%Error iw8.val=%h\n", iw8.val); $stop; end + + if (iwd.W1 !== 4) begin $write("%%Error iwd.W1=%0d\n", iwd.W1); $stop; end + if (iwd.W2 !== 8) begin $write("%%Error iwd.W2=%0d\n", iwd.W2); $stop; end + if (iwd.W3 !== 16) begin $write("%%Error iwd.W3=%0d\n", iwd.W3); $stop; end + if (iwd.W4 !== 17) begin $write("%%Error iwd.W4=%0d\n", iwd.W4); $stop; end + if ($bits(iwd.val) !== 17) begin + $write("%%Error $bits(iwd.val)=%0d\n", $bits(iwd.val)); $stop; + end + + if (iw16.W1 !== 16) begin $write("%%Error iw16.W1=%0d\n", iw16.W1); $stop; end + if (iw16.W2 !== 20) begin $write("%%Error iw16.W2=%0d\n", iw16.W2); $stop; end + if (iw16.W3 !== 40) begin $write("%%Error iw16.W3=%0d\n", iw16.W3); $stop; end + if (iw16.W4 !== 41) begin $write("%%Error iw16.W4=%0d\n", iw16.W4); $stop; end + if ($bits(iw16.val) !== 41) begin + $write("%%Error $bits(iw16.val)=%0d\n", $bits(iw16.val)); $stop; + end + if (iw16.val !== 41'h123456789AB) begin + $write("%%Error iw16.val=%h\n", iw16.val); $stop; + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_param_type_dep_hash_norm.py b/test_regress/t/t_param_type_dep_hash_norm.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_param_type_dep_hash_norm.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_param_type_dep_hash_norm.v b/test_regress/t/t_param_type_dep_hash_norm.v new file mode 100644 index 000000000..6566d71e0 --- /dev/null +++ b/test_regress/t/t_param_type_dep_hash_norm.v @@ -0,0 +1,65 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Hash-normalization: the port type must resolve per-specialization, not +// from the template default. Two pin values differing only in bits above +// the template-default width must still produce distinct specs when the +// actual spec's width is wider. Pin values equal after widthing must +// share a spec (#5479 dedup preserved). + +module test #( + parameter int width = 16, + parameter int width2 = width + 8, + parameter type data_t = logic [width2-1:0], + parameter data_t data = data_t'(0) +) (); +endmodule + +module t; + // width=24 -> width2=32 -> data_t is 32 bits + test #(.width(24), .data(32'hFFFFFFFF)) i_aa (); + test #(.width(24), .data(32'h11FFFFFF)) i_ab (); + test #(.width(24), .data(32'h11FFFFFF)) i_ac (); + // width=16 -> width2=24 -> data_t is 24 bits + test #(.width(16), .data(24'hFFFFFF)) i_bb (); + + initial begin + if ($bits(i_aa.data) !== 32) begin + $write("%%Error $bits(i_aa.data)=%0d expected 32\n", $bits(i_aa.data)); $stop; + end + if ($bits(i_ab.data) !== 32) begin + $write("%%Error $bits(i_ab.data)=%0d expected 32\n", $bits(i_ab.data)); $stop; + end + if ($bits(i_ac.data) !== 32) begin + $write("%%Error $bits(i_ac.data)=%0d expected 32\n", $bits(i_ac.data)); $stop; + end + if ($bits(i_bb.data) !== 24) begin + $write("%%Error $bits(i_bb.data)=%0d expected 24\n", $bits(i_bb.data)); $stop; + end + if (i_aa.data !== 32'hFFFFFFFF) begin + $write("%%Error i_aa.data=%h\n", i_aa.data); $stop; + end + if (i_ab.data !== 32'h11FFFFFF) begin + $write("%%Error i_ab.data=%h\n", i_ab.data); $stop; + end + if (i_ac.data !== 32'h11FFFFFF) begin + $write("%%Error i_ac.data=%h\n", i_ac.data); $stop; + end + if (i_bb.data !== 24'hFFFFFF) begin + $write("%%Error i_bb.data=%h\n", i_bb.data); $stop; + end + // Distinct full-32b values must NOT share a spec (values differ). + if (i_aa.data === i_ab.data) begin + $write("%%Error i_aa.data and i_ab.data must differ\n"); $stop; + end + // Equal pin values share a spec (#5479 dedup). + if (i_ab.data !== i_ac.data) begin + $write("%%Error i_ab.data and i_ac.data must match\n"); $stop; + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_param_type_dep_hier.py b/test_regress/t/t_param_type_dep_hier.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_param_type_dep_hier.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_param_type_dep_hier.v b/test_regress/t/t_param_type_dep_hier.v new file mode 100644 index 000000000..22012bb70 --- /dev/null +++ b/test_regress/t/t_param_type_dep_hier.v @@ -0,0 +1,62 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Ariane-shaped 3-level hierarchy with parameter forwarding. Each +// mid instance forwards its paramtype to its leaf. Three distinct +// width/value tuples catch cross-hierarchy template leakage. + +module leaf #( + parameter int W = 4, + parameter type T = logic [W-1:0], + parameter T VAL = '0 +) (); + logic [W-1:0] observed; + assign observed = VAL; +endmodule + +module mid #( + parameter int W = 4, + parameter type T = logic [W-1:0], + parameter T VAL = '0 +) (); + leaf #(.W(W), .T(T), .VAL(VAL)) l (); +endmodule + +module t; + mid #(.W(8), .VAL(8'hA5)) m_a (); + mid #(.W(16), .VAL(16'hBEEF)) m_b (); + mid #(.W(32), .VAL(32'hDEADBEEF)) m_c (); + + initial begin + #1; + if (m_a.l.W !== 8) begin $write("%%Error m_a.l.W=%0d\n", m_a.l.W); $stop; end + if ($bits(m_a.l.observed) !== 8) begin + $write("%%Error $bits(m_a.l.observed)=%0d\n", $bits(m_a.l.observed)); $stop; + end + if (m_a.l.observed !== 8'hA5) begin + $write("%%Error m_a.l.observed=%h\n", m_a.l.observed); $stop; + end + + if (m_b.l.W !== 16) begin $write("%%Error m_b.l.W=%0d\n", m_b.l.W); $stop; end + if ($bits(m_b.l.observed) !== 16) begin + $write("%%Error $bits(m_b.l.observed)=%0d\n", $bits(m_b.l.observed)); $stop; + end + if (m_b.l.observed !== 16'hBEEF) begin + $write("%%Error m_b.l.observed=%h\n", m_b.l.observed); $stop; + end + + if (m_c.l.W !== 32) begin $write("%%Error m_c.l.W=%0d\n", m_c.l.W); $stop; end + if ($bits(m_c.l.observed) !== 32) begin + $write("%%Error $bits(m_c.l.observed)=%0d\n", $bits(m_c.l.observed)); $stop; + end + if (m_c.l.observed !== 32'hDEADBEEF) begin + $write("%%Error m_c.l.observed=%h\n", m_c.l.observed); $stop; + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_param_type_dep_recompute.py b/test_regress/t/t_param_type_dep_recompute.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_param_type_dep_recompute.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_param_type_dep_recompute.v b/test_regress/t/t_param_type_dep_recompute.v new file mode 100644 index 000000000..21c9b81ed --- /dev/null +++ b/test_regress/t/t_param_type_dep_recompute.v @@ -0,0 +1,96 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Dependent value-param + type-param + value-param-of-type-param must be +// re-evaluated per specialization, not baked from the template default. +// Regression test for template poisoning in V3Param::cellPinCleanup: the +// normedNamep widthing block used to reach through a RefDType into the +// template's ParamTypeDType and constify its body, mutating the base +// module's dependent-param valuep so later specializations inherited the +// stale constant. Three instances with distinct .width values in the +// same compilation catch any such cross-instance leakage. + +module test #( + parameter int width = 16, + parameter int width2 = width + 8, + parameter type data_t = logic [width2-1:0], + parameter data_t data = data_t'(0) +) (); + // Internal signal declared as the dependent type param, driven to + // all-ones. Post-fix, $bits and value readback must match the + // spec's own resolved width, not the template default. + data_t data_t_sig; + initial data_t_sig = '1; +endmodule + +module t; + test #(.width(24), .data(32'h0)) inst_a (); + test #(.width(16), .data(24'h0)) inst_b (); + test #(.width( 8), .data(16'h0)) inst_c (); + + initial begin + #1; + // inst_a: width=24 -> width2=32 -> data_t is 32 bits + if (inst_a.width2 !== 32) begin + $write("%%Error inst_a.width2=%0d expected 32\n", inst_a.width2); $stop; + end + if ($bits(inst_a.data) !== 32) begin + $write("%%Error $bits(inst_a.data)=%0d expected 32\n", $bits(inst_a.data)); $stop; + end + if (inst_a.data !== 32'h0) begin + $write("%%Error inst_a.data=%h expected 0\n", inst_a.data); $stop; + end + if ($bits(inst_a.data_t_sig) !== 32) begin + $write("%%Error $bits(inst_a.data_t_sig)=%0d expected 32\n", + $bits(inst_a.data_t_sig)); $stop; + end + if (inst_a.data_t_sig !== 32'hFFFFFFFF) begin + $write("%%Error inst_a.data_t_sig=%h expected FFFFFFFF\n", + inst_a.data_t_sig); $stop; + end + + // inst_b: width=16 -> width2=24 -> data_t is 24 bits + if (inst_b.width2 !== 24) begin + $write("%%Error inst_b.width2=%0d expected 24\n", inst_b.width2); $stop; + end + if ($bits(inst_b.data) !== 24) begin + $write("%%Error $bits(inst_b.data)=%0d expected 24\n", $bits(inst_b.data)); $stop; + end + if (inst_b.data !== 24'h0) begin + $write("%%Error inst_b.data=%h expected 0\n", inst_b.data); $stop; + end + if ($bits(inst_b.data_t_sig) !== 24) begin + $write("%%Error $bits(inst_b.data_t_sig)=%0d expected 24\n", + $bits(inst_b.data_t_sig)); $stop; + end + if (inst_b.data_t_sig !== 24'hFFFFFF) begin + $write("%%Error inst_b.data_t_sig=%h expected FFFFFF\n", + inst_b.data_t_sig); $stop; + end + + // inst_c: width=8 -> width2=16 -> data_t is 16 bits + if (inst_c.width2 !== 16) begin + $write("%%Error inst_c.width2=%0d expected 16\n", inst_c.width2); $stop; + end + if ($bits(inst_c.data) !== 16) begin + $write("%%Error $bits(inst_c.data)=%0d expected 16\n", $bits(inst_c.data)); $stop; + end + if (inst_c.data !== 16'h0) begin + $write("%%Error inst_c.data=%h expected 0\n", inst_c.data); $stop; + end + if ($bits(inst_c.data_t_sig) !== 16) begin + $write("%%Error $bits(inst_c.data_t_sig)=%0d expected 16\n", + $bits(inst_c.data_t_sig)); $stop; + end + if (inst_c.data_t_sig !== 16'hFFFF) begin + $write("%%Error inst_c.data_t_sig=%h expected FFFF\n", + inst_c.data_t_sig); $stop; + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_param_type_dep_struct.py b/test_regress/t/t_param_type_dep_struct.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_param_type_dep_struct.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_param_type_dep_struct.v b/test_regress/t/t_param_type_dep_struct.v new file mode 100644 index 000000000..e2323f36b --- /dev/null +++ b/test_regress/t/t_param_type_dep_struct.v @@ -0,0 +1,59 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Packed struct type param with dependent field widths. $bits and +// field access on a typed-by-paramtype variable must see the correct +// per-spec field widths. Also exercises the #7445 CVA6 pattern +// (struct member access on a VARREF of a parameterized type). + +module m #( + parameter int W = 8, + parameter type T = struct packed { + logic [W-1:0] a; + logic [W-1:0] b; + } +) (); + T t_sig; + logic [W-1:0] a_sig; + initial t_sig = '1; + initial a_sig = t_sig.a; +endmodule + +module t; + m #(.W(4)) i4 (); + m #(.W(8)) i8 (); + m #(.W(16)) i16 (); + + initial begin + #1; + if ($bits(i4.t_sig) !== 8) begin $write("%%Error i4.t_sig bits\n"); $stop; end + if ($bits(i4.t_sig.a) !== 4) begin $write("%%Error i4.t_sig.a bits\n"); $stop; end + if ($bits(i4.t_sig.b) !== 4) begin $write("%%Error i4.t_sig.b bits\n"); $stop; end + if ($bits(i4.a_sig) !== 4) begin $write("%%Error i4.a_sig bits\n"); $stop; end + if (i4.t_sig.a !== 4'hF) begin $write("%%Error i4.t_sig.a=%h\n", i4.t_sig.a); $stop; end + if (i4.t_sig.b !== 4'hF) begin $write("%%Error i4.t_sig.b=%h\n", i4.t_sig.b); $stop; end + if (i4.a_sig !== 4'hF) begin $write("%%Error i4.a_sig=%h\n", i4.a_sig); $stop; end + + if ($bits(i8.t_sig) !== 16) begin $write("%%Error i8.t_sig bits\n"); $stop; end + if ($bits(i8.t_sig.a) !== 8) begin $write("%%Error i8.t_sig.a bits\n"); $stop; end + if ($bits(i8.t_sig.b) !== 8) begin $write("%%Error i8.t_sig.b bits\n"); $stop; end + if ($bits(i8.a_sig) !== 8) begin $write("%%Error i8.a_sig bits\n"); $stop; end + if (i8.t_sig.a !== 8'hFF) begin $write("%%Error i8.t_sig.a=%h\n", i8.t_sig.a); $stop; end + if (i8.t_sig.b !== 8'hFF) begin $write("%%Error i8.t_sig.b=%h\n", i8.t_sig.b); $stop; end + if (i8.a_sig !== 8'hFF) begin $write("%%Error i8.a_sig=%h\n", i8.a_sig); $stop; end + + if ($bits(i16.t_sig) !== 32) begin $write("%%Error i16.t_sig bits\n"); $stop; end + if ($bits(i16.t_sig.a) !== 16) begin $write("%%Error i16.t_sig.a bits\n"); $stop; end + if ($bits(i16.t_sig.b) !== 16) begin $write("%%Error i16.t_sig.b bits\n"); $stop; end + if ($bits(i16.a_sig) !== 16) begin $write("%%Error i16.a_sig bits\n"); $stop; end + if (i16.t_sig.a !== 16'hFFFF) begin $write("%%Error i16.t_sig.a=%h\n", i16.t_sig.a); $stop; end + if (i16.t_sig.b !== 16'hFFFF) begin $write("%%Error i16.t_sig.b=%h\n", i16.t_sig.b); $stop; end + if (i16.a_sig !== 16'hFFFF) begin $write("%%Error i16.a_sig=%h\n", i16.a_sig); $stop; end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule