diff --git a/include/verilated_fst_c.cpp b/include/verilated_fst_c.cpp index 5e29ee806..5dc3a2b11 100644 --- a/include/verilated_fst_c.cpp +++ b/include/verilated_fst_c.cpp @@ -293,7 +293,7 @@ void VerilatedFst::configure(const VerilatedTraceConfig& config) { // so always inline them. VL_ATTR_ALWINLINE -void VerilatedFstBuffer::emitEvent(uint32_t code, VlEvent newval) { +void VerilatedFstBuffer::emitEvent(uint32_t code, const VlEventBase* newval) { VL_DEBUG_IFDEF(assert(m_symbolp[code]);); fstWriterEmitValueChange(m_fst, m_symbolp[code], "1"); } diff --git a/include/verilated_fst_c.h b/include/verilated_fst_c.h index c4a65cfb2..0331ca5e9 100644 --- a/include/verilated_fst_c.h +++ b/include/verilated_fst_c.h @@ -177,7 +177,7 @@ class VerilatedFstBuffer VL_NOT_FINAL { // Implementations of duck-typed methods for VerilatedTraceBuffer. These are // called from only one place (the full* methods), so always inline them. - VL_ATTR_ALWINLINE void emitEvent(uint32_t code, VlEvent newval); + VL_ATTR_ALWINLINE void emitEvent(uint32_t code, const VlEventBase* newval); VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval); VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits); VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits); diff --git a/include/verilated_trace.h b/include/verilated_trace.h index f810db099..d9af41174 100644 --- a/include/verilated_trace.h +++ b/include/verilated_trace.h @@ -475,7 +475,7 @@ public: void fullQData(uint32_t* oldp, QData newval, int bits); void fullWData(uint32_t* oldp, const WData* newvalp, int bits); void fullDouble(uint32_t* oldp, double newval); - void fullEvent(uint32_t* oldp, VlEvent newval); + void fullEvent(uint32_t* oldp, const VlEventBase* newval); // In non-offload mode, these are called directly by the trace callbacks, // and are called chg*. In offload mode, they are called by the worker @@ -512,7 +512,9 @@ public: } } } - VL_ATTR_ALWINLINE void chgEvent(uint32_t* oldp, VlEvent newval) { fullEvent(oldp, newval); } + VL_ATTR_ALWINLINE void chgEvent(uint32_t* oldp, const VlEventBase* newval) { + fullEvent(oldp, newval); + } VL_ATTR_ALWINLINE void chgDouble(uint32_t* oldp, double newval) { double old; std::memcpy(&old, oldp, sizeof(old)); @@ -591,7 +593,7 @@ public: m_offloadBufferWritep += 4; VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp);); } - void chgEvent(uint32_t code, VlEvent newval) { + void chgEvent(uint32_t code, const VlEventBase* newval) { m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_EVENT; m_offloadBufferWritep[1] = code; m_offloadBufferWritep += 2; diff --git a/include/verilated_trace_imp.h b/include/verilated_trace_imp.h index 19dc1cb56..62516432f 100644 --- a/include/verilated_trace_imp.h +++ b/include/verilated_trace_imp.h @@ -185,7 +185,7 @@ void VerilatedTrace::offloadWorkerThreadMain() { continue; case VerilatedTraceOffloadCommand::CHG_EVENT: VL_TRACE_OFFLOAD_DEBUG("Command CHG_EVENT " << top); - traceBufp->chgEvent(oldp, *reinterpret_cast(readp)); + traceBufp->chgEvent(oldp, reinterpret_cast(readp)); continue; //=== @@ -846,7 +846,7 @@ void VerilatedTraceBuffer::fullBit(uint32_t* oldp, CData newval) { } template <> -void VerilatedTraceBuffer::fullEvent(uint32_t* oldp, VlEvent newval) { +void VerilatedTraceBuffer::fullEvent(uint32_t* oldp, const VlEventBase* newval) { const uint32_t code = oldp - m_sigs_oldvalp; *oldp = 1; // Do we really store an "event" ? emitEvent(code, newval); diff --git a/include/verilated_types.h b/include/verilated_types.h index 0f9ea4a02..0a92725a9 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -191,7 +192,18 @@ public: //=================================================================== // SystemVerilog event type -class VlEvent final { +class VlEventBase VL_NOT_FINAL { +public: + virtual ~VlEventBase() = default; + + virtual void fire() = 0; + virtual bool isFired() const = 0; + virtual bool isTriggered() const = 0; + virtual void clearFired() = 0; + virtual void clearTriggered() = 0; +}; + +class VlEvent final : public VlEventBase { // MEMBERS bool m_fired = false; // Fired on this scheduling iteration bool m_triggered = false; // Triggered state of event persisting until next time step @@ -199,20 +211,50 @@ class VlEvent final { public: // CONSTRUCTOR VlEvent() = default; - ~VlEvent() = default; + ~VlEvent() override = default; + friend std::string VL_TO_STRING(const VlEvent& e); + friend class VlAssignableEvent; // METHODS - void fire() { m_fired = m_triggered = true; } - bool isFired() const { return m_fired; } - bool isTriggered() const { return m_triggered; } - void clearFired() { m_fired = false; } - void clearTriggered() { m_triggered = false; } + void fire() override { m_fired = m_triggered = true; } + bool isFired() const override { return m_fired; } + bool isTriggered() const override { return m_triggered; } + void clearFired() override { m_fired = false; } + void clearTriggered() override { m_triggered = false; } }; +class VlAssignableEvent final : public std::shared_ptr, public VlEventBase { +public: + // Constructor + VlAssignableEvent() + : std::shared_ptr(new VlEvent) {} + ~VlAssignableEvent() override = default; + + // METHODS + void fire() override { (*this)->m_fired = (*this)->m_triggered = true; } + bool isFired() const override { return (*this)->m_fired; } + bool isTriggered() const override { return (*this)->m_triggered; } + void clearFired() override { (*this)->m_fired = false; } + void clearTriggered() override { (*this)->m_triggered = false; } +}; + +inline std::string VL_TO_STRING(const VlEventBase& e); + inline std::string VL_TO_STRING(const VlEvent& e) { return std::string{"triggered="} + (e.isTriggered() ? "true" : "false"); } +inline std::string VL_TO_STRING(const VlAssignableEvent& e) { + return "&{ " + VL_TO_STRING(*e) + " }"; +} + +inline std::string VL_TO_STRING(const VlEventBase& e) { + if (const VlAssignableEvent& assignable = dynamic_cast(e)) { + return VL_TO_STRING(assignable); + } + return std::string{"triggered="} + (e.isTriggered() ? "true" : "false"); +} + //=================================================================== // Random diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index 8ac37eb1b..e96840b2e 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -574,8 +574,8 @@ void VerilatedVcdBuffer::finishLine(uint32_t code, char* writep) { // so always inline them. VL_ATTR_ALWINLINE -void VerilatedVcdBuffer::emitEvent(uint32_t code, VlEvent newval) { - const bool triggered = newval.isTriggered(); +void VerilatedVcdBuffer::emitEvent(uint32_t code, const VlEventBase* newval) { + const bool triggered = newval->isTriggered(); // TODO : It seems that untriggered events are not filtered // should be tested before this last step if (triggered) { diff --git a/include/verilated_vcd_c.h b/include/verilated_vcd_c.h index 67b077364..a81bae28a 100644 --- a/include/verilated_vcd_c.h +++ b/include/verilated_vcd_c.h @@ -214,7 +214,7 @@ class VerilatedVcdBuffer VL_NOT_FINAL { // Implementation of VerilatedTraceBuffer interface // Implementations of duck-typed methods for VerilatedTraceBuffer. These are // called from only one place (the full* methods), so always inline them. - VL_ATTR_ALWINLINE void emitEvent(uint32_t code, VlEvent newval); + VL_ATTR_ALWINLINE void emitEvent(uint32_t code, const VlEventBase* newval); VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval); VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits); VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits); diff --git a/src/V3Ast.h b/src/V3Ast.h index 75cd1e151..3adf9580b 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -324,7 +324,7 @@ public: ET_BOTHEDGE, // POSEDGE | NEGEDGE (i.e.: 'edge' in Verilog) ET_POSEDGE, ET_NEGEDGE, - ET_EVENT, // VlEvent::isFired + ET_EVENT, // VlEventBase::isFired // Involving an expression ET_TRUE, // @@ -2016,6 +2016,7 @@ public: inline bool isDouble() const VL_MT_STABLE; inline bool isSigned() const VL_MT_STABLE; inline bool isString() const VL_MT_STABLE; + inline bool isEvent() const VL_MT_STABLE; // clang-format off VNUser user1u() const VL_MT_STABLE { diff --git a/src/V3AstInlines.h b/src/V3AstInlines.h index 3c40963b2..f47aa7be8 100644 --- a/src/V3AstInlines.h +++ b/src/V3AstInlines.h @@ -39,6 +39,10 @@ bool AstNode::isDouble() const VL_MT_STABLE { bool AstNode::isString() const VL_MT_STABLE { return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString(); } +bool AstNode::isEvent() const VL_MT_STABLE { + return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isEvent(); +} + bool AstNode::isSigned() const VL_MT_STABLE { return dtypep() && dtypep()->isSigned(); } bool AstNode::isClassHandleValue() const { diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 5545aa324..3ab69db0f 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -829,7 +829,7 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const { } else if (bdtypep->isProcessRef()) { info.m_type = "VlProcessRef"; } else if (bdtypep->isEvent()) { - info.m_type = "VlEvent"; + info.m_type = v3Global.assignsEvents() ? "VlAssignableEvent" : "VlEvent"; } else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width info.m_type = "CData" + bitvec; } else if (dtypep->widthMin() <= 16) { diff --git a/src/V3Cast.cpp b/src/V3Cast.cpp index 871ba5b16..c8181a118 100644 --- a/src/V3Cast.cpp +++ b/src/V3Cast.cpp @@ -190,7 +190,7 @@ private: && !VN_IS(backp, ArraySel) && !VN_IS(backp, StructSel) && !VN_IS(backp, RedXor) && (nodep->varp()->basicp() && !nodep->varp()->basicp()->isTriggerVec() && !nodep->varp()->basicp()->isForkSync() - && !nodep->varp()->basicp()->isProcessRef()) + && !nodep->varp()->basicp()->isProcessRef() && !nodep->varp()->basicp()->isEvent()) && backp->width() && castSize(nodep) != castSize(nodep->varp())) { // Cast vars to IData first, else below has upper bits wrongly set // CData x=3; out = (QData)(x<<30); diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index ea1ee4f7f..678cef81c 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -781,6 +781,7 @@ class EmitCTrace final : EmitCFunc { void emitTraceValue(AstTraceInc* nodep, int arrayindex) { if (AstVarRef* const varrefp = VN_CAST(nodep->valuep(), VarRef)) { AstVar* const varp = varrefp->varp(); + if (varp->isEvent()) { puts("&"); } puts("("); if (emitTraceIsScBigUint(nodep)) { puts("(uint32_t*)"); diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 85ccaae23..93d027a42 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -460,7 +460,13 @@ void EmitCSyms::emitSymHdr() { puts("uint32_t __Vm_baseCode = 0;" " ///< Used by trace routines when tracing multiple models\n"); } - if (v3Global.hasEvents()) puts("std::vector __Vm_triggeredEvents;\n"); + if (v3Global.hasEvents()) { + if (v3Global.assignsEvents()) { + puts("std::vector __Vm_triggeredEvents;\n"); + } else { + puts("std::vector __Vm_triggeredEvents;\n"); + } + } if (v3Global.hasClasses()) puts("VlDeleter __Vm_deleter;\n"); puts("bool __Vm_didInit = false;\n"); @@ -528,17 +534,29 @@ void EmitCSyms::emitSymHdr() { puts("const char* name() { return TOP.name(); }\n"); if (v3Global.hasEvents()) { - puts("void enqueueTriggeredEventForClearing(VlEvent& event) {\n"); + if (v3Global.assignsEvents()) { + puts("void enqueueTriggeredEventForClearing(VlAssignableEvent& event) {\n"); + } else { + puts("void enqueueTriggeredEventForClearing(VlEvent& event) {\n"); + } puts("#ifdef VL_DEBUG\n"); puts("if (VL_UNLIKELY(!event.isTriggered())) {\n"); puts("VL_FATAL_MT(__FILE__, __LINE__, __FILE__, \"event passed to " "'enqueueTriggeredEventForClearing' was not triggered\");\n"); puts("}\n"); puts("#endif\n"); - puts("__Vm_triggeredEvents.push_back(&event);\n"); + if (v3Global.assignsEvents()) { + puts("__Vm_triggeredEvents.push_back(event);\n"); + } else { + puts("__Vm_triggeredEvents.push_back(&event);\n"); + } puts("}\n"); puts("void clearTriggeredEvents() {\n"); - puts("for (const auto eventp : __Vm_triggeredEvents) eventp->clearTriggered();\n"); + if (v3Global.assignsEvents()) { + puts("for (auto& event : __Vm_triggeredEvents) event.clearTriggered();\n"); + } else { + puts("for (const auto eventp : __Vm_triggeredEvents) eventp->clearTriggered();\n"); + } puts("__Vm_triggeredEvents.clear();\n"); puts("}\n"); } diff --git a/src/V3Error.h b/src/V3Error.h index 3513c3902..acebf7e6b 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -195,8 +195,8 @@ public: "CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA", "CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG", "DECLFILENAME", "DEFPARAM", "DEPRECATED", - "ENCAPSULATED", "ENDLABEL", "ENUMVALUE", "EOFNEWLINE", "GENCLK", "GENUNNAMED", - "HIERBLOCK", + "ENCAPSULATED", "ENDLABEL", "ENUMVALUE", "EOFNEWLINE", "GENCLK", + "GENUNNAMED", "HIERBLOCK", "IFDEPTH", "IGNOREDRETURN", "IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE", "INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE", diff --git a/src/V3Global.h b/src/V3Global.h index 126392ad9..036a5515e 100644 --- a/src/V3Global.h +++ b/src/V3Global.h @@ -105,6 +105,7 @@ class V3Global final { std::atomic_int m_debugFileNumber{0}; // Number to append to debug files created bool m_assertDTypesResolved = false; // Tree should have dtypep()'s bool m_assertScoped = false; // Tree is scoped + bool m_assignsEvents = false; // Design uses assignments on SystemVerilog Events bool m_constRemoveXs = false; // Const needs to strip any Xs // Experimenting with always requiring heavy, see issue #2701 bool m_needTraceDumper = false; // Need __Vm_dumperp in symbols @@ -155,6 +156,8 @@ public: void needTraceDumper(bool flag) { m_needTraceDumper = flag; } bool dpi() const VL_MT_SAFE { return m_dpi; } void dpi(bool flag) { m_dpi = flag; } + bool assignsEvents() const { return m_assignsEvents; } + void setAssignsEvents() { m_assignsEvents = true; } bool hasEvents() const { return m_hasEvents; } void setHasEvents() { m_hasEvents = true; } bool hasClasses() const { return m_hasClasses; } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 30407d90d..57804cfb5 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -68,6 +68,8 @@ #include "V3Width.h" #include "V3Const.h" +#include "V3Error.h" +#include "V3Global.h" #include "V3MemberMap.h" #include "V3Number.h" #include "V3Randomize.h" @@ -4319,6 +4321,47 @@ private: return valuep; } + static void checkEventAssignement(const AstNodeAssign* const asgnp) { + string unsupEvtAsgn; + if (!usesDynamicScheduler(asgnp->lhsp())) unsupEvtAsgn = "to"; + if (asgnp->rhsp()->dtypep()->isEvent() && !usesDynamicScheduler(asgnp->rhsp())) { + unsupEvtAsgn += (unsupEvtAsgn.empty() ? "from" : " and from"); + } + if (!unsupEvtAsgn.empty()) { + asgnp->v3warn(E_UNSUPPORTED, "Assignement " + << unsupEvtAsgn + << " event in statically scheduled context.\n" + << asgnp->warnMore() + << "Static event " + "scheduling won't be able to handle this.\n" + << asgnp->warnMore() + << "... Suggest move the event into a " + "completely dynamic context, eg. a class, and " + "reference it only from such context."); + } + } + + static bool usesDynamicScheduler(AstNode* nodep) { + UASSERT_OBJ(nodep->dtypep()->isEvent(), nodep, "Node does not have an event dtype"); + + AstVarRef* vrefp; + while (true) { + vrefp = VN_CAST(nodep, VarRef); + if (vrefp) return usesDynamicScheduler(vrefp); + if (VN_IS(nodep, MemberSel)) { + return true; + } else if (AstNodeSel* selp = VN_CAST(nodep, NodeSel)) { + nodep = selp->fromp(); + } else { + return false; + } + } + } + + static bool usesDynamicScheduler(AstVarRef* vrefp) { + return VN_IS(vrefp->classOrPackagep(), Class) || vrefp->varp()->isFuncLocal(); + } + void visit(AstPatMember* nodep) override { AstNodeDType* const vdtypep = m_vup->dtypeNullp(); UASSERT_OBJ(vdtypep, nodep, "Pattern member type not assigned by AstPattern visitor"); @@ -4713,12 +4756,6 @@ private: iterateCheckAssign(nodep, "Assign RHS", nodep->rhsp(), FINAL, lhsDTypep); // if (debug()) nodep->dumpTree("- AssignOut: "); } - if (const AstBasicDType* const basicp = nodep->rhsp()->dtypep()->basicp()) { - if (basicp->isEvent()) { - // see t_event_copy.v for commentary on the mess involved - nodep->v3warn(E_UNSUPPORTED, "Unsupported: assignment of event data type"); - } - } if (auto* const controlp = nodep->timingControlp()) { if (VN_IS(m_ftaskp, Func)) { controlp->v3error("Timing controls are not legal in functions. Suggest use a task " @@ -4773,7 +4810,12 @@ private: newp->dtypeSetVoid(); nodep->replaceWith(newp->makeStmt()); VL_DO_DANGLING(pushDeletep(nodep), nodep); - // return; + return; + } + + if (nodep->hasDType() && nodep->dtypep()->isEvent()) { + checkEventAssignement(nodep); + v3Global.setAssignsEvents(); } } diff --git a/src/verilog.y b/src/verilog.y index 3d553f667..78c40b5c2 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3586,9 +3586,9 @@ statement_item: // IEEE: statement_item | yDISABLE idAny '.' idDotted ';' { $$ = nullptr; BBUNSUP($4, "Unsupported: disable with '.'"); } // // IEEE: event_trigger - | yP_MINUSGT idDotted/*hierarchical_identifier-event*/ ';' + | yP_MINUSGT expr ';' { $$ = new AstFireEvent{$1, $2, false}; } - | yP_MINUSGTGT delay_or_event_controlE idDotted/*hierarchical_identifier-event*/ ';' + | yP_MINUSGTGT delay_or_event_controlE expr ';' { $$ = new AstFireEvent{$1, $3, true}; } // // // IEEE: loop_statement diff --git a/test_regress/t/t_event_control_assign.pl b/test_regress/t/t_event_control_assign.pl new file mode 100755 index 000000000..06ac175ad --- /dev/null +++ b/test_regress/t/t_event_control_assign.pl @@ -0,0 +1,25 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2023 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 + +scenarios(simulator => 1); + +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + # Multithreading would cause a warning on event assignments + threads => 1, + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_event_control_assign.v b/test_regress/t/t_event_control_assign.v new file mode 100644 index 000000000..980bd9ef8 --- /dev/null +++ b/test_regress/t/t_event_control_assign.v @@ -0,0 +1,68 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + + +int evt_recv_cnt; +int new_evt_recv_cnt; + +module t(); + + class Foo; + event evt1; + + task automatic send_evt(); + fork + #10 begin ->evt1; end + begin + event new_event; + #20; + // This should cause an event merge but for now we don't support that. + evt1 = new_event; + end + #30 begin + @evt1 $display("Received a new event"); + new_evt_recv_cnt++; + end + join_none + endtask + + task wait_for_event(); + fork begin + @evt1 $display("Received evt1"); + evt_recv_cnt++; + end join_none + endtask + + endclass + + initial begin + Foo foo1; + foo1 = new; + + evt_recv_cnt = 0; + new_evt_recv_cnt = 0; + + for (int i = 0; i < 4; i++) begin + foo1.wait_for_event(); + #10; + foo1.send_evt(); + #90; + $display("- end of iteration -"); + if (evt_recv_cnt != i + 1) + $stop; + if (new_evt_recv_cnt != i) + $stop; + end + + if (evt_recv_cnt != 4) + $stop; + if (new_evt_recv_cnt != 3) + $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_event_control_pass.pl b/test_regress/t/t_event_control_pass.pl new file mode 100755 index 000000000..2f8329791 --- /dev/null +++ b/test_regress/t/t_event_control_pass.pl @@ -0,0 +1,24 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2023 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 + +scenarios(simulator => 1); + +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + threads => 1, + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_event_control_pass.v b/test_regress/t/t_event_control_pass.v new file mode 100644 index 000000000..5304a829b --- /dev/null +++ b/test_regress/t/t_event_control_pass.v @@ -0,0 +1,51 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +class Bar; + event evt; + + task automatic wait_for_event(); + @evt; + endtask + + function automatic event get_event(); + return evt; + endfunction +endclass + +class Foo; + task automatic send_event(Bar b); + ->b.get_event(); + endtask +endclass + +bit got_event; + +module t(); + + initial begin + Bar bar; + Foo foo; + foo = new; + bar = new; + got_event = 0; + + fork + begin + bar.wait_for_event(); + $display("Got the event!"); + got_event = 1; + end + #10 foo.send_event(bar); + join + + #99; + if (!got_event) + $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_event_copy.out b/test_regress/t/t_event_copy.out index 6be6494fe..8784c2883 100644 --- a/test_regress/t/t_event_copy.out +++ b/test_regress/t/t_event_copy.out @@ -1,10 +1,20 @@ -%Error-UNSUPPORTED: t/t_event_copy.v:100:13: Unsupported: assignment of event data type +%Error-UNSUPPORTED: t/t_event_copy.v:100:13: Assignement to and from event in statically scheduled context. : ... note: In instance 't' + : Static event scheduling won't be able to handle this. + : ... Suggest move the event into a completely dynamic context, eg. a class, and reference it only from such context. 100 | e4 = e3; | ^ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_event_copy.v:101:13: Unsupported: assignment of event data type +%Error-UNSUPPORTED: t/t_event_copy.v:101:13: Assignement to and from event in statically scheduled context. : ... note: In instance 't' + : Static event scheduling won't be able to handle this. + : ... Suggest move the event into a completely dynamic context, eg. a class, and reference it only from such context. 101 | e3 = e2; | ^ +%Error-UNSUPPORTED: t/t_event_copy.v:128:13: Assignement to event in statically scheduled context. + : ... note: In instance 't' + : Static event scheduling won't be able to handle this. + : ... Suggest move the event into a completely dynamic context, eg. a class, and reference it only from such context. + 128 | e3 = null; + | ^ %Error: Exiting due to diff --git a/test_regress/t/t_event_copy.pl b/test_regress/t/t_event_copy.pl index be66c40e6..bce8f74ca 100755 --- a/test_regress/t/t_event_copy.pl +++ b/test_regress/t/t_event_copy.pl @@ -13,6 +13,7 @@ scenarios(simulator => 1); compile( fails => $Self->{vlt_all}, expect_filename => $Self->{golden_filename}, + threads => 1, ); execute( diff --git a/test_regress/t/t_uvm_pkg_todo.vh b/test_regress/t/t_uvm_pkg_todo.vh index edaf36b07..a2a4c6ff7 100644 --- a/test_regress/t/t_uvm_pkg_todo.vh +++ b/test_regress/t/t_uvm_pkg_todo.vh @@ -7524,9 +7524,7 @@ virtual class uvm_event_base extends uvm_object; event e; if (wakeup) ->m_event; -//TODO issue #4468 - Fix UVM assignment of event data types -//TODO %Error-UNSUPPORTED: t/t_uvm_pkg_todo.vh:7477:25: Unsupported: assignment of event data type -//TODO m_event = e; + m_event = e; num_waiters = 0; on = 0; trigger_time = 0; @@ -7547,9 +7545,7 @@ virtual class uvm_event_base extends uvm_object; uvm_event_base e; super.do_copy(rhs); if(!$cast(e, rhs) || (e==null)) return; -//TODO issue #4468 - Fix UVM assignment of event data types -//TODO %Error-UNSUPPORTED: t/t_uvm_pkg_todo.vh:7498:25: Unsupported: assignment of event data type -//TODO m_event = e.m_event; + m_event = e.m_event; num_waiters = e.num_waiters; on = e.on; trigger_time = e.trigger_time;