// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Parse syntax tree // // 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 // //************************************************************************* #include "V3Ast.h" #include "V3Control.h" #include "V3Global.h" #include "V3ParseImp.h" // Defines YYTYPE; before including bison header #include class V3ParseGrammar final { public: AstVar* m_varAttrp = nullptr; // Current variable for attribute adding AstRange* m_gateRangep = nullptr; // Current range for gate declarations AstNode* m_scopedSigAttr = nullptr; // Pointer to default signal attribute AstCase* m_caseAttrp = nullptr; // Current case statement for attribute adding AstNodeDType* m_varDTypep = nullptr; // Pointer to data type for next signal declaration AstNodeDType* m_memDTypep = nullptr; // Pointer to data type for next member declaration AstDelay* m_netDelayp = nullptr; // Pointer to delay for next signal declaration AstStrengthSpec* m_netStrengthp = nullptr; // Pointer to strength for next net declaration FileLine* m_instModuleFl = nullptr; // Fileline of module referenced for instantiations AstPin* m_instParamp = nullptr; // Parameters for instantiations string m_instModule; // Name of module referenced for instantiations VVarType m_varDecl = VVarType::UNKNOWN; // Type for next signal declaration (reg/wire/etc) VDirection m_varIO = VDirection::NONE; // Direction for next signal declaration (reg/wire/etc) VLifetime m_varLifetime; // Static/Automatic for next signal bool m_impliedDecl = false; // Allow implied wire declarations bool m_varDeclTyped = false; // Var got reg/wire for dedup check bool m_pinAnsi = false; // In ANSI parameter or port list bool m_tracingParse = true; // Tracing disable for parser bool m_inImplements = false; // Is inside class implements list bool m_insideProperty = false; // Is inside property declaration bool m_specifyignWarned = false; // Issued a SPECIFYIGN warning bool m_typedPropertyPort = false; // Typed property port occurred on port lists bool m_modportImpExpActive = false; // Standalone ID is a tf_identifier instead of port_identifier bool m_modportImpExpLastIsExport = false; // Last import_export statement in modportPortsDecl is an export int m_pinNum = -1; // Pin number currently parsing std::stack m_pinStack; // Queue of pin numbers being parsed static int s_typeImpNum; // Implicit type number, incremented each module // CONSTRUCTORS V3ParseGrammar() {} static V3ParseGrammar* singletonp() { static V3ParseGrammar singleton; return &singleton; } // METHODS AstArg* argWrapList(AstNodeExpr* nodep) VL_MT_DISABLED; bool allTracingOn(FileLine* fl) { return v3Global.opt.trace() && m_tracingParse && fl->tracingOn(); } AstRange* scrubRange(AstNodeRange* rangep) VL_MT_DISABLED; AstNodePreSel* scrubSel(AstNodeExpr* fromp, AstNodePreSel* selp) VL_MT_DISABLED; AstNodeDType* createArray(AstNodeDType* basep, AstNodeRange* rangep, bool isPacked) VL_MT_DISABLED; AstVar* createVariable(FileLine* fileline, const string& name, AstNodeRange* arrayp, AstNode* attrsp) VL_MT_DISABLED; AstAssignW* createSupplyExpr(FileLine* fileline, const string& name, int value) VL_MT_DISABLED; std::string textQuoted(FileLine* fileline, const std::string& text) { return singletonp()->unquoteString(fileline, text); } AstNode* createCell(FileLine* fileline, const string& name, AstPin* pinlistp, AstNodeRange* rangelistp) { // Must clone m_instParamp as may be comma'ed list of instances AstCell* const nodep = new AstCell{ fileline, singletonp()->m_instModuleFl, name, singletonp()->m_instModule, pinlistp, (singletonp()->m_instParamp ? singletonp()->m_instParamp->cloneTree(true) : nullptr), singletonp()->scrubRange(rangelistp)}; nodep->trace(singletonp()->allTracingOn(fileline)); return nodep; } void createCoverGroupMethods(AstClass* nodep, AstNode* sampleArgs) { // Hidden static to take unspecified reference argument results AstVar* const defaultVarp = new AstVar{nodep->fileline(), VVarType::MEMBER, "__Vint", nodep->findIntDType()}; defaultVarp->lifetime(VLifetime::STATIC_EXPLICIT); nodep->addStmtsp(defaultVarp); // IEEE: function void sample() AstFunc* const funcp = new AstFunc{nodep->fileline(), "sample", nullptr, nullptr}; funcp->addStmtsp(sampleArgs); funcp->classMethod(true); funcp->dtypep(funcp->findVoidDType()); nodep->addMembersp(funcp); // IEEE: function void start(), void stop() for (const string& name : {"start"s, "stop"s}) { AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr}; funcp->classMethod(true); funcp->dtypep(funcp->findVoidDType()); nodep->addMembersp(funcp); } // IEEE: static function real get_coverage(optional ref int, optional ref int) // IEEE: function real get_inst_coverage(optional ref int, optional ref int) for (const string& name : {"get_coverage"s, "get_inst_coverage"s}) { AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr}; funcp->fileline()->warnOff(V3ErrorCode::NORETURN, true); funcp->isStatic(name == "get_coverage"); funcp->classMethod(true); funcp->dtypep(funcp->findVoidDType()); nodep->addMembersp(funcp); { AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, name, nodep->findDoubleDType()}; varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); varp->funcLocal(true); varp->direction(VDirection::OUTPUT); varp->funcReturn(true); funcp->fvarp(varp); } for (const string& varname : {"covered_bins"s, "total_bins"s}) { AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, varname, nodep->findStringDType()}; varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); varp->funcLocal(true); varp->direction(VDirection::INPUT); varp->valuep(new AstVarRef{nodep->fileline(), defaultVarp, VAccess::READ}); funcp->addStmtsp(varp); } } // IEEE: function void set_inst_name(string) { AstFunc* const funcp = new AstFunc{nodep->fileline(), "set_inst_name", nullptr, nullptr}; funcp->classMethod(true); funcp->dtypep(funcp->findVoidDType()); nodep->addMembersp(funcp); AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, "name", nodep->findStringDType()}; varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); varp->funcLocal(true); varp->direction(VDirection::INPUT); funcp->addStmtsp(varp); } } AstDisplay* createDisplayError(FileLine* fileline) { AstDisplay* nodep = new AstDisplay{fileline, VDisplayType::DT_ERROR, "", nullptr, nullptr}; AstNode::addNext(nodep, new AstStop{fileline, false}); return nodep; } AstNodeExpr* createGatePin(AstNodeExpr* exprp) { AstRange* const rangep = m_gateRangep; if (!rangep) { return exprp; } else { return new AstGatePin{rangep->fileline(), exprp, rangep->cloneTree(true)}; } } AstSenTree* createSenTreeChanged(FileLine* fl, AstNodeExpr* exprp) { return new AstSenTree{fl, new AstSenItem{fl, VEdgeType::ET_CHANGED, exprp}}; } AstSenTree* createSenTreeDotStar(FileLine* fl) { return new AstSenTree{fl, new AstSenItem{fl, VEdgeType::ET_COMBO_STAR, nullptr}}; } AstNodeExpr* createGlobalClockParseRef(FileLine* fl) { return new AstParseRef{fl, "__024global_clock", nullptr, nullptr}; } AstSenTree* createGlobalClockSenTree(FileLine* fl) { return createSenTreeChanged(fl, createGlobalClockParseRef(fl)); } AstNode* createNettype(FileLine* fl, const string& name) { // As nettypes are unsupported, we just alias to logic AstTypedef* const nodep = new AstTypedef{fl, name, nullptr, VFlagChildDType{}, new AstBasicDType{fl, VFlagLogicPacked{}, 1}}; V3ParseImp::parsep()->tagNodep(nodep); return nodep; } AstNode* createTypedef(FileLine* fl, const string& name, AstNode* attrsp, AstNodeDType* basep, AstNodeRange* rangep) { AstTypedef* const nodep = new AstTypedef{fl, name, attrsp, VFlagChildDType{}, singletonp()->createArray(basep, rangep, false)}; V3ParseImp::parsep()->tagNodep(nodep); return nodep; } AstNode* createTypedefFwd(FileLine* fl, const string& name, const VFwdType& fwdType) { AstTypedefFwd* const nodep = new AstTypedefFwd{fl, name, fwdType}; V3ParseImp::parsep()->tagNodep(nodep); return nodep; } void endLabel(FileLine* fl, AstNode* nodep, string* endnamep) { endLabel(fl, nodep->prettyName(), endnamep); } void endLabel(FileLine* fl, const string& name, string* endnamep) { if (fl && endnamep && *endnamep != "" && name != *endnamep && name != AstNode::prettyName(*endnamep)) { fl->v3warn(ENDLABEL, "End label '" << *endnamep << "' does not match begin label '" << name << "'"); } } void setDType(AstNodeDType* dtypep) { if (m_varDTypep) { UASSERT_OBJ(!m_varDTypep->backp(), m_varDTypep, "Should not link directly"); VL_DO_CLEAR(m_varDTypep->deleteTree(), m_varDTypep = nullptr); } m_varDTypep = dtypep; } void setNetDelay(AstDelay* netDelayp) { if (m_netDelayp) { UASSERT_OBJ(!m_netDelayp->backp(), m_netDelayp, "Should not link directly"); VL_DO_CLEAR(m_netDelayp->deleteTree(), m_netDelayp = nullptr); } m_netDelayp = netDelayp; } void setNetStrength(AstStrengthSpec* netStrengthp) { m_netStrengthp = netStrengthp; } void pinPush() { m_pinStack.push(m_pinNum); m_pinNum = 1; } void pinPop(FileLine* fl) { if (VL_UNCOVERABLE(m_pinStack.empty())) fl->v3fatalSrc("Underflow of pin stack"); m_pinNum = m_pinStack.top(); m_pinStack.pop(); } AstNodeDType* addRange(AstBasicDType* dtypep, AstNodeRange* rangesp, bool isPacked) { // If dtypep isn't basic, don't use this, call createArray() instead if (!rangesp) { return dtypep; } else { // If rangesp is "wire [3:3][2:2][1:1] foo [5:5][4:4]" // then [1:1] becomes the basicdtype range; everything else is arraying // the final [5:5][4:4] will be passed in another call to createArray AstNodeRange* rangearraysp = nullptr; if (dtypep->isRanged()) { rangearraysp = rangesp; // Already a range; everything is an array } else { AstNodeRange* finalp = rangesp; while (finalp->nextp()) finalp = VN_CAST(finalp->nextp(), Range); if (finalp != rangesp) { finalp->unlinkFrBack(); rangearraysp = rangesp; } if (AstRange* const finalRangep = VN_CAST(finalp, Range)) { // not an UnsizedRange if (dtypep->implicit()) { // It's no longer implicit but a wire logic type AstBasicDType* const newp = new AstBasicDType{ dtypep->fileline(), VBasicDTypeKwd::LOGIC, dtypep->numeric(), dtypep->width(), dtypep->widthMin()}; VL_DO_DANGLING(dtypep->deleteTree(), dtypep); dtypep = newp; } dtypep->rangep(finalRangep); } } return createArray(dtypep, rangearraysp, isPacked); } } string unquoteString(FileLine* fileline, const std::string& text) VL_MT_DISABLED; void checkDpiVer(FileLine* fileline, const string& str) { if (str != "DPI-C" && !v3Global.opt.bboxSys()) fileline->v3warn(E_UNSUPPORTED, "Unsupported DPI type '" << str << "': Use 'DPI-C' (IEEE 1800-2023 35.5.4)"); } // Given a list of clocking declarations, put them in clocking items AstClockingItem* makeClockingItemList(FileLine* flp, const VDirection direction, AstNodeExpr* skewp, AstNode* const clockingDeclps) { AstClockingItem* itemsp = nullptr; for (AstNode *nodep = clockingDeclps, *nextp; nodep; nodep = nextp) { nextp = nodep->nextp(); if (nextp) nextp->unlinkFrBackWithNext(); if (itemsp && skewp) skewp = skewp->cloneTree(false); AstClockingItem* itemp = new AstClockingItem{flp, direction, skewp, nodep}; itemsp = itemsp ? itemsp->addNext(itemp) : itemp; } return itemsp; } void setScopedSigAttr(AstNode* attrsp) { if (m_scopedSigAttr) { // clearing set attribute UASSERT_OBJ(!m_scopedSigAttr->backp(), m_scopedSigAttr, "Should not link directly"); VL_DO_DANGLING(m_scopedSigAttr->deleteTree(), m_scopedSigAttr); } m_scopedSigAttr = attrsp; } void createScopedSigAttr(VAttrType vattrT) { setScopedSigAttr(new AstAttrOf{V3ParseImp::parsep()->lexFileline(), vattrT}); } AstNode* cloneScopedSigAttr() const { return m_scopedSigAttr ? m_scopedSigAttr->cloneTree(true) : nullptr; } static void addForkStmtsp(AstFork* forkp, AstNode* stmtsp) { forkp->addStmtsp(stmtsp); for (AstNode* stmtp = stmtsp; stmtp; stmtp = stmtp->nextp()) { AstVar* const varp = VN_CAST(stmtp, Var); if (!varp) break; varp->unlinkFrBack(); forkp->addInitsp(varp); } } void createGenericIface(AstNode* const nodep, AstNodeRange* const rangep, AstNode* sigAttrListp, FileLine* const modportFileline = nullptr, const string& modportstrp = "") { m_varDecl = VVarType::GPARAM; m_varIO = VDirection::NONE; setDType(new AstParseTypeDType{nodep->fileline(), VFwdType::GENERIC_INTERFACE}); m_varDeclTyped = true; const std::string uniqueName = "__VGIfaceParam" + nodep->name(); AstNode::addNext(nodep, createVariable(nodep->fileline(), uniqueName, rangep, sigAttrListp)); m_varDecl = VVarType::IFACEREF; AstIfaceGenericDType* const refdtypep = new AstIfaceGenericDType{nodep->fileline(), modportFileline, modportstrp}; setDType(refdtypep); m_varDeclTyped = true; m_varIO = VDirection::INPUT; AstNode::addNext(nodep, createVariable(nodep->fileline(), nodep->name(), rangep, sigAttrListp)); m_varDecl = VVarType::VAR; } };