Fixes #7454.
This commit is contained in:
parent
c460f0e6a0
commit
5d1b4fe8a8
|
|
@ -3609,7 +3609,36 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
while (const AstNodePreSel* const preSelp = VN_CAST(exprp, NodePreSel)) {
|
||||
exprp = preSelp->fromp();
|
||||
}
|
||||
if (const AstVarRef* const varRefp = VN_CAST(exprp, VarRef)) {
|
||||
// Resolve pin expression to the enclosing module's port Var. At primary
|
||||
// LinkDot the expression may still be an AstParseRef, so also look up by name.
|
||||
AstVar* enclosingVarp = nullptr;
|
||||
const AstVarRef* const varRefp = VN_CAST(exprp, VarRef);
|
||||
if (varRefp) {
|
||||
enclosingVarp = varRefp->varp();
|
||||
} else if (const AstParseRef* const parseRefp = VN_CAST(exprp, ParseRef)) {
|
||||
if (m_modp) {
|
||||
if (VSymEnt* const symp
|
||||
= m_statep->getNodeSym(m_modp)->findIdFlat(parseRefp->name())) {
|
||||
enclosingVarp = VN_CAST(symp->nodep(), Var);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (enclosingVarp && enclosingVarp->varType() == VVarType::IFACEREF
|
||||
&& VN_IS(enclosingVarp->childDTypep()->skipRefp(), IfaceGenericDType)) {
|
||||
// Nested generic-iface forwarding (#7454): enclosing port is itself still
|
||||
// generic, so emit a placeholder __VGIfaceParam pin carrying a VarRef to
|
||||
// the outer port. V3Param rewrites it to the concrete IfaceRefDType once
|
||||
// the enclosing module is specialized (see
|
||||
// ParamProcessor::resolveGenericIfaceForwardingPins for the ordering
|
||||
// constraint that keeps the rewrite inside V3Param).
|
||||
AstVarRef* const fwdRefp
|
||||
= new AstVarRef{exprp->fileline(), enclosingVarp, VAccess::READ};
|
||||
AstPin* const newPinp = new AstPin{
|
||||
pinp->fileline(), paramNum, "__VGIfaceParam" + modIfaceVarp->name(), fwdRefp};
|
||||
newPinp->param(true);
|
||||
visit(newPinp);
|
||||
nodep->addParamsp(newPinp);
|
||||
} else if (varRefp) {
|
||||
const AstVar* const varp = varRefp->varp();
|
||||
if (const AstIfaceRefDType* const refp
|
||||
= VN_CAST(getElemDTypep(varp->childDTypep()), IfaceRefDType)) {
|
||||
|
|
|
|||
|
|
@ -1360,6 +1360,40 @@ class ParamProcessor final {
|
|||
return isEq.isNeqZero();
|
||||
}
|
||||
|
||||
// Rewrite placeholder __VGIfaceParam pins emitted by
|
||||
// LinkDotResolveVisitor::addImplicitParametersOfGenericIface for a nested
|
||||
// generic-interface forwarding chain (#7454). Must run here (not in
|
||||
// V3LinkDot's Param/Resolve visitor at LDS_PARAMED) because:
|
||||
// * linkDotParamed runs AFTER V3Param, at which point cellDeparam has
|
||||
// already consumed the pin via moduleFindOrClone (variant naming off
|
||||
// pinp->exprp()'s IfaceRefDType) and genericInterfaceVarSetup (hard
|
||||
// VN_AS cast to IfaceRefDType); and
|
||||
// * the enclosing module's port only becomes a concrete IfaceRefDType
|
||||
// after its own nodeDeparam has specialized it. That specialization
|
||||
// happens in V3Param's top-down walk, so the window between "outer
|
||||
// port resolved" and "inner cellDeparam starts" exists only here.
|
||||
void resolveGenericIfaceForwardingPins(AstPin* paramsp) {
|
||||
for (AstPin* pinp = paramsp; pinp; pinp = VN_AS(pinp->nextp(), Pin)) {
|
||||
if (!pinp->exprp()) continue;
|
||||
if (!VString::startsWith(pinp->name(), "__VGIfaceParam")) continue;
|
||||
const AstNodeVarRef* const fwdRefp = VN_CAST(pinp->exprp(), NodeVarRef);
|
||||
if (!fwdRefp) continue;
|
||||
const AstIfaceRefDType* const resolvedp
|
||||
= VN_CAST(fwdRefp->varp()->childDTypep()->skipRefp(), IfaceRefDType);
|
||||
UASSERT_OBJ(resolvedp, pinp,
|
||||
"Generic interface forwarding pin not specialized before use");
|
||||
AstIfaceRefDType* const newIrefp = new AstIfaceRefDType{
|
||||
resolvedp->fileline(), resolvedp->modportFileline(), resolvedp->cellName(),
|
||||
resolvedp->ifaceName(), resolvedp->modportName()};
|
||||
newIrefp->ifacep(resolvedp->ifacep());
|
||||
if (resolvedp->paramsp()) {
|
||||
newIrefp->addParamsp(resolvedp->paramsp()->cloneTree(true));
|
||||
}
|
||||
pinp->exprp()->unlinkFrBack()->deleteTree();
|
||||
pinp->exprp(newIrefp);
|
||||
}
|
||||
}
|
||||
|
||||
void cellPinCleanup(AstNode* nodep, AstPin* pinp, AstPin* paramsp, AstNodeModule* srcModp,
|
||||
string& longnamer, bool& any_overridesr) {
|
||||
if (!pinp->exprp()) return; // No-connect
|
||||
|
|
@ -2114,6 +2148,10 @@ public:
|
|||
}
|
||||
pinp = nextp;
|
||||
}
|
||||
// Nested generic-iface forwarding (#7454): rewrite VarRef placeholders
|
||||
// left by V3LinkDot to concrete IfaceRefDTypes now that the enclosing
|
||||
// module has been specialized.
|
||||
resolveGenericIfaceForwardingPins(cellp->paramsp());
|
||||
}
|
||||
// Create new module name with _'s between the constants
|
||||
UINFOTREE(10, nodep, "", "cell");
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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 (interface 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
|
||||
Loading…
Reference in New Issue