Fixes #7203.
This commit is contained in:
parent
e7a644a3fc
commit
efd60df2be
|
|
@ -1517,7 +1517,13 @@ public:
|
||||||
void visit(AstMemberSel* nodep) override {
|
void visit(AstMemberSel* nodep) override {
|
||||||
iterateAndNextConstNull(nodep->fromp());
|
iterateAndNextConstNull(nodep->fromp());
|
||||||
putnbs(nodep, "->");
|
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 {
|
void visit(AstStructSel* nodep) override {
|
||||||
iterateAndNextConstNull(nodep->fromp());
|
iterateAndNextConstNull(nodep->fromp());
|
||||||
|
|
|
||||||
|
|
@ -3574,6 +3574,32 @@ class WidthVisitor final : public VNVisitor {
|
||||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
return;
|
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);
|
UINFO(1, "found object " << foundp);
|
||||||
nodep->v3fatalSrc("MemberSel of non-variable\n"
|
nodep->v3fatalSrc("MemberSel of non-variable\n"
|
||||||
<< nodep->warnContextPrimary() << '\n'
|
<< nodep->warnContextPrimary() << '\n'
|
||||||
|
|
@ -3695,12 +3721,14 @@ class WidthVisitor final : public VNVisitor {
|
||||||
AstNode* memberSelIface(AstMemberSel* nodep, AstIfaceRefDType* adtypep) {
|
AstNode* memberSelIface(AstMemberSel* nodep, AstIfaceRefDType* adtypep) {
|
||||||
// Returns node if ok
|
// Returns node if ok
|
||||||
// No need to width-resolve the interface, as it was done when we did the child
|
// 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");
|
UASSERT_OBJ(ifacep, nodep, "Unlinked");
|
||||||
VSpellCheck speller;
|
VSpellCheck speller;
|
||||||
for (AstNode* itemp = ifacep->stmtsp(); itemp; itemp = itemp->nextp()) {
|
for (AstNode* itemp = ifacep->stmtsp(); itemp; itemp = itemp->nextp()) {
|
||||||
if (itemp->name() == nodep->name()) return itemp;
|
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());
|
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