diff --git a/src/V3Param.cpp b/src/V3Param.cpp index cfbecfdbb..53b202c72 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -1417,8 +1417,11 @@ class ParamProcessor final { any_overridesr = true; } + // Name the specialization by a value pin, or prepare a type pin, noting in + // overridingTypePinsr whether it differs from the default void cellPinCleanup(AstNode* nodep, AstPin* pinp, AstPin* paramsp, AstNodeModule* srcModp, - string& longnamer, bool& any_overridesr) { + string& longnamer, bool& any_overridesr, + std::unordered_set& overridingTypePinsr) { if (!pinp->exprp()) return; // No-connect if (AstVar* const modvarp = pinp->modVarp()) { resolveDeferredDotsReachableFrom(modvarp->subDTypep(), m_modp); @@ -1669,12 +1672,9 @@ class ParamProcessor final { if (classRefDTypep->paramsp() && classRefDTypep->classp() && classRefDTypep->classp()->hasGParam()) { classRefDeparam(classRefDTypep, classRefDTypep->classp()); - rawTypep = VN_CAST(pinp->exprp(), NodeDType); - exprp = rawTypep ? rawTypep->skipRefToNonRefp() : nullptr; } } - longnamer += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(exprp); - any_overridesr = true; + overridingTypePinsr.emplace(pinp); // Named once every pin is prepared } } } else { @@ -1945,9 +1945,37 @@ class ParamProcessor final { longname = parameterizedHierBlockName(srcModp, paramsp); any_overrides = longname != srcModp->name(); } else { + // Prepare every pin before naming any, as naming a value can depend on the others: + // fold each value pin, as a class reference's may not be yet, and resolve each type + std::unordered_set overridingTypePins; for (AstPin* pinp = paramsp; pinp; pinp = VN_AS(pinp->nextp(), Pin)) { + if (!pinp->exprp()) continue; // An empty override keeps the default + if (pinp->modPTypep()) { + cellPinCleanup(nodep, pinp, paramsp, srcModp, longname /*ref*/, + any_overrides /*ref*/, overridingTypePins /*ref*/); + continue; + } + AstVar* const modvarp = pinp->modVarp(); + if (!modvarp || !modvarp->isGParam()) continue; + resolveDeferredDotsReachableFrom(modvarp->subDTypep(), m_modp); + // An untyped pattern can't be folded without its parameter's type + const AstPattern* const patternp = VN_CAST(pinp->exprp(), Pattern); + if (patternp && !patternp->childDTypep()) continue; + if (!VN_IS(pinp->exprp(), Const) && !isAggregateParamValue(pinp->exprp())) { + V3Const::constifyParamsEdit(pinp->exprp()); + } + } + for (AstPin* pinp = paramsp; pinp; pinp = VN_AS(pinp->nextp(), Pin)) { + if (AstParamTypeDType* const modptp = pinp->modPTypep()) { + if (!overridingTypePins.count(pinp)) continue; + AstNodeDType* const typep = VN_AS(pinp->exprp(), NodeDType); + longname += "_" + paramSmallName(srcModp, modptp) + + paramValueNumber(typep->skipRefToNonRefp()); + any_overrides = true; + continue; + } cellPinCleanup(nodep, pinp, paramsp, srcModp, longname /*ref*/, - any_overrides /*ref*/); + any_overrides /*ref*/, overridingTypePins /*ref*/); } } IfaceRefRefs ifaceRefRefs; diff --git a/test_regress/t/t_class_param_pin_order.py b/test_regress/t/t_class_param_pin_order.py new file mode 100755 index 000000000..8a938befd --- /dev/null +++ b/test_regress/t/t_class_param_pin_order.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() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_class_param_pin_order.v b/test_regress/t/t_class_param_pin_order.v new file mode 100644 index 000000000..be1eb3fd8 --- /dev/null +++ b/test_regress/t/t_class_param_pin_order.v @@ -0,0 +1,86 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// A class specialization whose parameter's type depends on a parameter given +// after it, or on a type parameter, is named from every pin's folded value, +// so specializations with equal values are one class, and others are not. +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Michael Bedford Taylor +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +`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 + +// P's width comes from N +class clw #( + parameter int N = 1, + // verilator lint_off WIDTHTRUNC + parameter logic [N-1:0] P = '0 // Given wider values + // verilator lint_on WIDTHTRUNC +); + static int count; + static function int value(); + return int'(P); + endfunction +endclass + +// P and Q have type T +class clt #( + parameter type T = byte, + // verilator lint_off WIDTHTRUNC + parameter T P = 0, // Given wider values + parameter T Q = 0 + // verilator lint_on WIDTHTRUNC +); + static int count; + static function int value(); + return int'(Q); + endfunction +endclass + +module t; + localparam int TWO = 2; + logic [3:0] tv; + + typedef clw#(.P(8'h81), .N(TWO * 4)) clw81_t; // Width given after the value + typedef clw#(.P(8'h01), .N(TWO * 4)) clw01_t; // Differs from clw81_t only above bit 0 + typedef clw#(.P(8'h81), .N(TWO * 2)) clw4a_t; // Equal to clw4b_t once P is 4 bits + typedef clw#(.P(8'h01), .N(TWO * 2)) clw4b_t; + clw4a_t clw4a; + clw4b_t clw4b; + + typedef clt#(.P(8'h01), .T(type(tv)), .Q(8'h81)) clta_t; // Equal to cltb_t once T is known + typedef clt#(.P(8'h01), .T(type(tv)), .Q(8'h01)) cltb_t; + typedef clt#(.P(8'h02), .T(type(tv)), .Q(8'h01)) cltc_t; // The same, in the other order + typedef clt#(.P(8'h02), .T(type(tv)), .Q(8'h81)) cltd_t; + typedef clt#(.T(type(tv)), .P(8'h03), .Q(8'h81)) clte_t; // The same, with T given first + typedef clt#(.T(type(tv)), .P(8'h03), .Q(8'h01)) cltf_t; + clta_t clta; + cltb_t cltb; + + initial begin + `checkh(clw81_t::value(), 'h81); + `checkh(clw01_t::value(), 'h01); + clw4a = new; + clw4b = clw4a; // One specialization, so one type + clw4a_t::count = 7; + `checkd(clw4b_t::count, 7); + `checkh(clw4b_t::value(), 'h1); + + clta = new; + cltb = clta; // One specialization, so one type + clta_t::count = 1; + cltc_t::count = 2; + clte_t::count = 3; + `checkd(cltb_t::count, 1); + `checkd(cltd_t::count, 2); + `checkd(cltf_t::count, 3); + `checkh(cltb_t::value(), 'h1); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule