Fix modport selection of virtual interface handle (#7321)

This commit is contained in:
Yilou Wang 2026-03-25 12:16:52 +01:00 committed by GitHub
parent 860ac67c04
commit 728ddf3331
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 142 additions and 0 deletions

View File

@ -3522,6 +3522,23 @@ class WidthVisitor final : public VNVisitor {
nodep->didWidth(true);
return;
}
if (AstModport* const modportp = VN_CAST(foundp, Modport)) {
// Modport selection (e.g. vif.passive_mp) is compile-time
// type narrowing: replace MemberSel with fromp re-typed.
AstIfaceRefDType* const newDtypep = new AstIfaceRefDType{
nodep->fileline(), nodep->fileline(), adtypep->cellName(),
adtypep->ifaceName(), modportp->name()};
newDtypep->ifacep(adtypep->ifacep());
newDtypep->cellp(adtypep->cellp());
newDtypep->modportp(modportp);
newDtypep->isVirtual(adtypep->isVirtual());
v3Global.rootp()->typeTablep()->addTypesp(newDtypep);
AstNodeExpr* const fromp = nodep->fromp()->unlinkFrBack();
fromp->dtypep(newDtypep);
nodep->replaceWith(fromp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
UINFO(1, "found object " << foundp);
nodep->v3fatalSrc("MemberSel of non-variable\n"
<< nodep->warnContextPrimary() << '\n'

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(verilator_flags2=["--binary", "--timing"])
test.execute()
test.passes()

View File

@ -0,0 +1,107 @@
// 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 checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
// verilog_format: on
// Modport selection on virtual interface handles:
// vif.modport_name (direct)
// obj.vif.modport_name (chained through class member)
interface my_if (input logic clk);
logic [7:0] data;
clocking mon_cb @(posedge clk);
input data;
endclocking
modport passive_mp (clocking mon_cb);
modport active_mp (output data);
modport signal_mp (input data);
endinterface
class Context;
virtual my_if vif;
endclass
class Monitor;
virtual my_if.passive_mp mp;
function void connect_chain(Context cntxt);
mp = cntxt.vif.passive_mp;
endfunction
function void connect_tmp(Context cntxt);
automatic virtual my_if tmp = cntxt.vif;
mp = tmp.passive_mp;
endfunction
endclass
class Driver;
virtual my_if.active_mp drv;
function void connect(Context cntxt);
drv = cntxt.vif.active_mp;
endfunction
task drive(input logic [7:0] val);
drv.data = val;
endtask
endclass
class Reader;
virtual my_if.signal_mp sig;
function void connect(Context cntxt);
sig = cntxt.vif.signal_mp;
endfunction
endclass
module t;
logic clk = 0;
always #5 clk = ~clk;
my_if vif (.clk(clk));
initial begin
automatic Context c = new;
automatic Monitor m1 = new;
automatic Monitor m2 = new;
automatic Driver d = new;
automatic Reader r = new;
c.vif = vif;
// Connect via chain and tmp paths
m1.connect_chain(c);
m2.connect_tmp(c);
d.connect(c);
r.connect(c);
// Drive data through active_mp, read through passive_mp and signal_mp
d.drive(8'hAB);
@(posedge clk);
@(posedge clk);
// Verify clocking-block modport (m1 = chain, m2 = tmp)
`checkh(m1.mp.mon_cb.data, 8'hAB);
`checkh(m2.mp.mon_cb.data, 8'hAB);
// Verify signal modport (no clocking block)
`checkh(r.sig.data, 8'hAB);
// Drive new value, verify update propagates
d.drive(8'hCD);
@(posedge clk);
@(posedge clk);
`checkh(m1.mp.mon_cb.data, 8'hCD);
`checkh(r.sig.data, 8'hCD);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule