From 83a0085c4d799292350254c437c834d3fa6d302b Mon Sep 17 00:00:00 2001 From: Aleksander Kiryk Date: Fri, 20 Oct 2023 13:13:57 +0200 Subject: [PATCH] Support `wait fork` (#4586) --- include/verilated_types.h | 12 +++++++++--- src/V3AstNodeExpr.h | 1 + src/V3AstNodeOther.h | 1 + src/V3EmitCFunc.h | 2 +- src/V3EmitV.cpp | 2 +- src/V3Timing.cpp | 18 ++++++++++++++++-- src/V3Width.cpp | 12 ++++++++++-- test_regress/t/t_uvm_pkg_todo.vh | 4 +--- test_regress/t/t_wait_fork.out | 6 ------ test_regress/t/t_wait_fork.pl | 11 +++++++---- test_regress/t/t_wait_fork.v | 11 +++++++---- 11 files changed, 54 insertions(+), 26 deletions(-) delete mode 100644 test_regress/t/t_wait_fork.out diff --git a/include/verilated_types.h b/include/verilated_types.h index b296689d9..da517c372 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -120,15 +120,21 @@ public: void attach(VlProcess* childp) { m_children.insert(childp); } void detach(VlProcess* childp) { m_children.erase(childp); } - int state() { return m_state; } + int state() const { return m_state; } void state(int s) { m_state = s; } void disable() { state(KILLED); - disable_fork(); + disableFork(); } - void disable_fork() { + void disableFork() { for (VlProcess* childp : m_children) childp->disable(); } + bool completed() const { return state() == FINISHED || state() == KILLED; } + bool completedFork() const { + for (const VlProcess* const childp : m_children) + if (!childp->completed()) return false; + return true; + } }; inline std::string VL_TO_STRING(const VlProcessRef& p) { return std::string("process"); } diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 37a360170..f9de8e512 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -617,6 +617,7 @@ public: string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } bool same(const AstNode* /*samep*/) const override { return true; } + bool isPure() override { return pure(); } bool pure() const { return m_pure; } void pure(bool flag) { m_pure = flag; } }; diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 568a0f5b8..45946401b 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -3325,6 +3325,7 @@ public: explicit AstWaitFork(FileLine* fl) : ASTGEN_SUPER_WaitFork(fl) {} ASTGEN_MEMBERS_AstWaitFork; + bool isTimingControl() const override { return true; } }; class AstWhile final : public AstNodeStmt { // @astgen op1 := precondsp : List[AstNode] diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 0de6e001b..dbf5026a0 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -613,7 +613,7 @@ public: puts("]);\n"); } } - void visit(AstDisableFork* nodep) override { puts("vlProcess->disable_fork();\n"); } + void visit(AstDisableFork* nodep) override { puts("vlProcess->disableFork();\n"); } void visit(AstCReturn* nodep) override { puts("return ("); iterateAndNextConstNull(nodep->lhsp()); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 38bca92ba..d8fc2b42e 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -408,7 +408,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { void visit(AstCExpr* nodep) override { putfs(nodep, "$_CEXPR("); iterateAndNextConstNull(nodep->exprsp()); - puts(");\n"); + puts(")"); } void visit(AstUCStmt* nodep) override { putfs(nodep, "$c("); diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index 65948a526..ebeeb7fe9 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -275,6 +275,10 @@ private: visit(static_cast(nodep)); addFlags(m_procp, T_FORCES_PROC | T_NEEDS_PROC); } + void visit(AstWaitFork* nodep) override { + visit(static_cast(nodep)); + addFlags(m_procp, T_FORCES_PROC | T_NEEDS_PROC); + } void visit(AstCFunc* nodep) override { VL_RESTORER(m_procp); m_procp = nodep; @@ -573,8 +577,11 @@ private: // 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 { - return m_classp || nodep->exists([](const AstNodeVarRef* const refp) { - return refp->varp()->isFuncLocal(); + return m_classp || nodep->exists([](AstNode* const nodep) { + if (AstNodeVarRef* varp = VN_CAST(nodep, NodeVarRef)) { + return varp->varp()->isFuncLocal(); + } + return !nodep->isPure(); }); } // Returns true if the given trigger expression needs a destructive post update after trigger @@ -1050,6 +1057,13 @@ private: // var alwaysp->addNextHere(nodep); } + void visit(AstWaitFork* nodep) override { + AstCExpr* const exprp = new AstCExpr{nodep->fileline(), "vlProcess->completedFork()", 1}; + exprp->pure(false); + AstWait* const waitp = new AstWait{nodep->fileline(), exprp, nullptr}; + nodep->replaceWith(waitp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + } void visit(AstWait* nodep) override { // Wait on changed events related to the vars in the wait statement FileLine* const flp = nodep->fileline(); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index f5f9c85e8..30407d90d 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -693,6 +693,7 @@ private: if (nodep->fileline()->timingOn()) { if (v3Global.opt.timing().isSetFalse()) { nodep->v3warn(E_NOTIMING, "Support for disable fork statement requires --timing"); + VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); } else if (!v3Global.opt.timing().isSetTrue()) { nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how " << "disable fork should be handled"); @@ -700,8 +701,15 @@ private: } } void visit(AstWaitFork* nodep) override { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: wait fork statements"); - VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + if (nodep->fileline()->timingOn()) { + if (v3Global.opt.timing().isSetFalse()) { + nodep->v3warn(E_NOTIMING, "Support for disable fork statement requires --timing"); + VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + } else if (!v3Global.opt.timing().isSetTrue()) { + nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how " + << "disable fork should be handled"); + } + } } void visit(AstToLowerN* nodep) override { if (m_vup->prelim()) { diff --git a/test_regress/t/t_uvm_pkg_todo.vh b/test_regress/t/t_uvm_pkg_todo.vh index 0d71f3de3..c798374cc 100644 --- a/test_regress/t/t_uvm_pkg_todo.vh +++ b/test_regress/t/t_uvm_pkg_todo.vh @@ -14809,9 +14809,7 @@ class uvm_objection extends uvm_report_object; if (m_trace_mode) m_report(obj,source_obj,description,count,"all_dropped"); all_dropped(obj,source_obj,description, count); -//TODO issue #4465 - Support wait fork -//TODO %Error-UNSUPPORTED: t/t_uvm_pkg_todo.vh:14761:7: Unsupported: wait fork statements -//TODO wait fork; + wait fork; if (m_source_count.exists(obj) && m_source_count[obj] == 0) m_source_count.delete(obj); if (m_total_count.exists(obj) && m_total_count[obj] == 0) diff --git a/test_regress/t/t_wait_fork.out b/test_regress/t/t_wait_fork.out deleted file mode 100644 index b9cf141c8..000000000 --- a/test_regress/t/t_wait_fork.out +++ /dev/null @@ -1,6 +0,0 @@ -%Error-UNSUPPORTED: t/t_wait_fork.v:17:7: Unsupported: wait fork statements - : ... note: In instance 't' - 17 | wait fork; - | ^~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to diff --git a/test_regress/t/t_wait_fork.pl b/test_regress/t/t_wait_fork.pl index 0ca21a5ca..0db50fc52 100755 --- a/test_regress/t/t_wait_fork.pl +++ b/test_regress/t/t_wait_fork.pl @@ -10,10 +10,13 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(linter => 1); -lint( - verilator_flags2 => ['--lint-only --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_wait_fork.v b/test_regress/t/t_wait_fork.v index f404a53c2..65fbff20a 100644 --- a/test_regress/t/t_wait_fork.v +++ b/test_regress/t/t_wait_fork.v @@ -8,13 +8,16 @@ module t(/*AUTOARG*/); logic never; + integer n = 0; + initial begin - fork - #10; - #10; - join_none disable fork; + fork + #10 if (n != 0) $stop; else n = 1; + #15 if (n != 1) $stop; else n = 2; + join_none wait fork; + if (n != 2) $stop; $write("*-* All Finished *-*\n"); $finish; end