diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 53241ebdc..8f324ac60 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -58,6 +58,7 @@ Drew Ranck Drew Taussig Driss Hafdi Edgar E. Iglesias +Eric Mejdrich Eric Müller Eric Rippey Eunseo Song diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 902f5477f..8a3a70605 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -1406,13 +1406,16 @@ class ParamProcessor final { } cloneVarp->valuep(exprp->cloneTree(false)); if (AstNodeDType* const origDTypep = modvarp->subDTypep()) { - AstNodeDType* const dtypeClonep = origDTypep->cloneTree(false); - // Inline every param ref so widthing doesn't reach back into the template - // (#7411). Cycle detector for dependent parameters in the same module. + // Attach clone under cloneVarp so the root has a back pointer. + if (cloneVarp->childDTypep()) + cloneVarp->childDTypep()->unlinkFrBack()->deleteTree(); + cloneVarp->childDTypep(origDTypep->cloneTree(false)); + cloneVarp->dtypep(nullptr); + // Inline param refs so widthing doesn't touch the template (#7411). constexpr int maxSubstIters = 1000; for (int it = 0; it < maxSubstIters; ++it) { bool any = false; - dtypeClonep->foreach([&](AstVarRef* varrefp) { + cloneVarp->foreach([&](AstVarRef* varrefp) { AstVar* const targetp = varrefp->varp(); AstNode* replacep = nullptr; for (AstPin* pp = paramsp; pp; pp = VN_AS(pp->nextp(), Pin)) { @@ -1432,19 +1435,40 @@ 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. + std::vector> toReplace; + cloneVarp->foreach([&](AstRefDType* refp) { + AstParamTypeDType* const ptdp + = VN_CAST(refp->refDTypep(), ParamTypeDType); + if (!ptdp) return; + 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); + } + break; + } + } + }); + for (auto it = toReplace.rbegin(); it != toReplace.rend(); ++it) { + AstRefDType* const refp = it->first; + refp->replaceWith(it->second->cloneTree(false)); + VL_DO_DANGLING(refp->deleteTree(), refp); + any = true; + } if (!any) break; } // Bail if anything still points at the template. - dtypeClonep->foreach([&](AstVarRef* varrefp) { + cloneVarp->foreach([&](AstVarRef* varrefp) { varrefp->v3fatalSrc( "Unresolved VarRef '" << varrefp->prettyName() << "' in pin dtype clone. Pin: " << pinp->prettyNameQ() << " of " << nodep->prettyNameQ()); }); - if (cloneVarp->childDTypep()) - cloneVarp->childDTypep()->unlinkFrBack()->deleteTree(); - cloneVarp->childDTypep(dtypeClonep); - cloneVarp->dtypep(nullptr); } V3Const::constifyParamsEdit(cloneVarp); if (AstConst* const widthedp = VN_CAST(cloneVarp->valuep(), Const)) { diff --git a/test_regress/t/t_param_type_struct_member.py b/test_regress/t/t_param_type_struct_member.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_param_type_struct_member.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_struct_member.v b/test_regress/t/t_param_type_struct_member.v new file mode 100644 index 000000000..a493b99a6 --- /dev/null +++ b/test_regress/t/t_param_type_struct_member.v @@ -0,0 +1,44 @@ +// 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 + +// Method/member access on a value-parameter whose type is +// the enclosing module's type-parameter. + +typedef struct packed { + logic [7:0] S_TIMER; + logic [7:0] M_TIMER; + logic [7:0] M_EXT; +} my_irq_t; + +module leaf #( + parameter type interrupts_t = logic, + parameter interrupts_t INTERRUPTS = '0 +) (); + logic [7:0] observed; + always_comb observed = INTERRUPTS.M_TIMER; +endmodule + +module mid #( + parameter type interrupts_t = logic, + parameter interrupts_t INTERRUPTS = '0 +) (); + leaf #(.interrupts_t(interrupts_t), .INTERRUPTS(INTERRUPTS)) l(); +endmodule + +module t; + localparam type irq_t = my_irq_t; + localparam irq_t IRQ = '{S_TIMER: 8'hAA, M_TIMER: 8'h55, M_EXT: 8'hCC}; + mid #(.interrupts_t(irq_t), .INTERRUPTS(IRQ)) m(); + initial begin + #1; + if (m.l.observed !== 8'h55) begin + $write("%%Error: observed=%h expected 55\n", m.l.observed); + $stop; + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_param_type_struct_member2.py b/test_regress/t/t_param_type_struct_member2.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_param_type_struct_member2.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_struct_member2.v b/test_regress/t/t_param_type_struct_member2.v new file mode 100644 index 000000000..cf2173583 --- /dev/null +++ b/test_regress/t/t_param_type_struct_member2.v @@ -0,0 +1,36 @@ +// 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 + +// verilog_format: off +`define stop $stop +`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); +// verilog_format: on + +package pkg; + typedef struct packed { + logic [1:0][31:0] bar; + } T; + localparam T t = 64'h87654321_deadbeef; +endpackage + +module foo #( + parameter type T = int, + parameter T t = 0 +) (); + initial begin + `checkh(t.bar[0], 32'hdeadbeef); + `checkh(t.bar[1], 32'h87654321); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule + +module top; + foo #( + .T(pkg::T), + .t(pkg::t) + ) u_foo (); +endmodule