Fix virtual interface member propagation (#6175) (#6184)

This commit is contained in:
Yilou Wang 2025-07-18 15:07:31 +02:00 committed by GitHub
parent a21ecb2ab9
commit 9b99d9697f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 230 additions and 20 deletions

View File

@ -104,6 +104,17 @@ void invertAndMergeSenTreeMap(
for (const auto& pair : senTreeMap) result.emplace(pair.second, pair.first);
}
AstSenTree* findTriggeredIface(const AstVarScope* vscp,
const VirtIfaceTriggers::IfaceSensMap& vifTrigged,
const VirtIfaceTriggers::IfaceMemberSensMap& vifMemberTriggered) {
const auto ifaceIt = vifTrigged.find(vscp->varp()->sensIfacep());
if (ifaceIt != vifTrigged.end()) return ifaceIt->second;
for (const auto& memberIt : vifMemberTriggered) {
if (memberIt.first.m_ifacep == vscp->varp()->sensIfacep()) { return memberIt.second; }
}
return nullptr;
}
//============================================================================
// Code generation utility functions
@ -955,8 +966,10 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp,
AstSenTree* const dpiExportTriggered
= dpiExportTriggerVscp ? createTriggerSenTree(netlistp, trig.m_vscp, dpiExportTriggerIndex)
: nullptr;
const auto& vifTriggered
const auto& vifTriggeredIco
= virtIfaceTriggers.makeIfaceToSensMap(netlistp, firstVifTriggerIndex, trig.m_vscp);
const auto& vifMemberTriggeredIco
= virtIfaceTriggers.makeMemberToSensMap(netlistp, firstVifTriggerIndex, trig.m_vscp);
// Create and Order the body function
AstCFunc* const icoFuncp
@ -968,8 +981,9 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp,
}
if (varp->isWrittenByDpi()) out.push_back(dpiExportTriggered);
if (vscp->varp()->sensIfacep()) {
const auto it = vifTriggered.find(vscp->varp()->sensIfacep());
if (it != vifTriggered.end()) out.push_back(it->second);
AstSenTree* ifaceTriggered = findTriggeredIface(
vscp, vifTriggeredIco, vifMemberTriggeredIco);
out.push_back(ifaceTriggered);
}
});
splitCheck(icoFuncp);
@ -1371,6 +1385,8 @@ void schedule(AstNetlist* netlistp) {
const auto& vifTriggeredAct
= virtIfaceTriggers.makeIfaceToSensMap(netlistp, firstVifTriggerIndex, actTrig.m_vscp);
const auto& vifMemberTriggeredAct
= virtIfaceTriggers.makeMemberToSensMap(netlistp, firstVifTriggerIndex, actTrig.m_vscp);
AstCFunc* const actFuncp = V3Order::order(
netlistp, {&logicRegions.m_pre, &logicRegions.m_act, &logicReplicas.m_act}, trigToSenAct,
@ -1379,8 +1395,9 @@ void schedule(AstNetlist* netlistp) {
if (it != actTimingDomains.end()) out = it->second;
if (vscp->varp()->isWrittenByDpi()) out.push_back(dpiExportTriggeredAct);
if (vscp->varp()->sensIfacep()) {
const auto sit = vifTriggeredAct.find(vscp->varp()->sensIfacep());
if (sit != vifTriggeredAct.end()) out.push_back(sit->second);
AstSenTree* ifaceTriggered
= findTriggeredIface(vscp, vifTriggeredAct, vifMemberTriggeredAct);
out.push_back(ifaceTriggered);
}
});
splitCheck(actFuncp);
@ -1408,6 +1425,8 @@ void schedule(AstNetlist* netlistp) {
: nullptr;
const auto& vifTriggered
= virtIfaceTriggers.makeIfaceToSensMap(netlistp, firstVifTriggerIndex, trigVscp);
const auto& vifMemberTriggered
= virtIfaceTriggers.makeMemberToSensMap(netlistp, firstVifTriggerIndex, trigVscp);
const auto& timingDomains = timingKit.remapDomains(trigMap);
AstCFunc* const funcp = V3Order::order(
@ -1417,8 +1436,9 @@ void schedule(AstNetlist* netlistp) {
if (it != timingDomains.end()) out = it->second;
if (vscp->varp()->isWrittenByDpi()) out.push_back(dpiExportTriggered);
if (vscp->varp()->sensIfacep()) {
const auto sit = vifTriggered.find(vscp->varp()->sensIfacep());
if (sit != vifTriggered.end()) out.push_back(sit->second);
AstSenTree* ifaceTriggered
= findTriggeredIface(vscp, vifTriggered, vifMemberTriggered);
out.push_back(ifaceTriggered);
}
});

View File

@ -168,6 +168,7 @@ class VirtIfaceTriggers final {
}
};
public:
using IfaceMemberTrigger = std::pair<IfaceMember, AstVarScope*>;
using IfaceMemberTriggerVec = std::vector<IfaceMemberTrigger>;
using IfaceMemberSensMap = std::map<IfaceMember, AstSenTree*>;
@ -179,7 +180,6 @@ class VirtIfaceTriggers final {
IfaceMemberTriggerVec m_memberTriggers;
IfaceTriggerVec m_ifaceTriggers;
public:
void addMemberTrigger(const AstIface* ifacep, const AstVar* memberVarp,
AstVarScope* triggerVscp) {
m_memberTriggers.emplace_back(IfaceMember(ifacep, memberVarp), triggerVscp);

View File

@ -10,6 +10,7 @@ interface INTF;
logic x;
logic y;
logic z;
logic [7:0] data;
endinterface
class Dummy;
@ -17,28 +18,61 @@ class Dummy;
function new(virtual INTF vif);
this.vif = vif;
endfunction
task write_data(logic [7:0] d);
vif.data = d;
endtask
endclass
module t_virtual_interface_member_trigger();
logic s1, src_val;
logic s2;
INTF vintf();
assign vintf.x = s1;
assign vintf.y = src_val;
assign vintf.z = !vintf.y;
assign s2 = vintf.z;
// === Part 1: logic trigger false loop test ===
logic s1, s2, src_val;
INTF intf_loop();
virtual INTF vif_loop;
assign intf_loop.x = s1;
assign intf_loop.y = src_val;
assign intf_loop.z = !intf_loop.y;
assign s2 = intf_loop.z;
assign s1 = s2;
Dummy d;
// === Part 2: data transfer chain test ===
logic [7:0] data;
INTF intf_read();
INTF intf_write();
assign intf_read.data = data;
assign data = intf_write.data;
virtual INTF vif_read, vif_write;
Dummy cl_1, cl_2;
initial begin
d = new(vintf);
// Test 1: no false loop with member-level trigger
#1ns;
vif_loop = intf_loop;
cl_1 = new(vif_loop);
#1ns;
src_val = 0;
#1ns;
if (!(d.vif.x == 1 && d.vif.y == 0 && d.vif.z == 1 && s1 == 1 && s2 == 1)) $stop;
if (!(cl_1.vif.x == 1 && cl_1.vif.y == 0 && cl_1.vif.z == 1 && s1 == 1 && s2 == 1)) $stop;
// Test 2: write from module
#1ns;
vif_read = intf_read;
vif_write = intf_write;
#1ns;
vif_write.data = 8'hA5;
#1ns;
if (vif_read.data !== 8'hA5) $stop;
// Test 3: write from class
#1ns;
cl_2 = new(vif_write);
#1ns;
cl_2.write_data(8'hB7);
#1ns;
if (vif_read.data !== 8'hB7) $stop;
#5ns;
$write("*-* All Finished *-*\n");
$finish;
end

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=['--binary'])
test.execute()
test.passes()

View File

@ -0,0 +1,138 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by PlanV GmbH.
// SPDX-License-Identifier: CC0-1.0
`timescale 1ns/1ps
interface INTF();
logic clk;
logic [7:0] data;
logic valid;
logic ready;
endinterface
time TA = 5ns;
class intf_driver;
virtual INTF intf;
function new(virtual INTF intf);
this.intf = intf;
endfunction
task cycle_start();
#TA;
endtask
task cycle_end();
@(posedge intf.clk);
endtask
task init_master();
intf.data = '0;
intf.valid = 0;
endtask
task init_slave();
intf.ready = 0;
endtask
task recv_data(output logic [7:0] data);
intf.ready <= #TA 1;
cycle_start();
while (!(intf.valid && intf.ready)) begin cycle_end(); cycle_start(); end
cycle_end();
data = intf.data;
intf.ready <= #TA 0;
endtask
task send_data(input logic [7:0] data);
intf.data <= #TA data;
intf.valid <= #TA 1;
cycle_start();
while (!(intf.valid && intf.ready)) begin cycle_end(); cycle_start(); end
cycle_end();
intf.valid <= #TA 0;
endtask
endclass
module t_virtual_interface_member_trigger_realistic_case();
logic clk;
logic [7:0] data;
logic valid;
logic ready;
logic [7:0] recv_data;
INTF read_intf();
assign read_intf.clk = clk;
assign read_intf.data = data;
assign read_intf.valid = valid;
assign ready = read_intf.ready;
INTF write_intf();
assign write_intf.clk = clk;
assign data = write_intf.data;
assign valid = write_intf.valid;
assign write_intf.ready = ready;
intf_driver driver_master;
intf_driver driver_slave;
virtual INTF vif_read = read_intf;
virtual INTF vif_write = write_intf;
initial begin
repeat(1000) begin
clk = '1;
#10ns;
clk = '0;
#10ns;
end
end
initial begin
driver_master = new(vif_write);
driver_slave = new(vif_read);
driver_master.init_master();
driver_slave.init_slave();
fork
begin
#35ns;
driver_master.send_data(8'h42);
// $display("[%0d]: Write data: %02x", $time, write_intf.data);
#10ns;
driver_master.send_data(8'h43);
// $display("[%0d]: Write data: %02x", $time, write_intf.data);
#10ns;
driver_master.send_data(8'h44);
// $display("[%0d]: Write data: %02x", $time, write_intf.data);
end
begin
#10ns;
driver_slave.recv_data(recv_data);
// $display("[%0d]: Got data: %02x", $time, recv_data);
if (recv_data !== 8'h42) $stop;
#5ns;
driver_slave.recv_data(recv_data);
// $display("[%0d]: Got data: %02x", $time, recv_data);
if (recv_data !== 8'h43) $stop;
#15ns;
driver_slave.recv_data(recv_data);
// $display("[%0d]: Got data: %02x", $time, recv_data);
if (recv_data !== 8'h44) $stop;
end
join
$write("*-* All Finished *-*\n");
$finish;
end
// Dump waveforms
// initial begin
// $dumpfile("t_virtual_interface_member_trigger.vcd");
// $dumpvars(0, t_virtual_interface_member_trigger);
// end
endmodule