Fix virtual interface select from sub-interface instance (#7203) (#7370)

Fixes #7203.
This commit is contained in:
Yilou Wang 2026-04-04 01:04:10 +02:00 committed by GitHub
parent e7a644a3fc
commit efd60df2be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 129 additions and 3 deletions

View File

@ -1517,7 +1517,13 @@ public:
void visit(AstMemberSel* nodep) override {
iterateAndNextConstNull(nodep->fromp());
putnbs(nodep, "->");
puts(nodep->varp()->nameProtect());
if (nodep->varp()->isIfaceRef()) {
// varp is the __Viftop companion (e.g. "tx__Viftop"); use the
// MemberSel name which matches the cell's C++ member (e.g. "tx").
puts(nodep->nameProtect());
} else {
puts(nodep->varp()->nameProtect());
}
}
void visit(AstStructSel* nodep) override {
iterateAndNextConstNull(nodep->fromp());

View File

@ -3574,6 +3574,32 @@ class WidthVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
if (AstCell* const cellp = VN_CAST(foundp, Cell)) {
// Sub-interface cell selection (e.g. vif.tx): resolve to the
// companion __Viftop var created by V3LinkCells for its dtype.
if (VN_IS(cellp->modp(), Iface)) {
const string viftopName = cellp->name() + "__Viftop";
AstNodeModule* const parentIfacep = adtypep->ifaceViaCellp();
AstVar* viftopVarp = nullptr;
for (AstNode* itemp = parentIfacep->stmtsp(); itemp;
itemp = itemp->nextp()) {
if (AstVar* const vp = VN_CAST(itemp, Var)) {
if (vp->name() == viftopName) {
viftopVarp = vp;
break;
}
}
}
UASSERT_OBJ(viftopVarp, nodep,
"No __Viftop variable for sub-interface cell");
if (!viftopVarp->didWidth()) userIterate(viftopVarp, nullptr);
nodep->dtypep(viftopVarp->dtypep());
nodep->varp(viftopVarp);
viftopVarp->sensIfacep(VN_AS(cellp->modp(), Iface));
nodep->didWidth(true);
return;
}
}
UINFO(1, "found object " << foundp);
nodep->v3fatalSrc("MemberSel of non-variable\n"
<< nodep->warnContextPrimary() << '\n'
@ -3695,12 +3721,14 @@ class WidthVisitor final : public VNVisitor {
AstNode* memberSelIface(AstMemberSel* nodep, AstIfaceRefDType* adtypep) {
// Returns node if ok
// No need to width-resolve the interface, as it was done when we did the child
AstNodeModule* const ifacep = adtypep->ifacep();
// ifaceViaCellp() handles dtypes with cellp-only (no ifacep), as produced
// by sub-interface selection, enabling chained access (e.g. vif.tx.Tx).
AstNodeModule* const ifacep = adtypep->ifaceViaCellp();
UASSERT_OBJ(ifacep, nodep, "Unlinked");
VSpellCheck speller;
for (AstNode* itemp = ifacep->stmtsp(); itemp; itemp = itemp->nextp()) {
if (itemp->name() == nodep->name()) return itemp;
if (VN_IS(itemp, Var) || VN_IS(itemp, Modport)) {
if (VN_IS(itemp, Var) || VN_IS(itemp, Modport) || VN_IS(itemp, Cell)) {
speller.pushCandidate(itemp->prettyName());
}
}

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')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,74 @@
// 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
// Issue #7203: virtual interface select from sub-interface instance.
// The original reproducer: vip_agent holds vip_vif; vip_driver selects
// agent.vif.tx (a vip_tx_if sub-interface) into tx_vif.
interface vip_tx_if (
output reg Tx
);
endinterface
interface vip_if (
output reg Tx
);
vip_tx_if tx (Tx);
endinterface
package vip_pkg;
typedef virtual vip_if vip_vif;
typedef virtual vip_tx_if vip_tx_vif;
class vip_agent;
vip_vif vif;
endclass
class vip_driver;
vip_vif vif;
vip_tx_vif tx_vif;
virtual function void build_phase(vip_agent agent);
// Sub-interface select: dtype(agent.vif) -> vip_vif -> vip_if
vif = agent.vif;
tx_vif = agent.vif.tx;
endfunction
// Chained member access through sub-interface
virtual function void drive(logic val);
vif.tx.Tx = val;
endfunction
endclass
endpackage
module t;
logic wire_Tx;
vip_if vif_inst (.Tx(wire_Tx));
initial begin
automatic vip_pkg::vip_agent agent = new;
automatic vip_pkg::vip_driver driver = new;
agent.vif = vif_inst;
driver.vif = vif_inst;
// Test 1 (issue reproducer): sub-interface select compiles and runs
driver.build_phase(agent);
// Test 2: tx_vif now points to the sub-interface; write through it
driver.tx_vif.Tx = 1'b1;
`checkd(wire_Tx, 1'b1)
// Test 3: chained member write through virtual interface
driver.drive(1'b0);
`checkd(wire_Tx, 1'b0)
$write("*-* All Finished *-*\n");
$finish;
end
endmodule