From 1044398f95bb7eb0279efbe917b3278ede1abba4 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Sat, 12 Jul 2025 03:04:51 +0200 Subject: [PATCH] Support member-level triggers for virtual interfaces (#5166) (#6148) --- src/V3Sched.cpp | 12 +++ src/V3Sched.h | 46 +++++++- src/V3SchedVirtIface.cpp | 84 +++++++++++++-- .../t/t_interface_virtual_controlflow.out | 25 ++--- .../t/t_interface_virtual_controlflow.py | 2 +- .../t/t_interface_virtual_controlflow.v | 100 ++++++++++-------- .../t/t_interface_virtual_sched_act.out | 15 +-- .../t/t_interface_virtual_sched_act.py | 2 +- .../t/t_interface_virtual_sched_act.v | 27 +++-- .../t/t_interface_virtual_sched_ico.cpp | 51 --------- .../t/t_interface_virtual_sched_ico.out | 61 ++++------- .../t/t_interface_virtual_sched_ico.py | 2 +- .../t/t_interface_virtual_sched_ico.v | 43 +++++--- .../t/t_interface_virtual_sched_nba.out | 23 ++-- .../t/t_interface_virtual_sched_nba.py | 2 +- .../t/t_interface_virtual_sched_nba.v | 29 +++-- test_regress/t/t_interface_virtual_timing.out | 38 ++----- test_regress/t/t_interface_virtual_timing.v | 9 +- .../t/t_virtual_interface_member_trigger.py | 18 ++++ .../t/t_virtual_interface_member_trigger.v | 45 ++++++++ 20 files changed, 374 insertions(+), 260 deletions(-) delete mode 100644 test_regress/t/t_interface_virtual_sched_ico.cpp create mode 100755 test_regress/t/t_virtual_interface_member_trigger.py create mode 100755 test_regress/t/t_virtual_interface_member_trigger.v diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index cfb810d4b..633d8fbf3 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -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 diff --git a/src/V3Sched.h b/src/V3Sched.h index 4c430919b..5d197e5ca 100644 --- a/src/V3Sched.h +++ b/src/V3Sched.h @@ -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; + using IfaceMemberTriggerVec = std::vector; + using IfaceMemberSensMap = std::map; + using IfaceTrigger = std::pair; using IfaceTriggerVec = std::vector; using IfaceSensMap = std::map; - 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; diff --git a/src/V3SchedVirtIface.cpp b/src/V3SchedVirtIface.cpp index 3a7ba6b4c..06818e5cd 100644 --- a/src/V3SchedVirtIface.cpp +++ b/src/V3SchedVirtIface.cpp @@ -49,12 +49,15 @@ private: // TYPES using OnWriteToVirtIface = std::function; + using OnWriteToVirtIfaceMember + = std::function; // 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& 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); } diff --git a/test_regress/t/t_interface_virtual_controlflow.out b/test_regress/t/t_interface_virtual_controlflow.out index 303bceb13..2a88fdbd2 100644 --- a/test_regress/t/t_interface_virtual_controlflow.out +++ b/test_regress/t/t_interface_virtual_controlflow.out @@ -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 *-* diff --git a/test_regress/t/t_interface_virtual_controlflow.py b/test_regress/t/t_interface_virtual_controlflow.py index 7aedd7b28..f163beb3f 100755 --- a/test_regress/t/t_interface_virtual_controlflow.py +++ b/test_regress/t/t_interface_virtual_controlflow.py @@ -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) diff --git a/test_regress/t/t_interface_virtual_controlflow.v b/test_regress/t/t_interface_virtual_controlflow.v index 4e5dd4cbd..f8d03dc8e 100644 --- a/test_regress/t/t_interface_virtual_controlflow.v +++ b/test_regress/t/t_interface_virtual_controlflow.v @@ -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 diff --git a/test_regress/t/t_interface_virtual_sched_act.out b/test_regress/t/t_interface_virtual_sched_act.out index 10090eb57..7c8ef1b3f 100644 --- a/test_regress/t/t_interface_virtual_sched_act.out +++ b/test_regress/t/t_interface_virtual_sched_act.out @@ -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 diff --git a/test_regress/t/t_interface_virtual_sched_act.py b/test_regress/t/t_interface_virtual_sched_act.py index ab5dca066..1407fff26 100755 --- a/test_regress/t/t_interface_virtual_sched_act.py +++ b/test_regress/t/t_interface_virtual_sched_act.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile() +test.compile(verilator_flags2=["--binary"]) test.execute(expect_filename=test.golden_filename) diff --git a/test_regress/t/t_interface_virtual_sched_act.v b/test_regress/t/t_interface_virtual_sched_act.v index 258ac1620..7f37b8a37 100644 --- a/test_regress/t/t_interface_virtual_sched_act.v +++ b/test_regress/t/t_interface_virtual_sched_act.v @@ -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 diff --git a/test_regress/t/t_interface_virtual_sched_ico.cpp b/test_regress/t/t_interface_virtual_sched_ico.cpp deleted file mode 100644 index 04d8c1cc3..000000000 --- a/test_regress/t/t_interface_virtual_sched_ico.cpp +++ /dev/null @@ -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 - -int main(int argc, char** argv) { - const std::unique_ptr contextp{new VerilatedContext}; - contextp->debug(0); - contextp->commandArgs(argc, argv); - srand48(5); - - const std::unique_ptr 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; -} diff --git a/test_regress/t/t_interface_virtual_sched_ico.out b/test_regress/t/t_interface_virtual_sched_ico.out index 5f94c05df..e40299462 100644 --- a/test_regress/t/t_interface_virtual_sched_ico.out +++ b/test_regress/t/t_interface_virtual_sched_ico.out @@ -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 *-* diff --git a/test_regress/t/t_interface_virtual_sched_ico.py b/test_regress/t/t_interface_virtual_sched_ico.py index 5f13c2aee..980753f6d 100755 --- a/test_regress/t/t_interface_virtual_sched_ico.py +++ b/test_regress/t/t_interface_virtual_sched_ico.py @@ -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) diff --git a/test_regress/t/t_interface_virtual_sched_ico.v b/test_regress/t/t_interface_virtual_sched_ico.v index 3d85a7ec9..3e1b8e280 100644 --- a/test_regress/t/t_interface_virtual_sched_ico.v +++ b/test_regress/t/t_interface_virtual_sched_ico.v @@ -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 diff --git a/test_regress/t/t_interface_virtual_sched_nba.out b/test_regress/t/t_interface_virtual_sched_nba.out index abde98039..714cdcee8 100644 --- a/test_regress/t/t_interface_virtual_sched_nba.out +++ b/test_regress/t/t_interface_virtual_sched_nba.out @@ -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 *-* diff --git a/test_regress/t/t_interface_virtual_sched_nba.py b/test_regress/t/t_interface_virtual_sched_nba.py index ab5dca066..bded720e2 100755 --- a/test_regress/t/t_interface_virtual_sched_nba.py +++ b/test_regress/t/t_interface_virtual_sched_nba.py @@ -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) diff --git a/test_regress/t/t_interface_virtual_sched_nba.v b/test_regress/t/t_interface_virtual_sched_nba.v index dc610aa50..3a22fd5c9 100644 --- a/test_regress/t/t_interface_virtual_sched_nba.v +++ b/test_regress/t/t_interface_virtual_sched_nba.v @@ -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 diff --git a/test_regress/t/t_interface_virtual_timing.out b/test_regress/t/t_interface_virtual_timing.out index 2ca5ac2af..2c7435aaa 100644 --- a/test_regress/t/t_interface_virtual_timing.out +++ b/test_regress/t/t_interface_virtual_timing.out @@ -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 *-* diff --git a/test_regress/t/t_interface_virtual_timing.v b/test_regress/t/t_interface_virtual_timing.v index 258c7d8f6..6d6d285d7 100644 --- a/test_regress/t/t_interface_virtual_timing.v +++ b/test_regress/t/t_interface_virtual_timing.v @@ -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 diff --git a/test_regress/t/t_virtual_interface_member_trigger.py b/test_regress/t/t_virtual_interface_member_trigger.py new file mode 100755 index 000000000..bd059b0f2 --- /dev/null +++ b/test_regress/t/t_virtual_interface_member_trigger.py @@ -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() diff --git a/test_regress/t/t_virtual_interface_member_trigger.v b/test_regress/t/t_virtual_interface_member_trigger.v new file mode 100755 index 000000000..a6bf92d7f --- /dev/null +++ b/test_regress/t/t_virtual_interface_member_trigger.v @@ -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