Support method calls on a sub-interface via a virtual interface (#7800)

This commit is contained in:
Yilou Wang 2026-06-19 14:41:48 +02:00 committed by GitHub
parent 749b93e405
commit 59fba72cb6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 112 additions and 3 deletions

View File

@ -3811,7 +3811,7 @@ class WidthVisitor final : public VNVisitor {
if (!varp->didWidth()) userIterate(varp, nullptr);
nodep->dtypep(foundp->dtypep());
nodep->varp(varp);
AstIface* const ifacep = adtypep->ifacep();
AstIface* const ifacep = adtypep->ifaceViaCellp();
varp->sensIfacep(ifacep);
nodep->didWidth(true);
return;
@ -3852,7 +3852,25 @@ class WidthVisitor final : public VNVisitor {
UASSERT_OBJ(viftopVarp, nodep,
"No __Viftop variable for sub-interface cell");
if (!viftopVarp->didWidth()) userIterate(viftopVarp, nullptr);
nodep->dtypep(viftopVarp->dtypep());
AstNodeDType* subDtypep = viftopVarp->dtypep();
if (adtypep->isVirtual()) {
// A sub-interface selected through a virtual interface
// handle is itself a virtual reference (a runtime
// instance pointer), not a static instance. Mark the
// dtype virtual so a method call on it dispatches per
// instance instead of inlining to one fixed scope.
if (AstIfaceRefDType* const subRefp
= VN_CAST(subDtypep->skipRefp(), IfaceRefDType)) {
AstIfaceRefDType* const newDtypep = new AstIfaceRefDType{
nodep->fileline(), subRefp->cellName(), subRefp->ifaceName()};
newDtypep->ifacep(subRefp->ifacep());
newDtypep->cellp(subRefp->cellp());
newDtypep->isVirtual(true);
v3Global.rootp()->typeTablep()->addTypesp(newDtypep);
subDtypep = newDtypep;
}
}
nodep->dtypep(subDtypep);
nodep->varp(viftopVarp);
viftopVarp->sensIfacep(VN_AS(cellp->modp(), Iface));
nodep->didWidth(true);
@ -4803,7 +4821,9 @@ class WidthVisitor final : public VNVisitor {
}
}
void methodCallIfaceRef(AstMethodCall* nodep, AstIfaceRefDType* adtypep) {
AstIface* const ifacep = adtypep->ifacep();
// ifaceViaCellp() resolves the interface via cellp when ifacep is null,
// as for a sub-interface selected through a virtual interface handle.
AstIface* const ifacep = adtypep->ifaceViaCellp();
UINFO(5, __FUNCTION__ << ":" << nodep);
if (AstNodeFTask* const ftaskp
= VN_CAST(m_memberMap.findMember(ifacep, nodep->name()), NodeFTask)) {

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,71 @@
// 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 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 inner_if;
logic [7:0] cnt = 0;
function automatic void bump();
cnt = cnt + 1;
endfunction
function automatic logic [7:0] get();
return cnt;
endfunction
task automatic addn(input logic [7:0] n);
cnt = cnt + n;
endtask
endinterface
interface mid_if;
inner_if sub ();
endinterface
interface data_if;
logic [7:0] val = 0;
endinterface
interface outer_if;
inner_if a ();
inner_if b ();
mid_if m ();
data_if d ();
endinterface
class driver_c;
virtual outer_if vif;
task run();
vif.a.bump(); // sibling a: void function, +1
vif.b.bump(); // sibling b: void function, +1
vif.b.addn(8'd5); // sibling b: task with arg, +5
vif.m.sub.bump(); // two-level nesting, +1
vif.d.val = 8'd99; // sub-interface variable write via vif (no method)
endtask
function logic [7:0] read_a();
return vif.a.get(); // function returning value via vif sub-interface
endfunction
endclass
module t;
outer_if oif ();
driver_c drv;
initial begin
drv = new();
drv.vif = oif;
drv.run();
// Per-instance dispatch: a, b and m.sub are distinct instances.
`checkd(oif.a.cnt, 8'd1)
`checkd(oif.b.cnt, 8'd6)
`checkd(oif.m.sub.cnt, 8'd1)
`checkd(drv.read_a(), 8'd1)
`checkd(oif.d.val, 8'd99)
// Direct (non-virtual) sub-interface method call: covers the non-virtual dtype path.
oif.a.bump();
`checkd(oif.a.cnt, 8'd2)
$write("*-* All Finished *-*\n");
$finish;
end
endmodule