From d864057a607eba75c09093000e053a65a37c1b34 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Fri, 24 Oct 2025 15:00:07 +0200 Subject: [PATCH] Internals: Refactor AstNodeBlock representation (#6280) (#6588) Internals: Refactor AstNodeBlock representation (#6280) AstNodeBlock now has 2 child lists: 'declsp' to hold declarations within the block, and 'stmtsp' to hold the procedural statements. AstBegin is then just a simple subtype of AstNodeBlock. AstFork is a proper superset of AstNodeBlock (and also AstBegin), and adds 'forksp' which hold the parallel statements. Having the sequential 'stmtsp' in AstFork is required to properly implement variable initializers in fork blocks (IEEE 1800-2023 9.3.2), this makes that clear, while also separating the non AstNodeStmt declarations (for #6280). The actual fork branches in 'AstFork::forkps()' are all AstBegin nodes. This is required as lowering stages will introduce additional statements in each parallel branch. (We used to wrap AstFork statements into AstBegin in 3 different places, now they always are AstBegin and this is enforced via the type checker/V3Broken). Also fixes incorrect disabling of forked processes from within the `fork`. --- src/V3Assert.cpp | 8 +- src/V3AssertPre.cpp | 4 +- src/V3AstAttr.h | 1 + src/V3AstNodeStmt.h | 44 ++++---- src/V3AstNodes.cpp | 2 +- src/V3Begin.cpp | 73 ++++++------ src/V3Fork.cpp | 124 +++++++++------------ src/V3Hasher.cpp | 10 +- src/V3InstrCount.cpp | 3 +- src/V3LinkDot.cpp | 32 +++--- src/V3LinkJump.cpp | 104 ++++++++--------- src/V3LinkParse.cpp | 46 +++++--- src/V3ParseGrammar.h | 23 ++-- src/V3SchedTiming.cpp | 4 +- src/V3StackCount.cpp | 3 +- src/V3Timing.cpp | 43 ++++--- src/V3Width.cpp | 11 +- src/verilog.y | 97 +++++++--------- test_regress/t/t_constraint_json_only.out | 2 +- test_regress/t/t_disable_inside.v | 97 +++++++++++++++- test_regress/t/t_dump_json.out | 90 +++++++-------- test_regress/t/t_fork.out | 6 +- test_regress/t/t_fork_initial.v | 9 +- test_regress/t/t_json_only_tag.out | 2 +- test_regress/t/t_lint_block_redecl_bad.out | 8 +- 25 files changed, 467 insertions(+), 379 deletions(-) diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 7f2c31754..31ecce928 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -298,10 +298,10 @@ class AssertVisitor final : public VNVisitor { AstNode* const precondps = pExpr->precondp(); UASSERT_OBJ(precondps, pExpr, "Should have precondition"); precondps->unlinkFrBackWithNext()->addNext(ifp); - AstNodeStmt* const assertOnp - = newIfAssertOn(precondps, nodep->directive(), nodep->type()); - AstFork* const forkp = new AstFork{precondps->fileline(), "", assertOnp}; - forkp->joinType(VJoinType::JOIN_NONE); + AstNodeStmt* const aonp = newIfAssertOn(precondps, nodep->directive(), nodep->type()); + FileLine* const flp = precondps->fileline(); + AstFork* const forkp = new AstFork{flp, VJoinType::JOIN_NONE}; + forkp->addForksp(new AstBegin{flp, "", aonp, true}); return forkp; } diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index d697767c5..1c7885a67 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -247,8 +247,8 @@ private: // Create a fork so that this AlwaysObserved can be retriggered before the // assignment happens. Also then it can be combo, avoiding the need for creating // new triggers. - AstFork* const forkp = new AstFork{flp, "", ifp}; - forkp->joinType(VJoinType::JOIN_NONE); + AstFork* const forkp = new AstFork{flp, VJoinType::JOIN_NONE}; + forkp->addForksp(new AstBegin{flp, "", ifp, true}); // Use Observed for this to make sure we do not miss the event m_clockingp->addNextHere(new AstAlwaysObserved{ flp, new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}, forkp}); diff --git a/src/V3AstAttr.h b/src/V3AstAttr.h index bd52bd825..0645f9892 100644 --- a/src/V3AstAttr.h +++ b/src/V3AstAttr.h @@ -1300,6 +1300,7 @@ public: : m_e{_e} {} explicit VJoinType(int _e) : m_e(static_cast(_e)) {} // Need () or GCC 4.8 false warning + constexpr operator en() const { return m_e; } const char* ascii() const { static const char* const names[] = {"JOIN", "JOIN_ANY", "JOIN_NONE"}; return names[m_e]; diff --git a/src/V3AstNodeStmt.h b/src/V3AstNodeStmt.h index 444a129d7..0825e6b27 100644 --- a/src/V3AstNodeStmt.h +++ b/src/V3AstNodeStmt.h @@ -69,17 +69,16 @@ public: }; class AstNodeBlock VL_NOT_FINAL : public AstNodeStmt { // A Begin/fork block - // @astgen op2 := stmtsp : List[AstNode] // Parents: statement + // @astgen op1 := declsp : List[AstNode] // Declarations inside block + // @astgen op2 := stmtsp : List[AstNode] // Sequential statements inside block string m_name; // Name of block bool m_unnamed; // Originally unnamed (name change does not affect this) protected: - AstNodeBlock(VNType t, FileLine* fl, const string& name, AstNode* stmtsp) + AstNodeBlock(VNType t, FileLine* fl, const string& name) : AstNodeStmt{t, fl} - , m_name{name} { - addStmtsp(stmtsp); - m_unnamed = (name == ""); - } + , m_name{name} + , m_unnamed{name == ""} {} public: ASTGEN_MEMBERS_AstNodeBlock; @@ -1351,17 +1350,16 @@ public: // === AstNodeBlock === class AstBegin final : public AstNodeBlock { - // A Begin/end named block, only exists shortly after parsing until linking - // Parents: statement - + // A 'begin'/'end' named block. bool m_needProcess : 1; // Uses VlProcess const bool m_implied : 1; // Not inserted by user public: - // Node that puts name into the output stream AstBegin(FileLine* fl, const string& name, AstNode* stmtsp, bool implied) - : ASTGEN_SUPER_Begin(fl, name, stmtsp) + : ASTGEN_SUPER_Begin(fl, name) , m_needProcess{false} - , m_implied{implied} {} + , m_implied{implied} { + addStmtsp(stmtsp); + } ASTGEN_MEMBERS_AstBegin; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; @@ -1370,21 +1368,25 @@ public: bool implied() const { return m_implied; } }; class AstFork final : public AstNodeBlock { - // A fork named block - // @astgen op1 := initsp : List[AstNode] - // Parents: statement - // Children: statements - VJoinType m_joinType; // Join keyword type + // A 'fork'/'join*' named block. Note that this is a strict superset of + // AstBegin, and it does contain AstNodeBlock::stmtsp(), which are + // statements that execute sequentially before the parallel statements are + // spawned. This is necessary to implement things like local variable + // initializers properly. The parallel statements inside the fork must all + // be AstBegin, as lowering stages will introduce additional statements to + // be executed sequentially within eaach fork branch. + // + // @astgen op3 := forksp : List[AstBegin] + const VJoinType m_joinType; // Join keyword type public: - // Node that puts name into the output stream - AstFork(FileLine* fl, const string& name, AstNode* stmtsp) - : ASTGEN_SUPER_Fork(fl, name, stmtsp) {} + AstFork(FileLine* fl, VJoinType joinType, const string& name = "") + : ASTGEN_SUPER_Fork(fl, name) + , m_joinType{joinType} {} ASTGEN_MEMBERS_AstFork; bool isTimingControl() const override { return !joinType().joinNone(); } void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; VJoinType joinType() const { return m_joinType; } - void joinType(const VJoinType& flag) { m_joinType = flag; } }; // === AstNodeCoverOrAssert === diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index a23eb6c98..72b64604b 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -3039,7 +3039,7 @@ void AstCoverInc::dump(std::ostream& str) const { void AstCoverInc::dumpJson(std::ostream& str) const { dumpJsonGen(str); } void AstFork::dump(std::ostream& str) const { this->AstNodeBlock::dump(str); - if (!joinType().join()) str << " [" << joinType() << "]"; + str << " [" << joinType() << "]"; } void AstFork::dumpJson(std::ostream& str) const { dumpJsonStr(str, "joinType", joinType().ascii()); diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 998be7a21..cfa58e3ed 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -80,32 +80,28 @@ class BeginVisitor final : public VNVisitor { string dot(const string& a, const string& b) { return VString::dot(a, "__DOT__", b); } - void dotNames(const std::string& name, FileLine* const flp, AstNode* stmtsp, - const char* const blockName) { + void dotNames(const std::string& name, FileLine* const flp, const char* const blockName) { UINFO(8, "nname " << m_namedScope); - if (name != "") { // Else unneeded unnamed block - // Create data for dotted variable resolution - string dottedname = name + "__DOT__"; // So always found - string::size_type pos; - while ((pos = dottedname.find("__DOT__")) != string::npos) { - const string ident = dottedname.substr(0, pos); - dottedname = dottedname.substr(pos + std::strlen("__DOT__")); - if (name != "") { - m_displayScope = dot(m_displayScope, ident); - m_namedScope = dot(m_namedScope, ident); - } - m_unnamedScope = dot(m_unnamedScope, ident); - // Create CellInline for dotted var resolution - if (!m_ftaskp) { - AstCellInline* const inlinep - = new AstCellInline{flp, m_unnamedScope, blockName}; - m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells - } + // If unneeded unnamed block, whatever that means :) + if (name == "") return; + + // Create data for dotted variable resolution + std::string dottedname = name + "__DOT__"; // So always found + std::string::size_type pos; + while ((pos = dottedname.find("__DOT__")) != std::string::npos) { + const std::string ident = dottedname.substr(0, pos); + dottedname = dottedname.substr(pos + std::strlen("__DOT__")); + if (name != "") { + m_displayScope = dot(m_displayScope, ident); + m_namedScope = dot(m_namedScope, ident); + } + m_unnamedScope = dot(m_unnamedScope, ident); + // Create CellInline for dotted var resolution + if (!m_ftaskp) { + AstCellInline* const inlinep = new AstCellInline{flp, m_unnamedScope, blockName}; + m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells } } - - // Remap var names and replace lower Begins - iterateAndNextNull(stmtsp); } void liftNode(AstNode* nodep) { @@ -125,21 +121,14 @@ class BeginVisitor final : public VNVisitor { // VISITORS void visit(AstFork* nodep) override { - // Keep begins in forks to group their statements together - VL_RESTORER(m_keepBegins); - m_keepBegins = true; - // If a statement is not a begin, wrap it in a begin. This fixes an issue when the - // statement is a task call that gets inlined later (or any other statement that gets - // replaced with multiple statements) - for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { - if (!VN_IS(stmtp, Begin)) { - AstBegin* const beginp = new AstBegin{stmtp->fileline(), "", nullptr, false}; - stmtp->replaceWith(beginp); - beginp->addStmtsp(stmtp); - stmtp = beginp; - } + dotNames(nodep->name(), nodep->fileline(), "__FORK__"); + iterateAndNextNull(nodep->stmtsp()); + { + // Keep begins in forks to group their statements together + VL_RESTORER(m_keepBegins); + m_keepBegins = true; + iterateAndNextNull(nodep->forksp()); } - dotNames(nodep->name(), nodep->fileline(), nodep->stmtsp(), "__FORK__"); nodep->name(""); } void visit(AstForeach* nodep) override { @@ -215,7 +204,8 @@ class BeginVisitor final : public VNVisitor { VL_RESTORER(m_namedScope); VL_RESTORER(m_unnamedScope); UASSERT_OBJ(!m_keepBegins, nodep, "Should be able to eliminate all AstGenBlock"); - dotNames(nodep->name(), nodep->fileline(), nodep->itemsp(), "__BEGIN__"); + dotNames(nodep->name(), nodep->fileline(), "__BEGIN__"); + iterateAndNextNull(nodep->itemsp()); // Repalce node with body then delete if (AstNode* const itemsp = nodep->itemsp()) { nodep->addNextHere(itemsp->unlinkFrBackWithNext()); @@ -231,7 +221,8 @@ class BeginVisitor final : public VNVisitor { { VL_RESTORER(m_keepBegins); m_keepBegins = false; - dotNames(nodep->name(), nodep->fileline(), nodep->stmtsp(), "__BEGIN__"); + dotNames(nodep->name(), nodep->fileline(), "__BEGIN__"); + iterateChildren(nodep); } // Cleanup @@ -240,6 +231,10 @@ class BeginVisitor final : public VNVisitor { return; } AstNode* addsp = nullptr; + if (AstNode* const declsp = nodep->declsp()) { + declsp->unlinkFrBackWithNext(); + addsp = AstNode::addNext(addsp, declsp); + } if (AstNode* const stmtsp = nodep->stmtsp()) { stmtsp->unlinkFrBackWithNext(); addsp = AstNode::addNext(addsp, stmtsp); diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index 9e6135690..30698b340 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -137,8 +137,8 @@ public: UASSERT_OBJ(m_instance.initialized(), m_procp, "No dynamic scope prototype"); UASSERT_OBJ(!linked(), m_instance.m_handlep, "Handle already linked"); - if (VN_IS(m_procp, Fork)) { - linkNodesOfFork(memberMap); + if (AstFork* const forkp = VN_CAST(m_procp, Fork)) { + linkNodesOfFork(memberMap, forkp); return; } @@ -221,32 +221,20 @@ private: new AstVarRef{m_procp->fileline(), m_instance.m_handlep, VAccess::WRITE}, newp}; } - void linkNodesOfFork(VMemberMap& memberMap) { - // Special case - - AstFork* const forkp = VN_AS(m_procp, Fork); - VNRelinker forkHandle; - forkp->unlinkFrBack(&forkHandle); - - AstBegin* const beginp = new AstBegin{ - forkp->fileline(), - "_Vwrapped_" + (forkp->name().empty() ? "" : forkp->name() + "_") + cvtToStr(m_id), - m_instance.m_handlep, true}; - forkHandle.relink(beginp); - - AstNode* const instAsgnp = instantiateDynScope(memberMap); - - beginp->stmtsp()->addNext(instAsgnp); - beginp->stmtsp()->addNext(forkp); - - if (forkp->initsp()) { - forkp->initsp()->foreach([forkp](AstAssign* asgnp) { - asgnp->unlinkFrBack(); - forkp->addHereThisAsNext(asgnp); - }); - } - UASSERT_OBJ(!forkp->initsp(), forkp, "Leftover nodes in block_item_declaration"); - + // Wrap Fork in Begin + void linkNodesOfFork(VMemberMap& memberMap, AstFork* forkp) { + // Replace the Fork with a Begin + const std::string name = "_Vwrapped_" // + + (forkp->name().empty() ? "" : forkp->name() + "_") // + + std::to_string(m_id); + AstBegin* const beginp = new AstBegin{forkp->fileline(), name, m_instance.m_handlep, true}; + forkp->replaceWith(beginp); + // Create the dynamic scope in the Begin + beginp->addStmtsp(instantiateDynScope(memberMap)); + // Move all sequential statements there + if (forkp->stmtsp()) beginp->addStmtsp(forkp->stmtsp()->unlinkFrBackWithNext()); + // Put the Fork back at the end of the Begin + beginp->addStmtsp(forkp); m_modp->addStmtsp(m_instance.m_classp); } @@ -386,25 +374,24 @@ class DynScopeVisitor final : public VNVisitor { const bool oldAfterTimingControl = m_afterTimingControl; ForkDynScopeFrame* framep = nullptr; - if (nodep->initsp()) framep = pushDynScopeFrame(nodep); + if (nodep->declsp() || nodep->stmtsp()) framep = pushDynScopeFrame(nodep); - for (AstNode* stmtp = nodep->initsp(); stmtp; stmtp = stmtp->nextp()) { - if (AstVar* const varp = VN_CAST(stmtp, Var)) { - // This can be probably optimized to detect cases in which dynscopes - // could be avoided - if (!framep->instance().initialized()) framep->createInstancePrototype(); - framep->captureVarInsert(varp); - bindNodeToDynScope(varp, framep); - } else { - AstAssign* const asgnp = VN_CAST(stmtp, Assign); - UASSERT_OBJ(asgnp, stmtp, - "Invalid node under block item initialization part of fork"); - bindNodeToDynScope(asgnp->lhsp(), framep); - iterate(asgnp->rhsp()); - } + // This can be probably optimized to detect cases in which dynscopes could be avoided + for (AstNode* declp = nodep->declsp(); declp; declp = declp->nextp()) { + AstVar* const varp = VN_CAST(declp, Var); + UASSERT_OBJ(varp, declp, "Invalid node under block item initialization part of fork"); + if (!framep->instance().initialized()) framep->createInstancePrototype(); + framep->captureVarInsert(varp); + bindNodeToDynScope(varp, framep); + } + for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + AstAssign* const asgnp = VN_CAST(stmtp, Assign); + UASSERT_OBJ(asgnp, stmtp, "Invalid node under block item initialization part of fork"); + bindNodeToDynScope(asgnp->lhsp(), framep); + iterate(asgnp->rhsp()); } - for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + for (AstNode* stmtp = nodep->forksp(); stmtp; stmtp = stmtp->nextp()) { m_afterTimingControl = false; iterate(stmtp); } @@ -470,11 +457,10 @@ class DynScopeVisitor final : public VNVisitor { })) { nodep->user2(true); // Put it in a fork to prevent lifetime issues with the local - AstBegin* const beginp = new AstBegin{nodep->fileline(), "", nullptr, false}; - AstFork* const forkp = new AstFork{nodep->fileline(), "", beginp}; - forkp->joinType(VJoinType::JOIN_NONE); + FileLine* const flp = nodep->fileline(); + AstFork* const forkp = new AstFork{flp, VJoinType::JOIN_NONE}; nodep->replaceWith(forkp); - beginp->addStmtsp(nodep); + forkp->addForksp(new AstBegin{flp, "", nodep, false}); UINFO(9, "assign new fork " << forkp); } else { visit(static_cast(nodep)); @@ -559,7 +545,9 @@ class ForkVisitor final : public VNVisitor { return varp; } - AstNodeStmt* taskify(AstNode* stmtp) { + // Wrap body of the given Begin (an AstFork branch), in an AstTask, and + // replace body with a call to that task. Returns true iff wrapped. + bool taskify(AstBegin* beginp) { // Visit statement to gather variables (And recursively process) VL_RESTORER(m_forkLocalsp); VL_RESTORER(m_capturedVarsp); @@ -567,33 +555,26 @@ class ForkVisitor final : public VNVisitor { m_forkLocalsp.clear(); m_capturedVarsp = nullptr; m_capturedArgsp = nullptr; - iterate(stmtp); + iterate(beginp); // No need to do it if no variabels are captured - if (m_forkLocalsp.empty() && !m_capturedVarsp && !v3Global.opt.fTaskifyAll()) { - return nullptr; - } + if (m_forkLocalsp.empty() && !m_capturedVarsp && !v3Global.opt.fTaskifyAll()) return false; // Create task holding the statement and repalce statement with call to that task - FileLine* const flp = stmtp->fileline(); + FileLine* const flp = beginp->fileline(); const std::string name = "__VforkTask_" + std::to_string(m_nForkTasks++); AstTask* const taskp = new AstTask{flp, name, m_capturedVarsp}; m_tasksp = AstNode::addNext(m_tasksp, taskp); + if (beginp->declsp()) taskp->addStmtsp(beginp->declsp()->unlinkFrBackWithNext()); + if (beginp->stmtsp()) taskp->addStmtsp(beginp->stmtsp()->unlinkFrBackWithNext()); AstTaskRef* const callp = new AstTaskRef{flp, taskp, m_capturedArgsp}; - AstNodeStmt* const replacementp = callp->makeStmt(); - stmtp->replaceWith(replacementp); - if (AstBegin* const beginp = VN_CAST(stmtp, Begin)) { - taskp->addStmtsp(beginp->stmtsp()->unlinkFrBackWithNext()); - VL_DO_DANGLING(pushDeletep(beginp), beginp); - } else { - taskp->addStmtsp(stmtp); - } + beginp->addStmtsp(callp->makeStmt()); // Variables were moved under the task, so make sure they are marked as funcLocal for (AstVar* const localp : m_forkLocalsp) localp->funcLocal(true); - // Return the replacement - return replacementp; + // We did wrap the body + return true; } // VISITORS @@ -615,21 +596,18 @@ class ForkVisitor final : public VNVisitor { return; } - iterateAndNextNull(nodep->initsp()); - std::vector newps; + iterateAndNextNull(nodep->declsp()); + iterateAndNextNull(nodep->stmtsp()); + std::vector wrappedp; { VL_RESTORER(m_inFork); m_inFork = true; - for (AstNode *itemp = nodep->stmtsp(), *nextp; itemp; itemp = nextp) { - nextp = itemp->nextp(); - AstNodeStmt* const stmtp = VN_CAST(itemp, NodeStmt); - if (!stmtp) continue; - AstNodeStmt* const newp = taskify(stmtp); - if (newp) newps.push_back(newp); + for (AstBegin* itemp = nodep->forksp(); itemp; itemp = VN_AS(itemp->nextp(), Begin)) { + if (taskify(itemp)) wrappedp.push_back(itemp); } } // Analyze replacements in context of enclosing fork - for (AstNodeStmt* const stmtp : newps) iterate(stmtp); + for (AstBegin* const beginp : wrappedp) iterateAndNextNull(beginp); } void visit(AstVar* nodep) override { if (m_inFork) m_forkLocalsp.insert(nodep); diff --git a/src/V3Hasher.cpp b/src/V3Hasher.cpp index f16fc21c0..14438c196 100644 --- a/src/V3Hasher.cpp +++ b/src/V3Hasher.cpp @@ -519,11 +519,17 @@ class HasherVisitor final : public VNVisitorConst { void visit(AstNodeProcedure* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {}); } - void visit(AstNodeBlock* nodep) override { - m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { // + void visit(AstBegin* nodep) override { + m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { // m_hash += nodep->name(); }); } + void visit(AstFork* nodep) override { + m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { // + m_hash += nodep->name(); + m_hash += nodep->joinType(); + }); + } void visit(AstPin* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { m_hash += nodep->name(); diff --git a/src/V3InstrCount.cpp b/src/V3InstrCount.cpp index 653db37e2..1e099fb10 100644 --- a/src/V3InstrCount.cpp +++ b/src/V3InstrCount.cpp @@ -224,9 +224,10 @@ private: void visit(AstFork* nodep) override { if (m_ignoreRemaining) return; const VisitBase vb{this, nodep}; + iterateAndNextConstNull(nodep->stmtsp()); uint32_t totalCount = m_instrCount; // Sum counts in each statement until the first await - for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + for (AstNode* stmtp = nodep->forksp(); stmtp; stmtp = stmtp->nextp()) { reset(); iterateConst(stmtp); totalCount += m_instrCount; diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 56a2d23bd..1118e4a39 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1258,20 +1258,26 @@ class LinkDotFindVisitor final : public VNVisitor { // unnamed#'s would just confuse tracing variables in // places such as tasks, where "task ...; begin ... end" // are common. - for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { - if (VN_IS(stmtp, Var) || VN_IS(stmtp, Foreach)) { - std::string name; - const std::string stepStr = m_statep->forPrimary() - ? "" - : std::to_string(m_statep->stepNumber()) + "_"; - do { - ++m_modBlockNum; - name = "unnamedblk" + stepStr + cvtToStr(m_modBlockNum); - // Increment again if earlier pass of V3LinkDot claimed this name - } while (m_curSymp->findIdFlat(name)); - nodep->name(name); - break; + const auto containsDecl = [](const AstNode* nodesp) -> bool { + for (const AstNode* np = nodesp; np; np = np->nextp()) { + if (VN_IS(np, Var) || VN_IS(np, Foreach)) return true; } + return false; + }; + bool needName = nodep->declsp() || containsDecl(nodep->stmtsp()); + if (const AstFork* const forkp = VN_CAST(nodep, Fork)) { + needName = needName || containsDecl(forkp->forksp()); + } + if (needName) { + const std::string stepStr + = m_statep->forPrimary() ? "" : std::to_string(m_statep->stepNumber()) + "_"; + std::string name; + do { + ++m_modBlockNum; + name = "unnamedblk" + stepStr + cvtToStr(m_modBlockNum); + // Increment again if earlier pass of V3LinkDot claimed this name + } while (m_curSymp->findIdFlat(name)); + nodep->name(name); } } if (nodep->name() == "") { diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index ee831dc3a..952d772ca 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -48,7 +48,7 @@ class LinkJumpVisitor final : public VNVisitor { // AstBegin/etc::user1() -> AstJumpBlock*, for body of this loop // AstFinish::user1() -> bool, processed // AstNode::user2() -> AstJumpBlock*, for this block - // AstNodeBlock::user3() -> bool, true if contains a fork + // AstNodeBegin::user3() -> bool, true if contains a fork const VNUser1InUse m_user1InUse; const VNUser2InUse m_user2InUse; const VNUser3InUse m_user3InUse; @@ -80,9 +80,11 @@ class LinkJumpVisitor final : public VNVisitor { AstNode* underp = nullptr; bool under_and_next = true; - if (AstNodeBlock* const blockp = VN_CAST(nodep, NodeBlock)) { + if (AstBegin* const blockp = VN_CAST(nodep, Begin)) { + UASSERT_OBJ(!endOfIter, nodep, "No endOfIter for Begin"); underp = blockp->stmtsp(); } else if (AstNodeFTask* const fTaskp = VN_CAST(nodep, NodeFTask)) { + UASSERT_OBJ(!endOfIter, nodep, "No endOfIter for FTask"); underp = fTaskp->stmtsp(); } else if (AstForeach* const foreachp = VN_CAST(nodep, Foreach)) { if (endOfIter) { @@ -180,10 +182,10 @@ class LinkJumpVisitor final : public VNVisitor { // handle is pushed to the queue. `disable` statement is replaced with calling `kill()` // method on each element of the queue. FileLine* const fl = nodep->fileline(); - const std::string targetName = nodep->targetp()->name(); + AstNode* const targetp = nodep->targetp(); if (m_ftaskp) { - if (!m_ftaskp->exists([targetp = nodep->targetp()](const AstNodeBlock* blockp) - -> bool { return blockp == targetp; })) { + if (!m_ftaskp->exists( + [targetp](const AstNodeBlock* blockp) -> bool { return blockp == targetp; })) { // Disabling a fork, which is within the same task, is not a problem nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork from task / function"); } @@ -193,7 +195,7 @@ class LinkJumpVisitor final : public VNVisitor { = VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class); // Declare queue of processes (as a global variable for simplicity) AstVar* const processQueuep = new AstVar{ - fl, VVarType::VAR, m_queueNames.get(targetName), VFlagChildDType{}, + fl, VVarType::VAR, m_queueNames.get(targetp->name()), VFlagChildDType{}, new AstQueueDType{fl, VFlagChildDType{}, new AstClassRefDType{fl, processClassp, nullptr}, nullptr}}; processQueuep->lifetime(VLifetime::STATIC_EXPLICIT); @@ -219,12 +221,21 @@ class LinkJumpVisitor final : public VNVisitor { killQueueCall->classOrPackagep(processClassp); AstStmtExpr* const killStmtp = new AstStmtExpr{fl, killQueueCall}; nodep->addNextHere(killStmtp); - if (existsBlockAbove(targetName)) { - // process::kill doesn't kill the current process immediately, because it is in the - // running state. Since the current process has to be terminated immediately, we jump - // at the end of the fork that is being disabled - AstJumpBlock* const jumpBlockp = getJumpBlock(nodep->targetp(), false); - killStmtp->addNextHere(new AstJumpGo{fl, jumpBlockp}); + + // 'process::kill' does not immediately kill the current process + // executing the disable statement (because it's in the running state). + // If the disable statement is indeed executed by a process under the + // target AstFork, then jump to the end of that fork branch. + if (VN_IS(targetp, Fork)) { + AstNodeBlock* forkBranchp = nullptr; + for (AstNodeBlock* const blockp : vlstd::reverse_view(m_blockStack)) { + if (blockp == targetp) { + AstJumpBlock* const jmpBlockp = getJumpBlock(VN_AS(forkBranchp, Begin), false); + killStmtp->addNextHere(new AstJumpGo{fl, jmpBlockp}); + break; + } + forkBranchp = blockp; + } } } static bool directlyUnderFork(const AstNode* const nodep) { @@ -247,24 +258,24 @@ class LinkJumpVisitor final : public VNVisitor { m_ftaskp = nodep; iterateChildren(nodep); } - void visit(AstNodeBlock* nodep) override { + void visit(AstBegin* nodep) override { UINFO(8, " " << nodep); - VL_RESTORER(m_inFork); VL_RESTORER(m_unrollFull); m_blockStack.push_back(nodep); - { - if (VN_IS(nodep, Fork)) { - m_inFork = true; // And remains set for children - // Mark all upper blocks also, can stop once see - // one set to avoid O(n^2) - for (auto itr : vlstd::reverse_view(m_blockStack)) { - if (itr->user3()) break; - itr->user3(true); - } - } - nodep->user3(m_inFork); - iterateChildren(nodep); + iterateChildren(nodep); + m_blockStack.pop_back(); + } + void visit(AstFork* nodep) override { + UINFO(8, " " << nodep); + VL_RESTORER(m_unrollFull); + VL_RESTORER(m_inFork); + m_inFork = true; + // Mark all upper blocks, can stop once see one set to avoid O(n^2) + for (AstNodeBlock* const blockp : vlstd::reverse_view(m_blockStack)) { + if (blockp->user3SetOnce()) break; } + m_blockStack.push_back(nodep); + iterateChildren(nodep); m_blockStack.pop_back(); } void visit(AstStmtPragma* nodep) override { @@ -392,43 +403,32 @@ class LinkJumpVisitor final : public VNVisitor { void visit(AstDisable* nodep) override { UINFO(8, " DISABLE " << nodep); AstNode* const targetp = nodep->targetp(); - FileLine* const fl = nodep->fileline(); UASSERT_OBJ(targetp, nodep, "Unlinked disable statement"); if (VN_IS(targetp, Task)) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling task by name"); } else if (AstFork* const forkp = VN_CAST(targetp, Fork)) { std::vector forks; - for (AstNode* forkItemp = forkp->stmtsp(); forkItemp; forkItemp = forkItemp->nextp()) { - // Further handling of disable stmt requires all forks to be begin blocks - AstBegin* beginp = VN_CAST(forkItemp, Begin); - if (!beginp) { - beginp = new AstBegin{fl, "", nullptr, false}; - forkItemp->replaceWith(beginp); - beginp->addStmtsp(forkItemp); - // In order to continue the iteration - forkItemp = beginp; - } - forks.push_back(beginp); + for (AstBegin* itemp = forkp->forksp(); itemp; itemp = VN_AS(itemp->nextp(), Begin)) { + forks.push_back(itemp); } handleDisableOnFork(nodep, forks); } else if (AstBegin* const beginp = VN_CAST(targetp, Begin)) { - if (directlyUnderFork(beginp)) { - std::vector forks{beginp}; - handleDisableOnFork(nodep, forks); + if (existsBlockAbove(beginp->name())) { + if (beginp->user3()) { + nodep->v3warn(E_UNSUPPORTED, + "Unsupported: disabling block that contains a fork"); + } else { + // Jump to the end of the named block + AstJumpBlock* const blockp = getJumpBlock(beginp, false); + nodep->addNextHere(new AstJumpGo{nodep->fileline(), blockp}); + } } else { - const std::string targetName = beginp->name(); - if (existsBlockAbove(targetName)) { - if (beginp->user3()) { - nodep->v3warn(E_UNSUPPORTED, - "Unsupported: disabling block that contains a fork"); - } else { - // Jump to the end of the named block - AstJumpBlock* const blockp = getJumpBlock(beginp, false); - nodep->addNextHere(new AstJumpGo{nodep->fileline(), blockp}); - } + if (directlyUnderFork(beginp)) { + std::vector forks{beginp}; + handleDisableOnFork(nodep, forks); } else { nodep->v3warn(E_UNSUPPORTED, "disable isn't underneath a begin with name: '" - << targetName << "'"); + << beginp->name() << "'"); } } } else { diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 8a5036b72..fba477e6a 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -55,6 +55,7 @@ class LinkParseVisitor final : public VNVisitor { AstNodeModule* m_modp = nullptr; // Current module AstNodeProcedure* m_procedurep = nullptr; // Current procedure AstNodeFTask* m_ftaskp = nullptr; // Current task + AstNodeBlock* m_blockp = nullptr; // Current AstNodeBlock AstNodeDType* m_dtypep = nullptr; // Current data type AstNodeExpr* m_defaultInSkewp = nullptr; // Current default input skew AstNodeExpr* m_defaultOutSkewp = nullptr; // Current default output skew @@ -377,20 +378,27 @@ class LinkParseVisitor final : public VNVisitor { if (m_procedurep && VN_IS(m_procedurep, Always)) nodep->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); if (nodep->valuep()) { - // A variable with an = value can be three things: FileLine* const fl = nodep->valuep()->fileline(); + // A variable with an = value can be 4 things: if (nodep->isParam() || (m_ftaskp && nodep->isNonOutput())) { // 1. Parameters and function inputs: It's a default to use if not overridden } else if (!m_ftaskp && !VN_IS(m_modp, Class) && nodep->isNonOutput() && !nodep->isInput()) { - // Module inout/ref/constref: const default to use + // 2. Module inout/ref/constref: const default to use nodep->v3warn(E_UNSUPPORTED, "Unsupported: Default value on module inout/ref/constref: " << nodep->prettyNameQ()); nodep->valuep()->unlinkFrBack()->deleteTree(); - } // 2. Under modules/class, it's an initial value to be loaded at time 0 via an - // AstInitial - else if (m_valueModp) { + } else if (m_blockp) { + // 3. Under blocks, it's an initial value to be under an assign + // TODO: This is wrong if it's a static variable right? + FileLine* const newfl = new FileLine{fl}; + newfl->warnOff(V3ErrorCode::E_CONSTWRITTEN, true); + m_blockp->addStmtsp( + new AstAssign{newfl, new AstVarRef{newfl, nodep, VAccess::WRITE}, + VN_AS(nodep->valuep()->unlinkFrBack(), NodeExpr)}); + } else if (m_valueModp) { + // 4. Under modules/class, it's the time 0 initialziation value // Making an AstAssign (vs AstAssignW) to a wire is an error, suppress it FileLine* const newfl = new FileLine{fl}; newfl->warnOff(V3ErrorCode::PROCASSWIRE, true); @@ -405,13 +413,8 @@ class LinkParseVisitor final : public VNVisitor { } else { nodep->addNextHere(new AstInitialStatic{newfl, assp}); } - } // 4. Under blocks, it's an initial value to be under an assign - else { - FileLine* const newfl = new FileLine{fl}; - newfl->warnOff(V3ErrorCode::E_CONSTWRITTEN, true); - nodep->addNextHere( - new AstAssign{newfl, new AstVarRef{newfl, nodep, VAccess::WRITE}, - VN_AS(nodep->valuep()->unlinkFrBack(), NodeExpr)}); + } else { + nodep->v3fatalSrc("Variable with initializer in unexpected position"); } } } @@ -762,10 +765,23 @@ class LinkParseVisitor final : public VNVisitor { } iterateChildren(nodep); } - void visit(AstBegin* nodep) override { - V3Control::applyCoverageBlock(m_modp, nodep); + void visit(AstNodeBlock* nodep) override { + { + VL_RESTORER(m_blockp); + m_blockp = nodep; + // Temporarily unlink the statements so variable initializers can be inserted in order + AstNode* const stmtsp = nodep->stmtsp(); + if (stmtsp) stmtsp->unlinkFrBackWithNext(); + iterateAndNextNull(nodep->declsp()); + nodep->addStmtsp(stmtsp); + } + + if (AstBegin* const beginp = VN_CAST(nodep, Begin)) { + V3Control::applyCoverageBlock(m_modp, beginp); + } cleanFileline(nodep); - iterateChildren(nodep); + iterateAndNextNull(nodep->stmtsp()); + if (AstFork* const forkp = VN_CAST(nodep, Fork)) iterateAndNextNull(forkp->forksp()); } void visit(AstCase* nodep) override { V3Control::applyCase(nodep); diff --git a/src/V3ParseGrammar.h b/src/V3ParseGrammar.h index 9c9fbd302..29d660154 100644 --- a/src/V3ParseGrammar.h +++ b/src/V3ParseGrammar.h @@ -304,16 +304,6 @@ public: return m_scopedSigAttr ? m_scopedSigAttr->cloneTree(true) : nullptr; } - static void addForkStmtsp(AstFork* forkp, AstNode* stmtsp) { - forkp->addStmtsp(stmtsp); - for (AstNode* stmtp = stmtsp; stmtp; stmtp = stmtp->nextp()) { - AstVar* const varp = VN_CAST(stmtp, Var); - if (!varp) break; - varp->unlinkFrBack(); - forkp->addInitsp(varp); - } - } - void createGenericIface(AstNode* const nodep, AstNodeRange* const rangep, AstNode* sigAttrListp, FileLine* const modportFileline = nullptr, const string& modportstrp = "") { @@ -334,4 +324,17 @@ public: createVariable(nodep->fileline(), nodep->name(), rangep, sigAttrListp)); m_varDecl = VVarType::VAR; } + + // Wrap all statements in the given list in an AstBegin (except those already an AstBegin) + static AstBegin* wrapInBegin(AstNodeStmt* stmtsp) { + AstBegin* resp = nullptr; + for (AstNodeStmt *nodep = stmtsp, *nextp; nodep; nodep = nextp) { + nextp = VN_AS(nodep->nextp(), NodeStmt); + if (nextp) nextp->unlinkFrBackWithNext(); + AstBegin* beginp = VN_CAST(nodep, Begin); + if (!beginp) beginp = new AstBegin{nodep->fileline(), "", nodep, true}; + resp = AstNode::addNext(resp, beginp); + } + return resp; + } }; diff --git a/src/V3SchedTiming.cpp b/src/V3SchedTiming.cpp index 2d2694c9c..c6b2aaf64 100644 --- a/src/V3SchedTiming.cpp +++ b/src/V3SchedTiming.cpp @@ -360,8 +360,8 @@ void transformForks(AstNetlist* const netlistp) { // Replace self with the function calls (no co_await, as we don't want the main // process to suspend whenever any of the children do) // V3Dead could have removed all statements from the fork, so guard against it - AstNode* const stmtsp = nodep->stmtsp(); - if (stmtsp) nodep->addNextHere(stmtsp->unlinkFrBackWithNext()); + if (nodep->forksp()) nodep->addNextHere(nodep->forksp()->unlinkFrBackWithNext()); + if (nodep->stmtsp()) nodep->addNextHere(nodep->stmtsp()->unlinkFrBackWithNext()); VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); } void visit(AstBegin* nodep) override { diff --git a/src/V3StackCount.cpp b/src/V3StackCount.cpp index 8dc28c114..078e5b35b 100644 --- a/src/V3StackCount.cpp +++ b/src/V3StackCount.cpp @@ -142,10 +142,11 @@ private: void visit(AstFork* nodep) override { if (m_ignoreRemaining) return; const VisitBase vb{this, nodep}; + iterateAndNextConstNull(nodep->stmtsp()); uint32_t totalCount = m_stackSize; VL_RESTORER(m_ignoreRemaining); // Sum counts in each statement - for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + for (AstNode* stmtp = nodep->forksp(); stmtp; stmtp = stmtp->nextp()) { reset(); iterateConst(stmtp); totalCount += m_stackSize; diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index a7fc4da51..095e60d78 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -731,7 +731,7 @@ class TimingControlVisitor final : public VNVisitor { = createTemp(flp, forkp->name() + "__sync", getCreateForkSyncDTypep(), insertBeforep); unsigned joinCount = 0; // Needed for join counter // Add a .done() to each begin - for (AstNode* beginp = forkp->stmtsp(); beginp; beginp = beginp->nextp()) { + for (AstNode* beginp = forkp->forksp(); beginp; beginp = beginp->nextp()) { addForkDone(VN_AS(beginp, Begin), forkVscp); joinCount++; } @@ -1025,10 +1025,7 @@ class TimingControlVisitor final : public VNVisitor { // Put it in a fork so it doesn't block // Could already be the only thing directly under a fork, reuse that if possible AstFork* forkp = !nodep->nextp() ? VN_CAST(nodep->firstAbovep(), Fork) : nullptr; - if (!forkp) { - forkp = new AstFork{flp, "", nullptr}; - forkp->joinType(VJoinType::JOIN_NONE); - } + if (!forkp) forkp = new AstFork{flp, VJoinType::JOIN_NONE}; if (!m_underProcedure) { // If it's in a function, it won't be handled by V3Delayed // Put it behind an additional named event that gets triggered in the NBA region @@ -1043,7 +1040,7 @@ class TimingControlVisitor final : public VNVisitor { controlp->replaceWith(forkp); AstBegin* beginp = VN_CAST(controlp, Begin); if (!beginp) beginp = new AstBegin{nodep->fileline(), "", controlp, false}; - forkp->addStmtsp(beginp); + forkp->addForksp(beginp); controlp = forkp; } UASSERT_OBJ(nodep, controlp, "Assignment should have timing control"); @@ -1115,11 +1112,11 @@ class TimingControlVisitor final : public VNVisitor { AstNode* const controlp = nodep->timingControlp()->unlinkFrBack(); AstAssign* const assignp = new AstAssign{nodep->fileline(), lhs1p, rhs1p, controlp}; // Put the assignment in a fork..join_none. - AstBegin* const beginp = new AstBegin{flp, "", assignp, false}; - AstFork* const forkp = new AstFork{flp, "", beginp}; - forkp->joinType(VJoinType::JOIN_NONE); + AstFork* const forkp = new AstFork{flp, VJoinType::JOIN_NONE}; nodep->replaceWith(forkp); VL_DO_DANGLING(pushDeletep(nodep), nodep); + AstBegin* const beginp = new AstBegin{flp, "", assignp, false}; + forkp->addForksp(beginp); visit(forkp); // Visit now as we need to do some post-processing // IEEE 1800-2023 10.3.3 - if the RHS value differs from the currently scheduled value to // be assigned, the currently scheduled assignment is descheduled. To keep track if an @@ -1239,21 +1236,23 @@ class TimingControlVisitor final : public VNVisitor { void visit(AstFork* nodep) override { if (nodep->user1SetOnce()) return; v3Global.setUsesTiming(); + // Create a unique name for this fork - nodep->name("__Vfork_" + cvtToStr(++m_forkCnt)); - unsigned idx = 0; // Index for naming begins - AstNode* stmtp = nodep->stmtsp(); - // Put each statement in a begin - while (stmtp) { - UASSERT_OBJ(VN_IS(stmtp, Begin), nodep, - "All statements under forks must be begins at this point"); - AstBegin* const beginp = VN_AS(stmtp, Begin); - stmtp = beginp->nextp(); - iterate(beginp); - // Even if we do not find any awaits, we cannot simply inline the process here, as new - // awaits could be added later. + nodep->name("__Vfork_" + std::to_string(++m_forkCnt)); + + // TODO: Should process nodep->stmtsp() in case an earlier pass + // inserted something there, but as of today we don't need to. + + // Process and name each fork + size_t idx = 0; // Index for naming begins + for (AstBegin *itemp = nodep->forksp(), *nextp; itemp; itemp = nextp) { + nextp = VN_AS(itemp->nextp(), Begin); + iterate(itemp); + // Note: Even if we do not find any awaits, we cannot simply inline + // the process here, as new awaits could be added later. + // Name the begin (later the name will be used for a new function) - beginp->name(nodep->name() + "__" + cvtToStr(idx++)); + itemp->name(nodep->name() + "__" + std::to_string(idx++)); } if (!nodep->joinType().joinNone()) makeForkJoin(nodep); } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index a14775b4e..7934af6b5 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -741,16 +741,17 @@ class WidthVisitor final : public VNVisitor { } if (!nodep->fileline()->timingOn() // With no statements, begin is identical - || !nodep->stmtsp() + || !nodep->forksp() || (!v3Global.opt.timing().isSetTrue() // If no --timing && (v3Global.opt.bboxUnsup() // With one statement and no timing, a begin block does as good as a // fork/join or join_any - || (!nodep->stmtsp()->nextp() && !nodep->joinType().joinNone())))) { - AstNode* stmtsp = nullptr; - if (nodep->stmtsp()) stmtsp = nodep->stmtsp()->unlinkFrBack(); - AstBegin* const newp = new AstBegin{nodep->fileline(), nodep->name(), stmtsp, false}; + || (!nodep->forksp()->nextp() && !nodep->joinType().joinNone())))) { + AstBegin* const newp = new AstBegin{nodep->fileline(), nodep->name(), nullptr, false}; nodep->replaceWith(newp); + if (nodep->declsp()) newp->addDeclsp(nodep->declsp()->unlinkFrBackWithNext()); + if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->unlinkFrBackWithNext()); + if (nodep->forksp()) newp->addStmtsp(nodep->forksp()->unlinkFrBackWithNext()); VL_DO_DANGLING(nodep->deleteTree(), nodep); } else if (v3Global.opt.timing().isSetTrue()) { VL_RESTORER(m_underFork); diff --git a/src/verilog.y b/src/verilog.y index d6fdc251e..45ba222bd 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3360,15 +3360,23 @@ senitemEdge: // IEEE: part of event_expression seq_block: // ==IEEE: seq_block // // IEEE doesn't allow declarations in unnamed blocks, but several simulators do. // // So need AstBegin's even if unnamed to scope variables down - seq_blockFront blockDeclStmtListE yEND endLabelE - { $$ = $1; $1->addStmtsp($2); - GRAMMARP->endLabel($4, $1, $4); } + yBEGIN startLabelE blockDeclListE stmtListE yEND endLabelE + { + $$ = new AstBegin{$1, $2 ? *$2 : "", nullptr, false}; + GRAMMARP->endLabel($6, $$, $6); + $$->addDeclsp($3); + $$->addStmtsp($4); + } ; seq_blockPreId: // IEEE: seq_block, but called with leading ID - seq_blockFrontPreId blockDeclStmtListE yEND endLabelE - { $$ = $1; $1->addStmtsp($2); - GRAMMARP->endLabel($4, $1, $4); } + id yP_COLON__BEGIN yBEGIN blockDeclListE stmtListE yEND endLabelE + { + $$ = new AstBegin{$3, *$1, nullptr, false}; + GRAMMARP->endLabel($7, $$, $7); + $$->addDeclsp($4); + $$->addStmtsp($5); + } ; par_blockJoin: @@ -3378,61 +3386,28 @@ par_blockJoin: ; par_block: // ==IEEE: par_block - par_blockFront blockDeclStmtListE par_blockJoin endLabelE - { $$ = $1; $1->joinType($3); - V3ParseGrammar::addForkStmtsp($1, $2); - GRAMMARP->endLabel($4, $1, $4); } + yFORK startLabelE blockDeclListE stmtListE par_blockJoin endLabelE + { + $$ = new AstFork{$1, $5, $2 ? *$2 : ""}; + GRAMMARP->endLabel($6, $$, $6); + $$->addDeclsp($3); + $$->addForksp(V3ParseGrammar::wrapInBegin($4)); + } ; par_blockPreId: // ==IEEE: par_block but called with leading ID - par_blockFrontPreId blockDeclStmtListE par_blockJoin endLabelE - { $$ = $1; $1->joinType($3); - V3ParseGrammar::addForkStmtsp($1, $2); - GRAMMARP->endLabel($4, $1, $4); } - ; + id yP_COLON__FORK yFORK blockDeclListE stmtListE par_blockJoin endLabelE + { + $$ = new AstFork{$3, $6, *$1}; + GRAMMARP->endLabel($7, $$, $7); + $$->addDeclsp($4); + $$->addForksp(V3ParseGrammar::wrapInBegin($5)); + } + ; -seq_blockFront: // IEEE: part of seq_block - yBEGIN - { $$ = new AstBegin{$1, "", nullptr, false}; } - | yBEGIN ':' idAny/*new-block_identifier*/ - { $$ = new AstBegin{$3, *$3, nullptr, false}; } - ; - -par_blockFront: // IEEE: part of par_block - yFORK - { $$ = new AstFork{$1, "", nullptr}; } - | yFORK ':' idAny/*new-block_identifier*/ - { $$ = new AstFork{$3, *$3, nullptr}; } - ; - -seq_blockFrontPreId: // IEEE: part of seq_block/stmt with leading id - id/*block_identifier*/ yP_COLON__BEGIN yBEGIN - { $$ = new AstBegin{$3, *$1, nullptr, false}; } - ; - -par_blockFrontPreId: // IEEE: part of par_block/stmt with leading id - id/*block_identifier*/ yP_COLON__FORK yFORK - { $$ = new AstFork{$3, *$1, nullptr}; } - ; - - -blockDeclStmtList: // IEEE: { block_item_declaration } { statement or null } - // // The spec seems to suggest a empty declaration isn't ok, but most simulators take it - block_item_declarationList { $$ = $1; } - | block_item_declarationList stmtList { $$ = addNextNull($1, $2); } - | stmtList { $$ = $1; } - ; - -blockDeclStmtListE: // IEEE: [ { block_item_declaration } { statement or null } ] +blockDeclListE: // IEEE: [ block_item_declaration ] /*empty*/ { $$ = nullptr; } - | blockDeclStmtList { $$ = $1; } - ; - -block_item_declarationList: // IEEE: [ block_item_declaration ] - block_item_declaration { $$ = $1; } - | block_item_declarationList block_item_declaration { $$ = addNextNull($1, $2); } - // - | block_item_declarationList error ';' { $$ = $1; } // LCOV_EXCL_LINE + | blockDeclListE block_item_declaration { $$ = addNextNull($1, $2); } | error ';' { $$ = nullptr; } // LCOV_EXCL_LINE ; @@ -3442,6 +3417,11 @@ block_item_declaration: // ==IEEE: block_item_declaration | let_declaration { $$ = $1; } ; +stmtListE: + /*empty*/ { $$ = nullptr; } + | stmtList { $$ = $1; } + ; + stmtList: stmt { $$ = $1; } | stmtList stmt { $$ = addNextNull($1, $2); } @@ -6092,6 +6072,11 @@ strAsIntIgnore: // strAsInt, but never matches for when expr yaSTRING__IGNORE { $$ = nullptr; yyerror("Impossible token"); } ; +startLabelE: + /* empty */ { $$ = nullptr; $$ = nullptr; } + | ':' idAny { $$ = $2; $$ = $2; } + ; + endLabelE: /* empty */ { $$ = nullptr; $$ = nullptr; } | ':' idAny { $$ = $2; $$ = $2; } diff --git a/test_regress/t/t_constraint_json_only.out b/test_regress/t/t_constraint_json_only.out index fa6018ab8..926e4ed57 100644 --- a/test_regress/t/t_constraint_json_only.out +++ b/test_regress/t/t_constraint_json_only.out @@ -5,7 +5,7 @@ {"type":"VAR","name":"p","addr":"(G)","loc":"d,69:11,69:12","dtypep":"(H)","origName":"p","isSc":false,"isPrimaryIO":false,"isPrimaryClock":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"lifetime":"VSTATICI","varType":"VAR","dtypeName":"Packet","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"INITIAL","name":"","addr":"(I)","loc":"d,71:4,71:11","isSuspendable":false,"needProcess":false, "stmtsp": [ - {"type":"BEGIN","name":"","addr":"(J)","loc":"d,71:12,71:17","implied":false,"needProcess":false,"unnamed":true, + {"type":"BEGIN","name":"","addr":"(J)","loc":"d,71:12,71:17","implied":false,"needProcess":false,"unnamed":true,"declsp": [], "stmtsp": [ {"type":"DISPLAY","name":"","addr":"(K)","loc":"d,73:7,73:13", "fmtp": [ diff --git a/test_regress/t/t_disable_inside.v b/test_regress/t/t_disable_inside.v index 61cca422a..9b956abfc 100644 --- a/test_regress/t/t_disable_inside.v +++ b/test_regress/t/t_disable_inside.v @@ -9,13 +9,104 @@ module t; int x = 0; fork : fork_blk begin + #1; + disable fork_blk; // Disables both forked processes + $stop; + end + begin + if ($time != 0) $stop; x = 1; - disable fork_blk; - x = 2; + #2; + $stop; end join_none #1; - if (x != 1) $stop; + if (x != 1) begin + $display(x); + $stop; + end + end + + initial begin + int y = 0; + fork + begin : fork_branch + #1; + disable fork_branch; // Disables only this branch of the fork + $stop; + end + begin + if ($time != 0) $stop; + y = 1; + #2; + if ($time != 2) $stop; + y = 2; + end + join_none + #1; + if (y != 1) begin + $display(y); + $stop; + end + #1; + if (y != 2) begin + $display(y); + $stop; + end + end + + // TODO: This doesn't work due to the second fork branch not being added to + // the killQueue when the 'disable' is executed with no delay after + // the fork starts. See the case below which is the same, but the + // fork branches are in the opposite order so it happens to work. + //initial begin + // fork : fork_blk2 + // begin + // if ($time != 0) $stop; + // disable fork_blk2; + // $stop; + // end + // begin + // if ($time != 0) $stop; + // #1 $stop; + // end + // join_none + //end + + initial begin + fork : fork_blk3 + begin + if ($time != 0) $stop; + #1 $stop; + end + begin + if ($time != 0) $stop; + disable fork_blk3; + $stop; + end + join_none + end + + initial begin + fork : fork_blk4 + begin + if ($time != 0) $stop; + if ($c("false")) begin + disable fork_blk4; + $stop; + end + #1; + end + begin + if ($time != 0) $stop; + #1; + if ($time != 1) $stop; + end + join_none + end + + initial begin + #10; $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_dump_json.out b/test_regress/t/t_dump_json.out index 3ae39b1db..edd09da9f 100644 --- a/test_regress/t/t_dump_json.out +++ b/test_regress/t/t_dump_json.out @@ -166,7 +166,7 @@ ]} ], "stmtsp": [ - {"type":"BEGIN","name":"","addr":"(ZC)","loc":"e,36:27,36:32","implied":false,"needProcess":false,"unnamed":true, + {"type":"BEGIN","name":"","addr":"(ZC)","loc":"e,36:27,36:32","implied":false,"needProcess":false,"unnamed":true,"declsp": [], "stmtsp": [ {"type":"ASSIGNDLY","name":"","addr":"(AD)","loc":"e,40:11,40:13","dtypep":"UNLINKED", "rhsp": [ @@ -314,7 +314,7 @@ ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(BF)","loc":"e,43:21,43:26","implied":false,"needProcess":false,"unnamed":true, + {"type":"BEGIN","name":"","addr":"(BF)","loc":"e,43:21,43:26","implied":false,"needProcess":false,"unnamed":true,"declsp": [], "stmtsp": [ {"type":"ASSIGNDLY","name":"","addr":"(CF)","loc":"e,45:14,45:16","dtypep":"UNLINKED", "rhsp": [ @@ -344,7 +344,7 @@ ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(OF)","loc":"e,48:26,48:31","implied":false,"needProcess":false,"unnamed":true, + {"type":"BEGIN","name":"","addr":"(OF)","loc":"e,48:26,48:31","implied":false,"needProcess":false,"unnamed":true,"declsp": [], "stmtsp": [ {"type":"ASSIGNDLY","name":"","addr":"(PF)","loc":"e,49:14,49:16","dtypep":"UNLINKED", "rhsp": [ @@ -367,7 +367,7 @@ ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(XF)","loc":"e,51:26,51:31","implied":false,"needProcess":false,"unnamed":true,"stmtsp": []} + {"type":"BEGIN","name":"","addr":"(XF)","loc":"e,51:26,51:31","implied":false,"needProcess":false,"unnamed":true,"declsp": [],"stmtsp": []} ], "elsesp": [ {"type":"IF","name":"","addr":"(YF)","loc":"e,53:12,53:14", @@ -381,7 +381,7 @@ ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(CG)","loc":"e,53:27,53:32","implied":false,"needProcess":false,"unnamed":true, + {"type":"BEGIN","name":"","addr":"(CG)","loc":"e,53:27,53:32","implied":false,"needProcess":false,"unnamed":true,"declsp": [], "stmtsp": [ {"type":"DISPLAY","name":"","addr":"(DG)","loc":"e,54:10,54:16", "fmtp": [ @@ -485,7 +485,7 @@ ]} ], "stmtsp": [ - {"type":"BEGIN","name":"","addr":"(WH)","loc":"e,82:26,82:31","implied":false,"needProcess":false,"unnamed":true, + {"type":"BEGIN","name":"","addr":"(WH)","loc":"e,82:26,82:31","implied":false,"needProcess":false,"unnamed":true,"declsp": [], "stmtsp": [ {"type":"ASSIGNDLY","name":"","addr":"(XH)","loc":"e,83:11,83:13","dtypep":"UNLINKED", "rhsp": [ @@ -535,7 +535,7 @@ "assertTypesp": [ {"type":"CONST","name":"?32?sh8","addr":"(QI)","loc":"e,90:25,90:26","dtypep":"(NF)"} ],"directiveTypesp": []}, - {"type":"BEGIN","name":"blk","addr":"(RI)","loc":"e,91:15,91:18","implied":false,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"blk","addr":"(RI)","loc":"e,91:7,91:12","implied":false,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"DISABLE","name":"","addr":"(SI)","loc":"e,92:10,92:17", "targetRefp": [ @@ -547,9 +547,9 @@ ]}, {"type":"INITIAL","name":"","addr":"(UI)","loc":"e,95:4,95:11","isSuspendable":false,"needProcess":false, "stmtsp": [ - {"type":"BEGIN","name":"","addr":"(VI)","loc":"e,95:12,95:17","implied":false,"needProcess":false,"unnamed":true, + {"type":"BEGIN","name":"","addr":"(VI)","loc":"e,95:12,95:17","implied":false,"needProcess":false,"unnamed":true,"declsp": [], "stmtsp": [ - {"type":"BEGIN","name":"assert_simple_immediate_else","addr":"(WI)","loc":"e,96:7,96:35","implied":false,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assert_simple_immediate_else","addr":"(WI)","loc":"e,96:7,96:35","implied":false,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(XI)","loc":"e,96:37,96:43","type":"[SIMPLE_IMMEDIATE]", "propp": [ @@ -565,7 +565,7 @@ ],"filep": []} ],"passsp": []} ]}, - {"type":"BEGIN","name":"assert_simple_immediate_stmt","addr":"(CJ)","loc":"e,97:7,97:35","implied":false,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assert_simple_immediate_stmt","addr":"(CJ)","loc":"e,97:7,97:35","implied":false,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(DJ)","loc":"e,97:37,97:43","type":"[SIMPLE_IMMEDIATE]", "propp": [ @@ -581,7 +581,7 @@ ],"filep": []} ]} ]}, - {"type":"BEGIN","name":"assert_simple_immediate_stmt_else","addr":"(IJ)","loc":"e,98:7,98:40","implied":false,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assert_simple_immediate_stmt_else","addr":"(IJ)","loc":"e,98:7,98:40","implied":false,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(JJ)","loc":"e,98:42,98:48","type":"[SIMPLE_IMMEDIATE]", "propp": [ @@ -606,14 +606,14 @@ ],"filep": []} ]} ]}, - {"type":"BEGIN","name":"assume_simple_immediate","addr":"(RJ)","loc":"e,100:7,100:30","implied":false,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assume_simple_immediate","addr":"(RJ)","loc":"e,100:7,100:30","implied":false,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(SJ)","loc":"e,100:32,100:38","type":"[SIMPLE_IMMEDIATE]", "propp": [ {"type":"CONST","name":"?32?sh0","addr":"(TJ)","loc":"e,100:39,100:40","dtypep":"(N)"} ],"sentreep": [],"failsp": [],"passsp": []} ]}, - {"type":"BEGIN","name":"assume_simple_immediate_else","addr":"(UJ)","loc":"e,101:7,101:35","implied":false,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assume_simple_immediate_else","addr":"(UJ)","loc":"e,101:7,101:35","implied":false,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(VJ)","loc":"e,101:37,101:43","type":"[SIMPLE_IMMEDIATE]", "propp": [ @@ -629,7 +629,7 @@ ],"filep": []} ],"passsp": []} ]}, - {"type":"BEGIN","name":"assume_simple_immediate_stmt","addr":"(AK)","loc":"e,102:7,102:35","implied":false,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assume_simple_immediate_stmt","addr":"(AK)","loc":"e,102:7,102:35","implied":false,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(BK)","loc":"e,102:37,102:43","type":"[SIMPLE_IMMEDIATE]", "propp": [ @@ -645,7 +645,7 @@ ],"filep": []} ]} ]}, - {"type":"BEGIN","name":"assume_simple_immediate_stmt_else","addr":"(GK)","loc":"e,103:7,103:40","implied":false,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assume_simple_immediate_stmt_else","addr":"(GK)","loc":"e,103:7,103:40","implied":false,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(HK)","loc":"e,103:42,103:48","type":"[SIMPLE_IMMEDIATE]", "propp": [ @@ -674,7 +674,7 @@ ]}, {"type":"ALWAYS","name":"","addr":"(PK)","loc":"e,106:4,106:38","keyword":"always_comb","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ - {"type":"BEGIN","name":"assert_observed_deferred_immediate","addr":"(QK)","loc":"e,106:4,106:38","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assert_observed_deferred_immediate","addr":"(QK)","loc":"e,106:4,106:38","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(RK)","loc":"e,106:40,106:46","type":"[OBSERVED_DEFERRED_IMMEDIATE]", "propp": [ @@ -684,7 +684,7 @@ ]}, {"type":"ALWAYS","name":"","addr":"(TK)","loc":"e,107:4,107:43","keyword":"always_comb","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ - {"type":"BEGIN","name":"assert_observed_deferred_immediate_else","addr":"(UK)","loc":"e,107:4,107:43","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assert_observed_deferred_immediate_else","addr":"(UK)","loc":"e,107:4,107:43","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(VK)","loc":"e,107:45,107:51","type":"[OBSERVED_DEFERRED_IMMEDIATE]", "propp": [ @@ -703,7 +703,7 @@ ]}, {"type":"ALWAYS","name":"","addr":"(AL)","loc":"e,108:4,108:43","keyword":"always_comb","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ - {"type":"BEGIN","name":"assert_observed_deferred_immediate_stmt","addr":"(BL)","loc":"e,108:4,108:43","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assert_observed_deferred_immediate_stmt","addr":"(BL)","loc":"e,108:4,108:43","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(CL)","loc":"e,108:45,108:51","type":"[OBSERVED_DEFERRED_IMMEDIATE]", "propp": [ @@ -722,7 +722,7 @@ ]}, {"type":"ALWAYS","name":"","addr":"(HL)","loc":"e,109:4,109:48","keyword":"always_comb","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ - {"type":"BEGIN","name":"assert_observed_deferred_immediate_stmt_else","addr":"(IL)","loc":"e,109:4,109:48","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assert_observed_deferred_immediate_stmt_else","addr":"(IL)","loc":"e,109:4,109:48","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(JL)","loc":"e,109:50,109:56","type":"[OBSERVED_DEFERRED_IMMEDIATE]", "propp": [ @@ -750,7 +750,7 @@ ]}, {"type":"ALWAYS","name":"","addr":"(RL)","loc":"e,111:4,111:38","keyword":"always_comb","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ - {"type":"BEGIN","name":"assume_observed_deferred_immediate","addr":"(SL)","loc":"e,111:4,111:38","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assume_observed_deferred_immediate","addr":"(SL)","loc":"e,111:4,111:38","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(TL)","loc":"e,111:40,111:46","type":"[OBSERVED_DEFERRED_IMMEDIATE]", "propp": [ @@ -760,7 +760,7 @@ ]}, {"type":"ALWAYS","name":"","addr":"(VL)","loc":"e,112:4,112:43","keyword":"always_comb","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ - {"type":"BEGIN","name":"assume_observed_deferred_immediate_else","addr":"(WL)","loc":"e,112:4,112:43","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assume_observed_deferred_immediate_else","addr":"(WL)","loc":"e,112:4,112:43","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(XL)","loc":"e,112:45,112:51","type":"[OBSERVED_DEFERRED_IMMEDIATE]", "propp": [ @@ -779,7 +779,7 @@ ]}, {"type":"ALWAYS","name":"","addr":"(CM)","loc":"e,113:4,113:43","keyword":"always_comb","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ - {"type":"BEGIN","name":"assume_observed_deferred_immediate_stmt","addr":"(DM)","loc":"e,113:4,113:43","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assume_observed_deferred_immediate_stmt","addr":"(DM)","loc":"e,113:4,113:43","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(EM)","loc":"e,113:45,113:51","type":"[OBSERVED_DEFERRED_IMMEDIATE]", "propp": [ @@ -798,7 +798,7 @@ ]}, {"type":"ALWAYS","name":"","addr":"(JM)","loc":"e,114:4,114:48","keyword":"always_comb","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ - {"type":"BEGIN","name":"assume_observed_deferred_immediate_stmt_else","addr":"(KM)","loc":"e,114:4,114:48","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assume_observed_deferred_immediate_stmt_else","addr":"(KM)","loc":"e,114:4,114:48","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(LM)","loc":"e,114:50,114:56","type":"[OBSERVED_DEFERRED_IMMEDIATE]", "propp": [ @@ -826,7 +826,7 @@ ]}, {"type":"ALWAYS","name":"","addr":"(TM)","loc":"e,116:4,116:35","keyword":"always_comb","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ - {"type":"BEGIN","name":"assert_final_deferred_immediate","addr":"(UM)","loc":"e,116:4,116:35","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assert_final_deferred_immediate","addr":"(UM)","loc":"e,116:4,116:35","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(VM)","loc":"e,116:37,116:43","type":"[FINAL_DEFERRED_IMMEDIATE]", "propp": [ @@ -836,7 +836,7 @@ ]}, {"type":"ALWAYS","name":"","addr":"(XM)","loc":"e,117:4,117:40","keyword":"always_comb","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ - {"type":"BEGIN","name":"assert_final_deferred_immediate_else","addr":"(YM)","loc":"e,117:4,117:40","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assert_final_deferred_immediate_else","addr":"(YM)","loc":"e,117:4,117:40","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(ZM)","loc":"e,117:42,117:48","type":"[FINAL_DEFERRED_IMMEDIATE]", "propp": [ @@ -855,7 +855,7 @@ ]}, {"type":"ALWAYS","name":"","addr":"(EN)","loc":"e,118:4,118:40","keyword":"always_comb","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ - {"type":"BEGIN","name":"assert_final_deferred_immediate_stmt","addr":"(FN)","loc":"e,118:4,118:40","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assert_final_deferred_immediate_stmt","addr":"(FN)","loc":"e,118:4,118:40","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(GN)","loc":"e,118:42,118:48","type":"[FINAL_DEFERRED_IMMEDIATE]", "propp": [ @@ -874,7 +874,7 @@ ]}, {"type":"ALWAYS","name":"","addr":"(LN)","loc":"e,119:4,119:45","keyword":"always_comb","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ - {"type":"BEGIN","name":"assert_final_deferred_immediate_stmt_else","addr":"(MN)","loc":"e,119:4,119:45","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assert_final_deferred_immediate_stmt_else","addr":"(MN)","loc":"e,119:4,119:45","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(NN)","loc":"e,119:47,119:53","type":"[FINAL_DEFERRED_IMMEDIATE]", "propp": [ @@ -902,7 +902,7 @@ ]}, {"type":"ALWAYS","name":"","addr":"(VN)","loc":"e,121:4,121:35","keyword":"always_comb","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ - {"type":"BEGIN","name":"assume_final_deferred_immediate","addr":"(WN)","loc":"e,121:4,121:35","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assume_final_deferred_immediate","addr":"(WN)","loc":"e,121:4,121:35","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(XN)","loc":"e,121:37,121:43","type":"[FINAL_DEFERRED_IMMEDIATE]", "propp": [ @@ -912,7 +912,7 @@ ]}, {"type":"ALWAYS","name":"","addr":"(ZN)","loc":"e,122:4,122:40","keyword":"always_comb","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ - {"type":"BEGIN","name":"assume_final_deferred_immediate_else","addr":"(AO)","loc":"e,122:4,122:40","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assume_final_deferred_immediate_else","addr":"(AO)","loc":"e,122:4,122:40","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(BO)","loc":"e,122:42,122:48","type":"[FINAL_DEFERRED_IMMEDIATE]", "propp": [ @@ -931,7 +931,7 @@ ]}, {"type":"ALWAYS","name":"","addr":"(GO)","loc":"e,123:4,123:40","keyword":"always_comb","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ - {"type":"BEGIN","name":"assume_final_deferred_immediate_stmt","addr":"(HO)","loc":"e,123:4,123:40","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assume_final_deferred_immediate_stmt","addr":"(HO)","loc":"e,123:4,123:40","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(IO)","loc":"e,123:42,123:48","type":"[FINAL_DEFERRED_IMMEDIATE]", "propp": [ @@ -950,7 +950,7 @@ ]}, {"type":"ALWAYS","name":"","addr":"(NO)","loc":"e,124:4,124:45","keyword":"always_comb","isSuspendable":false,"needProcess":false,"sentreep": [], "stmtsp": [ - {"type":"BEGIN","name":"assume_final_deferred_immediate_stmt_else","addr":"(OO)","loc":"e,124:4,124:45","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assume_final_deferred_immediate_stmt_else","addr":"(OO)","loc":"e,124:4,124:45","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(PO)","loc":"e,124:47,124:53","type":"[FINAL_DEFERRED_IMMEDIATE]", "propp": [ @@ -989,7 +989,7 @@ {"type":"CONST","name":"?32?sh0","addr":"(BP)","loc":"e,127:22,127:23","dtypep":"(N)"} ]} ],"scopeNamep": []}, - {"type":"BEGIN","name":"assert_concurrent","addr":"(CP)","loc":"e,130:4,130:21","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assert_concurrent","addr":"(CP)","loc":"e,130:4,130:21","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(DP)","loc":"e,130:23,130:29","type":"[CONCURRENT]", "propp": [ @@ -1002,7 +1002,7 @@ ]} ],"sentreep": [],"failsp": [],"passsp": []} ]}, - {"type":"BEGIN","name":"assert_concurrent_else","addr":"(HP)","loc":"e,131:4,131:26","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assert_concurrent_else","addr":"(HP)","loc":"e,131:4,131:26","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(IP)","loc":"e,131:28,131:34","type":"[CONCURRENT]", "propp": [ @@ -1024,7 +1024,7 @@ ],"filep": []} ],"passsp": []} ]}, - {"type":"BEGIN","name":"assert_concurrent_stmt","addr":"(PP)","loc":"e,132:4,132:26","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assert_concurrent_stmt","addr":"(PP)","loc":"e,132:4,132:26","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(QP)","loc":"e,132:28,132:34","type":"[CONCURRENT]", "propp": [ @@ -1046,7 +1046,7 @@ ],"filep": []} ]} ]}, - {"type":"BEGIN","name":"assert_concurrent_stmt_else","addr":"(XP)","loc":"e,133:4,133:31","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assert_concurrent_stmt_else","addr":"(XP)","loc":"e,133:4,133:31","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(YP)","loc":"e,133:33,133:39","type":"[CONCURRENT]", "propp": [ @@ -1077,7 +1077,7 @@ ],"filep": []} ]} ]}, - {"type":"BEGIN","name":"assume_concurrent","addr":"(IQ)","loc":"e,135:4,135:21","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assume_concurrent","addr":"(IQ)","loc":"e,135:4,135:21","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(JQ)","loc":"e,135:23,135:29","type":"[CONCURRENT]", "propp": [ @@ -1090,7 +1090,7 @@ ]} ],"sentreep": [],"failsp": [],"passsp": []} ]}, - {"type":"BEGIN","name":"assume_concurrent_else","addr":"(NQ)","loc":"e,136:4,136:26","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assume_concurrent_else","addr":"(NQ)","loc":"e,136:4,136:26","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(OQ)","loc":"e,136:28,136:34","type":"[CONCURRENT]", "propp": [ @@ -1112,7 +1112,7 @@ ],"filep": []} ],"passsp": []} ]}, - {"type":"BEGIN","name":"assume_concurrent_stmt","addr":"(VQ)","loc":"e,137:4,137:26","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assume_concurrent_stmt","addr":"(VQ)","loc":"e,137:4,137:26","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(WQ)","loc":"e,137:28,137:34","type":"[CONCURRENT]", "propp": [ @@ -1134,7 +1134,7 @@ ],"filep": []} ]} ]}, - {"type":"BEGIN","name":"assume_concurrent_stmt_else","addr":"(DR)","loc":"e,138:4,138:31","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"assume_concurrent_stmt_else","addr":"(DR)","loc":"e,138:4,138:31","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"ASSERT","name":"","addr":"(ER)","loc":"e,138:33,138:39","type":"[CONCURRENT]", "propp": [ @@ -1165,7 +1165,7 @@ ],"filep": []} ]} ]}, - {"type":"BEGIN","name":"cover_concurrent","addr":"(OR)","loc":"e,140:4,140:20","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"cover_concurrent","addr":"(OR)","loc":"e,140:4,140:20","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"COVER","name":"","addr":"(PR)","loc":"e,140:22,140:27","type":"[CONCURRENT]", "propp": [ @@ -1175,7 +1175,7 @@ ]} ],"sentreep": [],"coverincsp": [],"passsp": []} ]}, - {"type":"BEGIN","name":"cover_concurrent_stmt","addr":"(SR)","loc":"e,141:4,141:25","implied":true,"needProcess":false,"unnamed":false, + {"type":"BEGIN","name":"cover_concurrent_stmt","addr":"(SR)","loc":"e,141:4,141:25","implied":true,"needProcess":false,"unnamed":false,"declsp": [], "stmtsp": [ {"type":"COVER","name":"","addr":"(TR)","loc":"e,141:27,141:32","type":"[CONCURRENT]", "propp": [ @@ -1301,7 +1301,7 @@ ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(QT)","loc":"d,55:44,55:49","implied":false,"needProcess":false,"unnamed":true, + {"type":"BEGIN","name":"","addr":"(QT)","loc":"d,55:44,55:49","implied":false,"needProcess":false,"unnamed":true,"declsp": [], "stmtsp": [ {"type":"STMTEXPR","name":"","addr":"(RT)","loc":"d,56:16,56:17", "exprp": [ @@ -1357,7 +1357,7 @@ ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(NU)","loc":"d,72:22,72:27","implied":false,"needProcess":false,"unnamed":true, + {"type":"BEGIN","name":"","addr":"(NU)","loc":"d,72:22,72:27","implied":false,"needProcess":false,"unnamed":true,"declsp": [], "stmtsp": [ {"type":"ASSIGN","name":"","addr":"(OU)","loc":"d,73:17,73:18","dtypep":"UNLINKED", "rhsp": [ @@ -1410,7 +1410,7 @@ ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(IV)","loc":"d,89:22,89:27","implied":false,"needProcess":false,"unnamed":true, + {"type":"BEGIN","name":"","addr":"(IV)","loc":"d,89:22,89:27","implied":false,"needProcess":false,"unnamed":true,"declsp": [], "stmtsp": [ {"type":"ASSIGN","name":"","addr":"(JV)","loc":"d,90:17,90:18","dtypep":"UNLINKED", "rhsp": [ @@ -1516,7 +1516,7 @@ ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(YW)","loc":"d,118:35,118:40","implied":false,"needProcess":false,"unnamed":true, + {"type":"BEGIN","name":"","addr":"(YW)","loc":"d,118:35,118:40","implied":false,"needProcess":false,"unnamed":true,"declsp": [], "stmtsp": [ {"type":"ASSIGN","name":"","addr":"(ZW)","loc":"d,119:20,119:22","dtypep":"UNLINKED", "rhsp": [ diff --git a/test_regress/t/t_fork.out b/test_regress/t/t_fork.out index 5f067c56a..a2e1e97f4 100644 --- a/test_regress/t/t_fork.out +++ b/test_regress/t/t_fork.out @@ -1,6 +1,6 @@ -%Error-NOTIMING: t/t_fork.v:10:14: Fork statements require --timing - : ... note: In instance 't' +%Error-NOTIMING: t/t_fork.v:10:7: Fork statements require --timing + : ... note: In instance 't' 10 | fork : fblk - | ^~~~ + | ^~~~ ... For error description see https://verilator.org/warn/NOTIMING?v=latest %Error: Exiting due to diff --git a/test_regress/t/t_fork_initial.v b/test_regress/t/t_fork_initial.v index 24780ede3..ef6ed69b1 100644 --- a/test_regress/t/t_fork_initial.v +++ b/test_regress/t/t_fork_initial.v @@ -8,8 +8,11 @@ module t(); initial fork reg i; i = 1'b1; - if (i != 1'b1) $stop; - $write("*-* All Finished *-*\n"); - $finish; + begin + #1; + if (i != 1'b1) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end join endmodule diff --git a/test_regress/t/t_json_only_tag.out b/test_regress/t/t_json_only_tag.out index 67e7f5fff..631588b69 100644 --- a/test_regress/t/t_json_only_tag.out +++ b/test_regress/t/t_json_only_tag.out @@ -33,7 +33,7 @@ ],"scopeNamep": []}, {"type":"INITIAL","name":"","addr":"(FB)","loc":"d,39:4,39:11","isSuspendable":false,"needProcess":false, "stmtsp": [ - {"type":"BEGIN","name":"","addr":"(GB)","loc":"d,39:12,39:17","implied":false,"needProcess":false,"unnamed":true, + {"type":"BEGIN","name":"","addr":"(GB)","loc":"d,39:12,39:17","implied":false,"needProcess":false,"unnamed":true,"declsp": [], "stmtsp": [ {"type":"STMTEXPR","name":"","addr":"(HB)","loc":"d,41:7,41:8", "exprp": [ diff --git a/test_regress/t/t_lint_block_redecl_bad.out b/test_regress/t/t_lint_block_redecl_bad.out index 95c7022c1..abc2b933d 100644 --- a/test_regress/t/t_lint_block_redecl_bad.out +++ b/test_regress/t/t_lint_block_redecl_bad.out @@ -1,8 +1,8 @@ -%Error: t/t_lint_block_redecl_bad.v:17:34: Duplicate declaration of block: 'COMB' +%Error: t/t_lint_block_redecl_bad.v:17:27: Duplicate declaration of block: 'COMB' 17 | for(i=0; i<9; i++ ) begin: COMB - | ^~~~ - t/t_lint_block_redecl_bad.v:14:35: ... Location of original declaration + | ^~~~~ + t/t_lint_block_redecl_bad.v:14:28: ... Location of original declaration 14 | for(i=0; i<10; i++ ) begin: COMB - | ^~~~ + | ^~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: Exiting due to