diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 0c6464e6e..23124a371 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -296,8 +296,8 @@ class ParamProcessor final { // Default parameter values of hierarchical blocks std::map m_defaultParameterValues; VNDeleter m_deleter; // Used to delay deletion of nodes - // Class default paramater dependencies - std::vector> m_classParams; + // Class default type paramater dependencies + std::vector> m_classTypeParams; std::unordered_map m_paramIndex; // Guard against infinite recursion in classTypeMatchesDefaultClone slow path @@ -1835,24 +1835,24 @@ class ParamProcessor final { UASSERT_OBJ(classp, nodep, "Class or interface ref has no classp/ifacep"); // Get the parameter list for this class - m_classParams.clear(); + m_classTypeParams.clear(); m_paramIndex.clear(); for (AstNode* stmtp = classp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { if (AstParamTypeDType* paramTypep = VN_CAST(stmtp, ParamTypeDType)) { // Only consider formal class type parameters (generic parameters), // not localparam type declarations inside the class body. if (!paramTypep->isGParam()) continue; - m_paramIndex.emplace(paramTypep, static_cast(m_classParams.size())); - m_classParams.emplace_back(paramTypep, -1); + m_paramIndex.emplace(paramTypep, static_cast(m_classTypeParams.size())); + m_classTypeParams.emplace_back(paramTypep, -1); } } // For each parameter, detect either a dependent default (copy from previous param) // or a direct type default (for ex. int). Store dependency index in // m_classParams[i].second, default type in defaultTypeNodes[i]. - std::vector defaultTypeNodes(m_classParams.size(), nullptr); - for (size_t i = 0; i < m_classParams.size(); ++i) { - AstParamTypeDType* const paramTypep = m_classParams[i].first; + std::vector defaultTypeNodes(m_classTypeParams.size(), nullptr); + for (size_t i = 0; i < m_classTypeParams.size(); ++i) { + AstParamTypeDType* const paramTypep = m_classTypeParams[i].first; // Parser places defaults/constraints under childDTypep as AstRequireDType AstRequireDType* const reqDtp = VN_CAST(paramTypep->getChildDTypep(), RequireDType); if (!reqDtp) continue; @@ -1862,7 +1862,7 @@ class ParamProcessor final { if (AstParamTypeDType* const sourceParamp = VN_CAST(refDtp->refDTypep(), ParamTypeDType)) { auto it = m_paramIndex.find(sourceParamp); - if (it != m_paramIndex.end()) { m_classParams[i].second = it->second; } + if (it != m_paramIndex.end()) { m_classTypeParams[i].second = it->second; } continue; // dependency handled } } @@ -1875,14 +1875,23 @@ class ParamProcessor final { } // Count existing pins and capture them by index for easy lookup + // Type parameters when named can be given in any order, but later we depend + // on the order when handling defaults. So here we reorder them + // for proper handling. The remaining ones are just pushed back std::vector pinsByIndex; + pinsByIndex.resize(m_classTypeParams.size(), nullptr); for (AstPin* pinp = paramsp; pinp; pinp = VN_AS(pinp->nextp(), Pin)) { - pinsByIndex.push_back(pinp); + if (AstParamTypeDType* typep = pinp->modPTypep()) { + pinsByIndex[m_paramIndex[typep]] = pinp; + } else { + pinsByIndex.push_back(pinp); + } } // For each missing parameter, get its pin from dependency or direct default - for (size_t paramIdx = pinsByIndex.size(); paramIdx < m_classParams.size(); paramIdx++) { - const int sourceParamIdx = m_classParams[paramIdx].second; + for (size_t paramIdx = 0; paramIdx < m_classTypeParams.size(); paramIdx++) { + if (pinsByIndex[paramIdx]) continue; + const int sourceParamIdx = m_classTypeParams[paramIdx].second; AstPin* newPinp = nullptr; @@ -1900,7 +1909,7 @@ class ParamProcessor final { if (newPinp) { newPinp->name("__paramNumber" + cvtToStr(paramIdx + 1)); newPinp->param(true); - newPinp->modPTypep(m_classParams[paramIdx].first); + newPinp->modPTypep(m_classTypeParams[paramIdx].first); if (classOrPackageRef) { classOrPackageRef->addParamsp(newPinp); } else if (classRefDType) { diff --git a/test_regress/t/t_param_order.py b/test_regress/t/t_param_order.py new file mode 100755 index 000000000..8a938befd --- /dev/null +++ b/test_regress/t/t_param_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_param_order.v b/test_regress/t/t_param_order.v new file mode 100644 index 000000000..4fef2cb30 --- /dev/null +++ b/test_regress/t/t_param_order.v @@ -0,0 +1,103 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Antmicro +// 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); +// verilog_format: on + +class obj; +endclass +class TypeParams #( + type T1 = obj, + type T2 = obj, + type T3 = obj +); + T1 t1; + T2 t2; + T3 t3; +endclass + +class ValueParams #( + int P1 = 1, + int P2 = 1, + int P3 = 1 +); + logic[P1:0] x1; + logic[P2:0] x2; + logic[P3:0] x3; +endclass + +class Mixed #( + type T1 = obj, + int P1 = 1, + type T2 = obj, + int P2 = 1, + type T3 = obj, + int P3 = 1 +); + T1 t1; + T2 t2; + T3 t3; + logic[P1:0] x1; + logic[P2:0] x2; + logic[P3:0] x3; +endclass + +module t; + TypeParams #( + .T2 (int), + .T3 (logic) + ) t; + obj o; + + ValueParams #( + .P3 (5), + .P2 (2) + ) v; + + Mixed #( + .P3 (3), + .T1 (logic), + .T3 (int), + .P2 (7) + ) m; + initial begin + o = new; + t = new; + t.t1 = o; + t.t2 = 32; + t.t3 = 1; + if (t.t1 != o) $stop; + `checkd(t.t2, 32); + `checkd(t.t3, 1); + + v = new; + v.x1 = 2; + v.x2 = 5; + v.x3 = 40; + `checkd(v.x1, 2); + `checkd(v.x2, 5); + `checkd(v.x3, 40); + + m = new; + m.t1 = 1; + m.t2 = o; + m.t3 = 12345; + m.x1 = 0; + m.x2 = 250; + m.x3 = 15; + `checkd(m.t1, 1); + if (m.t2 != o) $stop; + `checkd(m.t3, 12345); + `checkd(m.x1, 0); + `checkd(m.x2, 250); + `checkd(m.x3, 15); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule