diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index ad87ed3b6..3870edae8 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -57,6 +57,7 @@ Dan Ruelas-Petrisko Daniel Bates Danny Oler Dave Sargeant +David Garau David Horton David Ledger David Metz diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index e6e3e14f5..e0bd6fb5c 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -3653,8 +3653,11 @@ class LinkDotResolveVisitor final : public VNVisitor { return nullptr; } static const AstVar* getNextVarp(const AstNode* stmtsp) { + // Only IO ports, as parameters are on paramsp(), not the pinsp() list paired here while (stmtsp) { - if (const AstVar* const varp = VN_CAST(stmtsp, Var)) return varp; + if (const AstVar* const varp = VN_CAST(stmtsp, Var)) { + if (varp->isIO()) return varp; + } stmtsp = stmtsp->nextp(); } return nullptr; @@ -4470,7 +4473,9 @@ class LinkDotResolveVisitor final : public VNVisitor { AstIfaceRefDType* const ifacerefp = LinkDotState::ifaceRefFromArray(varp->subDTypep()); if (varp->isIfaceRef() && m_genericIfaceModule - && VN_IS(varp->childDTypep(), IfaceGenericDType)) { + && VN_IS(varp->childDTypep(), IfaceGenericDType) && !start) { + // Defer only dotted member access ('d.PARAM'), as V3Param must specialize + // first; a standalone ref ('.x(d)') resolves via allowVar below now ok = true; m_ds.m_unresolvedGenericIface = true; } else if (ifacerefp && varp->isIfaceRef()) { diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 72c33a6e7..251d8909d 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -2529,12 +2529,17 @@ class ParamVisitor final : public VNVisitor { std::deque m_strings; // Allocator for temporary strings std::map m_isCircular; // Stores information whether `AstRefDType` is circular + using VarsByName = std::unordered_map; + // Persists across modules; one specialized interface clone serves every module bound to it + std::unordered_map m_ifaceParams; // STATE - for current visit position (use VL_RESTORER) AstNodeModule* m_modp = nullptr; // Module iterating std::unordered_set m_ifacePortNames; // Interface port names in current module std::unordered_map m_ifaceInstCells; // Local interface instance cells in current module, keyed by name + VarsByName m_modIfaceRefs; // Interface-ref Vars in current module, keyed by name + bool m_modIfaceRefsDone = false; // m_modIfaceRefs has been gathered for m_modp string m_generateHierName; // Generate portion of hierarchy name // METHODS @@ -2573,7 +2578,10 @@ class ParamVisitor final : public VNVisitor { VL_RESTORER(m_modp); VL_RESTORER_CLEAR(m_ifacePortNames); VL_RESTORER_CLEAR(m_ifaceInstCells); + VL_RESTORER_CLEAR(m_modIfaceRefs); + VL_RESTORER(m_modIfaceRefsDone); m_modp = modp; + m_modIfaceRefsDone = false; iterateChildren(modp); } } @@ -2925,6 +2933,76 @@ class ParamVisitor final : public VNVisitor { if (!VN_IS(nodep->classOrPackageNodep(), Typedef)) visitCellOrClassRef(nodep, false); } + // Recurse into AstGenBlock as generate blocks aren't flattened until V3Begin::debeginAll, + // well after V3Param runs + static void gatherVars(AstNode* stmtsp, bool (*matchp)(const AstVar*), VarsByName& varsr) { + for (AstNode* nodep = stmtsp; nodep; nodep = nodep->nextp()) { + if (AstVar* const varp = VN_CAST(nodep, Var)) { + // emplace, not assign, so the first declaration of a name wins + if (matchp(varp)) varsr.emplace(varp->name(), varp); + } else if (AstGenBlock* const genp = VN_CAST(nodep, GenBlock)) { + gatherVars(genp->itemsp(), matchp, varsr); + } + } + } + const VarsByName& modIfaceRefs() { + if (!m_modIfaceRefsDone) { + gatherVars( + m_modp->stmtsp(), [](const AstVar* varp) { return varp->isIfaceRef(); }, + m_modIfaceRefs); + m_modIfaceRefsDone = true; + } + return m_modIfaceRefs; + } + const VarsByName& ifaceParams(AstNodeModule* ifacep) { + const auto pair = m_ifaceParams.emplace(ifacep, VarsByName{}); + if (pair.second) { + gatherVars( + ifacep->stmtsp(), [](const AstVar* varp) { return varp->isParam(); }, + pair.first->second); + } + return pair.first->second; + } + // Resolve a generic-interface 'ifacePort.member' Dot left unlinked by V3LinkDot, now that + // ifacePort may have been specialized. Returns nullptr if still unresolvable + AstNode* tryResolveGenericIfaceDot(AstDot* dotp) { + AstParseRef* const lhsp = VN_CAST(dotp->lhsp(), ParseRef); + AstParseRef* const rhsp = VN_CAST(dotp->rhsp(), ParseRef); + if (!lhsp || !rhsp) return nullptr; + // Always called from visit(AstVar) inside processWorkQ()'s VL_RESTORER(m_modp) scope + if (VL_UNCOVERABLE(!m_modp)) return nullptr; + + const auto& refVars = modIfaceRefs(); + const auto ifaceVarIt = refVars.find(lhsp->name()); + // V3LinkDot only defers Dots whose lhs is a non-array iface-ref var, so this can't fail + if (VL_UNCOVERABLE(ifaceVarIt == refVars.end())) return nullptr; + AstVar* const ifaceVarp = ifaceVarIt->second; + AstIfaceRefDType* const ifacerefp = VN_CAST(ifaceVarp->subDTypep(), IfaceRefDType); + if (VL_UNCOVERABLE(!ifacerefp || !ifacerefp->ifacep())) return nullptr; + + const auto& ifaceVars = ifaceParams(ifacerefp->ifacep()); + const auto targetVarIt = ifaceVars.find(rhsp->name()); + if (targetVarIt == ifaceVars.end()) return nullptr; + AstVar* const targetVarp = targetVarIt->second; + // processWorkQ() visits interface cells first, so the interface is already constified + if (VL_UNCOVERABLE(!VN_IS(targetVarp->valuep(), Const))) iterate(targetVarp); + if (VL_UNCOVERABLE(!targetVarp->valuep() || !VN_IS(targetVarp->valuep(), Const))) { + return nullptr; // LCOV_EXCL_LINE + } + return targetVarp->valuep()->cloneTree(false); + } + void resolveGenericIfaceDotsIn(AstNode* nodep) { + // Collect first: the deleteTree() below frees a node foreach() would still read from + std::vector dotps; + nodep->foreach([&](AstDot* dotp) { dotps.push_back(dotp); }); + for (AstDot* const dotp : dotps) { + if (AstNode* const newp = tryResolveGenericIfaceDot(dotp)) { + dotp->replaceWith(newp); + VL_DO_DANGLING(dotp->deleteTree(), dotp); + } + } + } + // Make sure all parameters are constantified void visit(AstVar* nodep) override { if (nodep->user2SetOnce()) return; // Process once @@ -2937,6 +3015,8 @@ class ParamVisitor final : public VNVisitor { nodep->v3error("Parameter without default value is never given value" << " (IEEE 1800-2023 6.20.1): " << nodep->prettyNameQ()); } else if (nodep->valuep()) { + // Resolve now: a sibling cell's pin fold may need this before linkDotParamed + resolveGenericIfaceDotsIn(nodep->valuep()); // If the value expression contains a VarXRef to an interface // localparam whose value is not yet constant, defer constification // to avoid premature widthing with unresolved values (see @@ -2952,9 +3032,8 @@ class ParamVisitor final : public VNVisitor { } }); if (hasUnresolvedLparamXRef) return; - // Defer if value has a class::member Dot, or references a deferred lparam - const bool hasDot = nodep->valuep()->exists( - [](AstDot* dotp) { return VN_IS(dotp->lhsp(), ClassOrPackageRef); }); + // Defer if value has any unresolved Dot, or references a deferred lparam + const bool hasDot = nodep->valuep()->exists([](AstDot*) { return true; }); bool refsDeferred = false; if (!hasDot) { const auto& deferredVarps = v3Global.rootp()->deferredParamVarps(); diff --git a/test_regress/t/t_interface_generic_iface_forward_concrete.py b/test_regress/t/t_interface_generic_iface_forward_concrete.py new file mode 100755 index 000000000..6ac2815da --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_forward_concrete.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=["--timing"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_iface_forward_concrete.v b/test_regress/t/t_interface_generic_iface_forward_concrete.v new file mode 100644 index 000000000..931251ad0 --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_forward_concrete.v @@ -0,0 +1,40 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 PlanV GmbH +// 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 + +interface inf #(parameter int PARAM = 1); + logic [PARAM-1:0] v; +endinterface + +module leaf (inf d); + initial begin + #1; + `checkd(d.v, 13); + `checkd(d.PARAM, 5); + end +endmodule + +module mid2 (interface c); + leaf leaf_i(.d(c)); +endmodule + +module mid1 (interface b); + mid2 mid2_i(.c(b)); +endmodule + +module t; + inf #(.PARAM(5)) a(); + mid1 mid1_i(.b(a)); + initial begin + a.v = 13; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_iface_forward_param.py b/test_regress/t/t_interface_generic_iface_forward_param.py new file mode 100755 index 000000000..6ac2815da --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_forward_param.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=["--timing"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_iface_forward_param.v b/test_regress/t/t_interface_generic_iface_forward_param.v new file mode 100644 index 000000000..b0b249e68 --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_forward_param.v @@ -0,0 +1,37 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2026 PlanV GmbH +// 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 + +interface inf #(parameter int PARAM = 1); + logic [PARAM-1:0] v; +endinterface + +module leaf (inf c); + initial begin + #1; + `checkd(c.v, 13) + `checkd(c.PARAM, 5) + end +endmodule + +module mid #(int PARAM = 1) (interface b); + leaf leaf_i(.c(b)); + initial `checkd(PARAM,7) +endmodule + +module t; + inf #(.PARAM(5)) a(); + mid #(.PARAM(7)) mid_i (.b(a)); + initial begin + a.v = 13; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_iface_param_cell_ref.py b/test_regress/t/t_interface_generic_iface_param_cell_ref.py new file mode 100755 index 000000000..a8d4caabb --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_param_cell_ref.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: 2025 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_iface_param_cell_ref.v b/test_regress/t/t_interface_generic_iface_param_cell_ref.v new file mode 100644 index 000000000..26c4fb4bb --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_param_cell_ref.v @@ -0,0 +1,45 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2025 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 + +interface inf #(parameter int PARAM = 1); + logic [PARAM-1:0] v; + modport mp (input v); +endinterface + +module Leaf #(parameter int PARAM = 1) (inf.mp leaf_a); + initial begin + #1; + `checkd(leaf_a.PARAM, PARAM); + end +endmodule + +module GenericModule (interface.mp a); + localparam LOC_PARAM = a.PARAM; + // A generic interface's parameter, parameterizing a sibling interface cell + inf #(.PARAM(LOC_PARAM)) nested_inst(); + Leaf #(.PARAM(LOC_PARAM)) leaf (nested_inst); + initial begin + #1; + `checkd(a.v, 7); + `checkd(a.PARAM, 13); + `checkd(LOC_PARAM, a.PARAM); + end +endmodule + +module t; + inf #(.PARAM(13)) inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 7; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_iface_param_dotchain.py b/test_regress/t/t_interface_generic_iface_param_dotchain.py new file mode 100755 index 000000000..f090e5249 --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_param_dotchain.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=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_iface_param_dotchain.v b/test_regress/t/t_interface_generic_iface_param_dotchain.v new file mode 100644 index 000000000..d1e3baf67 --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_param_dotchain.v @@ -0,0 +1,45 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 David Garau +// 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 + +interface leaf_if; + localparam int LEAF_PARAM = 42; + logic dummy; +endinterface + +interface inf #(parameter int PARAM = 1); + logic [PARAM-1:0] v; + // Makes 'a.nested.LEAF_PARAM' a chained Dot, whose lhsp is itself a Dot + leaf_if nested(); + parameter int ARR[2] = '{PARAM, PARAM + 1}; + modport mp (input v); +endinterface + +module GenericModule (interface.mp a); + localparam int LOC_CHAIN = a.nested.LEAF_PARAM; + // Makes the Dot's rhsp an indexed select, not a plain identifier + localparam int LOC_ARR = a.ARR[0]; + initial begin + #1; + `checkd(a.v, 7); + `checkd(LOC_CHAIN, 42); + `checkd(LOC_ARR, 13); + end +endmodule + +module t; + inf #(.PARAM(13)) inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 7; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_iface_param_genblock.py b/test_regress/t/t_interface_generic_iface_param_genblock.py new file mode 100755 index 000000000..f090e5249 --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_param_genblock.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=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_iface_param_genblock.v b/test_regress/t/t_interface_generic_iface_param_genblock.v new file mode 100644 index 000000000..4379b7e6f --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_param_genblock.v @@ -0,0 +1,53 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 David Garau +// 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 + +interface inf #(parameter int PARAM = 1); + logic [PARAM-1:0] v; + // A non-Var/non-GenBlock item the member gather must skip past + modport mp (input v); + if (1) begin : blk_decoy + // Same name as the real parameter below, but not a parameter + logic PARAM_IF; + end + if (1) begin : blk_if + localparam int PARAM_IF = PARAM + 100; + end + for (genvar i = 0; i < 1; i++) begin : blk_for + localparam int PARAM_FOR = PARAM + 200; + end +endinterface + +module GenericModule (interface.mp a); + // Two references to the same member, so the second reuses the gathered members + localparam int LOC_PARAM1 = a.PARAM; + localparam int LOC_PARAM2 = a.PARAM; + // Members inside generate blocks, reachable only by recursing into them + localparam int LOC_IF = a.PARAM_IF; + localparam int LOC_FOR = a.PARAM_FOR; + initial begin + #1; + `checkd(a.v, 7); + `checkd(LOC_PARAM1, 13); + `checkd(LOC_PARAM2, 13); + `checkd(LOC_IF, 113); + `checkd(LOC_FOR, 213); + end +endmodule + +module t; + inf #(.PARAM(13)) inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 7; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_iface_param_modgenblock.py b/test_regress/t/t_interface_generic_iface_param_modgenblock.py new file mode 100755 index 000000000..f090e5249 --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_param_modgenblock.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=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_iface_param_modgenblock.v b/test_regress/t/t_interface_generic_iface_param_modgenblock.v new file mode 100644 index 000000000..09815472e --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_param_modgenblock.v @@ -0,0 +1,41 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 David Garau +// 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 + +interface inf #(parameter int PARAM = 1); + logic [PARAM-1:0] v; + modport mp (input v); +endinterface + +module GenericModule (interface.mp a); + // Dotted localparam inside a generate block, feeding a sibling cell in the same block. + // The interface-ref gather must recurse in here yet still find port 'a' at module level. + // Generate blocks on the interface side are covered by t_interface_generic_iface_param_genblock. + if (1) begin : blk + localparam int LOC_PARAM = a.PARAM; + inf #(.PARAM(LOC_PARAM)) inner(); + end + initial begin + #1; + `checkd(a.v, 7); + `checkd(blk.LOC_PARAM, 13); + `checkd(blk.inner.PARAM, 13); + end +endmodule + +module t; + inf #(.PARAM(13)) inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 7; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_iface_param_ref.py b/test_regress/t/t_interface_generic_iface_param_ref.py new file mode 100755 index 000000000..a8d4caabb --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_param_ref.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: 2025 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_generic_iface_param_ref.v b/test_regress/t/t_interface_generic_iface_param_ref.v new file mode 100644 index 000000000..dfd3da681 --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_param_ref.v @@ -0,0 +1,41 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2025 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 + +interface inf #(int PARAM = 0); + logic[PARAM-1:0] v; + + modport port_in ( + input v + ); + modport port_out ( + output v + ); +endinterface + +module GenericModule #(PARAM=0) (interface.port_in a); + localparam int LOC_PARAM = a.PARAM; + initial begin + #1; + `checkd(a.v, 7); + `checkd(a.PARAM, 13); + `checkd(LOC_PARAM, a.PARAM); + end +endmodule + +module t; + inf #(.PARAM(13)) inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 7; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_generic_iface_param_ref_bad.out b/test_regress/t/t_interface_generic_iface_param_ref_bad.out new file mode 100644 index 000000000..27e3b9ba1 --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_param_ref_bad.out @@ -0,0 +1,7 @@ +%Error: t/t_interface_generic_iface_param_ref_bad.v:19:32: Can't find definition of 'NONEXISTENT_PARAM' in dotted variable/method: 'a.NONEXISTENT_PARAM' + : ... note: In instance 't.genericModule' + 19 | localparam int LOC_PARAM = a.NONEXISTENT_PARAM; + | ^~~~~~~~~~~~~~~~~ + ... Known scopes under 'a': + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: Exiting due to diff --git a/test_regress/t/t_interface_generic_iface_param_ref_bad.py b/test_regress/t/t_interface_generic_iface_param_ref_bad.py new file mode 100755 index 000000000..38cf36b43 --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_param_ref_bad.py @@ -0,0 +1,16 @@ +#!/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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_interface_generic_iface_param_ref_bad.v b/test_regress/t/t_interface_generic_iface_param_ref_bad.v new file mode 100644 index 000000000..5c75d6a16 --- /dev/null +++ b/test_regress/t/t_interface_generic_iface_param_ref_bad.v @@ -0,0 +1,34 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 David Garau +// 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 + +interface inf #(parameter int PARAM = 1); + logic [PARAM-1:0] v; + modport mp (input v); +endinterface + +module GenericModule (interface.mp a); + // A member that does not exist on the (now concrete) interface + localparam int LOC_PARAM = a.NONEXISTENT_PARAM; + initial begin + #1; + `checkd(a.v, 7); + end +endmodule + +module t; + inf #(.PARAM(13)) inf_inst(); + GenericModule genericModule (inf_inst); + initial begin + inf_inst.v = 7; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule