Fix parameter read through locally-declared interface instance (#7679)

This commit is contained in:
Nick Brereton 2026-05-28 21:20:49 -04:00 committed by GitHub
parent 3d126b77cd
commit 5d344ab8ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 107 additions and 8 deletions

View File

@ -2510,7 +2510,8 @@ class ParamVisitor final : public VNVisitor {
// STATE - for current visit position (use VL_RESTORER)
AstNodeModule* m_modp = nullptr; // Module iterating
std::unordered_set<std::string> m_ifacePortNames; // Interface port names in current module
std::unordered_set<std::string> m_ifaceInstNames; // Interface decl names in current module
std::unordered_map<std::string, AstCell*>
m_ifaceInstCells; // Local interface instance cells in current module, keyed by name
string m_generateHierName; // Generate portion of hierarchy name
// METHODS
@ -2548,10 +2549,10 @@ class ParamVisitor final : public VNVisitor {
{
VL_RESTORER(m_modp);
VL_RESTORER(m_ifacePortNames);
VL_RESTORER(m_ifaceInstNames);
VL_RESTORER(m_ifaceInstCells);
m_modp = modp;
m_ifacePortNames.clear();
m_ifaceInstNames.clear();
m_ifaceInstCells.clear();
iterateChildren(modp);
}
}
@ -2723,7 +2724,7 @@ class ParamVisitor final : public VNVisitor {
const string refname = getRefBaseName(refp);
isIfaceRef
= !refname.empty()
&& (m_ifacePortNames.count(refname) || m_ifaceInstNames.count(refname));
&& (m_ifacePortNames.count(refname) || m_ifaceInstCells.count(refname));
}
if (!isIfaceRef) {
@ -2780,7 +2781,7 @@ class ParamVisitor final : public VNVisitor {
const string refname = getRefBaseName(refp);
if (!refname.empty()
&& (m_ifacePortNames.count(refname)
|| m_ifaceInstNames.count(refname)))
|| m_ifaceInstCells.count(refname)))
return true;
}
}
@ -2879,8 +2880,7 @@ class ParamVisitor final : public VNVisitor {
}
void visit(AstCell* nodep) override {
checkParamNotHier(nodep->paramsp());
// Build cache of locally declared interface instance names
if (VN_IS(nodep->modp(), Iface)) { m_ifaceInstNames.insert(nodep->name()); }
if (VN_IS(nodep->modp(), Iface)) m_ifaceInstCells.emplace(nodep->name(), nodep);
visitCellOrClassRef(nodep, VN_IS(nodep->modp(), Iface));
}
void visit(AstIfaceRefDType* nodep) override {
@ -2984,6 +2984,21 @@ class ParamVisitor final : public VNVisitor {
}
return false;
}
void deparamIfaceCellNow(AstCell* cellp) {
if (!cellp->paramsp()) return;
if (!VN_IS(cellp->modp(), Iface)) return;
AstNodeModule* const srcModp = cellp->modp();
AstNodeModule* const newModp
= m_processor.nodeDeparam(cellp, srcModp, m_modp, m_modp->someInstanceName());
if (newModp && newModp != srcModp) {
if (V3LinkDotIfaceCapture::enabled()) {
m_processor.retargetIfaceRefs(m_modp, cellp->name());
}
specializeNestedIfaceCells(newModp);
}
}
void visit(AstNodeFTaskRef* nodep) override {
if (nodep->containsGenBlock()) {
// Needs relink, as may remove pointed-to task/func
@ -3034,9 +3049,10 @@ class ParamVisitor final : public VNVisitor {
}
}
// Interfaces declared in this module have cells
else if (const AstCell* const cellp = ifacerefp->cellp()) {
else if (AstCell* const cellp = ifacerefp->cellp()) {
if (dotted == cellp->name()) {
UINFO(9, "Iface matching scope: " << cellp);
deparamIfaceCellNow(cellp);
if (ifaceParamReplace(nodep, cellp->modp()->stmtsp())) { //
return;
}
@ -3044,6 +3060,13 @@ class ParamVisitor final : public VNVisitor {
}
}
}
// Fallback: a direct local interface instance ("inst.PARAM"), not reached via a port.
const auto ifaceCellIt = m_ifaceInstCells.find(dotted);
if (ifaceCellIt != m_ifaceInstCells.end()) {
AstCell* const cellp = ifaceCellIt->second;
deparamIfaceCellNow(cellp);
ifaceParamReplace(nodep, cellp->modp()->stmtsp());
}
}
}

View File

@ -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_st")
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,58 @@
// DESCRIPTION: Verilator: Get parameter from modport interface
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// 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 intf #(
parameter int ITEM_QTY = 1
);
logic item;
modport source(input item);
endinterface
module pass_through (
intf.source in_port,
output logic [31:0] item_qty
);
intf #(
.ITEM_QTY(in_port.ITEM_QTY)
) internal_port ();
if (internal_port.ITEM_QTY == 1) begin : g_saw_default_item_qty
$error("generate if evaluated internal_port.ITEM_QTY as interface default 1");
end
else if (internal_port.ITEM_QTY != 20) begin : g_bad_item_qty
$error("generate if evaluated internal_port.ITEM_QTY as neither 1 nor 20");
end
assign internal_port.item = in_port.item;
assign item_qty = internal_port.ITEM_QTY + internal_port.item;
endmodule
module t;
intf #(
.ITEM_QTY(20)
) in_port ();
logic [31:0] item_qty;
assign in_port.item = 1'b0;
pass_through dut (
.in_port (in_port),
.item_qty(item_qty)
);
initial begin
`checkd(item_qty, 20);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule