// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Resolve module/signal name references // // Code available from: http://www.veripool.org/verilator // //************************************************************************* // // Copyright 2003-2019 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. // // Verilator is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // //************************************************************************* // LinkResolve TRANSFORMATIONS: // Top-down traversal // Extracts: // Add SUB so that we subtract off the "base 0-start" of the array // SelBit: Convert to ArraySel // Add SUB so that we subtract off the "base 0-start" of the array // File operations // Convert normal var to FILE* type // SenItems: Convert pos/negedge of non-simple signals to temporaries //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include "V3Global.h" #include "V3String.h" #include "V3LinkResolve.h" #include "V3Ast.h" #include #include #include #include //###################################################################### // Link state, as a visitor of each AstNode class LinkResolveVisitor : public AstNVisitor { private: // NODE STATE // Entire netlist: // AstCaseItem::user2() // bool Moved default caseitems AstUser2InUse m_inuser2; // STATE // Below state needs to be preserved between each module call. AstNodeModule* m_modp; // Current module AstNodeFTask* m_ftaskp; // Function or task we're inside AstVAssert* m_assertp; // Current assertion int m_senitemCvtNum; // Temporary signal counter // METHODS VL_DEBUG_FUNC; // Declare debug() // VISITs // TODO: Most of these visitors are here for historical reasons. // TODO: ExpectDecriptor can move to data type resolution, and the rest // TODO: could move to V3LinkParse to get them out of the way of elaboration virtual void visit(AstNodeModule* nodep) { // Module: Create sim table for entire module and iterate UINFO(8,"MODULE "<dead()) return; m_modp = nodep; m_senitemCvtNum = 0; iterateChildren(nodep); m_modp = NULL; } virtual void visit(AstInitial* nodep) { iterateChildren(nodep); // Initial assignments under function/tasks can just be simple // assignments without the initial if (m_ftaskp) { nodep->replaceWith(nodep->bodysp()->unlinkFrBackWithNext()); VL_DANGLING(nodep); } } virtual void visit(AstVAssert* nodep) { if (m_assertp) nodep->v3error("Assert not allowed under another assert"); m_assertp = nodep; iterateChildren(nodep); m_assertp = NULL; } virtual void visit(AstVar* nodep) { iterateChildren(nodep); if (m_ftaskp) nodep->funcLocal(true); if (nodep->isSigModPublic()) { nodep->sigModPublic(false); // We're done with this attribute m_modp->modPublic(true); // Avoid flattening if signals are exposed } } virtual void visit(AstNodeVarRef* nodep) { // VarRef: Resolve its reference if (nodep->varp()) { nodep->varp()->usedParam(true); } iterateChildren(nodep); } virtual void visit(AstNodeFTask* nodep) { // NodeTask: Remember its name for later resolution // Remember the existing symbol table scope m_ftaskp = nodep; iterateChildren(nodep); m_ftaskp = NULL; if (nodep->dpiExport()) { nodep->scopeNamep(new AstScopeName(nodep->fileline())); } } virtual void visit(AstNodeFTaskRef* nodep) { iterateChildren(nodep); if (nodep->taskp() && (nodep->taskp()->dpiContext() || nodep->taskp()->dpiExport())) { nodep->scopeNamep(new AstScopeName(nodep->fileline())); } } virtual void visit(AstSenItem* nodep) { // Remove bit selects, and bark if it's not a simple variable iterateChildren(nodep); if (nodep->isClocked()) { // If it's not a simple variable wrap in a temporary // This is a bit unfortunate as we haven't done width resolution // and any width errors will look a bit odd, but it works. AstNode* sensp = nodep->sensp(); if (sensp && !VN_IS(sensp, NodeVarRef) && !VN_IS(sensp, Const)) { // Make a new temp wire string newvarname = "__Vsenitemexpr"+cvtToStr(++m_senitemCvtNum); AstVar* newvarp = new AstVar(sensp->fileline(), AstVarType::MODULETEMP, newvarname, VFlagLogicPacked(), 1); // We can't just add under the module, because we may be // inside a generate, begin, etc. // We know a SenItem should be under a SenTree/Always etc, // we we'll just hunt upwards AstNode* addwherep = nodep; // Add to this element's next while (VN_IS(addwherep, SenItem) || VN_IS(addwherep, SenTree)) { addwherep = addwherep->backp(); } if (!VN_IS(addwherep, Always)) { // Assertion perhaps? sensp->v3error("Unsupported: Non-single-bit pos/negedge clock statement under some complicated block"); addwherep = m_modp; } addwherep->addNext(newvarp); sensp->replaceWith(new AstVarRef(sensp->fileline(), newvarp, false)); AstAssignW* assignp = new AstAssignW (sensp->fileline(), new AstVarRef(sensp->fileline(), newvarp, true), sensp); addwherep->addNext(assignp); } } else { // Old V1995 sensitivity list; we'll probably mostly ignore bool did = 1; while (did) { did = 0; if (AstNodeSel* selp = VN_CAST(nodep->sensp(), NodeSel)) { AstNode* fromp = selp->fromp()->unlinkFrBack(); selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp); did = 1; } // NodeSel doesn't include AstSel.... if (AstSel* selp = VN_CAST(nodep->sensp(), Sel)) { AstNode* fromp = selp->fromp()->unlinkFrBack(); selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp); did = 1; } if (AstNodePreSel* selp = VN_CAST(nodep->sensp(), NodePreSel)) { AstNode* fromp = selp->lhsp()->unlinkFrBack(); selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp); did = 1; } } } if (!VN_IS(nodep->sensp(), NodeVarRef) && !VN_IS(nodep->sensp(), EnumItemRef) // V3Const will cleanup && !nodep->isIllegal()) { if (debug()) nodep->dumpTree(cout, "-tree: "); nodep->v3error("Unsupported: Complex statement in sensitivity list"); } } virtual void visit(AstSenGate* nodep) { nodep->v3fatalSrc("SenGates shouldn't be in tree yet"); } virtual void visit(AstNodePreSel* nodep) { if (!nodep->attrp()) { iterateChildren(nodep); // Constification may change the fromp() to a constant, which will lose the // variable we're extracting from (to determine MSB/LSB/endianness/etc.) // So we replicate it in another node // Note that V3Param knows not to replace AstVarRef's under AstAttrOf's AstNode* basefromp = AstArraySel::baseFromp(nodep); if (AstNodeVarRef* varrefp = VN_CAST(basefromp, NodeVarRef)) { // Maybe varxref - so need to clone nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE, varrefp->cloneTree(false))); } else if (AstUnlinkedRef* uvxrp = VN_CAST(basefromp, UnlinkedRef)) { // Maybe unlinked - so need to clone nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE, uvxrp->cloneTree(false))); } else if (AstMemberSel* fromp = VN_CAST(basefromp, MemberSel)) { nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::MEMBER_BASE, fromp->cloneTree(false))); } else if (AstEnumItemRef* fromp = VN_CAST(basefromp, EnumItemRef)) { nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::ENUM_BASE, fromp->cloneTree(false))); } else if (VN_IS(basefromp, Replicate)) { // From {...}[...] syntax in IEEE 2017 if (basefromp) { UINFO(1," Related node: "<v3error("Unsupported: Select of concatenation"); nodep = NULL; } else { if (basefromp) { UINFO(1," Related node: "<v3fatalSrc("Illegal bit select; no signal/member being extracted from"); } } } virtual void visit(AstCaseItem* nodep) { // Move default caseItems to the bottom of the list // That saves us from having to search each case list twice, for non-defaults and defaults iterateChildren(nodep); if (!nodep->user2() && nodep->isDefault() && nodep->nextp()) { nodep->user2(true); AstNode* nextp = nodep->nextp(); nodep->unlinkFrBack(); nextp->addNext(nodep); } } virtual void visit(AstPragma* nodep) { if (nodep->pragType() == AstPragmaType::PUBLIC_MODULE) { if (!m_modp) nodep->v3fatalSrc("PUBLIC_MODULE not under a module"); m_modp->modPublic(true); nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); } else if (nodep->pragType() == AstPragmaType::PUBLIC_TASK) { if (!m_ftaskp) nodep->v3fatalSrc("PUBLIC_TASK not under a task"); m_ftaskp->taskPublic(true); m_modp->modPublic(true); // Need to get to the task... nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); } else if (nodep->pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) { if (!v3Global.opt.coverageLine()) { // No need for block statements; may optimize better without nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); } } else { iterateChildren(nodep); } } string expectFormat(AstNode* nodep, const string& format, AstNode* argp, bool isScan) { // Check display arguments, return new format string string newFormat; bool inPct = false; string fmt; for (string::const_iterator it = format.begin(); it != format.end(); ++it) { char ch = *it; if (!inPct && ch=='%') { inPct = true; fmt = ch; } else if (inPct && (isdigit(ch) || ch=='.')) { fmt += ch; } else if (inPct) { inPct = false; fmt += ch; switch (tolower(ch)) { case '%': // %% - just output a % break; case 'm': // %m - auto insert "name" if (isScan) { nodep->v3error("Unsupported: %m in $fscanf"); fmt = ""; } break; case 'l': // %l - auto insert "library" if (isScan) { nodep->v3error("Unsupported: %l in $fscanf"); fmt = ""; } if (m_modp) fmt = VString::quotePercent(m_modp->prettyName()); break; default: // Most operators, just move to next argument if (!V3Number::displayedFmtLegal(ch)) { nodep->v3error("Unknown $display-like format code: %"<v3error("Missing arguments for $display-like format"); } else { argp = argp->nextp(); } } break; } // switch newFormat += fmt; } else { newFormat += ch; } } if (argp && !isScan) { int skipCount = 0; // number of args consume by any additional format strings while (argp) { if (skipCount) { argp = argp->nextp(); skipCount--; continue; } AstConst *constp = VN_CAST(argp, Const); bool isFromString = (constp) ? constp->num().isFromString() : false; if (isFromString) { int numchars = argp->dtypep()->width()/8; string str(numchars, ' '); // now scan for % operators bool inpercent = false; for (int i = 0; i < numchars; i++) { int ii = numchars - i - 1; char c = constp->num().dataByte(ii); str[i] = c; if (!inpercent && c == '%') { inpercent = true; } else if (inpercent) { inpercent = 0; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': inpercent = true; break; case '%': break; default: if (V3Number::displayedFmtLegal(c)) { skipCount++; } } } } newFormat.append(str); AstNode *nextp = argp->nextp(); argp->unlinkFrBack(); pushDeletep(argp); VL_DANGLING(argp); argp = nextp; } else { newFormat.append("%?"); // V3Width to figure it out argp = argp->nextp(); } } } return newFormat; } void expectDescriptor(AstNode* nodep, AstNodeVarRef* filep) { if (!filep) nodep->v3error("Unsupported: $fopen/$fclose/$f* descriptor must be a simple variable"); if (filep && filep->varp()) filep->varp()->attrFileDescr(true); } virtual void visit(AstFOpen* nodep) { iterateChildren(nodep); expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef)); } virtual void visit(AstFClose* nodep) { iterateChildren(nodep); expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef)); } virtual void visit(AstFEof* nodep) { iterateChildren(nodep); expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef)); } virtual void visit(AstFRead* nodep) { iterateChildren(nodep); expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef)); } virtual void visit(AstFScanF* nodep) { iterateChildren(nodep); expectFormat(nodep, nodep->text(), nodep->exprsp(), true); } virtual void visit(AstSScanF* nodep) { iterateChildren(nodep); expectFormat(nodep, nodep->text(), nodep->exprsp(), true); } virtual void visit(AstSFormatF* nodep) { iterateChildren(nodep); // Cleanup old-school displays without format arguments if (!nodep->hasFormat()) { if (nodep->text()!="") nodep->v3fatalSrc("Non-format $sformatf should have \"\" format"); if (VN_IS(nodep->exprsp(), Const) && VN_CAST(nodep->exprsp(), Const)->num().isFromString()) { AstConst* fmtp = VN_CAST(nodep->exprsp()->unlinkFrBack(), Const); nodep->text(fmtp->num().toString()); pushDeletep(fmtp); VL_DANGLING(fmtp); } nodep->hasFormat(true); } string newFormat = expectFormat(nodep, nodep->text(), nodep->exprsp(), false); nodep->text(newFormat); if ((VN_IS(nodep->backp(), Display) && VN_CAST(nodep->backp(), Display)->displayType().needScopeTracking()) || nodep->formatScopeTracking()) { nodep->scopeNamep(new AstScopeName(nodep->fileline())); } } virtual void visit(AstDisplay* nodep) { iterateChildren(nodep); } virtual void visit(AstUdpTable* nodep) { UINFO(5,"UDPTABLE "<stmtsp(); stmtp; stmtp=stmtp->nextp()) { if (AstVar* varp = VN_CAST(stmtp, Var)) { if (varp->isReadOnly()) { } else if (varp->isWritable()) { if (varoutp) { varp->v3error("Multiple outputs not allowed in udp modules"); } varoutp = varp; // Tie off m_modp->addStmtp(new AstAssignW( varp->fileline(), new AstVarRef(varp->fileline(), varp, true), new AstConst(varp->fileline(), AstConst::LogicFalse()))); } else { varp->v3error("Only inputs and outputs are allowed in udp modules"); } } } nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); } } virtual void visit(AstScCtor* nodep) { // Constructor info means the module must remain public m_modp->modPublic(true); iterateChildren(nodep); } virtual void visit(AstScDtor* nodep) { // Destructor info means the module must remain public m_modp->modPublic(true); iterateChildren(nodep); } virtual void visit(AstScInt* nodep) { // Special class info means the module must remain public m_modp->modPublic(true); iterateChildren(nodep); } virtual void visit(AstNode* nodep) { // Default: Just iterate iterateChildren(nodep); } public: // CONSTUCTORS explicit LinkResolveVisitor(AstNetlist* rootp) { m_ftaskp = NULL; m_modp = NULL; m_assertp = NULL; m_senitemCvtNum = 0; // iterate(rootp); } virtual ~LinkResolveVisitor() {} }; //###################################################################### // LinkBotupVisitor // Recurses cells backwards, so we can pick up those things that propagate // from child cells up to the top module. class LinkBotupVisitor : public AstNVisitor { private: // STATE AstNodeModule* m_modp; // Current module // METHODS VL_DEBUG_FUNC; // Declare debug() // VISITs virtual void visit(AstNetlist* nodep) { // Iterate modules backwards, in bottom-up order. iterateChildrenBackwards(nodep); } virtual void visit(AstNodeModule* nodep) { m_modp = nodep; iterateChildren(nodep); m_modp = NULL; } virtual void visit(AstCell* nodep) { // Parent module inherits child's publicity if (nodep->modp()->modPublic()) m_modp->modPublic(true); //** No iteration for speed } virtual void visit(AstNodeMath* nodep) { // Speedup } virtual void visit(AstNode* nodep) { // Default: Just iterate iterateChildren(nodep); } public: // CONSTUCTORS explicit LinkBotupVisitor(AstNetlist* rootp) { m_modp = NULL; // iterate(rootp); } virtual ~LinkBotupVisitor() {} }; //###################################################################### // Link class functions void V3LinkResolve::linkResolve(AstNetlist* rootp) { UINFO(4,__FUNCTION__<<": "<= 6); }