From 800af37975350c985af9e66de4d3aebd28c6ba34 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 23 Sep 2025 20:49:01 +0200 Subject: [PATCH] Internals: Refactor generate construct Ast handling (#6280) (#6470) Internals: Refactor generate construct Ast handling (#6280) We introduce AstNodeGen, the common base class of AstGenBlock, AstGenCase, AstGenFor, and AstGenIf, which together represent all SV generate constructs. Subsequently remove AstNodeFor, AstNodeCase (AstCase is now directly derived from AstNodeStmt) and adjust internals to work on the new representation. Output is identical modulo hashes do to changed AstNode type ids, no functional change intended. Step towards #6280. --- src/V3Assert.cpp | 9 +- src/V3AssertPre.cpp | 2 +- src/V3Ast.cpp | 3 +- src/V3AstNodeOther.h | 109 ++++++++-- src/V3AstNodeStmt.h | 152 ++++++-------- src/V3AstNodes.cpp | 18 +- src/V3Begin.cpp | 39 ++-- src/V3Broken.cpp | 2 +- src/V3Case.cpp | 106 ++++++---- src/V3Case.h | 4 +- src/V3Control.cpp | 27 +++ src/V3Control.h | 1 + src/V3Coverage.cpp | 12 +- src/V3Delayed.cpp | 3 - src/V3EmitCFunc.h | 2 +- src/V3EmitV.cpp | 83 +++++--- src/V3EmitXml.cpp | 5 + src/V3Fork.cpp | 4 +- src/V3LinkDot.cpp | 81 ++++++-- src/V3LinkInc.cpp | 4 - src/V3LinkJump.cpp | 4 +- src/V3LinkParse.cpp | 67 +++--- src/V3Param.cpp | 18 +- src/V3ParseImp.h | 2 +- src/V3Randomize.cpp | 20 +- src/V3Sched.cpp | 2 +- src/V3Simulate.h | 35 +--- src/V3Split.cpp | 2 +- src/V3SplitVar.cpp | 4 +- src/V3Task.cpp | 6 +- src/V3Timing.cpp | 2 +- src/V3Unroll.cpp | 34 +-- src/V3Unroll.h | 3 +- src/V3Width.cpp | 193 ++++++++++-------- src/Verilator.cpp | 2 + src/verilog.y | 62 +++--- test_regress/t/t_constraint_json_only.out | 2 +- test_regress/t/t_dump_json.out | 22 +- .../t/t_duplicated_gen_blocks_bad.out | 4 +- test_regress/t/t_json_only_begin_hier.out | 26 +-- test_regress/t/t_json_only_tag.out | 2 +- test_regress/t/t_xml_begin_hier.out | 26 +-- 42 files changed, 687 insertions(+), 517 deletions(-) diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 1e8d44b28..e5aadf836 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -129,7 +129,7 @@ class AssertVisitor final : public VNVisitor { // STATE AstNodeModule* m_modp = nullptr; // Last module - const AstBegin* m_beginp = nullptr; // Last begin + const AstNode* m_beginp = nullptr; // Last AstBegin/AstGenBlock unsigned m_monitorNum = 0; // Global $monitor numbering (not per module) AstVar* m_monitorNumVarp = nullptr; // $monitor number variable AstVar* m_monitorOffVarp = nullptr; // $monitoroff variable @@ -809,6 +809,13 @@ class AssertVisitor final : public VNVisitor { m_procedurep = nodep; iterateChildren(nodep); } + void visit(AstGenBlock* nodep) override { + // This code is needed rather than a visitor in V3Begin, + // because V3Assert is called before V3Begin + VL_RESTORER(m_beginp); + m_beginp = nodep; + iterateChildren(nodep); + } void visit(AstBegin* nodep) override { // This code is needed rather than a visitor in V3Begin, // because V3Assert is called before V3Begin diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index c19dd4eaf..a61e63f8f 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -349,7 +349,7 @@ private: nodep->findBasicDType(VBasicDTypeKwd::UINT32)}; cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); cntVarp->funcLocal(true); - AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, false, true}; + AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, true}; beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep}); beginp->addStmtsp(new AstWhile{ nodep->fileline(), diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 2c66b2da8..a50382885 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1078,8 +1078,7 @@ AstNode* AstNode::iterateSubtreeReturnEdits(VNVisitor& v) { } else if (!nodep->backp()) { // Calling on standalone tree; insert a shim node so we can keep // track, then delete it on completion - AstBegin* const tempp - = new AstBegin{nodep->fileline(), "[EditWrapper]", nodep, false, false}; + AstBegin* const tempp = new AstBegin{nodep->fileline(), "[EditWrapper]", nodep, false}; // nodep to null as may be replaced VL_DO_DANGLING(tempp->stmtsp()->accept(v), nodep); nodep = tempp->stmtsp()->unlinkFrBackWithNext(); diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 26bc726ec..d7d707d56 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -267,6 +267,13 @@ public: string name() const override VL_MT_STABLE { return m_name; } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; +class AstNodeGen VL_NOT_FINAL : public AstNode { + // Generate construct +public: + AstNodeGen(VNType t, FileLine* fl) + : AstNode{t, fl} {} + ASTGEN_MEMBERS_AstNodeGen; +}; class AstNodeModule VL_NOT_FINAL : public AstNode { // A module, package, program or interface declaration; // something that can live directly under the TOP, @@ -668,21 +675,6 @@ public: string name() const override VL_MT_STABLE { return m_name; } VUseType useType() const { return m_useType; } }; -class AstCaseItem final : public AstNode { - // Single item of a case statement - // @astgen op1 := condsp : List[AstNodeExpr] - // @astgen op2 := stmtsp : List[AstNode] -public: - AstCaseItem(FileLine* fl, AstNodeExpr* condsp, AstNode* stmtsp) - : ASTGEN_SUPER_CaseItem(fl) { - addCondsp(condsp); - addStmtsp(stmtsp); - } - ASTGEN_MEMBERS_AstCaseItem; - int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; } - bool isDefault() const { return condsp() == nullptr; } - bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } -}; class AstCell final : public AstNode { // A instantiation cell or interface call (don't know which until link) // @astgen op1 := pinsp : List[AstPin] // List of port assignments @@ -1137,6 +1129,19 @@ public: V3Graph* depGraphp() { return m_depGraphp; } const V3Graph* depGraphp() const { return m_depGraphp; } }; +class AstGenCaseItem final : public AstNode { + // Single item of an AstGenCase + // @astgen op1 := condsp : List[AstNodeExpr] + // @astgen op2 := itemsp : List[AstNode] +public: + AstGenCaseItem(FileLine* fl, AstNodeExpr* condsp, AstNode* itemsp) + : ASTGEN_SUPER_GenCaseItem(fl) { + addCondsp(condsp); + addItemsp(itemsp); + } + ASTGEN_MEMBERS_AstGenCaseItem; + bool isDefault() const { return condsp() == nullptr; } +}; class AstImplicit final : public AstNode { // Create implicit wires and do nothing else, for gates that are ignored // Parents: MODULE @@ -2287,22 +2292,18 @@ public: class AstBegin final : public AstNodeBlock { // A Begin/end named block, only exists shortly after parsing until linking // Parents: statement - // @astgen op1 := genforp : Optional[AstNode] - const bool m_generate : 1; // Underneath a generate 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 generate, bool implied) + AstBegin(FileLine* fl, const string& name, AstNode* stmtsp, bool implied) : ASTGEN_SUPER_Begin(fl, name, stmtsp) - , m_generate{generate} , m_needProcess{false} , m_implied{implied} {} ASTGEN_MEMBERS_AstBegin; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; - bool generate() const { return m_generate; } void setNeedProcess() { m_needProcess = true; } bool needProcess() const { return m_needProcess; } bool implied() const { return m_implied; } @@ -2463,6 +2464,74 @@ public: void dumpJson(std::ostream& str = std::cout) const override; }; +// === AstNodeGen === +class AstGenBlock final : public AstNodeGen { + // Generate 'begin' + // @astgen op1 := genforp : Optional[AstNode] + // @astgen op2 := itemsp : List[AstNode] + std::string m_name; // Name of block + const bool m_unnamed; // Originally unnamed (name change does not affect this) + const bool m_implied; // Not inserted by user + +public: + AstGenBlock(FileLine* fl, const string& name, AstNode* itemsp, bool implied) + : ASTGEN_SUPER_GenBlock(fl) + , m_name{name} + , m_unnamed{name.empty()} + , m_implied{implied} { + this->addItemsp(itemsp); + } + ASTGEN_MEMBERS_AstGenBlock; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + std::string name() const override VL_MT_STABLE { return m_name; } + void name(const std::string& name) override { m_name = name; } + bool unnamed() const { return m_unnamed; } + bool implied() const { return m_implied; } +}; +class AstGenCase final : public AstNodeGen { + // Generate 'case' + // @astgen op1 := exprp : AstNodeExpr // Condition (scurtinee) expression + // @astgen op2 := itemsp : List[AstGenCaseItem] +public: + AstGenCase(FileLine* fl, AstNodeExpr* exprp, AstGenCaseItem* itemsp) + : ASTGEN_SUPER_GenCase(fl) { + this->exprp(exprp); + this->addItemsp(itemsp); + } + ASTGEN_MEMBERS_AstGenCase; +}; +class AstGenFor final : public AstNodeGen { + // Generate 'for' + // @astgen op1 := initsp : List[AstNode] + // @astgen op2 := condp : AstNodeExpr + // @astgen op3 := incsp : List[AstNode] + // @astgen op4 := itemsp : List[AstNode] +public: + AstGenFor(FileLine* fl, AstNode* initsp, AstNodeExpr* condp, AstNode* incsp, AstNode* itemsp) + : ASTGEN_SUPER_GenFor(fl) { + this->addInitsp(initsp); + this->condp(condp); + this->addIncsp(incsp); + this->addItemsp(itemsp); + } + ASTGEN_MEMBERS_AstGenFor; +}; +class AstGenIf final : public AstNodeGen { + // Generate 'if' + // @astgen op1 := condp : AstNodeExpr + // @astgen op2 := thensp : List[AstNode] + // @astgen op3 := elsesp : List[AstNode] +public: + AstGenIf(FileLine* fl, AstNodeExpr* condp, AstNode* thensp, AstNode* elsesp) + : ASTGEN_SUPER_GenIf(fl) { + this->condp(condp); + this->addThensp(thensp); + this->addElsesp(elsesp); + } + ASTGEN_MEMBERS_AstGenIf; +}; + // === AstNodeModule === class AstClass final : public AstNodeModule { // @astgen op4 := extendsp : List[AstClassExtends] diff --git a/src/V3AstNodeStmt.h b/src/V3AstNodeStmt.h index 32c59eb61..e6ce9bf3e 100644 --- a/src/V3AstNodeStmt.h +++ b/src/V3AstNodeStmt.h @@ -70,21 +70,6 @@ public: bool isTimingControl() const override { return timingControlp(); } virtual bool brokeLhsMustBeLvalue() const = 0; }; -class AstNodeCase VL_NOT_FINAL : public AstNodeStmt { - // @astgen op1 := exprp : AstNodeExpr // Condition (scurtinee) expression - // @astgen op2 := itemsp : List[AstCaseItem] - // @astgen op3 := notParallelp : List[AstNode] // assertion code for non-full case's -protected: - AstNodeCase(VNType t, FileLine* fl, AstNodeExpr* exprp, AstCaseItem* itemsp) - : AstNodeStmt{t, fl} { - this->exprp(exprp); - addItemsp(itemsp); - } - -public: - ASTGEN_MEMBERS_AstNodeCase; - int instrCount() const override { return INSTR_COUNT_BRANCH; } -}; class AstNodeCoverOrAssert VL_NOT_FINAL : public AstNodeStmt { // Cover or Assert // Parents: {statement list} @@ -121,27 +106,6 @@ public: || this->type() == VAssertType::INTERNAL; } }; -class AstNodeFor VL_NOT_FINAL : public AstNodeStmt { - // @astgen op1 := initsp : List[AstNode] - // @astgen op2 := condp : AstNodeExpr - // @astgen op3 := incsp : List[AstNode] - // @astgen op4 := stmtsp : List[AstNode] -protected: - AstNodeFor(VNType t, FileLine* fl, AstNode* initsp, AstNodeExpr* condp, AstNode* incsp, - AstNode* stmtsp) - : AstNodeStmt{t, fl} { - addInitsp(initsp); - this->condp(condp); - addIncsp(incsp); - addStmtsp(stmtsp); - } - -public: - ASTGEN_MEMBERS_AstNodeFor; - bool isGateOptimizable() const override { return false; } - int instrCount() const override { return INSTR_COUNT_BRANCH; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } -}; class AstNodeForeach VL_NOT_FINAL : public AstNodeStmt { // @astgen op1 := arrayp : AstNode // @astgen op2 := stmtsp : List[AstNode] @@ -218,6 +182,23 @@ public: // === Concrete node types ===================================================== +// === AstNode === +class AstCaseItem final : public AstNode { + // Single item of AstCase/AstRandCase + // @astgen op1 := condsp : List[AstNodeExpr] + // @astgen op2 := stmtsp : List[AstNode] +public: + AstCaseItem(FileLine* fl, AstNodeExpr* condsp, AstNode* stmtsp) + : ASTGEN_SUPER_CaseItem(fl) { + addCondsp(condsp); + addStmtsp(stmtsp); + } + ASTGEN_MEMBERS_AstCaseItem; + int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; } + bool isDefault() const { return condsp() == nullptr; } + bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } +}; + // === AstNodeStmt === class AstAssertCtl final : public AstNodeStmt { // @astgen op1 := controlTypep : AstNodeExpr @@ -306,6 +287,47 @@ public: bool isPredictOptimizable() const override { return false; } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; +class AstCase final : public AstNodeStmt { + // Case statement + // @astgen op1 := exprp : AstNodeExpr // Condition (scurtinee) expression + // @astgen op2 := itemsp : List[AstCaseItem] + // @astgen op3 := notParallelp : List[AstNode] // assertion code for non-full case's + VCaseType m_casex; // 0=case, 1=casex, 2=casez + bool m_fullPragma = false; // Synthesis full_case + bool m_parallelPragma = false; // Synthesis parallel_case + bool m_uniquePragma = false; // unique case + bool m_unique0Pragma = false; // unique0 case + bool m_priorityPragma = false; // priority case +public: + AstCase(FileLine* fl, VCaseType casex, AstNodeExpr* exprp, AstCaseItem* itemsp) + : ASTGEN_SUPER_Case(fl) + , m_casex{casex} { + this->exprp(exprp); + addItemsp(itemsp); + } + ASTGEN_MEMBERS_AstCase; + int instrCount() const override { return INSTR_COUNT_BRANCH; } + string verilogKwd() const override { return casez() ? "casez" : casex() ? "casex" : "case"; } + bool sameNode(const AstNode* samep) const override { + return m_casex == VN_DBG_AS(samep, Case)->m_casex; + } + bool casex() const { return m_casex == VCaseType::CT_CASEX; } + bool casez() const { return m_casex == VCaseType::CT_CASEZ; } + bool caseInside() const { return m_casex == VCaseType::CT_CASEINSIDE; } + bool caseSimple() const { return m_casex == VCaseType::CT_CASE; } + void caseInsideSet() { m_casex = VCaseType::CT_CASEINSIDE; } + bool fullPragma() const { return m_fullPragma; } + void fullPragma(bool flag) { m_fullPragma = flag; } + bool parallelPragma() const { return m_parallelPragma; } + void parallelPragma(bool flag) { m_parallelPragma = flag; } + bool uniquePragma() const { return m_uniquePragma; } + void uniquePragma(bool flag) { m_uniquePragma = flag; } + bool unique0Pragma() const { return m_unique0Pragma; } + void unique0Pragma(bool flag) { m_unique0Pragma = flag; } + bool priorityPragma() const { return m_priorityPragma; } + void priorityPragma(bool flag) { m_priorityPragma = flag; } + string pragmaString() const; +}; class AstComment final : public AstNodeStmt { // Some comment to put into the output stream const string m_name; // Text of comment @@ -1130,49 +1152,6 @@ public: AstAlways* convertToAlways(); }; -// === AstNodeCase === -class AstCase final : public AstNodeCase { - // Case statement - VCaseType m_casex; // 0=case, 1=casex, 2=casez - bool m_fullPragma = false; // Synthesis full_case - bool m_parallelPragma = false; // Synthesis parallel_case - bool m_uniquePragma = false; // unique case - bool m_unique0Pragma = false; // unique0 case - bool m_priorityPragma = false; // priority case -public: - AstCase(FileLine* fl, VCaseType casex, AstNodeExpr* exprp, AstCaseItem* itemsp) - : ASTGEN_SUPER_Case(fl, exprp, itemsp) - , m_casex{casex} {} - ASTGEN_MEMBERS_AstCase; - string verilogKwd() const override { return casez() ? "casez" : casex() ? "casex" : "case"; } - bool sameNode(const AstNode* samep) const override { - return m_casex == VN_DBG_AS(samep, Case)->m_casex; - } - bool casex() const { return m_casex == VCaseType::CT_CASEX; } - bool casez() const { return m_casex == VCaseType::CT_CASEZ; } - bool caseInside() const { return m_casex == VCaseType::CT_CASEINSIDE; } - bool caseSimple() const { return m_casex == VCaseType::CT_CASE; } - void caseInsideSet() { m_casex = VCaseType::CT_CASEINSIDE; } - bool fullPragma() const { return m_fullPragma; } - void fullPragma(bool flag) { m_fullPragma = flag; } - bool parallelPragma() const { return m_parallelPragma; } - void parallelPragma(bool flag) { m_parallelPragma = flag; } - bool uniquePragma() const { return m_uniquePragma; } - void uniquePragma(bool flag) { m_uniquePragma = flag; } - bool unique0Pragma() const { return m_unique0Pragma; } - void unique0Pragma(bool flag) { m_unique0Pragma = flag; } - bool priorityPragma() const { return m_priorityPragma; } - void priorityPragma(bool flag) { m_priorityPragma = flag; } - string pragmaString() const; -}; -class AstGenCase final : public AstNodeCase { - // Generate Case statement -public: - AstGenCase(FileLine* fl, AstNodeExpr* exprp, AstCaseItem* itemsp) - : ASTGEN_SUPER_GenCase(fl, exprp, itemsp) {} - ASTGEN_MEMBERS_AstGenCase; -}; - // === AstNodeCoverOrAssert === class AstAssert final : public AstNodeCoverOrAssert { // @astgen op3 := failsp: List[AstNode] // Statements when propp is failing/falsey @@ -1215,14 +1194,6 @@ public: VAssertDirectiveType::RESTRICT) {} }; -// === AstNodeFor === -class AstGenFor final : public AstNodeFor { -public: - AstGenFor(FileLine* fl, AstNode* initsp, AstNodeExpr* condp, AstNode* incsp, AstNode* stmtsp) - : ASTGEN_SUPER_GenFor(fl, initsp, condp, incsp, stmtsp) {} - ASTGEN_MEMBERS_AstGenFor; -}; - // === AstNodeForeach === class AstConstraintForeach final : public AstNodeForeach { // Constraint foreach statement @@ -1245,12 +1216,7 @@ public: : ASTGEN_SUPER_ConstraintIf(fl, condp, thensp, elsesp) {} ASTGEN_MEMBERS_AstConstraintIf; }; -class AstGenIf final : public AstNodeIf { -public: - AstGenIf(FileLine* fl, AstNodeExpr* condp, AstNode* thensp, AstNode* elsesp) - : ASTGEN_SUPER_GenIf(fl, condp, thensp, elsesp) {} - ASTGEN_MEMBERS_AstGenIf; -}; + class AstIf final : public AstNodeIf { bool m_uniquePragma = false; // unique case bool m_unique0Pragma = false; // unique0 case diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 415c250b2..9e85e993c 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1928,6 +1928,18 @@ const char* AstEnumDType::broken() const { } void AstEnumItemRef::dumpJson(std::ostream& str) const { dumpJsonGen(str); } + +void AstGenBlock::dump(std::ostream& str) const { + this->AstNode::dump(str); + if (implied()) str << " [IMPLIED]"; + if (unnamed()) str << " [UNNAMED]"; +} +void AstGenBlock::dumpJson(std::ostream& str) const { + dumpJsonBoolFunc(str, implied); + dumpJsonBoolFunc(str, unnamed); + dumpJsonGen(str); +} + void AstIfaceRefDType::dump(std::ostream& str) const { this->AstNodeDType::dump(str); if (isPortDecl()) str << " [PORTDECL]"; @@ -2941,14 +2953,10 @@ void AstNodeBlock::dumpJson(std::ostream& str) const { } void AstBegin::dump(std::ostream& str) const { this->AstNodeBlock::dump(str); - if (generate()) str << " [GEN]"; - if (genforp()) str << " [GENFOR]"; if (implied()) str << " [IMPLIED]"; if (needProcess()) str << " [NPRC]"; } void AstBegin::dumpJson(std::ostream& str) const { - dumpJsonBoolFunc(str, generate); - dumpJsonBool(str, "genfor", bool(genforp())); dumpJsonBoolFunc(str, implied); dumpJsonBoolFunc(str, needProcess); dumpJsonGen(str); @@ -3258,7 +3266,7 @@ AstAlways* AstAssignW::convertToAlways() { if (hasTimingControl) { // If there's a timing control, put the assignment in a fork..join_none. This process won't // get marked as suspendable and thus will be scheduled normally - AstBegin* const beginp = new AstBegin{flp, "", bodysp, false, false}; + AstBegin* const beginp = new AstBegin{flp, "", bodysp, false}; AstFork* const forkp = new AstFork{flp, "", beginp}; forkp->joinType(VJoinType::JOIN_NONE); bodysp = forkp; diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 4f673211b..e6db8ec4b 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -73,31 +73,32 @@ class BeginVisitor final : public VNVisitor { string dot(const string& a, const string& b) { return VString::dot(a, "__DOT__", b); } - void dotNames(const AstNodeBlock* const nodep, const char* const blockName) { + void dotNames(const std::string& name, FileLine* const flp, AstNode* stmtsp, + const char* const blockName) { UINFO(8, "nname " << m_namedScope); - if (nodep->name() != "") { // Else unneeded unnamed block + if (name != "") { // Else unneeded unnamed block // Create data for dotted variable resolution - string dottedname = nodep->name() + "__DOT__"; // So always found + 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 (nodep->name() != "") { + 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{ - nodep->fileline(), m_unnamedScope, blockName, m_modp->timeunit()}; + AstCellInline* const inlinep + = new AstCellInline{flp, m_unnamedScope, blockName, m_modp->timeunit()}; m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells } } } // Remap var names and replace lower Begins - iterateAndNextNull(nodep->stmtsp()); + iterateAndNextNull(stmtsp); } void liftNode(AstNode* nodep) { @@ -125,14 +126,13 @@ class BeginVisitor final : public VNVisitor { // 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, false}; + AstBegin* const beginp = new AstBegin{stmtp->fileline(), "", nullptr, false}; stmtp->replaceWith(beginp); beginp->addStmtsp(stmtp); stmtp = beginp; } } - dotNames(nodep, "__FORK__"); + dotNames(nodep->name(), nodep->fileline(), nodep->stmtsp(), "__FORK__"); nodep->name(""); } void visit(AstForeach* nodep) override { @@ -199,6 +199,20 @@ class BeginVisitor final : public VNVisitor { m_liftedp = nullptr; } } + void visit(AstGenBlock* nodep) override { + // GenBlocks were only useful in variable creation, change names and delete + UINFO(8, " " << nodep); + VL_RESTORER(m_displayScope); + 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__"); + // Repalce node with body then delete + if (AstNode* const itemsp = nodep->itemsp()) { + nodep->addNextHere(itemsp->unlinkFrBackWithNext()); + } + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + } void visit(AstBegin* nodep) override { // Begin blocks were only useful in variable creation, change names and delete UINFO(8, " " << nodep); @@ -208,9 +222,8 @@ class BeginVisitor final : public VNVisitor { { VL_RESTORER(m_keepBegins); m_keepBegins = false; - dotNames(nodep, "__BEGIN__"); + dotNames(nodep->name(), nodep->fileline(), nodep->stmtsp(), "__BEGIN__"); } - UASSERT_OBJ(!nodep->genforp(), nodep, "GENFORs should have been expanded earlier"); // Cleanup if (m_keepBegins) { @@ -431,7 +444,7 @@ AstNode* V3Begin::convertToWhile(AstForeach* nodep) { AstNodeDType* fromDtp = fromp->dtypep()->skipRefp(); // Split into for loop // We record where the body needs to eventually go with bodyPointp - AstNode* bodyPointp = new AstBegin{nodep->fileline(), "[EditWrapper]", nullptr, false, false}; + AstNode* bodyPointp = new AstBegin{nodep->fileline(), "[EditWrapper]", nullptr, false}; AstNode* newp = nullptr; AstNode* lastp = nodep; AstVar* nestedIndexp = nullptr; diff --git a/src/V3Broken.cpp b/src/V3Broken.cpp index 765bf7160..8c7f4584e 100644 --- a/src/V3Broken.cpp +++ b/src/V3Broken.cpp @@ -382,7 +382,7 @@ void V3Broken::allowMidvisitorCheck(bool flag) { s_brokenAllowMidvisitorCheck = void V3Broken::selfTest() { // Exercise addNewed and deleted for coverage, as otherwise only used with VL_LEAK_CHECKS FileLine* const fl = new FileLine{FileLine::commandLineFilename()}; - AstNode* const newp = new AstBegin{fl, "[EditWrapper]", nullptr, false, false}; + AstNode* const newp = new AstBegin{fl, "[EditWrapper]", nullptr, false}; // Don't actually do it with VL_LEAK_CHECKS, when new/delete calls these. // Otherwise you call addNewed twice on the same address, which is an error. #ifndef VL_LEAK_CHECKS diff --git a/src/V3Case.cpp b/src/V3Case.cpp index b57e51e9f..b2f6d8f01 100644 --- a/src/V3Case.cpp +++ b/src/V3Case.cpp @@ -49,64 +49,80 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### class CaseLintVisitor final : public VNVisitorConst { - const AstNodeCase* m_caseExprp - = nullptr; // Under a CASE value node, if so the relevant case statement + // Under a CASE value node, if so the relevant case statement + const AstNode* m_casep = nullptr; // METHODS + template + static void detectMultipleDefaults(CaseItem* itemsp) { + bool hitDefault = false; + for (CaseItem* itemp = itemsp; itemp; itemp = AstNode::as(itemp->nextp())) { + if (!itemp->isDefault()) continue; + if (hitDefault) itemp->v3error("Multiple default statements in case statement."); + hitDefault = true; + } + } - void visit(AstNodeCase* nodep) override { - if (VN_IS(nodep, Case) && VN_AS(nodep, Case)->casex()) { + template + void checkXZinNonCaseX(AstNode* casep, AstNodeExpr* exprp, CaseItem* itemsp) { + VL_RESTORER(m_casep); + m_casep = casep; + iterateConst(exprp); + for (CaseItem* itemp = itemsp; itemp; itemp = AstNode::as(itemp->nextp())) { + iterateAndNextConstNull(itemp->condsp()); + } + } + + // VISITORS + void visit(AstGenCase* nodep) override { + // Detect multiple defaults + detectMultipleDefaults(nodep->itemsp()); + // Check for X/Z in non-casex statements + checkXZinNonCaseX(nodep, nodep->exprp(), nodep->itemsp()); + } + + void visit(AstCase* nodep) override { + if (nodep->casex()) { nodep->v3warn(CASEX, "Suggest casez (with ?'s) in place of casex (with X's)"); } // Detect multiple defaults - bool hitDefault = false; - for (AstCaseItem* itemp = nodep->itemsp(); itemp; - itemp = VN_AS(itemp->nextp(), CaseItem)) { - if (itemp->isDefault()) { - if (hitDefault) { - itemp->v3error("Multiple default statements in case statement."); - } - hitDefault = true; - } - } - + detectMultipleDefaults(nodep->itemsp()); // Check for X/Z in non-casex statements - { - VL_RESTORER(m_caseExprp); - m_caseExprp = nodep; - iterateConst(nodep->exprp()); - for (AstCaseItem* itemp = nodep->itemsp(); itemp; - itemp = VN_AS(itemp->nextp(), CaseItem)) { - iterateAndNextConstNull(itemp->condsp()); - } - } + checkXZinNonCaseX(nodep, nodep->exprp(), nodep->itemsp()); } void visit(AstConst* nodep) override { - // See also neverItem - if (m_caseExprp && nodep->num().isFourState()) { - if (VN_IS(m_caseExprp, GenCase)) { - nodep->v3error("Use of x/? constant in generate case statement, " - "(no such thing as 'generate casez')"); - } else if (VN_IS(m_caseExprp, Case) && VN_AS(m_caseExprp, Case)->casex()) { - // Don't sweat it, we already complained about casex in general - } else if (VN_IS(m_caseExprp, Case) - && (VN_AS(m_caseExprp, Case)->casez() - || VN_AS(m_caseExprp, Case)->caseInside())) { - if (nodep->num().isAnyX()) { - nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, " - "(perhaps intended ?/z in constant)"); - } - } else { - nodep->v3warn(CASEWITHX, "Use of x/? constant in case statement, " - "(perhaps intended casex/casez)"); - } + if (!nodep->num().isFourState()) return; + + // Error if generate case + if (VN_IS(m_casep, GenCase)) { + nodep->v3error("Use of x/? constant in generate case statement, " + "(no such thing as 'generate casez')"); + return; } + + // Otherwise must be a case statement + const AstCase* const casep = VN_AS(m_casep, Case); + + // Don't sweat it, we already complained about casex in general + if (casep->casex()) return; + + if (casep->casez() || casep->caseInside()) { + if (nodep->num().isAnyX()) { + nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, " + "(perhaps intended ?/z in constant)"); + } + return; + } + + nodep->v3warn(CASEWITHX, "Use of x/? constant in case statement, " + "(perhaps intended casex/casez)"); } void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } public: // CONSTRUCTORS - explicit CaseLintVisitor(AstNodeCase* nodep) { iterateConst(nodep); } + explicit CaseLintVisitor(AstCase* nodep) { iterateConst(nodep); } + explicit CaseLintVisitor(AstGenCase* nodep) { iterateConst(nodep); } ~CaseLintVisitor() override = default; }; @@ -562,7 +578,7 @@ class CaseVisitor final : public VNVisitor { // VISITORS void visit(AstCase* nodep) override { - V3Case::caseLint(nodep); + { CaseLintVisitor{nodep}; } iterateChildren(nodep); UINFOTREE(9, nodep, "", "case_old"); if (isCaseTreeFast(nodep) && v3Global.opt.fCase()) { @@ -605,7 +621,7 @@ void V3Case::caseAll(AstNetlist* nodep) { { CaseVisitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("case", 0, dumpTreeEitherLevel() >= 3); } -void V3Case::caseLint(AstNodeCase* nodep) { +void V3Case::caseLint(AstGenCase* nodep) { UINFO(4, __FUNCTION__ << ": "); { CaseLintVisitor{nodep}; } } diff --git a/src/V3Case.h b/src/V3Case.h index 98ce9fe73..2e7ec45eb 100644 --- a/src/V3Case.h +++ b/src/V3Case.h @@ -21,14 +21,14 @@ #include "verilatedos.h" class AstNetlist; -class AstNodeCase; +class AstGenCase; //============================================================================ class V3Case final { public: static void caseAll(AstNetlist* nodep) VL_MT_DISABLED; - static void caseLint(AstNodeCase* nodep) VL_MT_DISABLED; + static void caseLint(AstGenCase* nodep) VL_MT_DISABLED; }; #endif // Guard diff --git a/src/V3Control.cpp b/src/V3Control.cpp index 30d742dfd..37d00ccfa 100644 --- a/src/V3Control.cpp +++ b/src/V3Control.cpp @@ -259,6 +259,17 @@ public: } } + void applyBlock(AstGenBlock* nodep) { + const VPragmaType pragma = VPragmaType::COVERAGE_BLOCK_OFF; + if (!nodep->unnamed()) { + for (const string& i : m_coverageOffBlocks) { + if (VString::wildmatch(nodep->prettyOrigOrName(), i)) { + nodep->addItemsp(new AstPragma{nodep->fileline(), pragma}); + } + } + } + } + void applyBlock(AstNodeBlock* nodep) { const VPragmaType pragma = VPragmaType::COVERAGE_BLOCK_OFF; if (!nodep->unnamed()) { @@ -375,6 +386,13 @@ public: m_waivers.emplace_back(WaiverSetting{code, contents, newMatch}); } + void applyBlock(AstGenBlock* nodep) { + // Apply to block at this line + const VPragmaType pragma = VPragmaType::COVERAGE_BLOCK_OFF; + if (lineMatch(nodep->fileline()->lineno(), pragma)) { + nodep->addItemsp(new AstPragma{nodep->fileline(), pragma}); + } + } void applyBlock(AstNodeBlock* nodep) { // Apply to block at this line const VPragmaType pragma = VPragmaType::COVERAGE_BLOCK_OFF; @@ -741,6 +759,15 @@ void V3Control::applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep) { if (modp) modp->applyBlock(nodep); } +void V3Control::applyCoverageBlock(AstNodeModule* modulep, AstGenBlock* nodep) { + const string& filename = nodep->fileline()->filename(); + V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filename); + if (filep) filep->applyBlock(nodep); + const string& modname = modulep->prettyOrigOrName(); + V3ControlModule* const modp = V3ControlResolver::s().modules().resolve(modname); + if (modp) modp->applyBlock(nodep); +} + void V3Control::applyIgnores(FileLine* filelinep) { const string& filename = filelinep->filename(); V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filename); diff --git a/src/V3Control.h b/src/V3Control.h index fcc807620..1d1685f46 100644 --- a/src/V3Control.h +++ b/src/V3Control.h @@ -48,6 +48,7 @@ public: static void applyCase(AstCase* nodep); static void applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep); + static void applyCoverageBlock(AstNodeModule* modulep, AstGenBlock* nodep); static void applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp); static void applyIgnores(FileLine* filelinep); static void applyModule(AstNodeModule* modulep); diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index 718a0a0d2..c5f271ebf 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -710,6 +710,16 @@ class CoverageVisitor final : public VNVisitor { lineTrack(nodep); } } + void visit(AstGenBlock* nodep) override { + // Similar to AstBegin + VL_RESTORER(m_beginHier); + if (nodep->name() != "") { + m_beginHier = m_beginHier + (m_beginHier != "" ? "__DOT__" : "") + nodep->name(); + } + iterateChildren(nodep); + lineTrack(nodep); + } + void visit(AstBegin* nodep) override { // Record the hierarchy of any named begins, so we can apply to user // coverage points. This is because there may be cov points inside @@ -718,7 +728,7 @@ class CoverageVisitor final : public VNVisitor { // covers the code in that line.) VL_RESTORER(m_beginHier); VL_RESTORER(m_inToggleOff); - if (!nodep->generate()) m_inToggleOff = true; + m_inToggleOff = true; if (nodep->name() != "") { m_beginHier = m_beginHier + (m_beginHier != "" ? "__DOT__" : "") + nodep->name(); } diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index 0fc8d95dc..25719374d 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -1297,9 +1297,6 @@ class DelayedVisitor final : public VNVisitor { // Record write reference recordWriteRef(nodep, false); } - void visit(AstNodeFor* nodep) override { // LCOV_EXCL_LINE - nodep->v3fatalSrc("For statements should have been converted to while statements"); - } void visit(AstWhile* nodep) override { VL_RESTORER(m_inLoop); m_inLoop = true; diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 2ae9b359d..9d192d302 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -680,7 +680,7 @@ public: iterateAndNextConstNull(nodep->exprp()); puts("}\n"); } - void visit(AstNodeCase* nodep) override { // LCOV_EXCL_LINE + void visit(AstCase* nodep) override { // LCOV_EXCL_LINE // In V3Case... nodep->v3fatalSrc("Case statements should have been reduced out"); } diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 8b47a33d9..a320a8daa 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -89,6 +89,54 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { putfs(nodep, func ? "endfunction\n" : "endtask\n"); } + void visit(AstGenBlock* nodep) override { + const std::string name = nodep->name().empty() ? "" : " : " + nodep->name(); + putbs("/* generate */ begin" + name + '\n'); + iterateChildrenConst(nodep); + puts("end" + name + '\n'); + } + void visit(AstGenCase* nodep) override { + putfs(nodep, "/* generate */ case ("); + iterateAndNextConstNull(nodep->exprp()); + puts(")\n"); + iterateAndNextConstNull(nodep->itemsp()); + putqs(nodep, "endcase\n"); + } + void visit(AstGenCaseItem* nodep) override { + if (nodep->condsp()) { + iterateAndNextConstNull(nodep->condsp()); + } else { + putbs("default"); + } + iterateAndNextConstNull(nodep->itemsp()); + } + void visit(AstGenFor* nodep) override { + putfs(nodep, "/* generate */ for ("); + { + VL_RESTORER(m_suppressSemi); + m_suppressSemi = true; + iterateAndNextConstNull(nodep->initsp()); + puts(";"); + iterateAndNextConstNull(nodep->condp()); + puts(";"); + iterateAndNextConstNull(nodep->incsp()); + } + puts(") begin\n"); + iterateAndNextConstNull(nodep->itemsp()); + putqs(nodep, "end\n"); + } + void visit(AstGenIf* nodep) override { + putfs(nodep, ""); + puts("/* generate */ if ("); + iterateAndNextConstNull(nodep->condp()); + puts(") begin\n"); + iterateAndNextConstNull(nodep->thensp()); + if (nodep->elsesp()) { + putqs(nodep, "end else begin\n"); + iterateAndNextConstNull(nodep->elsesp()); + } + putqs(nodep, "end\n"); + } void visit(AstBegin* nodep) override { if (nodep->name() == "") { putbs("begin\n"); @@ -193,23 +241,19 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { if (nodep->sensp()) puts(" "); iterateChildrenConst(nodep); } - void visit(AstNodeCase* nodep) override { + void visit(AstCase* nodep) override { putfs(nodep, ""); - if (const AstCase* const casep = VN_CAST(nodep, Case)) { - if (casep->priorityPragma()) puts("priority "); - if (casep->uniquePragma()) puts("unique "); - if (casep->unique0Pragma()) puts("unique0 "); - } + if (nodep->priorityPragma()) puts("priority "); + if (nodep->uniquePragma()) puts("unique "); + if (nodep->unique0Pragma()) puts("unique0 "); puts(nodep->verilogKwd()); puts(" ("); iterateAndNextConstNull(nodep->exprp()); puts(")\n"); - if (const AstCase* const casep = VN_CAST(nodep, Case)) { - if (casep->fullPragma() || casep->parallelPragma()) { - puts(" // synopsys"); - if (casep->fullPragma()) puts(" full_case"); - if (casep->parallelPragma()) puts(" parallel_case"); - } + if (nodep->fullPragma() || nodep->parallelPragma()) { + puts(" // synopsys"); + if (nodep->fullPragma()) puts(" full_case"); + if (nodep->parallelPragma()) puts(" parallel_case"); } iterateAndNextConstNull(nodep->itemsp()); putqs(nodep, "endcase\n"); @@ -348,21 +392,6 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { iterateAndNextConstNull(nodep->exprsp()); puts(");\n"); } - void visit(AstNodeFor* nodep) override { - putfs(nodep, "for ("); - { - VL_RESTORER(m_suppressSemi); - m_suppressSemi = true; - iterateAndNextConstNull(nodep->initsp()); - puts(";"); - iterateAndNextConstNull(nodep->condp()); - puts(";"); - iterateAndNextConstNull(nodep->incsp()); - } - puts(") begin\n"); - iterateAndNextConstNull(nodep->stmtsp()); - putqs(nodep, "end\n"); - } void visit(AstRepeat* nodep) override { putfs(nodep, "repeat ("); iterateAndNextConstNull(nodep->countp()); diff --git a/src/V3EmitXml.cpp b/src/V3EmitXml.cpp index 19a292e46..6b210de3a 100644 --- a/src/V3EmitXml.cpp +++ b/src/V3EmitXml.cpp @@ -410,6 +410,11 @@ class HierCellsXmlVisitor final : public VNVisitorConst { m_hier = hier; m_hasChildren = true; } + void visit(AstGenBlock* nodep) override { + VL_RESTORER(m_hier); + if (nodep->name() != "") m_hier += nodep->name() + "."; + iterateChildrenConst(nodep); + } void visit(AstBegin* nodep) override { VL_RESTORER(m_hier); if (nodep->name() != "") m_hier += nodep->name() + "."; diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index fc0d54700..14811cd24 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -231,7 +231,7 @@ private: AstBegin* const beginp = new AstBegin{ forkp->fileline(), "_Vwrapped_" + (forkp->name().empty() ? "" : forkp->name() + "_") + cvtToStr(m_id), - m_instance.m_handlep, false, true}; + m_instance.m_handlep, true}; forkHandle.relink(beginp); AstNode* const instAsgnp = instantiateDynScope(memberMap); @@ -470,7 +470,7 @@ 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, false}; + AstBegin* const beginp = new AstBegin{nodep->fileline(), "", nullptr, false}; AstFork* const forkp = new AstFork{nodep->fileline(), "", beginp}; forkp->joinType(VJoinType::JOIN_NONE); nodep->replaceWith(forkp); diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index ba8ff8018..d7815c1b1 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -260,6 +260,8 @@ public: return "block"; } else if (VN_IS(nodep, Iface)) { return "interface"; + } else if (VN_IS(nodep, GenBlock)) { + return "generate block"; } else { return nodep->prettyTypeName(); } @@ -295,8 +297,8 @@ public: } else if (foundp->imported()) { // From package // We don't throw VARHIDDEN as if the import is later the symbol // table's import wouldn't warn - } else if (forPrimary() && VN_IS(nodep, Begin) && VN_IS(fnodep, Begin) - && VN_AS(nodep, Begin)->generate()) { + } else if (forPrimary() && VN_IS(nodep, GenBlock) + && (VN_IS(fnodep, Begin) || VN_IS(fnodep, GenBlock))) { // Begin: ... blocks often replicate under genif/genfor, so // suppress duplicate checks. See t_gen_forif.v for an example. } else { @@ -882,7 +884,6 @@ class LinkDotFindVisitor final : public VNVisitor { string m_hierParamsName; // Name of module with hierarchical type parameters, empty when not used string m_scope; // Scope text - const AstNodeBlock* m_blockp = nullptr; // Current Begin/end block const AstNodeFTask* m_ftaskp = nullptr; // Current function/task bool m_inRecursion = false; // Inside a recursive module int m_paramNum = 0; // Parameter number, for position based connection @@ -1158,7 +1159,6 @@ class LinkDotFindVisitor final : public VNVisitor { iterateChildren(nodep); // Recurse in, preserving state VL_RESTORER(m_scope); - VL_RESTORER(m_blockp); VL_RESTORER(m_modSymp); VL_RESTORER(m_curSymp); VL_RESTORER(m_paramNum); @@ -1181,7 +1181,6 @@ class LinkDotFindVisitor final : public VNVisitor { { m_scope = m_scope + "." + nodep->name(); m_curSymp = m_modSymp = m_statep->insertCell(aboveSymp, m_modSymp, nodep, m_scope); - m_blockp = nullptr; m_inRecursion = nodep->recursive(); // We don't report NotFoundModule, as may be a unused module in a generate if (nodep->modp()) iterate(nodep->modp()); @@ -1216,6 +1215,43 @@ class LinkDotFindVisitor final : public VNVisitor { nodep->user1p(m_curSymp); iterateChildren(nodep); } + void visit(AstGenBlock* nodep) override { // FindVisitor:: + UINFO(5, " " << nodep); + if (nodep->name() == "" && nodep->unnamed()) { + // Unnamed blocks are only important when they contain var + // decls, so search for them. (Otherwise adding all the + // unnamed#'s would just confuse tracing variables in + // places such as tasks, where "task ...; begin ... end" + // are common. + for (AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) { + if (VN_IS(itemp, Var) || VN_IS(itemp, 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; + } + } + } + if (nodep->name() == "") { + iterateChildren(nodep); + } else { + VL_RESTORER(m_curSymp); + { + m_curSymp + = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_classOrPackagep); + m_curSymp->fallbackp(VL_RESTORER_PREV(m_curSymp)); + // Iterate + iterateChildren(nodep); + } + } + } void visit(AstNodeBlock* nodep) override { // FindVisitor:: UINFO(5, " " << nodep); if (nodep->name() == "" && nodep->unnamed()) { @@ -1243,10 +1279,8 @@ class LinkDotFindVisitor final : public VNVisitor { if (nodep->name() == "") { iterateChildren(nodep); } else { - VL_RESTORER(m_blockp); VL_RESTORER(m_curSymp); { - m_blockp = nodep; m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_classOrPackagep); m_curSymp->fallbackp(VL_RESTORER_PREV(m_curSymp)); @@ -2331,6 +2365,9 @@ class LinkDotScopeVisitor final : public VNVisitor { // We have stored the link, we don't need these any more VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); } + void visit(AstNodeGen* nodep) override { // LCOV_EXCL_LINE + nodep->v3fatalSrc("Generate constructs should have been reduced out"); + } // For speed, don't recurse things that can't have scope // Note we allow AstNodeStmt's as generates may be under them void visit(AstCell*) override {} // ScopeVisitor:: @@ -3509,6 +3546,7 @@ class LinkDotResolveVisitor final : public VNVisitor { } if (!foundp) { } else if (VN_IS(foundp->nodep(), Cell) || VN_IS(foundp->nodep(), NodeBlock) + || VN_IS(foundp->nodep(), GenBlock) || VN_IS(foundp->nodep(), Netlist) // for $root || VN_IS(foundp->nodep(), Module)) { // if top if (allowScope) { @@ -3521,14 +3559,12 @@ class LinkDotResolveVisitor final : public VNVisitor { // last component, `targetp()` field will be overwritten by next components m_ds.m_disablep->targetp(foundp->nodep()); } - if (const AstBegin* const beginp = VN_CAST(foundp->nodep(), Begin)) { - if (beginp->generate()) { - m_ds.m_genBlk = true; - if (m_ds.m_disablep) { - m_ds.m_disablep->v3warn( - E_UNSUPPORTED, - "Unsupported: Generate block referenced by disable"); - } + if (VN_IS(foundp->nodep(), GenBlock)) { + m_ds.m_genBlk = true; + if (m_ds.m_disablep) { + m_ds.m_disablep->v3warn( + E_UNSUPPORTED, + "Unsupported: Generate block referenced by disable"); } } // Upper AstDot visitor will handle it from here @@ -4472,6 +4508,21 @@ class LinkDotResolveVisitor final : public VNVisitor { LINKDOT_VISIT_START(); iterateChildren(nodep); } + void visit(AstGenBlock* nodep) override { + LINKDOT_VISIT_START(); + UINFO(5, indent() << "visit " << nodep); + checkNoDot(nodep); + { + VL_RESTORER(m_curSymp); + VL_RESTORER(m_ds); + if (nodep->name() != "") { + m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep); + UINFO(5, indent() << "cur=se" << cvtToHex(m_curSymp)); + } + iterateChildren(nodep); + } + UINFO(5, indent() << "cur=se" << cvtToHex(m_curSymp)); + } void visit(AstNodeBlock* nodep) override { LINKDOT_VISIT_START(); UINFO(5, indent() << "visit " << nodep); diff --git a/src/V3LinkInc.cpp b/src/V3LinkInc.cpp index c45d93af0..d8b92b984 100644 --- a/src/V3LinkInc.cpp +++ b/src/V3LinkInc.cpp @@ -162,10 +162,6 @@ class LinkIncVisitor final : public VNVisitor { m_insStmtp = nullptr; // Next thing should be new statement iterateAndNextNull(nodep->stmtsp()); } - void visit(AstNodeFor* nodep) override { // LCOV_EXCL_LINE - nodep->v3fatalSrc( - "For statements should have been converted to while statements in V3Begin.cpp"); - } void visit(AstDelay* nodep) override { m_insStmtp = nodep; iterateAndNextNull(nodep->lhsp()); diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index cc1aa8678..2796a4a45 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -289,7 +289,7 @@ class LinkJumpVisitor final : public VNVisitor { // Note var can be signed or unsigned based on original number. AstNodeExpr* const countp = nodep->countp()->unlinkFrBackWithNext(); const string name = "__Vrepeat"s + cvtToStr(m_modRepeatNum++); - AstBegin* const beginp = new AstBegin{nodep->fileline(), "", nullptr, false, true}; + AstBegin* const beginp = new AstBegin{nodep->fileline(), "", nullptr, true}; // Spec says value is integral, if negative is ignored AstVar* const varp = new AstVar{nodep->fileline(), VVarType::BLOCKTEMP, name, nodep->findSigned32DType()}; @@ -425,7 +425,7 @@ class LinkJumpVisitor final : public VNVisitor { // 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, false}; + beginp = new AstBegin{fl, "", nullptr, false}; forkItemp->replaceWith(beginp); beginp->addStmtsp(forkItemp); // In order to continue the iteration diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 7df26971d..195a8377c 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -111,19 +111,19 @@ class LinkParseVisitor final : public VNVisitor { iterateChildren(nodep); } - bool nestedIfBegin(AstBegin* nodep) { // Point at begin inside the GenIf + bool nestedIfBegin(AstGenBlock* nodep) { // Point at begin inside the GenIf // IEEE says directly nested item is not a new block // The genblk name will get attached to the if true/false LOWER begin block(s) // 1: GENIF - // -> 1:3: BEGIN [GEN] [IMPLIED] // nodep passed to this function + // -> 1:3: GENBLOCK [IMPLIED] // nodep passed to this function // 1:3:1: GENIF - // 1:3:1:2: BEGIN genblk1 [GEN] [IMPLIED] + // 1:3:1:2: GENBLOCK genblk1 [IMPLIED] const AstNode* const backp = nodep->backp(); return (nodep->implied() // User didn't provide begin/end && VN_IS(backp, GenIf) && VN_CAST(backp, GenIf)->elsesp() == nodep && !nodep->nextp() // No other statements under upper genif else - && (VN_IS(nodep->stmtsp(), GenIf)) // Begin has if underneath - && !nodep->stmtsp()->nextp()); // Has only one item + && (VN_IS(nodep->itemsp(), GenIf)) // Begin has if underneath + && !nodep->itemsp()->nextp()); // Has only one item } void checkIndent(AstNode* nodep, AstNode* childp) { @@ -657,7 +657,7 @@ class LinkParseVisitor final : public VNVisitor { void visit(AstCover* nodep) override { visitIterateNoValueMod(nodep); } void visit(AstRestrict* nodep) override { visitIterateNoValueMod(nodep); } - void visit(AstBegin* nodep) override { + void visit(AstGenBlock* nodep) override { V3Control::applyCoverageBlock(m_modp, nodep); cleanFileline(nodep); VL_RESTORER(m_beginDepth); @@ -671,13 +671,13 @@ class LinkParseVisitor final : public VNVisitor { if (nodep->genforp()) { ++m_genblkNum; if (nodep->name() == "") assignGenBlkNum = m_genblkNum; - } else if (nodep->generate() && nodep->name() == "" - && (VN_IS(backp, CaseItem) || VN_IS(backp, GenIf)) && !nestedIf) { + } else if (nodep->name() == "" && (VN_IS(backp, GenCaseItem) || VN_IS(backp, GenIf)) + && !nestedIf) { assignGenBlkNum = m_genblkAbove; } if (assignGenBlkNum != -1) { nodep->name("genblk" + cvtToStr(assignGenBlkNum)); - if (nodep->stmtsp()) { + if (nodep->itemsp()) { nodep->v3warn(GENUNNAMED, "Unnamed generate block " << nodep->prettyNameQ() << " (IEEE 1800-2023 27.6)\n" @@ -696,6 +696,31 @@ class LinkParseVisitor final : public VNVisitor { iterateChildren(nodep); } } + void visit(AstGenCase* nodep) override { + ++m_genblkNum; + cleanFileline(nodep); + VL_RESTORER(m_genblkAbove); + VL_RESTORER(m_genblkNum); + m_genblkAbove = m_genblkNum; + m_genblkNum = 0; + iterateChildren(nodep); + } + void visit(AstGenIf* nodep) override { + cleanFileline(nodep); + checkIndent(nodep, nodep->elsesp() ? nodep->elsesp() : nodep->thensp()); + const bool nestedIf = (VN_IS(nodep->backp(), GenBlock) + && nestedIfBegin(VN_CAST(nodep->backp(), GenBlock))); + if (nestedIf) { + iterateChildren(nodep); + } else { + ++m_genblkNum; + VL_RESTORER(m_genblkAbove); + VL_RESTORER(m_genblkNum); + m_genblkAbove = m_genblkNum; + m_genblkNum = 0; + iterateChildren(nodep); + } + } void visit(AstCell* nodep) override { if (nodep->origName().empty()) { if (!VN_IS(nodep->modp(), Primitive)) { // Module/Program/Iface @@ -710,31 +735,11 @@ class LinkParseVisitor final : public VNVisitor { } iterateChildren(nodep); } - void visit(AstGenCase* nodep) override { - ++m_genblkNum; + void visit(AstBegin* nodep) override { + V3Control::applyCoverageBlock(m_modp, nodep); cleanFileline(nodep); - VL_RESTORER(m_genblkAbove); - VL_RESTORER(m_genblkNum); - m_genblkAbove = m_genblkNum; - m_genblkNum = 0; iterateChildren(nodep); } - void visit(AstGenIf* nodep) override { - cleanFileline(nodep); - checkIndent(nodep, nodep->elsesp() ? nodep->elsesp() : nodep->thensp()); - const bool nestedIf - = (VN_IS(nodep->backp(), Begin) && nestedIfBegin(VN_CAST(nodep->backp(), Begin))); - if (nestedIf) { - iterateChildren(nodep); - } else { - ++m_genblkNum; - VL_RESTORER(m_genblkAbove); - VL_RESTORER(m_genblkNum); - m_genblkAbove = m_genblkNum; - m_genblkNum = 0; - iterateChildren(nodep); - } - } void visit(AstCase* nodep) override { V3Control::applyCase(nodep); cleanFileline(nodep); diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 8151b2713..a67c158a9 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -1610,7 +1610,7 @@ class ParamVisitor final : public VNVisitor { } } - void visit(AstBegin* nodep) override { + void visit(AstGenBlock* nodep) override { // Parameter substitution for generated for loops. // TODO Unlike generated IF, we don't have to worry about short-circuiting the // conditional expression, since this is currently restricted to simple @@ -1663,8 +1663,8 @@ class ParamVisitor final : public VNVisitor { V3Const::constifyParamsEdit(nodep->exprp()); // exprp may change const AstConst* const exprp = VN_AS(nodep->exprp(), Const); // Constify - for (AstCaseItem* itemp = nodep->itemsp(); itemp; - itemp = VN_AS(itemp->nextp(), CaseItem)) { + for (AstGenCaseItem* itemp = nodep->itemsp(); itemp; + itemp = VN_AS(itemp->nextp(), GenCaseItem)) { for (AstNode* ep = itemp->condsp(); ep;) { AstNode* const nextp = ep->nextp(); // May edit list iterateAndNextNull(ep); @@ -1673,8 +1673,8 @@ class ParamVisitor final : public VNVisitor { } } // Item match - for (AstCaseItem* itemp = nodep->itemsp(); itemp; - itemp = VN_AS(itemp->nextp(), CaseItem)) { + for (AstGenCaseItem* itemp = nodep->itemsp(); itemp; + itemp = VN_AS(itemp->nextp(), GenCaseItem)) { if (!itemp->isDefault()) { for (AstNode* ep = itemp->condsp(); ep; ep = ep->nextp()) { if (const AstConst* const ccondp = VN_CAST(ep, Const)) { @@ -1682,7 +1682,7 @@ class ParamVisitor final : public VNVisitor { match.opEq(ccondp->num(), exprp->num()); if (!hit && match.isNeqZero()) { hit = true; - keepp = itemp->stmtsp(); + keepp = itemp->itemsp(); } } else { itemp->v3error("Generate Case item does not evaluate to constant"); @@ -1691,12 +1691,12 @@ class ParamVisitor final : public VNVisitor { } } // Else default match - for (AstCaseItem* itemp = nodep->itemsp(); itemp; - itemp = VN_AS(itemp->nextp(), CaseItem)) { + for (AstGenCaseItem* itemp = nodep->itemsp(); itemp; + itemp = VN_AS(itemp->nextp(), GenCaseItem)) { if (itemp->isDefault()) { if (!hit) { hit = true; - keepp = itemp->stmtsp(); + keepp = itemp->itemsp(); } } } diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index 722896b89..e4f850116 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -252,7 +252,7 @@ public: // would misfire AstNode* newBlock(FileLine* fl, AstNode* nodep) { if (nodep) return nodep; - return new AstBegin{fl, "", nullptr, false, true}; + return new AstBegin{fl, "", nullptr, true}; } // Bison sometimes needs error context without a token, so remember last token's line diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 631a70dbf..6737dd5a9 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -597,7 +597,7 @@ class ConstraintExprVisitor final : public VNVisitor { { AstBegin* const tempp - = new AstBegin{fl, "[EditWrapper]", itemsp->unlinkFrBackWithNext(), false, false}; + = new AstBegin{fl, "[EditWrapper]", itemsp->unlinkFrBackWithNext(), false}; VL_DO_DANGLING(iterateAndNextNull(tempp->stmtsp()), itemsp); itemsp = tempp->stmtsp(); if (itemsp) itemsp->unlinkFrBackWithNext(); @@ -937,6 +937,10 @@ class ConstraintExprVisitor final : public VNVisitor { } VL_DO_DANGLING(nodep->deleteTree(), nodep); } + void visit(AstGenBlock* nodep) override { + // Dubious but this is what we used to do. Does that mean no randomzie + // methods work under a generage block? + } void visit(AstBegin* nodep) override {} void visit(AstConstraintForeach* nodep) override { // Convert to plain foreach @@ -951,7 +955,7 @@ class ConstraintExprVisitor final : public VNVisitor { exprsp->addNext(new AstBegin{ fl, "", new AstForeach{fl, nodep->arrayp()->unlinkFrBack(), new AstCStmt{fl, cstmtp}}, - false, true}); + true}); exprsp->addNext( new AstText{fl, "return ret.empty() ? \"#b1\" : \"(bvand\" + ret + \")\";})()"}); AstNodeExpr* const newp = new AstCExpr{fl, exprsp}; @@ -963,7 +967,7 @@ class ConstraintExprVisitor final : public VNVisitor { new AstBegin{fl, "", new AstForeach{fl, nodep->arrayp()->unlinkFrBack(), nodep->stmtsp()->unlinkFrBackWithNext()}, - false, true}); + true}); } VL_DO_DANGLING(nodep->deleteTree(), nodep); } @@ -1027,8 +1031,8 @@ class ConstraintExprVisitor final : public VNVisitor { cstmtp->addNext(iterateSubtreeReturnEdits(itemp)); cstmtp->addNext(new AstText{fl, ";"}); AstNode* const exprsp = new AstText{fl, "([&]{ std::string ret;"}; - exprsp->addNext(new AstBegin{ - fl, "", new AstForeach{fl, arrayp, new AstCStmt{fl, cstmtp}}, false, true}); + exprsp->addNext( + new AstBegin{fl, "", new AstForeach{fl, arrayp, new AstCStmt{fl, cstmtp}}, true}); exprsp->addNext( new AstText{fl, "return ret.empty() ? \"#b0\" : \"(bvor\" + ret + \")\";})()"}); AstNodeExpr* const newp = new AstCExpr{fl, exprsp}; @@ -1530,7 +1534,7 @@ class RandomizeVisitor final : public VNVisitor { new AstAssign{fl, new AstVarRef{fl, iterVarp, VAccess::WRITE}, new AstAdd{fl, new AstConst{fl, 1}, new AstVarRef{fl, iterVarp, VAccess::READ}}}}); - return new AstBegin{fl, "", stmtsp, false, true}; + return new AstBegin{fl, "", stmtsp, true}; } static AstNodeStmt* wrapIfRandMode(AstClass* classp, AstVar* const varp, AstNodeStmt* stmtp) { const RandomizeMode rmode = {.asInt = varp->user1()}; @@ -1939,7 +1943,7 @@ class RandomizeVisitor final : public VNVisitor { AstVarRef* const refp = new AstVarRef{fl, classp, memberVarp, VAccess::WRITE}; AstNodeStmt* const stmtp = newRandStmtsp(fl, refp, randcVarp, basicFvarp); if (!refp->backp()) VL_DO_DANGLING(refp->deleteTree(), refp); - basicRandomizep->addStmtsp(new AstBegin{fl, "", stmtp, false, false}); + basicRandomizep->addStmtsp(new AstBegin{fl, "", stmtp, false}); } }); } @@ -2072,7 +2076,7 @@ class RandomizeVisitor final : public VNVisitor { stmtsp->addNext(setStmtsp); stmtsp->addNext(m_stmtp); stmtsp->addNext(restoreStmtsp); - relinker.relink(new AstBegin{nodep->fileline(), "", stmtsp, false, true}); + relinker.relink(new AstBegin{nodep->fileline(), "", stmtsp, true}); } } diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index ae1b4fa9b..13e841772 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -345,7 +345,7 @@ void splitCheck(AstCFunc* ofuncp) { // Unlink all statements, then add item by item to new sub-functions AstBegin* const tempp = new AstBegin{ofuncp->fileline(), "[EditWrapper]", - ofuncp->stmtsp()->unlinkFrBackWithNext(), false, false}; + ofuncp->stmtsp()->unlinkFrBackWithNext(), false}; // Currently we do not use finalsp in V3Sched, if we do, it needs to be handled here UASSERT_OBJ(!ofuncp->finalsp(), ofuncp, "Should not have any finalps"); while (tempp->stmtsp()) { diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 2c4c6a824..dc77cb410 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -991,7 +991,7 @@ private: checkNodeInfo(nodep); iterateChildrenConst(nodep); } - void visit(AstNodeCase* nodep) override { + void visit(AstCase* nodep) override { if (jumpingOver()) return; UINFO(5, " CASE " << nodep); checkNodeInfo(nodep); @@ -1083,39 +1083,6 @@ private: checkNodeInfo(nodep); } - void visit(AstNodeFor* nodep) override { - // Doing lots of Whiles is slow, so only for parameters - UINFO(5, " FOR " << nodep); - if (!m_params) { - badNodeType(nodep); - return; - } - checkNodeInfo(nodep); - if (m_checkOnly) { - iterateChildrenConst(nodep); - } else if (optimizable()) { - int loops = 0; - iterateAndNextConstNull(nodep->initsp()); - while (true) { - UINFO(5, " FOR-ITER " << nodep); - iterateAndNextConstNull(nodep->condp()); - if (!optimizable()) break; - if (!fetchConst(nodep->condp())->num().isNeqZero()) { // - break; - } - iterateAndNextConstNull(nodep->stmtsp()); - iterateAndNextConstNull(nodep->incsp()); - if (loops++ > v3Global.opt.unrollCountAdjusted(VOptionBool{}, m_params, true)) { - clearOptimizable(nodep, "Loop unrolling took too long; probably this is an" - "infinite loop, or use /*verilator unroll_full*/, or " - "set --unroll-count above " - + cvtToStr(loops)); - break; - } - } - } - } - void visit(AstWhile* nodep) override { // Doing lots of Whiles is slow, so only for parameters if (jumpingOver()) return; diff --git a/src/V3Split.cpp b/src/V3Split.cpp index dca6a21f2..1fb156076 100644 --- a/src/V3Split.cpp +++ b/src/V3Split.cpp @@ -338,7 +338,7 @@ protected: void visit(AstAlways* nodep) override = 0; void visit(AstNodeIf* nodep) override = 0; - // We don't do AstNodeFor/AstWhile loops, due to the standard question + // We don't do AstWhile loops, due to the standard question // of what is before vs. after void visit(AstAssignDly* nodep) override { diff --git a/src/V3SplitVar.cpp b/src/V3SplitVar.cpp index 7345bd773..cc2f96920 100644 --- a/src/V3SplitVar.cpp +++ b/src/V3SplitVar.cpp @@ -194,7 +194,7 @@ struct SplitVarImpl VL_NOT_FINAL { stmtp->unlinkFrBack(); // Insert begin-end because temp value may be inserted to this block later. const std::string name = "__VsplitVarBlk" + cvtToStr(modp->user1Inc(1)); - ap->addStmtsp(new AstBegin{ap->fileline(), name, stmtp, false, false}); + ap->addStmtsp(new AstBegin{ap->fileline(), name, stmtp, false}); } } @@ -204,7 +204,7 @@ struct SplitVarImpl VL_NOT_FINAL { // Insert begin-end because temp value may be inserted to this block later. FileLine* const fl = initp->fileline(); const std::string name = "__VsplitVarBlk" + cvtToStr(modp->user1Inc(1)); - initp->replaceWith(new AstInitial{fl, new AstBegin{fl, name, stmtp, false, false}}); + initp->replaceWith(new AstInitial{fl, new AstBegin{fl, name, stmtp, false}}); VL_DO_DANGLING(initp->deleteTree(), initp); } } diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 472004421..34eb47844 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -1362,7 +1362,7 @@ class TaskVisitor final : public VNVisitor { if (bodysp) { unlinkAndClone(nodep, bodysp, true); AstBegin* const tempp - = new AstBegin{nodep->fileline(), "[EditWrapper]", bodysp, false, false}; + = new AstBegin{nodep->fileline(), "[EditWrapper]", bodysp, false}; VL_DANGLING(bodysp); // If we cloned due to recursion, now need to rip out the ports // (that remained in place) then got cloned @@ -1624,10 +1624,6 @@ class TaskVisitor final : public VNVisitor { nodep->v3fatalSrc( "Foreach statements should have been converted to while statements in V3Begin.cpp"); } - void visit(AstNodeFor* nodep) override { // LCOV_EXCL_LINE - nodep->v3fatalSrc( - "For statements should have been converted to while statements in V3Begin.cpp"); - } void visit(AstNodeStmt* nodep) override { VL_RESTORER(m_insStmtp); m_insStmtp = nodep; diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index 05a9b5914..fce48e427 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -1029,7 +1029,7 @@ class TimingControlVisitor final : public VNVisitor { } controlp->replaceWith(forkp); AstBegin* beginp = VN_CAST(controlp, Begin); - if (!beginp) beginp = new AstBegin{nodep->fileline(), "", controlp, false, false}; + if (!beginp) beginp = new AstBegin{nodep->fileline(), "", controlp, false}; forkp->addStmtsp(beginp); controlp = forkp; } diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index f8abcc2cd..15293ed96 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -209,8 +209,7 @@ class UnrollVisitor final : public VNVisitor { if (loopValue) { AstConst* varValuep = new AstConst{nodep->fileline(), *loopValue}; // Iteration requires a back, so put under temporary node - AstBegin* tempp - = new AstBegin{nodep->fileline(), "[EditWrapper]", clonep, false, false}; + AstBegin* tempp = new AstBegin{nodep->fileline(), "[EditWrapper]", clonep, false}; replaceVarRef(tempp->stmtsp(), varValuep); clonep = tempp->stmtsp()->unlinkFrBackWithNext(); VL_DO_CLEAR(tempp->deleteTree(), tempp = nullptr); @@ -303,8 +302,7 @@ class UnrollVisitor final : public VNVisitor { AstNode* clonep = initp->cloneTree(true); AstConst* varValuep = new AstConst{nodep->fileline(), loopValue}; // Iteration requires a back, so put under temporary node - AstBegin* tempp - = new AstBegin{nodep->fileline(), "[EditWrapper]", clonep, false, false}; + AstBegin* tempp = new AstBegin{nodep->fileline(), "[EditWrapper]", clonep, false}; replaceVarRef(clonep, varValuep); clonep = tempp->stmtsp()->unlinkFrBackWithNext(); VL_DO_CLEAR(tempp->deleteTree(), tempp = nullptr); @@ -329,8 +327,8 @@ class UnrollVisitor final : public VNVisitor { AstConst* varValuep = new AstConst{nodep->fileline(), loopValue}; if (oneloopp) { // Iteration requires a back, so put under temporary node - AstBegin* const tempp = new AstBegin{oneloopp->fileline(), "[EditWrapper]", - oneloopp, false, false}; + AstBegin* const tempp + = new AstBegin{oneloopp->fileline(), "[EditWrapper]", oneloopp, false}; replaceVarRef(tempp->stmtsp(), varValuep); oneloopp = tempp->stmtsp()->unlinkFrBackWithNext(); VL_DO_DANGLING(tempp->deleteTree(), tempp); @@ -338,8 +336,7 @@ class UnrollVisitor final : public VNVisitor { if (m_generate) { const string index = AstNode::encodeNumber(varValuep->toSInt()); const string nname = m_beginName + "__BRA__" + index + "__KET__"; - oneloopp - = new AstBegin{oneloopp->fileline(), nname, oneloopp, true, false}; + oneloopp = new AstGenBlock{oneloopp->fileline(), nname, oneloopp, false}; } VL_DO_DANGLING(pushDeletep(varValuep), varValuep); if (newbodysp) { @@ -421,9 +418,7 @@ class UnrollVisitor final : public VNVisitor { } } void visit(AstGenFor* nodep) override { - if (!m_generate) { - iterateChildren(nodep); - } // else V3Param will recursively call each for loop to be unrolled for us + UASSERT_OBJ(m_generate, nodep, "There should be no GenFor left when unrolling all"); if (!m_varModeCheck) { // Constify before unroll call, as it may change what is underneath. if (nodep->initsp()) V3Const::constifyEdit(nodep->initsp()); // initsp may change @@ -439,7 +434,7 @@ class UnrollVisitor final : public VNVisitor { // deleted by V3Const. VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); } else if (forUnrollCheck(nodep, VOptionBool{}, nodep->initsp(), nodep->condp(), - nodep->incsp(), nodep->stmtsp())) { + nodep->incsp(), nodep->itemsp())) { VL_DO_DANGLING(pushDeletep(nodep), nodep); // Did replacement } else { nodep->v3error("For loop doesn't have genvar index, or is malformed"); @@ -448,13 +443,6 @@ class UnrollVisitor final : public VNVisitor { } } } - void visit(AstNodeFor* nodep) override { - if (m_generate) { // Ignore for's when expanding genfor's - iterateChildren(nodep); - } else { - nodep->v3fatalSrc("V3Begin should have removed standard FORs"); - } - } void visit(AstVarRef* nodep) override { if (m_varModeCheck && nodep->varp() == m_forVarp && nodep->varScopep() == m_forVscp @@ -514,18 +502,16 @@ UnrollStateful::UnrollStateful() : m_unrollerp{new UnrollVisitor} {} UnrollStateful::~UnrollStateful() { delete m_unrollerp; } -void UnrollStateful::unrollGen(AstNodeFor* nodep, const string& beginName) { +void UnrollStateful::unrollGen(AstGenFor* nodep, const string& beginName) { UINFO(5, __FUNCTION__ << ": "); m_unrollerp->process(nodep, true, beginName); } -void UnrollStateful::unrollAll(AstNetlist* nodep) { m_unrollerp->process(nodep, false, ""); } - void V3Unroll::unrollAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ":"); { - UnrollStateful unroller; - unroller.unrollAll(nodep); + UnrollVisitor visitor; + visitor.process(nodep, false, ""); } // Destruct before checking V3Global::dumpCheckGlobalTree("unroll", 0, dumpTreeEitherLevel() >= 3); } diff --git a/src/V3Unroll.h b/src/V3Unroll.h index 50d51b86b..bc16c4a4e 100644 --- a/src/V3Unroll.h +++ b/src/V3Unroll.h @@ -38,8 +38,7 @@ public: UnrollStateful() VL_MT_DISABLED; ~UnrollStateful() VL_MT_DISABLED; // METHODS - void unrollGen(AstNodeFor* nodep, const string& beginName) VL_MT_DISABLED; - void unrollAll(AstNetlist* nodep) VL_MT_DISABLED; + void unrollGen(AstGenFor* nodep, const string& beginName) VL_MT_DISABLED; }; //============================================================================ diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 1d0167c52..ca05b8516 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -744,8 +744,7 @@ class WidthVisitor final : public VNVisitor { || (!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, false}; + AstBegin* const newp = new AstBegin{nodep->fileline(), nodep->name(), stmtsp, false}; nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); } else if (v3Global.opt.timing().isSetTrue()) { @@ -5261,91 +5260,123 @@ class WidthVisitor final : public VNVisitor { //-------------------- // Top levels - void visit(AstNodeCase* nodep) override { + template + void handleCaseType(AstNode* casep, AstNodeExpr* exprp, CaseItem* itemsp) { + AstAttrOf* const exprap = VN_CAST(exprp, AttrOf); + if (!exprap) return; + if (exprap->attrType() != VAttrType::TYPEID) return; + + const AstNodeDType* const exprDtp = exprap->dtypep(); + UINFO(9, "case type exprDtp " << exprDtp); + // V3Param may have a pointer to this case statement, and we need + // dotted references to remain properly named, so rather than + // removing we convert it to a "normal" expression "case (1) ..." + FileLine* const newfl = casep->fileline(); + newfl->warnOff(V3ErrorCode::CASEINCOMPLETE, true); // Side effect of transform + newfl->warnOff(V3ErrorCode::CASEOVERLAP, true); // Side effect of transform + casep->fileline(newfl); + for (CaseItem* itemp = itemsp; itemp; itemp = AstNode::as(itemp->nextp())) { + if (itemp->isDefault()) continue; + bool hit = false; + for (AstNode* condp = itemp->condsp(); condp; condp = condp->nextp()) { + const AstAttrOf* const condAttrp = VN_CAST(condp, AttrOf); + if (!condAttrp) { + condp->v3error("Case(type) statement requires items that have type() items"); + } else { + AstNodeDType* const condDtp = condAttrp->dtypep(); + if (AstNode::computeCastable(exprDtp, condDtp, casep) == VCastable::SAMEISH) { + hit = true; + break; + } + } + } + pushDeletep(itemp->condsp()->unlinkFrBackWithNext()); + // Item condition becomes constant 1 if hits else 0 + itemp->addCondsp(new AstConst{newfl, AstConst::BitTrue{}, hit}); + } + exprap->replaceWith(new AstConst{newfl, AstConst::BitTrue{}}); + VL_DO_DANGLING(pushDeletep(exprap), exprap); + } + + template + void handleCase(AstNode* casep, AstNodeExpr* exprp, CaseItem* itemsp) { // IEEE-2012 12.5: // Width: MAX(expr, all items) // Signed: Only if expr, and all items signed + // Take width as maximum across all items, if any is real whole thing is real + AstNodeDType* subDTypep = exprp->dtypep(); + for (CaseItem* itemp = itemsp; itemp; itemp = AstNode::as(itemp->nextp())) { + for (AstNode* condp = itemp->condsp(); condp; condp = condp->nextp()) { + if (condp->dtypep() == subDTypep) continue; + + if (condp->dtypep()->isDouble() || subDTypep->isDouble()) { + subDTypep = casep->findDoubleDType(); + } else if (condp->dtypep()->isString() || subDTypep->isString()) { + subDTypep = casep->findStringDType(); + } else { + const int width = std::max(subDTypep->width(), condp->width()); + const int mwidth = std::max(subDTypep->widthMin(), condp->widthMin()); + const bool issigned = subDTypep->isSigned() && condp->isSigned(); + subDTypep = casep->findLogicDType(width, mwidth, VSigning::fromBool(issigned)); + } + } + } + + // Apply width + iterateCheck(casep, "Case expression", exprp, CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP); + for (CaseItem* itemp = itemsp; itemp; itemp = AstNode::as(itemp->nextp())) { + for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) { + nextcp = condp->nextp(); // Final may cause the node to get replaced + iterateCheck(casep, "Case Item", condp, CONTEXT_DET, FINAL, subDTypep, EXTEND_LHS); + } + } + } + + void visit(AstGenCase* nodep) override { assertAtStatement(nodep); + // Type check expression and case item conditions, but not bodies + userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p()); + for (AstGenCaseItem *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) { + // Prelim may cause the node to get replaced, pick up next up front + nextip = VN_AS(itemp->nextp(), GenCaseItem); + for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) { + // Prelim may cause the node to get replaced, pick up next up front + nextcp = condp->nextp(); + VL_DO_DANGLING(userIterate(condp, WidthVP{CONTEXT_DET, PRELIM}.p()), condp); + } + } + // Deal with case(type(data_type)) + handleCaseType(nodep, nodep->exprp(), nodep->itemsp()); + // Type check + handleCase(nodep, nodep->exprp(), nodep->itemsp()); + } + void visit(AstGenFor* nodep) override { + assertAtStatement(nodep); + userIterateAndNext(nodep->initsp(), nullptr); + iterateCheckBool(nodep, "For Test Condition", nodep->condp(), BOTH); + userIterateAndNext(nodep->incsp(), nullptr); + } + void visit(AstGenIf* nodep) override { + assertAtStatement(nodep); + iterateCheckBool(nodep, "If", nodep->condp(), BOTH); + } + + void visit(AstCase* nodep) override { + assertAtStatement(nodep); + // Type check expression case item conditions and bodies userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p()); for (AstCaseItem *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) { nextip = VN_AS(itemp->nextp(), CaseItem); // Prelim may cause the node to get replaced - if (!VN_IS(nodep, GenCase)) userIterateAndNext(itemp->stmtsp(), nullptr); + userIterateAndNext(itemp->stmtsp(), nullptr); for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) { nextcp = condp->nextp(); // Prelim may cause the node to get replaced VL_DO_DANGLING(userIterate(condp, WidthVP{CONTEXT_DET, PRELIM}.p()), condp); } } - // Deal with case(type(data_type)) - if (AstAttrOf* const exprap = VN_CAST(nodep->exprp(), AttrOf)) { - if (exprap->attrType() == VAttrType::TYPEID) { - const AstNodeDType* const exprDtp = exprap->dtypep(); - UINFO(9, "case type exprDtp " << exprDtp); - // V3Param may have a pointer to this case statement, and we need - // dotted references to remain properly named, so rather than - // removing we convert it to a "normal" expression "case (1) ..." - FileLine* const newfl = nodep->fileline(); - newfl->warnOff(V3ErrorCode::CASEINCOMPLETE, true); // Side effect of transform - newfl->warnOff(V3ErrorCode::CASEOVERLAP, true); // Side effect of transform - nodep->fileline(newfl); - for (AstCaseItem* itemp = nodep->itemsp(); itemp; - itemp = VN_AS(itemp->nextp(), CaseItem)) { - if (!itemp->isDefault()) { - bool hit = false; - for (AstNode* condp = itemp->condsp(); condp; condp = condp->nextp()) { - const AstAttrOf* const condAttrp = VN_CAST(condp, AttrOf); - if (!condAttrp) { - condp->v3error( - "Case(type) statement requires items that have type() items"); - } else { - AstNodeDType* const condDtp = condAttrp->dtypep(); - if (AstNode::computeCastable(exprDtp, condDtp, nodep) - == VCastable::SAMEISH) { - hit = true; - break; - } - } - } - pushDeletep(itemp->condsp()->unlinkFrBackWithNext()); - // Item condition becomes constant 1 if hits else 0 - itemp->addCondsp(new AstConst{newfl, AstConst::BitTrue{}, hit}); - } - } - VL_DO_DANGLING(pushDeletep(exprap->unlinkFrBack()), exprap); - nodep->exprp(new AstConst{newfl, AstConst::BitTrue{}}); - } - } - - // Take width as maximum across all items, if any is real whole thing is real - AstNodeDType* subDTypep = nodep->exprp()->dtypep(); - for (AstCaseItem* itemp = nodep->itemsp(); itemp; - itemp = VN_AS(itemp->nextp(), CaseItem)) { - for (AstNode* condp = itemp->condsp(); condp; condp = condp->nextp()) { - if (condp->dtypep() != subDTypep) { - if (condp->dtypep()->isDouble() || subDTypep->isDouble()) { - subDTypep = nodep->findDoubleDType(); - } else if (condp->dtypep()->isString() || subDTypep->isString()) { - subDTypep = nodep->findStringDType(); - } else { - const int width = std::max(subDTypep->width(), condp->width()); - const int mwidth = std::max(subDTypep->widthMin(), condp->widthMin()); - const bool issigned = subDTypep->isSigned() && condp->isSigned(); - subDTypep - = nodep->findLogicDType(width, mwidth, VSigning::fromBool(issigned)); - } - } - } - } - // Apply width - iterateCheck(nodep, "Case expression", nodep->exprp(), CONTEXT_DET, FINAL, subDTypep, - EXTEND_EXP); - for (AstCaseItem* itemp = nodep->itemsp(); itemp; - itemp = VN_AS(itemp->nextp(), CaseItem)) { - for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) { - nextcp = condp->nextp(); // Final may cause the node to get replaced - iterateCheck(nodep, "Case Item", condp, CONTEXT_DET, FINAL, subDTypep, EXTEND_LHS); - } - } + handleCaseType(nodep, nodep->exprp(), nodep->itemsp()); + // Type check + handleCase(nodep, nodep->exprp(), nodep->itemsp()); } void visit(AstRandCase* nodep) override { // IEEE says each item is a int (32-bits), and sizes are based on natural sizing, @@ -5365,14 +5396,6 @@ class WidthVisitor final : public VNVisitor { } } - void visit(AstNodeFor* nodep) override { - assertAtStatement(nodep); - userIterateAndNext(nodep->initsp(), nullptr); - iterateCheckBool(nodep, "For Test Condition", nodep->condp(), - BOTH); // it's like an if() condition. - if (!VN_IS(nodep, GenFor)) userIterateAndNext(nodep->stmtsp(), nullptr); - userIterateAndNext(nodep->incsp(), nullptr); - } void visit(AstRepeat* nodep) override { assertAtStatement(nodep); userIterateAndNext(nodep->countp(), WidthVP{SELF, BOTH}.p()); @@ -5388,10 +5411,8 @@ class WidthVisitor final : public VNVisitor { void visit(AstNodeIf* nodep) override { assertAtStatement(nodep); // UINFOTREE(1, nodep, "", "IfPre"); - if (!VN_IS(nodep, GenIf)) { // for m_paramsOnly - userIterateAndNext(nodep->thensp(), nullptr); - userIterateAndNext(nodep->elsesp(), nullptr); - } + userIterateAndNext(nodep->thensp(), nullptr); + userIterateAndNext(nodep->elsesp(), nullptr); iterateCheckBool(nodep, "If", nodep->condp(), BOTH); // it's like an if() condition. // UINFOTREE(1, nodep, "", "IfOut"); } diff --git a/src/Verilator.cpp b/src/Verilator.cpp index df1d018fe..be46c7687 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -179,6 +179,7 @@ static void process() { // Remove parameters by cloning modules to de-parameterized versions // This requires some width calculations and constant propagation + // No more AstGenCase/AstGenFor/AstGenIf after this V3Param::param(v3Global.rootp()); V3LinkDot::linkDotParamed(v3Global.rootp()); // Cleanup as made new modules V3LinkLValue::linkLValue(v3Global.rootp()); // Resolve new VarRefs @@ -271,6 +272,7 @@ static void process() { // Task inlining & pushing BEGINs names to variables/cells // Begin processing must be after Param, before module inlining + // No more AstGenBlocks after this V3Begin::debeginAll(v3Global.rootp()); // Flatten cell names, before inliner // Expand inouts, stage 2 diff --git a/src/verilog.y b/src/verilog.y index 57655ac5e..d6a660d3a 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -2713,7 +2713,7 @@ generate_block_or_null: // IEEE: generate_block_or_null (called from gen // // IEEE: generate_block // // Must always return a BEGIN node, or nullptr - see GenFor construction ~c~generate_item - { $$ = $1 ? (new AstBegin{$1->fileline(), "", $1, true, true}) : nullptr; } + { $$ = $1 ? (new AstGenBlock{$1->fileline(), "", $1, true}) : nullptr; } | ~c~genItemBegin { $$ = $1; } ; @@ -2722,19 +2722,19 @@ c_generate_block_or_null: // IEEE: generate_block_or_null (for checkers) ; genItemBegin: // IEEE: part of generate_block - yBEGIN ~c~genItemList yEND { $$ = new AstBegin{$1, "", $2, true, false}; } + yBEGIN ~c~genItemList yEND { $$ = new AstGenBlock{$1, "", $2, false}; } | yBEGIN yEND { $$ = nullptr; } | id yP_COLON__BEGIN yBEGIN ~c~genItemList yEND endLabelE - { $$ = new AstBegin{$1, *$1, $4, true, false}; + { $$ = new AstGenBlock{$1, *$1, $4, false}; GRAMMARP->endLabel($6, *$1, $6); } | id yP_COLON__BEGIN yBEGIN yEND endLabelE - { $$ = new AstBegin{$1, *$1, nullptr, true, false}; + { $$ = new AstGenBlock{$1, *$1, nullptr, false}; GRAMMARP->endLabel($5, *$1, $5); } | yBEGIN ':' idAny ~c~genItemList yEND endLabelE - { $$ = new AstBegin{$3, *$3, $4, true, false}; + { $$ = new AstGenBlock{$3, *$3, $4, false}; GRAMMARP->endLabel($6, *$3, $6); } | yBEGIN ':' idAny yEND endLabelE - { $$ = new AstBegin{$3, *$3, nullptr, true, false}; + { $$ = new AstGenBlock{$3, *$3, nullptr, false}; GRAMMARP->endLabel($5, *$3, $5); } ; @@ -2803,27 +2803,23 @@ c_conditional_generate_construct: // IEEE: conditional_generate_construc loop_generate_construct: // ==IEEE: loop_generate_construct yFOR '(' genvar_initialization ';' expr ';' genvar_iteration ')' ~c~generate_block_or_null { // Convert BEGIN(...) to BEGIN(GENFOR(...)), as we need the BEGIN to hide the local genvar - AstBegin* lowerBegp = VN_CAST($9, Begin); - UASSERT_OBJ(!($9 && !lowerBegp), $9, "Child of GENFOR should have been begin"); - - if (!lowerBegp) lowerBegp = new AstBegin{$1, "", nullptr, true, false}; // Empty body - AstNode* const lowerNoBegp = lowerBegp->stmtsp(); - if (lowerNoBegp) lowerNoBegp->unlinkFrBackWithNext(); - // - AstBegin* const blkp = new AstBegin{$1, lowerBegp->name(), nullptr, true, true}; + AstGenBlock* lowerp = VN_CAST($9, GenBlock); + UASSERT_OBJ(!$9 || lowerp, $9, "Child of GENFOR should have been begin"); + AstNode* const itemsp = lowerp && lowerp->itemsp() ? lowerp->itemsp()->unlinkFrBackWithNext() : nullptr; + AstGenBlock* const blkp = new AstGenBlock{$1, lowerp ? lowerp->name() : "", nullptr, true}; // V3LinkDot detects BEGIN(GENFOR(...)) as a special case AstNode* initp = $3; AstNode* const varp = $3; if (VN_IS(varp, Var)) { // Genvar initp = varp->nextp(); initp->unlinkFrBackWithNext(); // Detach 2nd from varp, make 1st init - blkp->addStmtsp(varp); + blkp->addItemsp(varp); } // Statements are under 'genforp' as instances under this // for loop won't get an extra layer of hierarchy tacked on - blkp->genforp(new AstGenFor{$1, initp, $5, $7, lowerNoBegp}); + blkp->genforp(new AstGenFor{$1, initp, $5, $7, itemsp}); $$ = blkp; - VL_DO_DANGLING(lowerBegp->deleteTree(), lowerBegp); + DEL(lowerp); } ; @@ -2878,22 +2874,22 @@ genvar_iteration: // ==IEEE: genvar_iteration new AstConst{$2, AstConst::StringToParse{}, "'b1"}}}; } ; -case_generate_itemList: // IEEE: { case_generate_itemList } +case_generate_itemList: // IEEE: { case_generate_itemList } ~c~case_generate_item { $$ = $1; } | ~c~case_generate_itemList ~c~case_generate_item { $$ = $1; $1->addNext($2); } ; -c_case_generate_itemList: // IEEE: { case_generate_item } (for checkers) +c_case_generate_itemList: // IEEE: { case_generate_item } (for checkers) BISONPRE_COPY(case_generate_itemList,{s/~c~/c_/g}) // {copied} ; -case_generate_item: // ==IEEE: case_generate_item - caseCondList colon ~c~generate_block_or_null { $$ = new AstCaseItem{$2, $1, $3}; } - | yDEFAULT colon ~c~generate_block_or_null { $$ = new AstCaseItem{$1, nullptr, $3}; } - | yDEFAULT ~c~generate_block_or_null { $$ = new AstCaseItem{$1, nullptr, $2}; } +case_generate_item: // ==IEEE: case_generate_item + caseCondList colon ~c~generate_block_or_null { $$ = new AstGenCaseItem{$2, $1, $3}; } + | yDEFAULT colon ~c~generate_block_or_null { $$ = new AstGenCaseItem{$1, nullptr, $3}; } + | yDEFAULT ~c~generate_block_or_null { $$ = new AstGenCaseItem{$1, nullptr, $2}; } ; -c_case_generate_item: // IEEE: case_generate_item (for checkers) +c_case_generate_item: // IEEE: case_generate_item (for checkers) BISONPRE_COPY(case_generate_item,{s/~c~/c_/g}) // {copied} ; @@ -3409,9 +3405,9 @@ par_blockPreId: // ==IEEE: par_block but called with leading ID seq_blockFront: // IEEE: part of seq_block yBEGIN - { $$ = new AstBegin{$1, "", nullptr, false, false}; } + { $$ = new AstBegin{$1, "", nullptr, false}; } | yBEGIN ':' idAny/*new-block_identifier*/ - { $$ = new AstBegin{$3, *$3, nullptr, false, false}; } + { $$ = new AstBegin{$3, *$3, nullptr, false}; } ; par_blockFront: // IEEE: part of par_block @@ -3423,7 +3419,7 @@ par_blockFront: // IEEE: part of par_block seq_blockFrontPreId: // IEEE: part of seq_block/stmt with leading id id/*block_identifier*/ yP_COLON__BEGIN yBEGIN - { $$ = new AstBegin{$3, *$1, nullptr, false, false}; } + { $$ = new AstBegin{$3, *$1, nullptr, false}; } ; par_blockFrontPreId: // IEEE: part of par_block/stmt with leading id @@ -3468,7 +3464,7 @@ stmtList: stmt: // IEEE: statement_or_null == function_statement_or_null statement_item { $$ = $1; } // // S05 block creation rule - | id/*block_identifier*/ ':' statement_item { $$ = new AstBegin{$1, *$1, $3, false, false}; } + | id/*block_identifier*/ ':' statement_item { $$ = new AstBegin{$1, *$1, $3, false}; } // // from _or_null | ';' { $$ = nullptr; } // // labeled par_block/seq_block with leading ':' @@ -3590,7 +3586,7 @@ statement_item: // IEEE: statement_item | yDO stmtBlock yWHILE '(' expr ')' ';' { $$ = new AstDoWhile{$1, $5, $2}; } // // IEEE says array_identifier here, but dotted accepted in VMM and 1800-2009 | yFOREACH '(' idClassSelForeach ')' stmtBlock - { $$ = new AstBegin{$1, "", new AstForeach{$1, $3, $5}, false, true}; } + { $$ = new AstBegin{$1, "", new AstForeach{$1, $3, $5}, true}; } // // // IEEE: jump_statement | yRETURN ';' { $$ = new AstReturn{$1}; } @@ -3652,10 +3648,10 @@ statement_item: // IEEE: statement_item statementFor: // IEEE: part of statement yFOR beginForParen for_initialization expr ';' for_stepE ')' stmtBlock - { $$ = new AstBegin{$1, "", $3, false, true}; + { $$ = new AstBegin{$1, "", $3, true}; $$->addStmtsp(new AstWhile{$1, $4, $8, $6}); } | yFOR beginForParen for_initialization ';' for_stepE ')' stmtBlock - { $$ = new AstBegin{$1, "", $3, false, true}; + { $$ = new AstBegin{$1, "", $3, true}; $$->addStmtsp(new AstWhile{$1, new AstConst{$1, AstConst::BitTrue{}}, $7, $5}); } ; beginForParen: // IEEE: Part of statement (for loop beginning paren) @@ -6160,7 +6156,7 @@ assertion_item: // ==IEEE: assertion_item deferred_immediate_assertion_item: // ==IEEE: deferred_immediate_assertion_item deferred_immediate_assertion_statement { $$ = $1; } | id/*block_identifier*/ ':' deferred_immediate_assertion_statement - { $$ = new AstBegin{$1, *$1, $3, false, true}; } + { $$ = new AstBegin{$1, *$1, $3, true}; } ; procedural_assertion_statement: // ==IEEE: procedural_assertion_statement @@ -6216,7 +6212,7 @@ deferred_immediate_assertion_statement: // ==IEEE: deferred_immediate_as concurrent_assertion_item: // IEEE: concurrent_assertion_item concurrent_assertion_statement { $$ = $1; } | id/*block_identifier*/ ':' concurrent_assertion_statement - { $$ = new AstBegin{$1, *$1, $3, false, true}; } + { $$ = new AstBegin{$1, *$1, $3, true}; } // // IEEE: checker_instantiation // // identical to module_instantiation; see etcInst ; diff --git a/test_regress/t/t_constraint_json_only.out b/test_regress/t/t_constraint_json_only.out index 7ae46914e..fa6018ab8 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","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + {"type":"BEGIN","name":"","addr":"(J)","loc":"d,71:12,71:17","implied":false,"needProcess":false,"unnamed":true, "stmtsp": [ {"type":"DISPLAY","name":"","addr":"(K)","loc":"d,73:7,73:13", "fmtp": [ diff --git a/test_regress/t/t_dump_json.out b/test_regress/t/t_dump_json.out index 3ff9c864e..d30f223d7 100644 --- a/test_regress/t/t_dump_json.out +++ b/test_regress/t/t_dump_json.out @@ -160,7 +160,7 @@ ]} ], "stmtsp": [ - {"type":"BEGIN","name":"","addr":"(XC)","loc":"e,36:27,36:32","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + {"type":"BEGIN","name":"","addr":"(XC)","loc":"e,36:27,36:32","implied":false,"needProcess":false,"unnamed":true, "stmtsp": [ {"type":"ASSIGNDLY","name":"","addr":"(YC)","loc":"e,40:11,40:13","dtypep":"UNLINKED", "rhsp": [ @@ -308,7 +308,7 @@ ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(ZE)","loc":"e,43:21,43:26","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + {"type":"BEGIN","name":"","addr":"(ZE)","loc":"e,43:21,43:26","implied":false,"needProcess":false,"unnamed":true, "stmtsp": [ {"type":"ASSIGNDLY","name":"","addr":"(AF)","loc":"e,45:14,45:16","dtypep":"UNLINKED", "rhsp": [ @@ -338,7 +338,7 @@ ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(MF)","loc":"e,48:26,48:31","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + {"type":"BEGIN","name":"","addr":"(MF)","loc":"e,48:26,48:31","implied":false,"needProcess":false,"unnamed":true, "stmtsp": [ {"type":"ASSIGNDLY","name":"","addr":"(NF)","loc":"e,49:14,49:16","dtypep":"UNLINKED", "rhsp": [ @@ -361,7 +361,7 @@ ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(VF)","loc":"e,51:26,51:31","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [],"stmtsp": []} + {"type":"BEGIN","name":"","addr":"(VF)","loc":"e,51:26,51:31","implied":false,"needProcess":false,"unnamed":true,"stmtsp": []} ], "elsesp": [ {"type":"IF","name":"","addr":"(WF)","loc":"e,53:12,53:14", @@ -375,7 +375,7 @@ ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(AG)","loc":"e,53:27,53:32","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + {"type":"BEGIN","name":"","addr":"(AG)","loc":"e,53:27,53:32","implied":false,"needProcess":false,"unnamed":true, "stmtsp": [ {"type":"DISPLAY","name":"","addr":"(BG)","loc":"e,54:10,54:16", "fmtp": [ @@ -479,7 +479,7 @@ ]} ], "stmtsp": [ - {"type":"BEGIN","name":"","addr":"(UH)","loc":"e,82:26,82:31","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + {"type":"BEGIN","name":"","addr":"(UH)","loc":"e,82:26,82:31","implied":false,"needProcess":false,"unnamed":true, "stmtsp": [ {"type":"ASSIGNDLY","name":"","addr":"(VH)","loc":"e,83:11,83:13","dtypep":"UNLINKED", "rhsp": [ @@ -529,7 +529,7 @@ "assertTypesp": [ {"type":"CONST","name":"?32?sh8","addr":"(OI)","loc":"e,90:25,90:26","dtypep":"(LF)"} ],"directiveTypesp": []}, - {"type":"BEGIN","name":"blk","addr":"(PI)","loc":"e,91:15,91:18","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":false,"genforp": [], + {"type":"BEGIN","name":"blk","addr":"(PI)","loc":"e,91:15,91:18","implied":false,"needProcess":false,"unnamed":false, "stmtsp": [ {"type":"DISABLE","name":"","addr":"(QI)","loc":"e,92:10,92:17", "targetRefp": [ @@ -639,7 +639,7 @@ ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(GK)","loc":"d,55:44,55:49","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + {"type":"BEGIN","name":"","addr":"(GK)","loc":"d,55:44,55:49","implied":false,"needProcess":false,"unnamed":true, "stmtsp": [ {"type":"STMTEXPR","name":"","addr":"(HK)","loc":"d,56:16,56:17", "exprp": [ @@ -695,7 +695,7 @@ ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(DL)","loc":"d,72:22,72:27","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + {"type":"BEGIN","name":"","addr":"(DL)","loc":"d,72:22,72:27","implied":false,"needProcess":false,"unnamed":true, "stmtsp": [ {"type":"ASSIGN","name":"","addr":"(EL)","loc":"d,73:17,73:18","dtypep":"UNLINKED", "rhsp": [ @@ -748,7 +748,7 @@ ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(YL)","loc":"d,89:22,89:27","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + {"type":"BEGIN","name":"","addr":"(YL)","loc":"d,89:22,89:27","implied":false,"needProcess":false,"unnamed":true, "stmtsp": [ {"type":"ASSIGN","name":"","addr":"(ZL)","loc":"d,90:17,90:18","dtypep":"UNLINKED", "rhsp": [ @@ -854,7 +854,7 @@ ]} ], "thensp": [ - {"type":"BEGIN","name":"","addr":"(ON)","loc":"d,118:35,118:40","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + {"type":"BEGIN","name":"","addr":"(ON)","loc":"d,118:35,118:40","implied":false,"needProcess":false,"unnamed":true, "stmtsp": [ {"type":"ASSIGN","name":"","addr":"(PN)","loc":"d,119:20,119:22","dtypep":"UNLINKED", "rhsp": [ diff --git a/test_regress/t/t_duplicated_gen_blocks_bad.out b/test_regress/t/t_duplicated_gen_blocks_bad.out index 96296dc1a..df9a128c0 100644 --- a/test_regress/t/t_duplicated_gen_blocks_bad.out +++ b/test_regress/t/t_duplicated_gen_blocks_bad.out @@ -1,4 +1,4 @@ -%Error: t/t_duplicated_gen_blocks_bad.v:11:12: Duplicate declaration of block: 'block' +%Error: t/t_duplicated_gen_blocks_bad.v:11:12: Duplicate declaration of generate block: 'block' : ... note: In instance 't' 11 | begin : block | ^~~~~ @@ -6,7 +6,7 @@ 9 | begin : block | ^~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_duplicated_gen_blocks_bad.v:15:23: Duplicate declaration of block: 'block1' +%Error: t/t_duplicated_gen_blocks_bad.v:15:23: Duplicate declaration of generate block: 'block1' : ... note: In instance 't' 15 | if (X > 1) begin : block1 | ^~~~~~ diff --git a/test_regress/t/t_json_only_begin_hier.out b/test_regress/t/t_json_only_begin_hier.out index 452f2903d..c3d82d83b 100644 --- a/test_regress/t/t_json_only_begin_hier.out +++ b/test_regress/t/t_json_only_begin_hier.out @@ -3,21 +3,21 @@ {"type":"MODULE","name":"test","addr":"(E)","loc":"d,22:8,22:12","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"test","level":2,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ {"type":"VAR","name":"N","addr":"(F)","loc":"d,24:12,24:13","dtypep":"(G)","origName":"N","isSc":false,"isPrimaryIO":false,"isPrimaryClock":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":true,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"lifetime":"VSTATICI","varType":"GENVAR","dtypeName":"integer","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"BEGIN","name":"FOR_GENERATE","addr":"(H)","loc":"d,25:14,25:17","generate":true,"genfor":false,"implied":true,"needProcess":false,"unnamed":false,"genforp": [],"stmtsp": []}, - {"type":"BEGIN","name":"FOR_GENERATE[0]","addr":"(I)","loc":"d,27:21,27:31","generate":true,"genfor":false,"implied":false,"needProcess":false,"unnamed":false,"genforp": [], - "stmtsp": [ + {"type":"GENBLOCK","name":"FOR_GENERATE","addr":"(H)","loc":"d,25:14,25:17","implied":true,"unnamed":false,"genforp": [],"itemsp": []}, + {"type":"GENBLOCK","name":"FOR_GENERATE[0]","addr":"(I)","loc":"d,27:21,27:31","implied":false,"unnamed":false,"genforp": [], + "itemsp": [ {"type":"CELL","name":"submod_for","addr":"(J)","loc":"d,27:21,27:31","origName":"submod_for","recursive":false,"modp":"(K)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []}, - {"type":"BEGIN","name":"genblk1","addr":"(L)","loc":"d,28:19,28:24","generate":true,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], - "stmtsp": [ + {"type":"GENBLOCK","name":"genblk1","addr":"(L)","loc":"d,28:19,28:24","implied":false,"unnamed":true,"genforp": [], + "itemsp": [ {"type":"CELL","name":"submod_2","addr":"(M)","loc":"d,29:25,29:33","origName":"submod_2","recursive":false,"modp":"(K)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []} ]}, {"type":"CELL","name":"submod_3","addr":"(N)","loc":"d,31:21,31:29","origName":"submod_3","recursive":false,"modp":"(K)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []} ]}, - {"type":"BEGIN","name":"FOR_GENERATE[1]","addr":"(O)","loc":"d,27:21,27:31","generate":true,"genfor":false,"implied":false,"needProcess":false,"unnamed":false,"genforp": [], - "stmtsp": [ + {"type":"GENBLOCK","name":"FOR_GENERATE[1]","addr":"(O)","loc":"d,27:21,27:31","implied":false,"unnamed":false,"genforp": [], + "itemsp": [ {"type":"CELL","name":"submod_for","addr":"(P)","loc":"d,27:21,27:31","origName":"submod_for","recursive":false,"modp":"(K)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []}, - {"type":"BEGIN","name":"genblk1","addr":"(Q)","loc":"d,28:19,28:24","generate":true,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], - "stmtsp": [ + {"type":"GENBLOCK","name":"genblk1","addr":"(Q)","loc":"d,28:19,28:24","implied":false,"unnamed":true,"genforp": [], + "itemsp": [ {"type":"CELL","name":"submod_2","addr":"(R)","loc":"d,29:25,29:33","origName":"submod_2","recursive":false,"modp":"(K)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []} ]}, {"type":"CELL","name":"submod_3","addr":"(S)","loc":"d,31:21,31:29","origName":"submod_3","recursive":false,"modp":"(K)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []} @@ -25,11 +25,11 @@ ]}, {"type":"MODULE","name":"submod","addr":"(K)","loc":"d,10:8,10:14","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"submod","level":3,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [], "stmtsp": [ - {"type":"BEGIN","name":"submod_gen","addr":"(T)","loc":"d,12:19,12:29","generate":true,"genfor":false,"implied":false,"needProcess":false,"unnamed":false,"genforp": [], - "stmtsp": [ + {"type":"GENBLOCK","name":"submod_gen","addr":"(T)","loc":"d,12:19,12:29","implied":false,"unnamed":false,"genforp": [], + "itemsp": [ {"type":"VAR","name":"l1_sig","addr":"(U)","loc":"d,13:14,13:20","dtypep":"(V)","origName":"l1_sig","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":"WIRE","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"BEGIN","name":"nested_gen","addr":"(W)","loc":"d,14:23,14:33","generate":true,"genfor":false,"implied":false,"needProcess":false,"unnamed":false,"genforp": [], - "stmtsp": [ + {"type":"GENBLOCK","name":"nested_gen","addr":"(W)","loc":"d,14:23,14:33","implied":false,"unnamed":false,"genforp": [], + "itemsp": [ {"type":"CELL","name":"submod_nested","addr":"(X)","loc":"d,15:21,15:34","origName":"submod_nested","recursive":false,"modp":"(Y)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []} ]}, {"type":"CELL","name":"submod_l1","addr":"(Z)","loc":"d,17:17,17:26","origName":"submod_l1","recursive":false,"modp":"(Y)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []} diff --git a/test_regress/t/t_json_only_tag.out b/test_regress/t/t_json_only_tag.out index a74077111..b73c21345 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","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [], + {"type":"BEGIN","name":"","addr":"(GB)","loc":"d,39:12,39:17","implied":false,"needProcess":false,"unnamed":true, "stmtsp": [ {"type":"STMTEXPR","name":"","addr":"(HB)","loc":"d,41:7,41:8", "exprp": [ diff --git a/test_regress/t/t_xml_begin_hier.out b/test_regress/t/t_xml_begin_hier.out index 32276460d..efb01dd5f 100644 --- a/test_regress/t/t_xml_begin_hier.out +++ b/test_regress/t/t_xml_begin_hier.out @@ -47,30 +47,30 @@ - - + + - + - + - - + + - + - + - + - + - + - + - +