Support method calls on a sub-interface via a virtual interface (#7800)
This commit is contained in:
parent
749b93e405
commit
59fba72cb6
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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,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
|
||||
Loading…
Reference in New Issue