From 49e2002704e212623591e5fd9d321d3a2c93e2fb Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Thu, 18 Jun 2026 14:28:49 +0200 Subject: [PATCH] Support method calls on a sub-interface via a virtual interface --- src/V3Width.cpp | 36 ++++++---- ...> t_interface_virtual_sub_iface_method.py} | 6 +- .../t/t_interface_virtual_sub_iface_method.v | 68 +++++++++++++++++++ ...interface_virtual_sub_iface_method_bad.out | 6 -- ...t_interface_virtual_sub_iface_method_bad.v | 33 --------- 5 files changed, 96 insertions(+), 53 deletions(-) rename test_regress/t/{t_interface_virtual_sub_iface_method_bad.py => t_interface_virtual_sub_iface_method.py} (83%) create mode 100644 test_regress/t/t_interface_virtual_sub_iface_method.v delete mode 100644 test_regress/t/t_interface_virtual_sub_iface_method_bad.out delete mode 100644 test_regress/t/t_interface_virtual_sub_iface_method_bad.v diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 6f541236b..b97837a48 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -3789,7 +3789,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; @@ -3830,7 +3830,26 @@ 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. + AstIfaceRefDType* const subRefp + = VN_CAST(subDtypep->skipRefp(), IfaceRefDType); + if (subRefp) { + 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); @@ -4781,16 +4800,9 @@ class WidthVisitor final : public VNVisitor { } } void methodCallIfaceRef(AstMethodCall* nodep, AstIfaceRefDType* adtypep) { - AstIface* const ifacep = adtypep->ifacep(); - if (!ifacep) { - // A sub-interface reached through a virtual interface handle has a - // ref dtype with cellp set but ifacep null; the per-instance binding - // is not modelled, so the call cannot be resolved to one instance. - nodep->v3warn(E_UNSUPPORTED, "Unsupported: method call on a sub-interface " - "accessed through a virtual interface"); - nodep->dtypeSetVoid(); - return; - } + // 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)) { diff --git a/test_regress/t/t_interface_virtual_sub_iface_method_bad.py b/test_regress/t/t_interface_virtual_sub_iface_method.py similarity index 83% rename from test_regress/t/t_interface_virtual_sub_iface_method_bad.py rename to test_regress/t/t_interface_virtual_sub_iface_method.py index 38cf36b43..8a938befd 100755 --- a/test_regress/t/t_interface_virtual_sub_iface_method_bad.py +++ b/test_regress/t/t_interface_virtual_sub_iface_method.py @@ -9,8 +9,10 @@ import vltest_bootstrap -test.scenarios('linter') +test.scenarios('simulator') -test.lint(fails=True, expect_filename=test.golden_filename) +test.compile() + +test.execute() test.passes() diff --git a/test_regress/t/t_interface_virtual_sub_iface_method.v b/test_regress/t/t_interface_virtual_sub_iface_method.v new file mode 100644 index 000000000..709e99c9f --- /dev/null +++ b/test_regress/t/t_interface_virtual_sub_iface_method.v @@ -0,0 +1,68 @@ +// 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) + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_virtual_sub_iface_method_bad.out b/test_regress/t/t_interface_virtual_sub_iface_method_bad.out deleted file mode 100644 index 5a9ec7a0b..000000000 --- a/test_regress/t/t_interface_virtual_sub_iface_method_bad.out +++ /dev/null @@ -1,6 +0,0 @@ -%Error-UNSUPPORTED: t/t_interface_virtual_sub_iface_method_bad.v:21:16: Unsupported: method call on a sub-interface accessed through a virtual interface - : ... note: In instance 't' - 21 | vif.sub_if.set_active(); - | ^~~~~~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to diff --git a/test_regress/t/t_interface_virtual_sub_iface_method_bad.v b/test_regress/t/t_interface_virtual_sub_iface_method_bad.v deleted file mode 100644 index 723751c03..000000000 --- a/test_regress/t/t_interface_virtual_sub_iface_method_bad.v +++ /dev/null @@ -1,33 +0,0 @@ -// 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 - -interface inner_if; - logic active = 1'b0; - function automatic void set_active(); - active = 1'b1; - endfunction -endinterface - -interface outer_if; - inner_if sub_if (); -endinterface - -class cfg_c; - virtual outer_if vif; - function void doit(); - vif.sub_if.set_active(); - endfunction -endclass - -module t; - outer_if oif (); - cfg_c cfg; - initial begin - cfg = new(); - cfg.vif = oif; - cfg.doit(); - end -endmodule