parent
a21ecb2ab9
commit
9b99d9697f
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
|
@ -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
|
Loading…
Reference in New Issue