diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index d3f0aee1e8f..a8aa63160cb 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1197,6 +1197,8 @@ class AstNetlist final : public AstNode { AstVar* m_delaySchedulerp = nullptr; // The delay scheduler variable AstVarScope* m_nbaEventp = nullptr; // The NBA event variable AstVarScope* m_nbaEventTriggerp = nullptr; // If set to 1, the NBA event should get triggered + AstVarScope* m_inactiveEventp = nullptr; // The Inactive event variable + AstVarScope* m_inactiveEventTriggerp = nullptr; // If set to 1, the Inactive event should get triggered AstTopScope* m_topScopep = nullptr; // The singleton AstTopScope under the top module VTimescale m_timeunit; // Global time unit VTimescale m_timeprecision; // Global time precision @@ -1230,6 +1232,10 @@ class AstNetlist final : public AstNode { void nbaEventp(AstVarScope* const varScopep) { m_nbaEventp = varScopep; } AstVarScope* nbaEventTriggerp() const { return m_nbaEventTriggerp; } void nbaEventTriggerp(AstVarScope* const varScopep) { m_nbaEventTriggerp = varScopep; } + AstVarScope* inactiveEventp() const { return m_inactiveEventp; } + void inactiveEventp(AstVarScope* const varScopep) { m_inactiveEventp = varScopep; } + AstVarScope* inactiveEventTriggerp() const { return m_inactiveEventTriggerp; } + void inactiveEventTriggerp(AstVarScope* const varScopep) { m_inactiveEventTriggerp = varScopep; } void stdPackagep(AstPackage* const packagep) { m_stdPackagep = packagep; } AstPackage* stdPackagep() const { return m_stdPackagep; } AstTopScope* topScopep() const { return m_topScopep; } diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index cab3165f99a..a0a72047c5f 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -611,6 +611,7 @@ struct EvalLoop { AstVarScope* continuep = nullptr; // The loop itself and statements around it AstNodeStmt* stmtsp = nullptr; + AstNodeIf* ifp = nullptr; }; //============================================================================ @@ -653,6 +654,7 @@ EvalLoop makeEvalLoop(AstNetlist* netlistp, const string& tag, const string& nam AstNodeStmt* nodep = nullptr; if (v3Global.opt.profExec()) nodep = profExecSectionPush(flp, "loop " + tag); nodep = AstNode::addNext(nodep, setVar(counterp, 0)); + AstIf* ifp; nodep->addNext(buildLoop(netlistp, continuep, [&](AstWhile* loopp) { // Compute triggers loopp->addStmtsp(computeTriggers()); @@ -661,7 +663,7 @@ EvalLoop makeEvalLoop(AstNetlist* netlistp, const string& tag, const string& nam AstVarRef* const refp = new AstVarRef{flp, trigVscp, VAccess::READ}; AstCMethodHard* const callp = new AstCMethodHard{flp, refp, "any"}; callp->dtypeSetBit(); - AstIf* const ifp = new AstIf{flp, callp}; + ifp = new AstIf{flp, callp}; loopp->addStmtsp(ifp); ifp->addThensp(setVar(continuep, 1)); @@ -707,7 +709,7 @@ EvalLoop makeEvalLoop(AstNetlist* netlistp, const string& tag, const string& nam if (v3Global.opt.profExec()) nodep->addNext(profExecSectionPop(flp)); - return {counterp, continuep, nodep}; + return {counterp, continuep, nodep, ifp}; } //============================================================================ @@ -953,6 +955,22 @@ void createEval(AstNetlist* netlistp, // return resultp; }); + // If the Inactive event exists, trigger it in 'nba' + if (netlistp->inactiveEventp()) { + UASSERT(netlistp->inactiveEventTriggerp(), "NBA event trigger var should exist"); + AstIf* const ifp + = new AstIf{flp, new AstVarRef{flp, netlistp->inactiveEventTriggerp(), VAccess::READ}}; + ifp->addThensp(setVar(activeEvalLoop.continuep, 1)); + ifp->addThensp(setVar(netlistp->inactiveEventTriggerp(), 0)); + AstCMethodHard* const firep = new AstCMethodHard{ + flp, new AstVarRef{flp, netlistp->inactiveEventp(), VAccess::WRITE}, "fire"}; + firep->dtypeSetVoid(); + ifp->addThensp(firep->makeStmt()); + activeEvalLoop.ifp->addElsesp(ifp); + netlistp->inactiveEventp(nullptr); + netlistp->inactiveEventTriggerp(nullptr); + } + // Create the NBA eval loop. This uses the Active eval loop in the trigger section. const auto& nbaEvalLoop = makeEvalLoop( netlistp, "nba", "NBA", nbaKit.m_vscp, nbaKit.m_dumpp, diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index 0d170c90c81..990c5a0d12e 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -605,6 +605,33 @@ class TimingControlVisitor final : public VNVisitor { new AstVarRef{flp, m_netlistp->nbaEventTriggerp(), VAccess::WRITE}, new AstConst{flp, AstConst::BitTrue{}}}; } + // Creates the event variable to trigger in NBA region + AstEventControl* createInactiveEventControl(FileLine* flp) { + if (!m_netlistp->inactiveEventp()) { + auto* const inactiveEventDtp = new AstBasicDType{ + m_scopeTopp->fileline(), VBasicDTypeKwd::EVENT, VSigning::UNSIGNED}; + m_netlistp->typeTablep()->addTypesp(inactiveEventDtp); + m_netlistp->inactiveEventp( + m_scopeTopp->createTemp("__VinactiveEvent", inactiveEventDtp)); + v3Global.setHasEvents(); + } + return new AstEventControl{ + flp, + new AstSenTree{flp, new AstSenItem{flp, VEdgeType::ET_EVENT, + new AstVarRef{flp, m_netlistp->inactiveEventp(), + VAccess::READ}}}, + nullptr}; + } + // Creates the variable that, if set, causes the NBA event to be triggered + AstAssign* createInactiveEventTriggerAssignment(FileLine* flp) { + if (!m_netlistp->inactiveEventTriggerp()) { + m_netlistp->inactiveEventTriggerp( + m_scopeTopp->createTemp("__VinactiveEventTrigger", 1)); + } + return new AstAssign{ + flp, new AstVarRef{flp, m_netlistp->inactiveEventTriggerp(), VAccess::WRITE}, + new AstConst{flp, AstConst::BitTrue{}}}; + } // Returns true if we are under a class or the given tree has any references to locals. These // are cases where static, globally-evaluated triggers are not suitable. bool needDynamicTrigger(AstNode* const nodep) const { @@ -877,8 +904,14 @@ class TimingControlVisitor final : public VNVisitor { AstNodeExpr* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack()); auto* const constp = VN_CAST(valuep, Const); if (constp && constp->isZero()) { - nodep->v3warn(ZERODLY, "Unsupported: #0 delays do not schedule process resumption in " - "the Inactive region"); + AstEventControl* const eventControlp = createInactiveEventControl(flp); + if (AstNode* const stmtsp = nodep->stmtsp()) { + eventControlp->addStmtsp(stmtsp->unlinkFrBackWithNext()); + } + nodep->addNextHere(eventControlp); + nodep->replaceWith(createInactiveEventTriggerAssignment(flp)); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + return; } else { // Scale the delay if (valuep->dtypep()->isDouble()) { diff --git a/test_regress/t/t_timing_zerodly_unsup.out b/test_regress/t/t_timing_zerodly.out similarity index 100% rename from test_regress/t/t_timing_zerodly_unsup.out rename to test_regress/t/t_timing_zerodly.out diff --git a/test_regress/t/t_timing_zerodly_unsup.pl b/test_regress/t/t_timing_zerodly.pl similarity index 76% rename from test_regress/t/t_timing_zerodly_unsup.pl rename to test_regress/t/t_timing_zerodly.pl index 8ab3c0995a6..9e4a1a1ffa0 100755 --- a/test_regress/t/t_timing_zerodly_unsup.pl +++ b/test_regress/t/t_timing_zerodly.pl @@ -8,12 +8,15 @@ # Version 2.0. # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -scenarios(linter => 1); +scenarios(simulator => 1); -lint( - verilator_flags2 => ["--timing"], - fails => 1, - expect_filename => $Self->{golden_filename}, +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); + +execute( + check_finished => 1, ); ok(1); diff --git a/test_regress/t/t_timing_zerodly_unsup.v b/test_regress/t/t_timing_zerodly.v similarity index 71% rename from test_regress/t/t_timing_zerodly_unsup.v rename to test_regress/t/t_timing_zerodly.v index 7fba70252d8..dd8f499672f 100644 --- a/test_regress/t/t_timing_zerodly_unsup.v +++ b/test_regress/t/t_timing_zerodly.v @@ -9,8 +9,10 @@ module t; logic v = 0; initial #1 begin fork - #0 if (v) $finish; - else $stop; + #0 if (v) begin + $write("*-* All Finished *-*\n"); + $finish; + end else $stop; join_none ->e; end