// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: AstNode sub-types representing statements // // Code available from: https://verilator.org // //************************************************************************* // // Copyright 2003-2025 by Wilson Snyder. This program is free software; you // can redistribute it and/or modify it under the terms of either the GNU // Lesser General Public License Version 3 or the Perl Artistic License // Version 2.0. // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 // //************************************************************************* // // This files contains all 'AstNode' sub-types that are statements. // //************************************************************************* #ifndef VERILATOR_V3ASTNODESTMT_H_ #define VERILATOR_V3ASTNODESTMT_H_ #ifndef VERILATOR_V3AST_H_ #error "Use V3Ast.h as the include" #include "V3Ast.h" // This helps code analysis tools pick up symbols in V3Ast.h #define VL_NOT_FINAL // This #define fixes broken code folding in the CLion IDE #endif // === Abstract base node types (AstNode*) ===================================== class AstNodeStmt VL_NOT_FINAL : public AstNode { // Procedural statement protected: AstNodeStmt(VNType t, FileLine* fl) : AstNode{t, fl} {} public: ASTGEN_MEMBERS_AstNodeStmt; // METHODS void addNextStmt(AstNode* newp, AstNode* belowp) override; // Stop statement searchback here void dump(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override; }; class AstNodeAssign VL_NOT_FINAL : public AstNodeStmt { // Iteration is in order, and we want rhsp to be visited first (which is the execution order) // @astgen op1 := rhsp : AstNodeExpr // @astgen op2 := lhsp : AstNodeExpr // @astgen op3 := timingControlp : Optional[AstNode] protected: AstNodeAssign(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNode* timingControlp = nullptr) : AstNodeStmt{t, fl} { this->rhsp(rhsp); this->lhsp(lhsp); this->timingControlp(timingControlp); dtypeFrom(lhsp); } public: ASTGEN_MEMBERS_AstNodeAssign; // Clone single node, just get same type back. virtual AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) = 0; bool hasDType() const override VL_MT_SAFE { return true; } virtual bool cleanRhs() const { return true; } int instrCount() const override { return widthInstrs(); } bool sameNode(const AstNode*) const override { return true; } string verilogKwd() const override { return "="; } bool isTimingControl() const override { return timingControlp(); } }; class AstNodeBlock VL_NOT_FINAL : public AstNodeStmt { // A Begin/fork block // @astgen op2 := stmtsp : List[AstNode] // Parents: statement string m_name; // Name of block bool m_unnamed; // Originally unnamed (name change does not affect this) protected: AstNodeBlock(VNType t, FileLine* fl, const string& name, AstNode* stmtsp) : AstNodeStmt{t, fl} , m_name{name} { addStmtsp(stmtsp); m_unnamed = (name == ""); } public: ASTGEN_MEMBERS_AstNodeBlock; bool maybePointedTo() const override VL_MT_SAFE { return true; } void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; string name() const override VL_MT_STABLE { return m_name; } // * = Block name void name(const string& name) override { m_name = name; } bool unnamed() const { return m_unnamed; } }; class AstNodeCoverOrAssert VL_NOT_FINAL : public AstNodeStmt { // Cover or Assert // Parents: {statement list} // @astgen op1 := propp : AstNode // @astgen op2 := sentreep : Optional[AstSenTree] // op3 used by some sub-types only // @astgen op4 := passsp: List[AstNode] // Statements when propp is passing/truthly string m_name; // Name to report const VAssertType m_type; // Assertion/cover type const VAssertDirectiveType m_directive; // Assertion directive type public: AstNodeCoverOrAssert(VNType t, FileLine* fl, AstNode* propp, AstNode* passsp, VAssertType type, VAssertDirectiveType directive, const string& name = "") : AstNodeStmt{t, fl} , m_name{name} , m_type{type} , m_directive{directive} { this->propp(propp); addPasssp(passsp); } ASTGEN_MEMBERS_AstNodeCoverOrAssert; string name() const override VL_MT_STABLE { return m_name; } // * = Var name bool sameNode(const AstNode* samep) const override { return samep->name() == name(); } void name(const string& name) override { m_name = name; } void dump(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override; VAssertType type() const VL_MT_SAFE { return m_type; } VAssertDirectiveType directive() const { return m_directive; } bool immediate() const { return this->type().containsAny(VAssertType::SIMPLE_IMMEDIATE | VAssertType::OBSERVED_DEFERRED_IMMEDIATE | VAssertType::FINAL_DEFERRED_IMMEDIATE) || this->type() == VAssertType::INTERNAL; } }; class AstNodeForeach VL_NOT_FINAL : public AstNodeStmt { // @astgen op1 := arrayp : AstNode // @astgen op2 := stmtsp : List[AstNode] public: AstNodeForeach(VNType t, FileLine* fl, AstNode* arrayp, AstNode* stmtsp) : AstNodeStmt(t, fl) { this->arrayp(arrayp); addStmtsp(stmtsp); } ASTGEN_MEMBERS_AstNodeForeach; bool isGateOptimizable() const override { return false; } int instrCount() const override { return INSTR_COUNT_BRANCH; } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; class AstNodeIf VL_NOT_FINAL : public AstNodeStmt { // @astgen op1 := condp : AstNodeExpr // @astgen op2 := thensp : List[AstNode] // @astgen op3 := elsesp : List[AstNode] VBranchPred m_branchPred; // Branch prediction as taken/untaken? bool m_isBoundsCheck; // True if this if node is for assertion/bounds checking protected: AstNodeIf(VNType t, FileLine* fl, AstNodeExpr* condp, AstNode* thensp, AstNode* elsesp) : AstNodeStmt{t, fl} { this->condp(condp); addThensp(thensp); addElsesp(elsesp); isBoundsCheck(false); } public: ASTGEN_MEMBERS_AstNodeIf; bool isGateOptimizable() const override { return false; } bool isGateDedupable() const override { return true; } int instrCount() const override { return INSTR_COUNT_BRANCH; } bool sameNode(const AstNode* /*samep*/) const override { return true; } void branchPred(VBranchPred flag) { m_branchPred = flag; } VBranchPred branchPred() const { return m_branchPred; } void isBoundsCheck(bool flag) { m_isBoundsCheck = flag; } bool isBoundsCheck() const { return m_isBoundsCheck; } }; class AstNodeReadWriteMem VL_NOT_FINAL : public AstNodeStmt { // @astgen op1 := filenamep : AstNodeExpr // @astgen op2 := memp : AstNodeExpr // @astgen op3 := lsbp : Optional[AstNodeExpr] // @astgen op4 := msbp : Optional[AstNodeExpr] const bool m_isHex; // readmemh, not readmemb public: AstNodeReadWriteMem(VNType t, FileLine* fl, bool hex, AstNodeExpr* filenamep, AstNodeExpr* memp, AstNodeExpr* lsbp, AstNodeExpr* msbp) : AstNodeStmt{t, fl} , m_isHex{hex} { this->filenamep(filenamep); this->memp(memp); this->lsbp(lsbp); this->msbp(msbp); } ASTGEN_MEMBERS_AstNodeReadWriteMem; bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool isPure() override { return false; } bool isOutputter() override { return true; } bool isUnlikely() const override { return true; } bool sameNode(const AstNode* samep) const override { return isHex() == VN_DBG_AS(samep, NodeReadWriteMem)->isHex(); } bool isHex() const { return m_isHex; } virtual const char* cFuncPrefixp() const = 0; }; // === 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; } }; // === AstNodeStmt === class AstAssertCtl final : public AstNodeStmt { // @astgen op1 := controlTypep : AstNodeExpr // @astgen op2 := assertTypesp : Optional[AstNodeExpr] // @astgen op3 := directiveTypesp : Optional[AstNodeExpr] // Type of assertcontrol task; either known from parser or from evaluated // controlTypep expression. VAssertCtlType m_ctlType; // $assert keyword type (control_type) VAssertType m_assertTypes; // Types of assertions affected VAssertDirectiveType m_directiveTypes; // Types of directives affected public: AstAssertCtl(FileLine* fl, VAssertCtlType ctlType, uint32_t assertType, uint32_t directiveType, AstNodeExpr* levelp = nullptr, AstNodeExpr* itemsp = nullptr); AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr* assertTypesp = nullptr, AstNodeExpr* directiveTypep = nullptr, AstNodeExpr* levelp = nullptr, AstNodeExpr* itemsp = nullptr); ASTGEN_MEMBERS_AstAssertCtl; string verilogKwd() const override { return m_ctlType.ascii(); } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool isPure() override { return false; } bool isOutputter() override { return true; } VAssertCtlType ctlType() const { return m_ctlType; } void ctlType(int32_t type) { m_ctlType = VAssertCtlType{type}; } VAssertType ctlAssertTypes() const { return m_assertTypes; } void ctlAssertTypes(VAssertType types) { m_assertTypes = types; } VAssertDirectiveType ctlDirectiveTypes() const { return m_directiveTypes; } void ctlDirectiveTypes(VAssertDirectiveType types) { m_directiveTypes = types; } void dump(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override; }; class AstBreak final : public AstNodeStmt { public: explicit AstBreak(FileLine* fl) : ASTGEN_SUPER_Break(fl) {} ASTGEN_MEMBERS_AstBreak; string verilogKwd() const override { return "break"; } bool isBrancher() const override { V3ERROR_NA_RETURN(true); } // Node removed early }; class AstCReset final : public AstNodeStmt { // Reset variable at startup // @astgen op1 := varrefp : AstVarRef const bool m_constructing; // Previously cleared by constructor public: AstCReset(FileLine* fl, AstVarRef* varrefp, bool constructing) : ASTGEN_SUPER_CReset(fl) , m_constructing{constructing} { this->varrefp(varrefp); } ASTGEN_MEMBERS_AstCReset; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool sameNode(const AstNode* samep) const override { return constructing() == VN_DBG_AS(samep, CReset)->constructing(); } bool constructing() const { return m_constructing; } }; class AstCReturn final : public AstNodeStmt { // C++ return from a function // @astgen op1 := lhsp : AstNodeExpr public: AstCReturn(FileLine* fl, AstNodeExpr* lhsp) : ASTGEN_SUPER_CReturn(fl) { this->lhsp(lhsp); } ASTGEN_MEMBERS_AstCReturn; int instrCount() const override { return widthInstrs(); } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; class AstCStmt final : public AstNodeStmt { // Emit C statement // @astgen op1 := exprsp : List[AstNode] public: AstCStmt(FileLine* fl, AstNode* exprsp) : ASTGEN_SUPER_CStmt(fl) { addExprsp(exprsp); } inline AstCStmt(FileLine* fl, const string& textStmt); ASTGEN_MEMBERS_AstCStmt; bool isGateOptimizable() const override { return false; } 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 const bool m_showAt; // Show "at " public: AstComment(FileLine* fl, const string& name, bool showAt = false) : ASTGEN_SUPER_Comment(fl) , m_name{name} , m_showAt{showAt} {} ASTGEN_MEMBERS_AstComment; string name() const override VL_MT_STABLE { return m_name; } // * = Text bool sameNode(const AstNode* samep) const override { return true; } // Ignore name in comments virtual bool showAt() const { return m_showAt; } }; class AstConstraintExpr final : public AstNodeStmt { // Constraint expression // @astgen op1 := exprp : AstNodeExpr bool m_isDisableSoft = false; // Disable soft constraint expression bool m_isSoft = false; // Soft constraint expression public: AstConstraintExpr(FileLine* fl, AstNodeExpr* exprp) : ASTGEN_SUPER_ConstraintExpr(fl) { this->exprp(exprp); } ASTGEN_MEMBERS_AstConstraintExpr; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool sameNode(const AstNode* /*samep*/) const override { return true; } bool isDisableSoft() const { return m_isDisableSoft; } void isDisableSoft(bool flag) { m_isDisableSoft = flag; } bool isSoft() const { return m_isSoft; } void isSoft(bool flag) { m_isSoft = flag; } }; class AstConstraintUnique final : public AstNodeStmt { // Constraint unique statement // @astgen op1 := rangesp : List[AstNode] public: AstConstraintUnique(FileLine* fl, AstNode* rangesp) : ASTGEN_SUPER_ConstraintUnique(fl) { addRangesp(rangesp); } ASTGEN_MEMBERS_AstConstraintUnique; bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; class AstContinue final : public AstNodeStmt { public: explicit AstContinue(FileLine* fl) : ASTGEN_SUPER_Continue(fl) {} ASTGEN_MEMBERS_AstContinue; string verilogKwd() const override { return "continue"; } bool isBrancher() const override { V3ERROR_NA_RETURN(true); } // Node removed early }; class AstCoverInc final : public AstNodeStmt { // Coverage analysis point; increment coverage count // @astgen op1 := toggleExprp : Optional[AstNodeExpr] // [After V3Clock] // @astgen op2 := toggleCovExprp : Optional[AstNodeExpr] // [After V3Clock] // These are expressions to which the node corresponds. Used only in toggle coverage // // @astgen ptr := m_declp : AstNodeCoverDecl // [After V3CoverageJoin] Declaration public: AstCoverInc(FileLine* fl, AstNodeCoverDecl* declp) : ASTGEN_SUPER_CoverInc(fl) , m_declp{declp} {} ASTGEN_MEMBERS_AstCoverInc; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; } bool sameNode(const AstNode* samep) const override { return declp() == VN_DBG_AS(samep, CoverInc)->declp(); } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool isOutputter() override { return true; } bool isPure() override { return false; } AstNodeCoverDecl* declp() const { return m_declp; } // Where defined }; class AstCoverToggle final : public AstNodeStmt { // Toggle analysis of given signal // Parents: MODULE // @astgen op1 := incp : AstCoverInc // @astgen op2 := origp : AstNodeExpr // @astgen op3 := changep : AstNodeExpr public: AstCoverToggle(FileLine* fl, AstCoverInc* incp, AstNodeExpr* origp, AstNodeExpr* changep) : ASTGEN_SUPER_CoverToggle(fl) { this->incp(incp); this->origp(origp); this->changep(changep); } ASTGEN_MEMBERS_AstCoverToggle; int instrCount() const override { return 3 + INSTR_COUNT_BRANCH + INSTR_COUNT_LD; } bool sameNode(const AstNode* /*samep*/) const override { return true; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return true; } bool isOutputter() override { return false; // Though the AstCoverInc under this is an outputter } // but isPure() true }; class AstDelay final : public AstNodeStmt { // Delay statement // @astgen op1 := lhsp : AstNodeExpr // Delay value // @astgen op2 := stmtsp : List[AstNode] // Statements under delay VTimescale m_timeunit; // Delay's time unit const bool m_isCycle; // True if it is a cycle delay public: AstDelay(FileLine* fl, AstNodeExpr* lhsp, bool isCycle) : ASTGEN_SUPER_Delay(fl) , m_isCycle{isCycle} { this->lhsp(lhsp); } ASTGEN_MEMBERS_AstDelay; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; bool isTimingControl() const override { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; } void timeunit(const VTimescale& flag) { m_timeunit = flag; } VTimescale timeunit() const { return m_timeunit; } bool isCycleDelay() const { return m_isCycle; } }; class AstDisable final : public AstNodeStmt { // @astgen op1 := targetRefp : Optional[AstNodeExpr] // Reference to link in V3LinkDot // @astgen ptr := m_targetp : Optional[AstNode] // Task or block after V3LinkDot public: AstDisable(FileLine* fl, AstNodeExpr* targetRefp) : ASTGEN_SUPER_Disable(fl) { this->targetRefp(targetRefp); } ASTGEN_MEMBERS_AstDisable; const char* broken() const override; void dump(std::ostream& str) const override; void targetp(AstNode* nodep) { m_targetp = nodep; } AstNode* targetp() const { return m_targetp; } bool isBrancher() const override { V3ERROR_NA_RETURN(true); } // Node removed early }; class AstDisableFork final : public AstNodeStmt { // A "disable fork" statement public: explicit AstDisableFork(FileLine* fl) : ASTGEN_SUPER_DisableFork(fl) {} ASTGEN_MEMBERS_AstDisableFork; }; class AstDisplay final : public AstNodeStmt { // Parents: stmtlist // @astgen op1 := fmtp : AstSFormatF // @astgen op2 := filep : Optional[AstNodeExpr] // file (must resolve to a VarRef) VDisplayType m_displayType; public: AstDisplay(FileLine* fl, VDisplayType dispType, const string& text, AstNodeExpr* filep, AstNodeExpr* exprsp, char missingArgChar = 'd') : ASTGEN_SUPER_Display(fl) , m_displayType{dispType} { fmtp(new AstSFormatF{fl, text, true, exprsp, missingArgChar}); this->filep(filep); } AstDisplay(FileLine* fl, VDisplayType dispType, AstNodeExpr* filep, AstNodeExpr* exprsp, char missingArgChar = 'd') : ASTGEN_SUPER_Display(fl) , m_displayType{dispType} { fmtp(new AstSFormatF{fl, AstSFormatF::NoFormat{}, exprsp, missingArgChar}); this->filep(filep); } ASTGEN_MEMBERS_AstDisplay; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; const char* broken() const override { BROKEN_RTN(!fmtp()); return nullptr; } string verilogKwd() const override { return (filep() ? "$f"s + string{displayType().ascii()} : "$"s + string{displayType().ascii()}); } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering bool isOutputter() override { return true; } // SPECIAL: $display makes output bool isUnlikely() const override { return true; } bool sameNode(const AstNode* samep) const override { return displayType() == VN_DBG_AS(samep, Display)->displayType(); } int instrCount() const override { return INSTR_COUNT_PLI; } VDisplayType displayType() const { return m_displayType; } void displayType(VDisplayType type) { m_displayType = type; } // * = Add a newline for $display bool addNewline() const { return displayType().addNewline(); } }; class AstDumpCtl final : public AstNodeStmt { // $dumpon etc // Parents: expr // @astgen op1 := exprp : Optional[AstNodeExpr] // Expression based on type of statement const VDumpCtlType m_ctlType; // Type of operation public: AstDumpCtl(FileLine* fl, VDumpCtlType ctlType, AstNodeExpr* exprp = nullptr) : ASTGEN_SUPER_DumpCtl(fl) , m_ctlType{ctlType} { this->exprp(exprp); } ASTGEN_MEMBERS_AstDumpCtl; string verilogKwd() const override { return ctlType().ascii(); } bool isGateOptimizable() const override { return false; } bool isOutputter() override { return true; } bool isPredictOptimizable() const override { return false; } bool isPure() override { return false; } virtual bool cleanOut() const { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; } VDumpCtlType ctlType() const { return m_ctlType; } }; class AstEventControl final : public AstNodeStmt { // Parents: stmtlist // @astgen op1 := sentreep : Optional[AstSenTree] // @astgen op2 := stmtsp : List[AstNode] public: AstEventControl(FileLine* fl, AstSenTree* sentreep, AstNode* stmtsp) : ASTGEN_SUPER_EventControl(fl) { this->sentreep(sentreep); addStmtsp(stmtsp); } ASTGEN_MEMBERS_AstEventControl; string verilogKwd() const override { return "@(%l) %r"; } bool isTimingControl() const override { return true; } int instrCount() const override { return 0; } }; class AstFClose final : public AstNodeStmt { // Parents: stmtlist // @astgen op1 := filep : AstNodeExpr // file (must be a VarRef) public: AstFClose(FileLine* fl, AstNodeExpr* filep) : ASTGEN_SUPER_FClose(fl) { this->filep(filep); } ASTGEN_MEMBERS_AstFClose; string verilogKwd() const override { return "$fclose"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool isPure() override { return false; } bool isOutputter() override { return true; } bool isUnlikely() const override { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; class AstFFlush final : public AstNodeStmt { // Parents: stmtlist // @astgen op1 := filep : Optional[AstNodeExpr] // file (must be a VarRef) public: AstFFlush(FileLine* fl, AstNodeExpr* filep) : ASTGEN_SUPER_FFlush(fl) { this->filep(filep); } ASTGEN_MEMBERS_AstFFlush; string verilogKwd() const override { return "$fflush"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool isPure() override { return false; } bool isOutputter() override { return true; } bool isUnlikely() const override { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; class AstFinish final : public AstNodeStmt { public: explicit AstFinish(FileLine* fl) : ASTGEN_SUPER_Finish(fl) {} ASTGEN_MEMBERS_AstFinish; bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering bool isOutputter() override { return true; } // SPECIAL: $display makes output bool isUnlikely() const override { return true; } int instrCount() const override { return 0; } // Rarely executes bool sameNode(const AstNode* samep) const override { return fileline() == samep->fileline(); } }; class AstFireEvent final : public AstNodeStmt { // '-> _' and '->> _' event trigger statements // @astgen op1 := operandp : AstNodeExpr const bool m_delayed; // Delayed (->>) vs non-delayed (->) public: AstFireEvent(FileLine* fl, AstNodeExpr* operandp, bool delayed) : ASTGEN_SUPER_FireEvent(fl) , m_delayed{delayed} { this->operandp(operandp); } ASTGEN_MEMBERS_AstFireEvent; bool isDelayed() const { return m_delayed; } }; class AstJumpBlock final : public AstNodeStmt { // Block of code that might contain AstJumpGo statements as children, // which when exectued branch to right after the referenced AstJumpBlock. // AstJumpBlocks can nest, and an AstJumpGo can reference any of the // enclosing AstJumpBlocks (can break out of mulitple levels). // Parents: {statement list} // Children: {statement list, with JumpGo below} // @astgen op1 := stmtsp : List[AstNode] VIsCached m_purity; // Pure state public: // After construction must call ->labelp to associate with appropriate label AstJumpBlock(FileLine* fl, AstNode* stmtsp) : ASTGEN_SUPER_JumpBlock(fl) { addStmtsp(stmtsp); } ASTGEN_MEMBERS_AstJumpBlock; int instrCount() const override { return 0; } bool maybePointedTo() const override VL_MT_SAFE { return true; } bool isPure() override; private: bool getPurityRecurse() const; }; class AstJumpGo final : public AstNodeStmt { // Branch to right after the referenced encloding AstJumpBlock // Parents: statement, including the referenced AstJumpBlock // Children: none // // @astgen ptr := m_blockp : AstJumpBlock // The AstJumpBlock we are branching after public: AstJumpGo(FileLine* fl, AstJumpBlock* blockp) : ASTGEN_SUPER_JumpGo(fl) , m_blockp{blockp} {} ASTGEN_MEMBERS_AstJumpGo; const char* broken() const override; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; int instrCount() const override { return INSTR_COUNT_BRANCH; } bool sameNode(const AstNode* samep) const override { return blockp() == VN_DBG_AS(samep, JumpGo)->blockp(); } bool isGateOptimizable() const override { return false; } bool isBrancher() const override { return true; // SPECIAL: We don't process code after breaks } AstJumpBlock* blockp() const { return m_blockp; } }; class AstLoop final : public AstNodeStmt { // An inifinite loop, used to model all source level procedural loops. // Executes as: // while (true) { // stmtsp; // // <- 'continue' inside 'stmtsp goes here // contsp; // } // 'contsp' is moved into 'stmtsp' in LinkJump when 'continue' statements are resovled. // @astgen op1 := stmtsp : List[AstNode] // @astgen op2 := contsp : List[AstNode] // Empty after LinkJump VOptionBool m_unroll; // Full, none, or default unrolling public: AstLoop(FileLine* fl) : ASTGEN_SUPER_Loop(fl) {} ASTGEN_MEMBERS_AstLoop; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; bool sameNode(const AstNode* thatp) const override { return m_unroll == VN_DBG_AS(thatp, Loop)->m_unroll; } bool isGateOptimizable() const override { return false; } int instrCount() const override { return INSTR_COUNT_BRANCH; } bool maybePointedTo() const override VL_MT_SAFE { return true; } // ACCESSORS VOptionBool unroll() const { return m_unroll; } void unroll(const VOptionBool flag) { m_unroll = flag; } }; class AstLoopTest final : public AstNodeStmt { // The condition test inside an AstLoop. If the condition is true, // execution continues after this AstLoopTest statement. If the condition // is false, control is transfered to after the corresponding AstLoop. // In other words: AstLoopTest is like a conditional 'break' statement, // which breaks out of the loop if the condition is false. // @astgen op1 := condp : AstNodeExpr // The loop condition // @astgen ptr := m_loopp : AstLoop // The corresponding AstLoop public: AstLoopTest(FileLine* fl, AstLoop* loopp, AstNodeExpr* condp) : ASTGEN_SUPER_LoopTest(fl) , m_loopp{loopp} { this->condp(condp); } ASTGEN_MEMBERS_AstLoopTest; const char* broken() const override; void dump(std::ostream& str) const override; bool sameNode(const AstNode*) const override { return true; } bool isGateOptimizable() const override { return false; } bool isBrancher() const override { return true; } int instrCount() const override { return 0; } // ACCESSORS AstLoop* loopp() const { return m_loopp; } }; class AstMonitorOff final : public AstNodeStmt { const bool m_off; // Monitor off. Using 0=on allows faster init and comparison public: AstMonitorOff(FileLine* fl, bool off) : ASTGEN_SUPER_MonitorOff(fl) , m_off{off} {} ASTGEN_MEMBERS_AstMonitorOff; string verilogKwd() const override { return m_off ? "$monitoroff" : "$monitoron"; } bool isGateOptimizable() const override { return false; } // Though deleted before opt bool isPredictOptimizable() const override { return false; } // Though deleted before opt bool isPure() override { return false; } // Though deleted before opt bool isOutputter() override { return true; } // Though deleted before opt int instrCount() const override { return INSTR_COUNT_PLI; } bool sameNode(const AstNode* samep) const override { return m_off == VN_DBG_AS(samep, MonitorOff)->m_off; } bool off() const { return m_off; } }; class AstPrintTimeScale final : public AstNodeStmt { // Parents: stmtlist string m_name; // Parent module name VTimescale m_timeunit; // Parent module time unit public: explicit AstPrintTimeScale(FileLine* fl) : ASTGEN_SUPER_PrintTimeScale(fl) {} ASTGEN_MEMBERS_AstPrintTimeScale; void name(const string& name) override { m_name = name; } string name() const override VL_MT_STABLE { return m_name; } // * = Var name void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; string verilogKwd() const override { return "$printtimescale"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool isPure() override { return false; } bool isOutputter() override { return true; } int instrCount() const override { return INSTR_COUNT_PLI; } void timeunit(const VTimescale& flag) { m_timeunit = flag; } VTimescale timeunit() const { return m_timeunit; } }; class AstRandCase final : public AstNodeStmt { // @astgen op2 := itemsp : List[AstCaseItem] public: AstRandCase(FileLine* fl, AstCaseItem* itemsp) : ASTGEN_SUPER_RandCase(fl) { addItemsp(itemsp); } ASTGEN_MEMBERS_AstRandCase; int instrCount() const override { return INSTR_COUNT_BRANCH; } }; class AstRelease final : public AstNodeStmt { // Procedural 'release' statement // @astgen op1 := lhsp : AstNodeExpr public: AstRelease(FileLine* fl, AstNodeExpr* lhsp) : ASTGEN_SUPER_Release(fl) { this->lhsp(lhsp); } ASTGEN_MEMBERS_AstRelease; }; class AstRepeat final : public AstNodeStmt { // @astgen op1 := countp : AstNodeExpr // @astgen op2 := stmtsp : List[AstNode] public: AstRepeat(FileLine* fl, AstNodeExpr* countp, AstNode* stmtsp) : ASTGEN_SUPER_Repeat(fl) { this->countp(countp); addStmtsp(stmtsp); } ASTGEN_MEMBERS_AstRepeat; bool isGateOptimizable() const override { return false; } // Not relevant - converted to FOR int instrCount() const override { return INSTR_COUNT_BRANCH; } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; class AstReturn final : public AstNodeStmt { // @astgen op1 := lhsp : Optional[AstNodeExpr] public: explicit AstReturn(FileLine* fl, AstNodeExpr* lhsp = nullptr) : ASTGEN_SUPER_Return(fl) { this->lhsp(lhsp); } ASTGEN_MEMBERS_AstReturn; string verilogKwd() const override { return "return"; } bool isBrancher() const override { V3ERROR_NA_RETURN(true); } // Node removed early }; class AstSFormat final : public AstNodeStmt { // Parents: statement container // @astgen op1 := fmtp : AstSFormatF // @astgen op2 := lhsp : AstNodeExpr public: AstSFormat(FileLine* fl, AstNodeExpr* lhsp, const string& text, AstNodeExpr* exprsp, char missingArgChar = 'd') : ASTGEN_SUPER_SFormat(fl) { fmtp(new AstSFormatF{fl, text, true, exprsp, missingArgChar}); this->lhsp(lhsp); } AstSFormat(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* exprsp, char missingArgChar = 'd') : ASTGEN_SUPER_SFormat(fl) { fmtp(new AstSFormatF{fl, AstSFormatF::NoFormat{}, exprsp, missingArgChar}); this->lhsp(lhsp); } ASTGEN_MEMBERS_AstSFormat; const char* broken() const override { BROKEN_RTN(!fmtp()); return nullptr; } string verilogKwd() const override { return "$sformat"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return true; } bool isPure() override { return true; } bool isOutputter() override { return false; } virtual bool cleanOut() const { return false; } int instrCount() const override { return INSTR_COUNT_PLI; } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; class AstSetuphold final : public AstNodeStmt { // Verilog $setuphold // @astgen op1 := refevp : AstNodeExpr // @astgen op2 := dataevp : AstNodeExpr // @astgen op3 := delrefp : Optional[AstNodeExpr] // @astgen op4 := deldatap : Optional[AstNodeExpr] public: AstSetuphold(FileLine* fl, AstNodeExpr* refevp, AstNodeExpr* dataevp, AstNodeExpr* delrefp = nullptr, AstNodeExpr* deldatap = nullptr) : ASTGEN_SUPER_Setuphold(fl) { this->refevp(refevp); this->dataevp(dataevp); this->delrefp(delrefp); this->deldatap(deldatap); } ASTGEN_MEMBERS_AstSetuphold; bool sameNode(const AstNode* /*samep*/) const override { return true; } }; class AstSplitPlaceholder final : public AstNodeStmt { public: // Dummy node used within V3Split; never exists outside of V3Split. explicit AstSplitPlaceholder(FileLine* fl) : ASTGEN_SUPER_SplitPlaceholder(fl) {} ASTGEN_MEMBERS_AstSplitPlaceholder; }; class AstStackTraceT final : public AstNodeStmt { // $stacktrace used as task public: explicit AstStackTraceT(FileLine* fl) : ASTGEN_SUPER_StackTraceT(fl) {} ASTGEN_MEMBERS_AstStackTraceT; string verilogKwd() const override { return "$stacktrace"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool isPure() override { return false; } bool isOutputter() override { return true; } bool isUnlikely() const override { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; class AstStmtExpr final : public AstNodeStmt { // Expression in statement position // @astgen op1 := exprp : AstNodeExpr public: AstStmtExpr(FileLine* fl, AstNodeExpr* exprp) : ASTGEN_SUPER_StmtExpr(fl) { this->exprp(exprp); } ASTGEN_MEMBERS_AstStmtExpr; // cppcheck-suppress uselessOverride bool isPure() override { return exprp()->isPure(); } }; class AstStop final : public AstNodeStmt { const bool m_isFatal; // $fatal not $stop public: AstStop(FileLine* fl, bool isFatal) : ASTGEN_SUPER_Stop(fl) , m_isFatal{isFatal} {} ASTGEN_MEMBERS_AstStop; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering bool isOutputter() override { return true; } // SPECIAL: $display makes output bool isUnlikely() const override { return true; } int instrCount() const override { return 0; } // Rarely executes bool sameNode(const AstNode* samep) const override { return fileline() == samep->fileline(); } string emitVerilog() const { return m_isFatal ? "$fatal" : "$stop"; } bool isFatal() const { return m_isFatal; } }; class AstSystemT final : public AstNodeStmt { // $system used as task // @astgen op1 := lhsp : AstNodeExpr public: AstSystemT(FileLine* fl, AstNodeExpr* lhsp) : ASTGEN_SUPER_SystemT(fl) { this->lhsp(lhsp); } ASTGEN_MEMBERS_AstSystemT; string verilogKwd() const override { return "$system"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool isPure() override { return false; } bool isOutputter() override { return true; } bool isUnlikely() const override { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; class AstTimeFormat final : public AstNodeStmt { // Parents: stmtlist // @astgen op1 := unitsp : Optional[AstNodeExpr] // @astgen op2 := precisionp : Optional[AstNodeExpr] // @astgen op3 := suffixp : Optional[AstNodeExpr] // @astgen op4 := widthp : Optional[AstNodeExpr] public: AstTimeFormat(FileLine* fl, AstNodeExpr* unitsp, AstNodeExpr* precisionp, AstNodeExpr* suffixp, AstNodeExpr* widthp) : ASTGEN_SUPER_TimeFormat(fl) { this->unitsp(unitsp); this->precisionp(precisionp); this->suffixp(suffixp); this->widthp(widthp); } ASTGEN_MEMBERS_AstTimeFormat; string verilogKwd() const override { return "$timeformat"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool isPure() override { return false; } bool isOutputter() override { return true; } int instrCount() const override { return INSTR_COUNT_PLI; } }; class AstTraceDecl final : public AstNodeStmt { // Trace point declaration // Separate from AstTraceInc; as a declaration can't be deleted // Parents: {statement list} // Expression being traced - Moved to AstTraceInc by V3Trace // @astgen op1 := valuep : Optional[AstNodeExpr] uint32_t m_code{0}; // Trace identifier code uint32_t m_fidx{0}; // Trace function index const string m_showname; // Name of variable const VNumRange m_bitRange; // Property of var the trace details const VNumRange m_arrayRange; // Property of var the trace details const VVarType m_varType; // Type of variable (for localparam vs. param) const VDirection m_declDirection; // Declared direction input/output etc public: AstTraceDecl(FileLine* fl, const string& showname, AstVar* varp, // For input/output state etc AstNodeExpr* valuep, const VNumRange& bitRange, const VNumRange& arrayRange) : ASTGEN_SUPER_TraceDecl(fl) , m_showname{showname} , m_bitRange{bitRange} , m_arrayRange{arrayRange} , m_varType{varp->varType()} , m_declDirection{varp->declDirection()} { dtypeFrom(valuep); this->valuep(valuep); } void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; int instrCount() const override { return 100; } // Large... ASTGEN_MEMBERS_AstTraceDecl; string name() const override VL_MT_STABLE { return m_showname; } bool maybePointedTo() const override VL_MT_SAFE { return true; } bool hasDType() const override VL_MT_SAFE { return true; } bool sameNode(const AstNode* samep) const override { return false; } string showname() const { return m_showname; } // * = Var name // Details on what we're tracing uint32_t code() const { return m_code; } void code(uint32_t code) { m_code = code; } uint32_t fidx() const { return m_fidx; } void fidx(uint32_t fidx) { m_fidx = fidx; } uint32_t codeInc() const { return (m_arrayRange.ranged() ? m_arrayRange.elements() : 1) * valuep()->dtypep()->widthWords() * (VL_EDATASIZE / 32); // A code is always 32-bits } const VNumRange& bitRange() const { return m_bitRange; } const VNumRange& arrayRange() const { return m_arrayRange; } VVarType varType() const { return m_varType; } VDirection declDirection() const { return m_declDirection; } }; class AstTraceInc final : public AstNodeStmt { // Trace point dump // @astgen op1 := valuep : AstNodeExpr // Expression being traced (from decl) // // @astgen ptr := m_declp : AstTraceDecl // Pointer to declaration const uint32_t m_baseCode; // Trace code base value in function containing this AstTraceInc const VTraceType m_traceType; // Is this a const/full/incremental dump public: AstTraceInc(FileLine* fl, AstTraceDecl* declp, VTraceType traceType, uint32_t baseCode = 0) : ASTGEN_SUPER_TraceInc(fl) , m_baseCode{baseCode} , m_traceType{traceType} , m_declp{declp} { dtypeFrom(declp); // Note: A clone is necessary (instead of using declp()->valuep()), // for insertion of local temporaries in V3Premit valuep(declp->valuep()->cloneTree(true)); } ASTGEN_MEMBERS_AstTraceInc; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; int instrCount() const override { return 10 + 2 * INSTR_COUNT_LD; } bool hasDType() const override VL_MT_SAFE { return true; } bool sameNode(const AstNode* samep) const override { return declp() == VN_DBG_AS(samep, TraceInc)->declp(); } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool isOutputter() override { return true; } bool isPure() override { return false; } AstTraceDecl* declp() const { return m_declp; } VTraceType traceType() const { return m_traceType; } uint32_t baseCode() const { return m_baseCode; } }; class AstTracePopPrefix final : public AstNodeStmt { public: explicit AstTracePopPrefix(FileLine* fl) : ASTGEN_SUPER_TracePopPrefix(fl) {} ASTGEN_MEMBERS_AstTracePopPrefix; bool sameNode(const AstNode* samep) const override { return false; } }; class AstTracePushPrefix final : public AstNodeStmt { const string m_prefix; // Prefix to add to signal names const VTracePrefixType m_prefixType; // Type of prefix being pushed public: AstTracePushPrefix(FileLine* fl, const string& prefix, VTracePrefixType prefixType) : ASTGEN_SUPER_TracePushPrefix(fl) , m_prefix{prefix} , m_prefixType{prefixType} {} ASTGEN_MEMBERS_AstTracePushPrefix; bool sameNode(const AstNode* samep) const override { return false; } string prefix() const { return m_prefix; } VTracePrefixType prefixType() const { return m_prefixType; } }; class AstUCStmt final : public AstNodeStmt { // User $c statement // @astgen op1 := exprsp : List[AstNode] // (some are AstText) public: AstUCStmt(FileLine* fl, AstNode* exprsp) : ASTGEN_SUPER_UCStmt(fl) { addExprsp(exprsp); } ASTGEN_MEMBERS_AstUCStmt; bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool isPure() override { return false; } bool isOutputter() override { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; class AstWait final : public AstNodeStmt { // @astgen op1 := condp : AstNodeExpr // @astgen op2 := stmtsp : List[AstNode] public: AstWait(FileLine* fl, AstNodeExpr* condp, AstNode* stmtsp) : ASTGEN_SUPER_Wait(fl) { this->condp(condp); addStmtsp(stmtsp); } ASTGEN_MEMBERS_AstWait; bool isTimingControl() const override { return true; } }; class AstWaitFork final : public AstNodeStmt { // A "wait fork" statement public: explicit AstWaitFork(FileLine* fl) : ASTGEN_SUPER_WaitFork(fl) {} ASTGEN_MEMBERS_AstWaitFork; bool isTimingControl() const override { return true; } }; // === AstNodeAssign === class AstAssign final : public AstNodeAssign { public: AstAssign(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNode* timingControlp = nullptr) : ASTGEN_SUPER_Assign(fl, lhsp, rhsp, timingControlp) { dtypeFrom(lhsp); } ASTGEN_MEMBERS_AstAssign; AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { AstNode* const controlp = timingControlp() ? timingControlp()->cloneTree(false) : nullptr; return new AstAssign{fileline(), lhsp, rhsp, controlp}; } }; class AstAssignDly final : public AstNodeAssign { public: AstAssignDly(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNode* timingControlp = nullptr) : ASTGEN_SUPER_AssignDly(fl, lhsp, rhsp, timingControlp) {} ASTGEN_MEMBERS_AstAssignDly; AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { AstNode* const controlp = timingControlp() ? timingControlp()->cloneTree(false) : nullptr; return new AstAssignDly{fileline(), lhsp, rhsp, controlp}; } bool isGateOptimizable() const override { return false; } string verilogKwd() const override { return "<="; } }; class AstAssignForce final : public AstNodeAssign { // Procedural 'force' statement public: AstAssignForce(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp) : ASTGEN_SUPER_AssignForce(fl, lhsp, rhsp) {} ASTGEN_MEMBERS_AstAssignForce; AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { return new AstAssignForce{fileline(), lhsp, rhsp}; } }; class AstAssignW final : public AstNodeAssign { // Like assign, but wire/assign's in verilog, the only setting of the specified variable // @astgen op4 := strengthSpecp : Optional[AstStrengthSpec] public: AstAssignW(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNode* timingControlp = nullptr) : ASTGEN_SUPER_AssignW(fl, lhsp, rhsp, timingControlp) {} ASTGEN_MEMBERS_AstAssignW; AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { AstNode* const controlp = timingControlp() ? timingControlp()->cloneTree(false) : nullptr; return new AstAssignW{fileline(), lhsp, rhsp, controlp}; } bool isTimingControl() const override { return timingControlp() || lhsp()->exists([](const AstNodeVarRef* refp) { return refp->access().isWriteOrRW() && refp->varp()->delayp(); }); } AstDelay* getLhsNetDelay() const; AstAlways* convertToAlways(); }; // === AstNodeBlock === class AstBegin final : public AstNodeBlock { // A Begin/end named block, only exists shortly after parsing until linking // Parents: statement bool m_needProcess : 1; // Uses VlProcess const bool m_implied : 1; // Not inserted by user public: // Node that puts name into the output stream AstBegin(FileLine* fl, const string& name, AstNode* stmtsp, bool implied) : ASTGEN_SUPER_Begin(fl, name, stmtsp) , m_needProcess{false} , m_implied{implied} {} ASTGEN_MEMBERS_AstBegin; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; void setNeedProcess() { m_needProcess = true; } bool needProcess() const { return m_needProcess; } bool implied() const { return m_implied; } }; class AstFork final : public AstNodeBlock { // A fork named block // @astgen op1 := initsp : List[AstNode] // Parents: statement // Children: statements VJoinType m_joinType; // Join keyword type public: // Node that puts name into the output stream AstFork(FileLine* fl, const string& name, AstNode* stmtsp) : ASTGEN_SUPER_Fork(fl, name, stmtsp) {} ASTGEN_MEMBERS_AstFork; bool isTimingControl() const override { return !joinType().joinNone(); } void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; VJoinType joinType() const { return m_joinType; } void joinType(const VJoinType& flag) { m_joinType = flag; } }; // === AstNodeCoverOrAssert === class AstAssert final : public AstNodeCoverOrAssert { // @astgen op3 := failsp: List[AstNode] // Statements when propp is failing/falsey public: ASTGEN_MEMBERS_AstAssert; AstAssert(FileLine* fl, AstNode* propp, AstNode* passsp, AstNode* failsp, VAssertType type, VAssertDirectiveType directive, const string& name = "") : ASTGEN_SUPER_Assert(fl, propp, passsp, type, directive, name) { addFailsp(failsp); } }; class AstAssertIntrinsic final : public AstNodeCoverOrAssert { // A $cast or other compiler inserted assert, that must run even without --assert option // @astgen op3 := failsp: List[AstNode] // Statements when propp is failing/falsey public: ASTGEN_MEMBERS_AstAssertIntrinsic; AstAssertIntrinsic(FileLine* fl, AstNode* propp, AstNode* passsp, AstNode* failsp, const string& name = "") // Intrinsic asserts are always enabled thus 'type' field is set to INTERNAL. : ASTGEN_SUPER_AssertIntrinsic(fl, propp, passsp, VAssertType::INTERNAL, VAssertDirectiveType::INTRINSIC, name) { addFailsp(failsp); } }; class AstCover final : public AstNodeCoverOrAssert { // @astgen op3 := coverincsp: List[AstNode] // Coverage node public: ASTGEN_MEMBERS_AstCover; AstCover(FileLine* fl, AstNode* propp, AstNode* stmtsp, VAssertType type, const string& name = "") : ASTGEN_SUPER_Cover(fl, propp, stmtsp, type, VAssertDirectiveType::COVER, name) {} }; class AstRestrict final : public AstNodeCoverOrAssert { public: ASTGEN_MEMBERS_AstRestrict; AstRestrict(FileLine* fl, AstNode* propp) // Intrinsic asserts are always ignored thus 'type' field is set to INTERNAL. : ASTGEN_SUPER_Restrict(fl, propp, nullptr, VAssertType::INTERNAL, VAssertDirectiveType::RESTRICT) {} }; // === AstNodeForeach === class AstConstraintForeach final : public AstNodeForeach { // Constraint foreach statement public: AstConstraintForeach(FileLine* fl, AstNodeExpr* exprp, AstNode* bodysp) : ASTGEN_SUPER_ConstraintForeach(fl, exprp, bodysp) {} ASTGEN_MEMBERS_AstConstraintForeach; }; class AstForeach final : public AstNodeForeach { public: AstForeach(FileLine* fl, AstNode* arrayp, AstNode* stmtsp) : ASTGEN_SUPER_Foreach(fl, arrayp, stmtsp) {} ASTGEN_MEMBERS_AstForeach; }; // === AstNodeIf === class AstConstraintIf final : public AstNodeIf { public: AstConstraintIf(FileLine* fl, AstNodeExpr* condp, AstNode* thensp, AstNode* elsesp) : ASTGEN_SUPER_ConstraintIf(fl, condp, thensp, elsesp) {} ASTGEN_MEMBERS_AstConstraintIf; }; class AstIf final : public AstNodeIf { bool m_uniquePragma = false; // unique case bool m_unique0Pragma = false; // unique0 case bool m_priorityPragma = false; // priority case public: AstIf(FileLine* fl, AstNodeExpr* condp, AstNode* thensp = nullptr, AstNode* elsesp = nullptr) : ASTGEN_SUPER_If(fl, condp, thensp, elsesp) {} ASTGEN_MEMBERS_AstIf; 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; } }; // === AstNodeReadWriteMem === class AstReadMem final : public AstNodeReadWriteMem { public: AstReadMem(FileLine* fl, bool hex, AstNodeExpr* filenamep, AstNodeExpr* memp, AstNodeExpr* lsbp, AstNodeExpr* msbp) : ASTGEN_SUPER_ReadMem(fl, hex, filenamep, memp, lsbp, msbp) {} ASTGEN_MEMBERS_AstReadMem; string verilogKwd() const override { return (isHex() ? "$readmemh" : "$readmemb"); } const char* cFuncPrefixp() const override { return "VL_READMEM_"; } }; class AstWriteMem final : public AstNodeReadWriteMem { public: AstWriteMem(FileLine* fl, bool hex, AstNodeExpr* filenamep, AstNodeExpr* memp, AstNodeExpr* lsbp, AstNodeExpr* msbp) : ASTGEN_SUPER_WriteMem(fl, hex, filenamep, memp, lsbp, msbp) {} ASTGEN_MEMBERS_AstWriteMem; string verilogKwd() const override { return (isHex() ? "$writememh" : "$writememb"); } const char* cFuncPrefixp() const override { return "VL_WRITEMEM_"; } }; #endif // Guard