Fix module parameters not re-evaluated upon instantiation (#7463) (#7477)

This commit is contained in:
em2machine 2026-04-23 12:30:42 -04:00 committed by GitHub
parent ba3937734f
commit 121fd3f613
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 621 additions and 14 deletions

View File

@ -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<std::pair<AstRefDType*, AstNodeDType*>> 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);
}

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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