Fix parameter read through locally-declared interface instance (#7679)
This commit is contained in:
parent
3d126b77cd
commit
5d344ab8ff
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue