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)) {
|
while (const AstNodePreSel* const preSelp = VN_CAST(exprp, NodePreSel)) {
|
||||||
exprp = preSelp->fromp();
|
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();
|
const AstVar* const varp = varRefp->varp();
|
||||||
if (const AstIfaceRefDType* const refp
|
if (const AstIfaceRefDType* const refp
|
||||||
= VN_CAST(getElemDTypep(varp->childDTypep()), IfaceRefDType)) {
|
= VN_CAST(getElemDTypep(varp->childDTypep()), IfaceRefDType)) {
|
||||||
|
|
|
||||||
|
|
@ -1360,6 +1360,40 @@ class ParamProcessor final {
|
||||||
return isEq.isNeqZero();
|
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,
|
void cellPinCleanup(AstNode* nodep, AstPin* pinp, AstPin* paramsp, AstNodeModule* srcModp,
|
||||||
string& longnamer, bool& any_overridesr) {
|
string& longnamer, bool& any_overridesr) {
|
||||||
if (!pinp->exprp()) return; // No-connect
|
if (!pinp->exprp()) return; // No-connect
|
||||||
|
|
@ -2114,6 +2148,10 @@ public:
|
||||||
}
|
}
|
||||||
pinp = nextp;
|
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
|
// Create new module name with _'s between the constants
|
||||||
UINFOTREE(10, nodep, "", "cell");
|
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