diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 94e40d384..c652b4f39 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -152,6 +152,7 @@ Krzysztof Sychla Kuba Ober Lan Zongwei Larry Doolittle +Leela Pakanati Liam Braun Luca Colagrande Ludwig Rogiers diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 31f30e127..8a9c23d98 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -810,17 +810,32 @@ class ParamProcessor final { } } - // Helper to resolve DOT to RefDType for class type references + // Helper to resolve DOT to RefDType for class type references. + // If the class is parameterized and not yet specialized, specialize it first. + // This handles cases like: iface #(param_class#(value)::typedef_name) void resolveDotToTypedef(AstNode* exprp) { AstDot* const dotp = VN_CAST(exprp, Dot); if (!dotp) return; - const AstClassOrPackageRef* const classRefp = VN_CAST(dotp->lhsp(), ClassOrPackageRef); + AstClassOrPackageRef* const classRefp = VN_CAST(dotp->lhsp(), ClassOrPackageRef); if (!classRefp) return; - const AstClass* const lhsClassp = VN_CAST(classRefp->classOrPackageSkipp(), Class); - if (!lhsClassp) return; AstParseRef* const parseRefp = VN_CAST(dotp->rhsp(), ParseRef); if (!parseRefp) return; + const AstClass* lhsClassp = VN_CAST(classRefp->classOrPackageSkipp(), Class); + if (classRefp->paramsp()) { + // ClassOrPackageRef has parameters - may need to specialize the class + AstClass* const srcClassp = VN_CAST(classRefp->classOrPackageNodep(), Class); + if (srcClassp && srcClassp->hasGParam()) { + // Specialize if the reference still points to the generic class + if (lhsClassp == srcClassp || !lhsClassp) { + UINFO(9, "resolveDotToTypedef: specializing " << srcClassp->name() << endl); + classRefDeparam(classRefp, srcClassp); + lhsClassp = VN_CAST(classRefp->classOrPackageSkipp(), Class); + } + } + } + if (!lhsClassp) return; + AstTypedef* const tdefp = VN_CAST(m_memberMap.findMember(lhsClassp, parseRefp->name()), Typedef); if (tdefp) { diff --git a/test_regress/t/t_iface_param_class_typedef.py b/test_regress/t/t_iface_param_class_typedef.py new file mode 100644 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_iface_param_class_typedef.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_iface_param_class_typedef.v b/test_regress/t/t_iface_param_class_typedef.v new file mode 100644 index 000000000..3da3e7c8a --- /dev/null +++ b/test_regress/t/t_iface_param_class_typedef.v @@ -0,0 +1,38 @@ +// 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 + +// Test that parameterized class typedefs work as interface type parameters +// See issue #6983 + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +class flux_st #(parameter int WIDTH=32); + typedef struct packed { logic [WIDTH-1:0] data; } pld_t; +endclass + +interface flux_if #(parameter type PLD_T = logic); + logic rdy; + logic vld; + PLD_T pld; + modport drive (input rdy, output vld, output pld); + modport sink (output rdy, input vld, input pld); +endinterface + +module t; + // Test using parameterized class typedef as interface type parameter + flux_if #(flux_st#(64)::pld_t) w_flux_st (); + + initial begin + `checkd($bits(w_flux_st.pld), 64); + w_flux_st.pld.data = 64'hDEADBEEF_CAFEBABE; + `checkd(w_flux_st.pld.data, 64'hDEADBEEF_CAFEBABE); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule