parent
77180c4020
commit
1044398f95
|
@ -1189,6 +1189,18 @@ VirtIfaceTriggers::makeIfaceToSensMap(AstNetlist* const netlistp, size_t vifTrig
|
|||
return ifaceToSensMap;
|
||||
}
|
||||
|
||||
VirtIfaceTriggers::IfaceMemberSensMap
|
||||
VirtIfaceTriggers::makeMemberToSensMap(AstNetlist* const netlistp, size_t vifTriggerIndex,
|
||||
AstVarScope* trigVscp) const {
|
||||
IfaceMemberSensMap memberToSensMap;
|
||||
for (const auto& p : m_memberTriggers) {
|
||||
memberToSensMap.emplace(
|
||||
std::make_pair(p.first, createTriggerSenTree(netlistp, trigVscp, vifTriggerIndex)));
|
||||
++vifTriggerIndex;
|
||||
}
|
||||
return memberToSensMap;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// Top level entry-point to scheduling
|
||||
|
||||
|
|
|
@ -153,17 +153,55 @@ public:
|
|||
};
|
||||
|
||||
class VirtIfaceTriggers final {
|
||||
// Represents a specific member in a virtual interface
|
||||
struct IfaceMember final {
|
||||
const AstIface* m_ifacep; // Interface type
|
||||
const AstVar* m_memberVarp; // pointer to member field
|
||||
|
||||
IfaceMember(const AstIface* ifacep, const AstVar* memberVarp)
|
||||
: m_ifacep(ifacep)
|
||||
, m_memberVarp(memberVarp) {}
|
||||
|
||||
bool operator<(const IfaceMember& other) const {
|
||||
if (m_ifacep != other.m_ifacep) return m_ifacep < other.m_ifacep;
|
||||
return m_memberVarp < other.m_memberVarp;
|
||||
}
|
||||
};
|
||||
|
||||
using IfaceMemberTrigger = std::pair<IfaceMember, AstVarScope*>;
|
||||
using IfaceMemberTriggerVec = std::vector<IfaceMemberTrigger>;
|
||||
using IfaceMemberSensMap = std::map<IfaceMember, AstSenTree*>;
|
||||
|
||||
using IfaceTrigger = std::pair<const AstIface*, AstVarScope*>;
|
||||
using IfaceTriggerVec = std::vector<IfaceTrigger>;
|
||||
using IfaceSensMap = std::map<const AstIface*, AstSenTree*>;
|
||||
IfaceTriggerVec m_triggers;
|
||||
|
||||
IfaceMemberTriggerVec m_memberTriggers;
|
||||
IfaceTriggerVec m_ifaceTriggers;
|
||||
|
||||
public:
|
||||
void emplace_back(IfaceTrigger&& p) { m_triggers.emplace_back(std::move(p)); }
|
||||
IfaceTriggerVec::const_iterator begin() const { return m_triggers.begin(); }
|
||||
IfaceTriggerVec::const_iterator end() const { return m_triggers.end(); }
|
||||
void addMemberTrigger(const AstIface* ifacep, const AstVar* memberVarp,
|
||||
AstVarScope* triggerVscp) {
|
||||
m_memberTriggers.emplace_back(IfaceMember(ifacep, memberVarp), triggerVscp);
|
||||
}
|
||||
|
||||
AstVarScope* findMemberTrigger(const AstIface* ifacep, const AstVar* memberVarp) const {
|
||||
IfaceMember target{ifacep, memberVarp};
|
||||
for (const auto& pair : m_memberTriggers) {
|
||||
if (!(pair.first < target) && !(target < pair.first)) return pair.second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IfaceMemberSensMap makeMemberToSensMap(AstNetlist* netlistp, size_t vifTriggerIndex,
|
||||
AstVarScope* trigVscp) const;
|
||||
|
||||
void emplace_back(IfaceTrigger&& p) { m_ifaceTriggers.emplace_back(std::move(p)); }
|
||||
IfaceTriggerVec::const_iterator begin() const { return m_ifaceTriggers.begin(); }
|
||||
IfaceTriggerVec::const_iterator end() const { return m_ifaceTriggers.end(); }
|
||||
IfaceSensMap makeIfaceToSensMap(AstNetlist* netlistp, size_t vifTriggerIndex,
|
||||
AstVarScope* trigVscp) const;
|
||||
|
||||
VL_UNCOPYABLE(VirtIfaceTriggers);
|
||||
VirtIfaceTriggers() = default;
|
||||
VirtIfaceTriggers(VirtIfaceTriggers&&) = default;
|
||||
|
|
|
@ -49,12 +49,15 @@ private:
|
|||
|
||||
// TYPES
|
||||
using OnWriteToVirtIface = std::function<void(AstVarRef*, AstIface*)>;
|
||||
using OnWriteToVirtIfaceMember
|
||||
= std::function<void(AstVarRef*, AstIface*, const std::string&)>;
|
||||
|
||||
// STATE
|
||||
AstNetlist* const m_netlistp; // Root node
|
||||
AstAssign* m_trigAssignp = nullptr; // Previous/current trigger assignment
|
||||
AstIface* m_trigAssignIfacep = nullptr; // Interface type whose trigger is assigned
|
||||
// by m_trigAssignp
|
||||
AstVar* m_trigAssignMemberVarp; // Member pointer whose trigger is assigned
|
||||
V3UniqueNames m_vifTriggerNames{"__VvifTrigger"}; // Unique names for virt iface
|
||||
// triggers
|
||||
VirtIfaceTriggers m_triggers; // Interfaces and corresponding trigger vars
|
||||
|
@ -73,6 +76,25 @@ private:
|
|||
}
|
||||
});
|
||||
}
|
||||
// For each write across a virtual interface boundary (member-level tracking)
|
||||
static void foreachWrittenVirtIfaceMember(
|
||||
AstNode* const nodep, const std::function<void(AstVarRef*, AstIface*, AstVar*)>& onWrite) {
|
||||
nodep->foreach([&](AstVarRef* const refp) {
|
||||
if (refp->access().isReadOnly()) return;
|
||||
if (AstIfaceRefDType* const dtypep = VN_CAST(refp->varp()->dtypep(), IfaceRefDType)) {
|
||||
if (dtypep->isVirtual()) {
|
||||
if (AstMemberSel* const memberSelp = VN_CAST(refp->firstAbovep(), MemberSel)) {
|
||||
// Extract the member varp from the MemberSel node
|
||||
AstVar* memberVarp = memberSelp->varp();
|
||||
onWrite(refp, dtypep->ifacep(), memberVarp);
|
||||
}
|
||||
}
|
||||
} else if (AstIface* const ifacep = refp->varp()->sensIfacep()) {
|
||||
AstVar* memberVarp = refp->varp();
|
||||
onWrite(refp, ifacep, memberVarp);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Returns true if there is a write across a virtual interface boundary
|
||||
static bool writesToVirtIface(const AstNode* const nodep) {
|
||||
return nodep->exists([](const AstVarRef* const refp) {
|
||||
|
@ -103,12 +125,31 @@ private:
|
|||
return new AstVarRef{flp, VN_AS(ifacep->user1p(), VarScope), VAccess::WRITE};
|
||||
}
|
||||
|
||||
// Create trigger reference for a specific interface member
|
||||
AstVarRef* createVirtIfaceMemberTriggerRefp(FileLine* const flp, AstIface* ifacep,
|
||||
const AstVar* memberVarp) {
|
||||
// Check if we already have a trigger for this specific member
|
||||
AstVarScope* existingTrigger = m_triggers.findMemberTrigger(ifacep, memberVarp);
|
||||
if (!existingTrigger) {
|
||||
AstScope* const scopeTopp = m_netlistp->topScopep()->scopep();
|
||||
// Create a unique name for this member trigger
|
||||
const std::string triggerName
|
||||
= m_vifTriggerNames.get(ifacep) + "_Vtrigm_" + memberVarp->name();
|
||||
AstVarScope* const vscp = scopeTopp->createTemp(triggerName, 1);
|
||||
m_triggers.addMemberTrigger(ifacep, memberVarp, vscp);
|
||||
existingTrigger = vscp;
|
||||
}
|
||||
return new AstVarRef{flp, existingTrigger, VAccess::WRITE};
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeProcedure* nodep) override {
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
m_trigAssignp = nullptr;
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
m_trigAssignIfacep = nullptr;
|
||||
VL_RESTORER(m_trigAssignMemberVarp);
|
||||
m_trigAssignMemberVarp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstCFunc* nodep) override {
|
||||
|
@ -116,6 +157,8 @@ private:
|
|||
m_trigAssignp = nullptr;
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
m_trigAssignIfacep = nullptr;
|
||||
VL_RESTORER(m_trigAssignMemberVarp);
|
||||
m_trigAssignMemberVarp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstAssignW* nodep) override {
|
||||
|
@ -140,11 +183,13 @@ private:
|
|||
{
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
VL_RESTORER(m_trigAssignMemberVarp);
|
||||
iterateAndNextNull(nodep->thensp());
|
||||
}
|
||||
{
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
VL_RESTORER(m_trigAssignMemberVarp);
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
}
|
||||
if (v3Global.usesTiming()) {
|
||||
|
@ -152,6 +197,7 @@ private:
|
|||
// branch
|
||||
m_trigAssignp = nullptr;
|
||||
m_trigAssignIfacep = nullptr;
|
||||
m_trigAssignMemberVarp = nullptr;
|
||||
}
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
|
@ -161,18 +207,21 @@ private:
|
|||
{
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
VL_RESTORER(m_trigAssignMemberVarp);
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
}
|
||||
if (v3Global.usesTiming()) {
|
||||
// Clear the trigger assignment, as there could have been timing controls in the loop
|
||||
m_trigAssignp = nullptr;
|
||||
m_trigAssignIfacep = nullptr;
|
||||
m_trigAssignMemberVarp = nullptr;
|
||||
}
|
||||
}
|
||||
void visit(AstJumpBlock* nodep) override {
|
||||
{
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
VL_RESTORER(m_trigAssignMemberVarp);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
if (v3Global.usesTiming()) {
|
||||
|
@ -180,29 +229,48 @@ private:
|
|||
// block
|
||||
m_trigAssignp = nullptr;
|
||||
m_trigAssignIfacep = nullptr;
|
||||
m_trigAssignMemberVarp = nullptr;
|
||||
}
|
||||
}
|
||||
void visit(AstNodeStmt* nodep) override {
|
||||
if (v3Global.usesTiming()
|
||||
&& nodep->exists([](AstNode* nodep) { return nodep->isTimingControl(); })) {
|
||||
m_trigAssignp = nullptr; // Could be after a delay - need new trigger assignment
|
||||
m_trigAssignp = nullptr;
|
||||
m_trigAssignIfacep = nullptr;
|
||||
// No restorer, as following statements should not reuse the old assignment
|
||||
m_trigAssignMemberVarp = nullptr;
|
||||
}
|
||||
FileLine* const flp = nodep->fileline();
|
||||
foreachWrittenVirtIface(nodep, [&](AstVarRef*, AstIface* ifacep) {
|
||||
if (ifacep != m_trigAssignIfacep) {
|
||||
// Write to different interface type than before - need new trigger assignment
|
||||
// No restorer, as following statements should not reuse the old assignment
|
||||
|
||||
foreachWrittenVirtIfaceMember(nodep, [&](AstVarRef*, AstIface* ifacep,
|
||||
AstVar* memberVarp) {
|
||||
if (ifacep != m_trigAssignIfacep || memberVarp != m_trigAssignMemberVarp) {
|
||||
// Write to different interface member than before - need new trigger assignment
|
||||
m_trigAssignIfacep = ifacep;
|
||||
m_trigAssignMemberVarp = memberVarp;
|
||||
m_trigAssignp = nullptr;
|
||||
}
|
||||
if (!m_trigAssignp) {
|
||||
m_trigAssignp = new AstAssign{flp, createVirtIfaceTriggerRefp(flp, ifacep),
|
||||
new AstConst{flp, AstConst::BitTrue{}}};
|
||||
m_trigAssignp
|
||||
= new AstAssign{flp, createVirtIfaceMemberTriggerRefp(flp, ifacep, memberVarp),
|
||||
new AstConst{flp, AstConst::BitTrue{}}};
|
||||
nodep->addNextHere(m_trigAssignp);
|
||||
}
|
||||
});
|
||||
// Fallback to whole-interface tracking if no member-specific assignments found
|
||||
if (!m_trigAssignp) {
|
||||
foreachWrittenVirtIface(nodep, [&](AstVarRef*, AstIface* ifacep) {
|
||||
if (ifacep != m_trigAssignIfacep) {
|
||||
m_trigAssignIfacep = ifacep;
|
||||
m_trigAssignMemberVarp = nullptr;
|
||||
m_trigAssignp = nullptr;
|
||||
}
|
||||
if (!m_trigAssignp) {
|
||||
m_trigAssignp = new AstAssign{flp, createVirtIfaceTriggerRefp(flp, ifacep),
|
||||
new AstConst{flp, AstConst::BitTrue{}}};
|
||||
nodep->addNextHere(m_trigAssignp);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
void visit(AstNodeExpr*) override {} // Accelerate
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
|
|
@ -1,19 +1,12 @@
|
|||
[0] vif1.data==0000
|
||||
[0] intf2.data==0000
|
||||
[0] vif4.data==0000
|
||||
[10] intf2.data==beef
|
||||
[20] vif1.data==dead
|
||||
[20] vif4.data==face
|
||||
[30] intf2.data==beef
|
||||
[40] vif1.data==dead
|
||||
[40] vif4.data==face
|
||||
[50] intf2.data==beef
|
||||
[60] vif1.data==dead
|
||||
[60] vif4.data==face
|
||||
[70] intf2.data==beef
|
||||
[80] intf2.data==beef
|
||||
[80] vif4.data==cafe
|
||||
[90] intf2.data==beef
|
||||
[100] intf2.data==beef
|
||||
[100] vif4.data==deaf
|
||||
[0] vif3.data==0000
|
||||
[0] intf4.data==0000
|
||||
[5000] intf2.data==beef
|
||||
[15000] vif1.data==dead
|
||||
[15000] vif3.data==fafa
|
||||
[15000] intf4.data==face
|
||||
[75000] intf4.data==cafe
|
||||
[95000] vif3.data==cafe
|
||||
[95000] intf4.data==deaf
|
||||
*-* All Finished *-*
|
||||
|
|
|
@ -11,7 +11,7 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["-fno-reorder"])
|
||||
test.compile(verilator_flags2=["--exe --main --timing -fno-reorder"])
|
||||
|
||||
test.execute(expect_filename=test.golden_filename)
|
||||
|
||||
|
|
|
@ -5,63 +5,69 @@
|
|||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
interface Bus1;
|
||||
logic [15:0] data;
|
||||
logic [15:0] data;
|
||||
endinterface
|
||||
interface Bus2;
|
||||
logic [15:0] data;
|
||||
logic [15:0] data;
|
||||
endinterface
|
||||
interface Bus3;
|
||||
logic [15:0] data;
|
||||
logic [15:0] data;
|
||||
endinterface
|
||||
|
||||
module t (
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
integer cyc = 0;
|
||||
Bus1 intf1();
|
||||
Bus2 intf2();
|
||||
Bus3 intf3(), intf4();
|
||||
virtual Bus1 vif1 = intf1;
|
||||
virtual Bus2 vif2 = intf2;
|
||||
virtual Bus3 vif3 = intf3, vif4 = intf4;
|
||||
module t_controlflow;
|
||||
logic clk = 0;
|
||||
integer cyc = 0;
|
||||
Bus1 intf1();
|
||||
Bus2 intf2();
|
||||
Bus3 intf3(), intf4();
|
||||
virtual Bus1 vif1 = intf1;
|
||||
virtual Bus2 vif2 = intf2;
|
||||
virtual Bus3 vif3 = intf3, vif4 = intf4;
|
||||
|
||||
// Finish on negedge so that $finish is last
|
||||
always @(negedge clk)
|
||||
if (cyc >= 10) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
// Finish on negedge so that $finish is last
|
||||
always @(negedge clk) begin
|
||||
if (cyc >= 10) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
function void assign_to_intf3();
|
||||
if ($c("1")) return;
|
||||
intf3.data = 'hcafe;
|
||||
endfunction
|
||||
function void assign_to_intf3();
|
||||
intf3.data = 'hcafe;
|
||||
endfunction
|
||||
|
||||
always @(posedge clk) begin
|
||||
logic foo = 1;
|
||||
cyc <= cyc + 1;
|
||||
if (cyc == 1 || cyc == 3 || cyc == 5) intf1.data = 'hdead;
|
||||
else vif2.data = 'hbeef;
|
||||
if (cyc == 1 || cyc == 3 || cyc == 5) begin
|
||||
if (cyc >= 3) $c("// dummy statement");
|
||||
else intf3.data = 'hfafa;
|
||||
intf4.data = 'hface;
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc == 1 || cyc == 3 || cyc == 5) intf1.data = 'hdead;
|
||||
else vif2.data = 'hbeef;
|
||||
if (cyc == 1 || cyc == 3 || cyc == 5) begin
|
||||
if (cyc < 3) intf3.data = 'hfafa;
|
||||
intf4.data = 'hface;
|
||||
end
|
||||
if (cyc == 7) begin
|
||||
intf4.data = 'hcafe;
|
||||
end
|
||||
if (cyc == 9) begin
|
||||
assign_to_intf3;
|
||||
intf4.data = 'hdeaf;
|
||||
end
|
||||
end
|
||||
|
||||
always @(vif1.data) begin
|
||||
$write("[%0t] vif1.data==%h\n", $time, vif1.data);
|
||||
end
|
||||
always @(intf2.data) begin
|
||||
$write("[%0t] intf2.data==%h\n", $time, intf2.data);
|
||||
end
|
||||
always @(vif3.data) begin
|
||||
$write("[%0t] vif3.data==%h\n", $time, vif3.data);
|
||||
end
|
||||
always @(intf4.data) begin
|
||||
$write("[%0t] intf4.data==%h\n", $time, intf4.data);
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (20) #5ns clk = ~clk;
|
||||
end
|
||||
if (cyc == 7) begin
|
||||
while ($c("0")) begin
|
||||
foo = 0;
|
||||
intf3.data = 'hbebe;
|
||||
end
|
||||
intf4.data = 'hcafe;
|
||||
end
|
||||
if (cyc == 9) begin
|
||||
assign_to_intf3;
|
||||
intf4.data = 'hdeaf;
|
||||
end
|
||||
end
|
||||
|
||||
always_comb $write("[%0t] vif1.data==%h\n", $time, vif1.data);
|
||||
always_comb $write("[%0t] intf2.data==%h\n", $time, intf2.data);
|
||||
always_comb $write("[%0t] vif4.data==%h\n", $time, vif4.data);
|
||||
endmodule
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
[0] data==0000
|
||||
[0] data==0000
|
||||
[10] data==0000
|
||||
[20] data==dead
|
||||
[20] data==beef
|
||||
[20] data==beef
|
||||
[30] data==beef
|
||||
[40] data==face
|
||||
[40] data==cafe
|
||||
[40] data==cafe
|
||||
[50] data==cafe
|
||||
*-* All Finished *-*
|
||||
[20000] data==dead
|
||||
[30000] data==beef
|
||||
[40000] data==face
|
||||
[50000] data==cafe
|
||||
|
|
|
@ -11,7 +11,7 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile()
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute(expect_filename=test.golden_filename)
|
||||
|
||||
|
|
|
@ -8,10 +8,8 @@ interface Bus;
|
|||
logic [15:0] data;
|
||||
endinterface
|
||||
|
||||
module t (
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
module t_sched_act;
|
||||
logic clk = 0;
|
||||
integer cyc = 0;
|
||||
Bus intf();
|
||||
virtual Bus vif = intf;
|
||||
|
@ -23,10 +21,10 @@ module t (
|
|||
|
||||
// Finish on negedge so that $finish is last
|
||||
always @(negedge clk)
|
||||
if (cyc >= 5) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
if (cyc >= 6) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
always @(posedge clk or data) begin
|
||||
if (cyc == 1) intf.data <= 'hdead;
|
||||
|
@ -35,6 +33,15 @@ module t (
|
|||
else if (cyc == 4) intf.data <= 'hcafe;
|
||||
end
|
||||
|
||||
assign data = vif.data;
|
||||
always_comb $write("[%0t] data==%h\n", $time, data);
|
||||
always @(negedge clk) begin
|
||||
data <= vif.data;
|
||||
end
|
||||
|
||||
always @(data) begin
|
||||
$write("[%0t] data==%h\n", $time, data);
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (10) #5ns clk = ~clk;
|
||||
end
|
||||
endmodule
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2023 by Geza Lore. 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
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
#include "Vt_interface_virtual_sched_ico.h"
|
||||
#include "Vt_interface_virtual_sched_ico__Syms.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
||||
contextp->debug(0);
|
||||
contextp->commandArgs(argc, argv);
|
||||
srand48(5);
|
||||
|
||||
const std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX};
|
||||
topp->clk = false;
|
||||
topp->inc1 = 1;
|
||||
topp->eval();
|
||||
topp->inc2 = 1;
|
||||
topp->eval();
|
||||
|
||||
bool flop = true;
|
||||
while (!contextp->gotFinish() && contextp->time() < 100000) {
|
||||
contextp->timeInc(5);
|
||||
if (topp->clk) {
|
||||
if (flop) {
|
||||
topp->inc1 += 1;
|
||||
} else {
|
||||
topp->inc2 += 1;
|
||||
}
|
||||
flop = !flop;
|
||||
}
|
||||
topp->clk = !topp->clk;
|
||||
topp->eval();
|
||||
}
|
||||
|
||||
if (!contextp->gotFinish()) {
|
||||
vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish");
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,43 +1,20 @@
|
|||
[0] intf1.inc==0
|
||||
[0] vif2.inc==0
|
||||
[0] intf1.inc==1
|
||||
[0] vif2.inc==0
|
||||
[0] intf1.inc==1
|
||||
[0] vif2.inc==0
|
||||
[0] intf1.inc==1
|
||||
[0] vif2.inc==1
|
||||
[5] intf1.inc==1
|
||||
[5] vif2.inc==1
|
||||
[10] intf1.inc==2
|
||||
[10] vif2.inc==1
|
||||
[15] intf1.inc==2
|
||||
[15] vif2.inc==1
|
||||
[20] intf1.inc==2
|
||||
[20] vif2.inc==2
|
||||
[25] intf1.inc==2
|
||||
[25] vif2.inc==2
|
||||
[30] intf1.inc==3
|
||||
[30] vif2.inc==2
|
||||
[35] intf1.inc==3
|
||||
[35] vif2.inc==2
|
||||
[40] intf1.inc==3
|
||||
[40] vif2.inc==3
|
||||
[45] intf1.inc==3
|
||||
[45] vif2.inc==3
|
||||
[50] intf1.inc==4
|
||||
[50] vif2.inc==3
|
||||
[55] intf1.inc==4
|
||||
[55] vif2.inc==3
|
||||
[60] intf1.inc==4
|
||||
[60] vif2.inc==4
|
||||
[65] intf1.inc==4
|
||||
[65] vif2.inc==4
|
||||
[70] intf1.inc==5
|
||||
[70] vif2.inc==4
|
||||
[75] intf1.inc==5
|
||||
[75] vif2.inc==4
|
||||
[80] intf1.inc==5
|
||||
[80] vif2.inc==5
|
||||
[85] intf1.inc==5
|
||||
[85] vif2.inc==5
|
||||
[0] intf1.inc==00000000
|
||||
[0] vif2.inc==00000001
|
||||
[5000] intf1.inc==00000001
|
||||
[10000] vif2.inc==00000002
|
||||
[15000] intf1.inc==00000002
|
||||
[20000] vif2.inc==00000003
|
||||
[25000] intf1.inc==00000003
|
||||
[30000] vif2.inc==00000004
|
||||
[35000] intf1.inc==00000004
|
||||
[40000] vif2.inc==00000005
|
||||
[45000] intf1.inc==00000005
|
||||
[50000] vif2.inc==00000006
|
||||
[55000] intf1.inc==00000006
|
||||
[60000] vif2.inc==00000007
|
||||
[65000] intf1.inc==00000007
|
||||
[70000] vif2.inc==00000008
|
||||
[75000] intf1.inc==00000008
|
||||
[80000] vif2.inc==00000009
|
||||
[85000] intf1.inc==00000009
|
||||
*-* All Finished *-*
|
||||
|
|
|
@ -11,7 +11,7 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('vlt_all')
|
||||
|
||||
test.compile(make_main=False, v_flags2=["--exe", test.pli_filename])
|
||||
test.compile(verilator_flags2=["--exe --main --timing"])
|
||||
|
||||
test.execute(expect_filename=test.golden_filename)
|
||||
|
||||
|
|
|
@ -8,15 +8,11 @@ interface If;
|
|||
logic [31:0] inc;
|
||||
endinterface
|
||||
|
||||
module top (
|
||||
clk,
|
||||
inc1,
|
||||
inc2
|
||||
);
|
||||
module top;
|
||||
|
||||
input clk;
|
||||
input [31:0] inc1;
|
||||
input [31:0] inc2;
|
||||
logic clk = 0;
|
||||
logic [31:0] inc1 = 0;
|
||||
logic [31:0] inc2 = 0;
|
||||
int cyc = 0;
|
||||
|
||||
If intf1();
|
||||
|
@ -24,18 +20,41 @@ module top (
|
|||
virtual If vif1 = intf1;
|
||||
virtual If vif2 = intf2;
|
||||
|
||||
assign vif1.inc = inc1;
|
||||
// assign vif1.inc = inc1;
|
||||
always @(posedge clk) begin
|
||||
vif1.inc <= inc1;
|
||||
end
|
||||
assign intf2.inc = inc2;
|
||||
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc >= 8) begin
|
||||
if (cyc >= 10) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
always_comb $write("[%0t] intf1.inc==%0h\n", $time, intf1.inc);
|
||||
always_comb $write("[%0t] vif2.inc==%0h\n", $time, vif2.inc);
|
||||
always @(intf1.inc) begin
|
||||
$write("[%0t] intf1.inc==%h\n", $time, intf1.inc);
|
||||
end
|
||||
always @(vif2.inc) begin
|
||||
$write("[%0t] vif2.inc==%h\n", $time, vif2.inc);
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (30) #5ns clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
inc1 = 1;
|
||||
inc2 = 1;
|
||||
|
||||
repeat (8) begin
|
||||
#10ns;
|
||||
inc1 = inc1 + 1;
|
||||
inc2 = inc2 + 1;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
|
@ -1,21 +1,10 @@
|
|||
[0] intf1.data==0000
|
||||
[0] intf2.data==0000
|
||||
[0] vif3.data==0000
|
||||
[0] intf2.data==0000
|
||||
[10] intf2.data==0000
|
||||
[10] vif3.data==0000
|
||||
[20] intf1.data==dead
|
||||
[20] intf2.data==0000
|
||||
[20] vif3.data==0000
|
||||
[30] intf2.data==dead
|
||||
[30] vif3.data==0000
|
||||
[40] intf1.data==beef
|
||||
[40] intf2.data==dead
|
||||
[40] vif3.data==0000
|
||||
[50] intf2.data==beef
|
||||
[50] vif3.data==0000
|
||||
[60] intf2.data==beef
|
||||
[60] vif3.data==face
|
||||
[70] intf2.data==beef
|
||||
[70] vif3.data==cafe
|
||||
[15000] intf1.data==dead
|
||||
[30000] intf2.data==dead
|
||||
[35000] intf1.data==beef
|
||||
[50000] intf2.data==beef
|
||||
[55000] vif3.data==face
|
||||
[65000] vif3.data==cafe
|
||||
*-* All Finished *-*
|
||||
|
|
|
@ -11,7 +11,7 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile()
|
||||
test.compile(verilator_flags2=["--exe --main --timing"])
|
||||
|
||||
test.execute(expect_filename=test.golden_filename)
|
||||
|
||||
|
|
|
@ -16,10 +16,9 @@ interface Bus3;
|
|||
logic [15:0] data;
|
||||
endinterface
|
||||
|
||||
module t (
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
module t;
|
||||
|
||||
logic clk = 0;
|
||||
integer cyc = 0;
|
||||
Bus1 intf1();
|
||||
Bus2 intf2();
|
||||
|
@ -29,7 +28,10 @@ module t (
|
|||
virtual Bus3 vif3 = intf3;
|
||||
|
||||
logic [15:0] data;
|
||||
assign vif2.data = data;
|
||||
// assign vif2.data = data;
|
||||
always @(negedge clk) begin
|
||||
vif2.data <= data;
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
|
@ -54,7 +56,18 @@ module t (
|
|||
$finish;
|
||||
end
|
||||
|
||||
always_comb $write("[%0t] intf1.data==%h\n", $time, intf1.data);
|
||||
always_comb $write("[%0t] intf2.data==%h\n", $time, intf2.data);
|
||||
always_comb $write("[%0t] vif3.data==%h\n", $time, vif3.data);
|
||||
always @(intf1.data) begin
|
||||
$write("[%0t] intf1.data==%h\n", $time, intf1.data);
|
||||
end
|
||||
always @(intf2.data) begin
|
||||
$write("[%0t] intf2.data==%h\n", $time, intf2.data);
|
||||
end
|
||||
always @(vif3.data) begin
|
||||
$write("[%0t] vif3.data==%h\n", $time, vif3.data);
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (20) #5ns clk = ~clk;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
|
@ -1,29 +1,11 @@
|
|||
vif1.data==dead
|
||||
intf2.data==0000
|
||||
vif1.data==dead
|
||||
intf2.data==0000
|
||||
intf2.data==beef
|
||||
vif1.data==dead
|
||||
intf2.data==beef
|
||||
intf2.data==beef
|
||||
vif1.data==cafe
|
||||
intf2.data==beef
|
||||
intf2.data==face
|
||||
vif1.data==cafe
|
||||
intf2.data==face
|
||||
intf2.data==face
|
||||
vif1.data==feed
|
||||
intf2.data==face
|
||||
intf2.data==deed
|
||||
vif1.data==feed
|
||||
intf2.data==deed
|
||||
intf2.data==deed
|
||||
vif1.data==deaf
|
||||
intf2.data==deed
|
||||
intf2.data==fafa
|
||||
vif1.data==deaf
|
||||
intf2.data==fafa
|
||||
intf2.data==fafa
|
||||
vif1.data==bebe
|
||||
intf2.data==fafa
|
||||
[0] vif1.data==dead
|
||||
[0] intf2.data==0000
|
||||
[1] intf2.data==beef
|
||||
[2] vif1.data==cafe
|
||||
[3] intf2.data==face
|
||||
[4] vif1.data==feed
|
||||
[5] intf2.data==deed
|
||||
[6] vif1.data==deaf
|
||||
[7] intf2.data==fafa
|
||||
[8] vif1.data==bebe
|
||||
*-* All Finished *-*
|
||||
|
|
|
@ -37,6 +37,11 @@ module t;
|
|||
$finish;
|
||||
end
|
||||
|
||||
always_comb if ($time < 9) $write("vif1.data==%h\n", vif1.data);
|
||||
always_comb if ($time < 9) $write("intf2.data==%h\n", intf2.data);
|
||||
always @(vif1.data) begin
|
||||
if ($time < 9) $write("[%0t] vif1.data==%h\n", $time, vif1.data);
|
||||
end
|
||||
always @(intf2.data) begin
|
||||
if ($time < 9) $write("[%0t] intf2.data==%h\n", $time, intf2.data);
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
|
@ -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,45 @@
|
|||
// 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 x;
|
||||
logic y;
|
||||
logic z;
|
||||
endinterface
|
||||
|
||||
class Dummy;
|
||||
virtual INTF vif;
|
||||
function new(virtual INTF vif);
|
||||
this.vif = vif;
|
||||
endfunction
|
||||
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;
|
||||
assign s1 = s2;
|
||||
|
||||
Dummy d;
|
||||
|
||||
initial begin
|
||||
d = new(vintf);
|
||||
#1ns;
|
||||
src_val = 0;
|
||||
#1ns;
|
||||
if (!(d.vif.x == 1 && d.vif.y == 0 && d.vif.z == 1 && s1 == 1 && s2 == 1)) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
Loading…
Reference in New Issue