Fixes #7203.
This commit is contained in:
parent
e7a644a3fc
commit
efd60df2be
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue