diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 68f378fd1..1f0faadb9 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -1940,6 +1940,7 @@ public: , m_processor{netlistp} { // Relies on modules already being in top-down-order iterate(netlistp); + V3Width::clearTypeMap(); } ~ParamVisitor() override = default; VL_UNCOPYABLE(ParamVisitor); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index ec931747f..03c93e43a 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -207,13 +207,16 @@ class WidthVisitor final : public VNVisitor { using TableMap = std::map, AstVar*>; using PatVecMap = std::map; using DTypeMap = std::map; + using ModVarExpMap = std::map; // STATE V3UniqueNames m_insideTempNames; // For generating unique temporary variable names for // `inside` expressions VMemberMap m_memberMap; // Member names cached for fast lookup + ModVarExpMap m_modVarExpMap; V3TaskConnectState m_taskConnectState; // State to cache V3Task::taskConnects WidthVP* m_vup = nullptr; // Current node state + static std::map m_typeMap; bool m_underFork = false; // Visiting under a fork bool m_underSExpr = false; // Visiting under a sequence expression AstNode* m_seqUnsupp = nullptr; // Property has unsupported node @@ -232,6 +235,7 @@ class WidthVisitor final : public VNVisitor { const bool m_doGenerate; // Do errors later inside generate statement bool m_streamConcat = false; // True if visiting arguments of stream concatenation int m_dtTables = 0; // Number of created data type tables + bool m_hasParamRef; // Whether there is parameter ref. TableMap m_tableMap; // Created tables so can remove duplicates std::map m_queueDTypeIndexed; // Queues with given index type @@ -2095,6 +2099,11 @@ class WidthVisitor final : public VNVisitor { new AstConst(elementsNewFl, 1)}, true}}; } + AstUnpackArrayDType* arrDtypep = VN_CAST(newp, UnpackArrayDType); + if (m_hasParamRef && arrDtypep) { + if (m_typeMap.find(arrDtypep) == m_typeMap.end()) + m_typeMap[arrDtypep] = arrDtypep->cloneTree(false); + } nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); // Normally parent's iteration would cover this, but we might have entered by a specific @@ -2708,6 +2717,7 @@ class WidthVisitor final : public VNVisitor { nodep->doingWidth(false); } void visit(AstNodeVarRef* nodep) override { + if (m_modVarExpMap.find(nodep->varp()) != m_modVarExpMap.end()) { m_hasParamRef = true; } if (nodep->didWidth()) return; if (!nodep->varp()) { if (m_paramsOnly && VN_IS(nodep, VarXRef)) { @@ -6195,12 +6205,26 @@ class WidthVisitor final : public VNVisitor { bool didWidth = false; if (AstPattern* const patternp = VN_CAST(nodep->exprp(), Pattern)) { const AstVar* const modVarp = nodep->modVarp(); + m_hasParamRef = false; // Convert BracketArrayDType userIterate(modVarp->childDTypep(), WidthVP{SELF, BOTH}.p()); // May relink pointed to node AstNodeDType* const setDtp = modVarp->childDTypep(); - if (!patternp->childDTypep()) patternp->childDTypep(setDtp->cloneTree(false)); + if (!patternp->childDTypep()) { + if (m_typeMap.find(setDtp) != m_typeMap.end()) { + AstNodeDType* const currentDtypep = m_typeMap[setDtp]->cloneTree(false); + currentDtypep->foreach([&](AstNodeVarRef* nodep) { + if (m_modVarExpMap.find(nodep->varp()) != m_modVarExpMap.end()) + nodep->replaceWith( + m_modVarExpMap[nodep->varp()]->cloneTree(false)); + }); + patternp->childDTypep(currentDtypep); + VL_DO_DANGLING(userIterate(currentDtypep, nullptr), currentDtypep); + } else + patternp->childDTypep(setDtp->cloneTree(false)); + } userIterateChildren(nodep, WidthVP{setDtp, BOTH}.p()); + m_hasParamRef = false; didWidth = true; } if (!didWidth) userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); @@ -6335,7 +6359,21 @@ class WidthVisitor final : public VNVisitor { if (nodep->rangep()) userIterateAndNext(nodep->rangep(), WidthVP{SELF, BOTH}.p()); userIterateAndNext(nodep->pinsp(), nullptr); } + if (nodep->paramsp()) { + nodep->paramsp()->foreach([&](const AstPin* pinNodep) { + if (!VN_CAST(pinNodep->exprp(), Pattern)) { + if (AstNodeExpr* const nodeExprp = VN_CAST(pinNodep->exprp(), NodeExpr)) { + m_modVarExpMap[pinNodep->modVarp()] = nodeExprp->cloneTree(false); + } + } + }); + } userIterateAndNext(nodep->paramsp(), nullptr); + for (auto itr = m_modVarExpMap.begin(); itr != m_modVarExpMap.end(); itr++) { + AstNodeExpr* exprp = itr->second; + VL_DO_DANGLING(exprp->deleteTree(), exprp); + } + m_modVarExpMap.clear(); } void visit(AstGatePin* nodep) override { assertAtExpr(nodep); @@ -9167,6 +9205,13 @@ class WidthVisitor final : public VNVisitor { } public: + static void clearTypeMap() { + for (auto itr = m_typeMap.begin(); itr != m_typeMap.end(); itr++) { + AstNodeDType* nodeDTypep = itr->second; + VL_DO_DANGLING(nodeDTypep->deleteTree(), nodeDTypep); + } + m_typeMap.clear(); + } // CONSTRUCTORS WidthVisitor(bool paramsOnly, // [in] TRUE if we are considering parameters only. bool doGenerate) // [in] TRUE if we are inside a generate statement and @@ -9179,6 +9224,7 @@ public: } ~WidthVisitor() override = default; }; +std::map WidthVisitor::m_typeMap; //###################################################################### // Width class functions @@ -9224,3 +9270,4 @@ AstNode* V3Width::widthGenerateParamsEdit( // No WidthRemoveVisitor, as don't want to drop $signed etc inside gen blocks return nodep; } +void V3Width::clearTypeMap() { WidthVisitor::clearTypeMap(); } diff --git a/src/V3Width.h b/src/V3Width.h index 88eb86c2a..e05a75fda 100644 --- a/src/V3Width.h +++ b/src/V3Width.h @@ -31,6 +31,7 @@ public: static void width(AstNetlist* nodep) VL_MT_DISABLED; static AstNode* widthParamsEdit(AstNode* nodep) VL_MT_DISABLED; static AstNode* widthGenerateParamsEdit(AstNode* nodep) VL_MT_DISABLED; + static void clearTypeMap(); // For use only in WidthVisitor // Replace AstSelBit, etc with AstSel/AstArraySel diff --git a/test_regress/t/t_param_init_attr.py b/test_regress/t/t_param_init_attr.py new file mode 100755 index 000000000..84ad9365e --- /dev/null +++ b/test_regress/t/t_param_init_attr.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +# Compile only test. +test.compile() + +test.passes() diff --git a/test_regress/t/t_param_init_attr.v b/test_regress/t/t_param_init_attr.v new file mode 100755 index 000000000..ef8cc13f9 --- /dev/null +++ b/test_regress/t/t_param_init_attr.v @@ -0,0 +1,39 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2017 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t(); + parameter LEN1 = 8; + test #( + .LEN(LEN1), + .LST('{LEN1{0}}) + ) + inst1(); + + parameter LEN2 = 3; + test #( + .LEN(LEN2*2), + .LST('{LEN2*2{0}}) + ) + inst2(); + + test #( + .LEN(LEN1 + LEN2), + .LST('{(LEN1+LEN2){0}}) + ) + inst3(); + + test #( + .LEN(LEN1 + LEN2), + .LST('{LEN1{0}, LEN2{1}}) + ) + inst4(); +endmodule + +module test #( + parameter LEN = 4, + parameter LST[LEN] = '{LEN{0}} +)(); +endmodule