diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 5b8f0ee2b..9ecfb4782 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -958,6 +958,9 @@ class ParamProcessor final { 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, m_classParams.size()); m_classParams.emplace_back(paramTypep, -1); } @@ -1194,6 +1197,24 @@ public: VL_UNCOPYABLE(ParamProcessor); }; +//###################################################################### +// This visitor records classes that are referenced with parameter pins +class ClassPinsMarkVisitor final : public VNVisitorConst { + +public: + explicit ClassPinsMarkVisitor(AstNetlist* netlistp) { iterateConst(netlistp); } + + void visit(AstClassOrPackageRef* nodep) override { + if (nodep->paramsp()) { + if (AstClass* const classp = VN_CAST(nodep->classOrPackageSkipp(), Class)) { + classp->user3p(classp); + } + } + } + void visit(AstClass* nodep) override {} // don't iterate inside classes + void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } +}; + //###################################################################### // Process parameter visitor @@ -1661,6 +1682,9 @@ public: // Relies on modules already being in top-down-order iterate(netlistp); + // Mark classes which cannot be removed because they are still referenced + ClassPinsMarkVisitor markVisitor{netlistp}; + relinkDots(); // Re-sort module list to be in topological order and fix-up incorrect levels. We need to diff --git a/test_regress/t/t_bug6418.py b/test_regress/t/t_bug6418.py new file mode 100755 index 000000000..319c0ff4a --- /dev/null +++ b/test_regress/t/t_bug6418.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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('vlt') + +test.compile() + +test.passes() diff --git a/test_regress/t/t_bug6418.v b/test_regress/t/t_bug6418.v new file mode 100644 index 000000000..70ed0e502 --- /dev/null +++ b/test_regress/t/t_bug6418.v @@ -0,0 +1,48 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +package pk1; + typedef struct packed { + int AddrBits; + int DataBits; + } cfg_t; +endpackage + +virtual class a_class_t #( + parameter pk1::cfg_t CFG = 0 +); + // verilator lint_off ASCRANGE + localparam type addr_t = logic [CFG.AddrBits-1:0]; + localparam type data_t = logic [CFG.DataBits-1:0]; + // verilator lint_on ASCRANGE + + typedef struct packed { + addr_t addr; + data_t data; + } pkt_t; +endclass + +interface ifc #( + parameter pk1::cfg_t CFG = 0 +); + a_class_t #(CFG)::pkt_t p; +endinterface + +module a_to_b #( + parameter pk1::cfg_t ACFG = 0 +) ( + ifc bus +); + // sturf +endmodule + +module t; + localparam pk1::cfg_t ACFG = '{AddrBits : 64, DataBits : 64}; + + ifc #(.CFG(ACFG)) the_bus (); + + a_to_b #(ACFG) a_to_b (.bus(the_bus)); +endmodule