//************************************************************************* // DESCRIPTION: Verilator: Resolve module/signal name references // // Code available from: http://www.veripool.org/verilator // // AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli // //************************************************************************* // // Copyright 2003-2010 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 #include #include #include #include #include #include "V3Global.h" #include "V3LinkResolve.h" #include "V3Ast.h" //###################################################################### // 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 static int debug() { static int level = -1; if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); return level; } // VISITs virtual void visit(AstNodeModule* nodep, AstNUser*) { // Module: Create sim table for entire module and iterate UINFO(8,"MODULE "<iterateChildren(*this); m_modp = NULL; } virtual void visit(AstVAssert* nodep, AstNUser*) { if (m_assertp) nodep->v3error("Assert not allowed under another assert"); m_assertp = nodep; nodep->iterateChildren(*this); m_assertp = NULL; } virtual void visit(AstVar* nodep, AstNUser*) { nodep->iterateChildren(*this); if (nodep->isIO() && !nodep->dtypeSkipRefp()->castBasicDType()) { nodep->v3error("Unsupported: Inputs and outputs must be simple data types; no arrays"); } 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, AstNUser*) { // VarRef: Resolve its reference if (nodep->varp()) { nodep->varp()->usedParam(true); } nodep->iterateChildren(*this); } virtual void visit(AstNodeFTask* nodep, AstNUser*) { // NodeTask: Remember its name for later resolution // Remember the existing symbol table scope m_ftaskp = nodep; nodep->iterateChildren(*this); m_ftaskp = NULL; if (nodep->dpiExport()) { nodep->scopeNamep(new AstScopeName(nodep->fileline())); } } virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { nodep->iterateChildren(*this); if (nodep->taskp() && (nodep->taskp()->dpiContext() || nodep->taskp()->dpiExport())) { nodep->scopeNamep(new AstScopeName(nodep->fileline())); } } virtual void visit(AstSenItem* nodep, AstNUser*) { // Remove bit selects, and bark if it's not a simple variable nodep->iterateChildren(*this); 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 && !sensp->castNodeVarRef() && !sensp->castConst()) { // Make a new temp wire string newvarname = "__Vsenitemexpr"+cvtToStr(++m_senitemCvtNum); AstVar* newvarp = new AstVar (sensp->fileline(), AstVarType::MODULETEMP, newvarname, AstLogicPacked(), 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 (addwherep->castSenItem() || addwherep->castSenTree()) { addwherep = addwherep->backp(); } if (!addwherep->castAlways()) { // 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 = nodep->sensp()->castNodeSel()) { AstNode* fromp = selp->fromp()->unlinkFrBack(); selp->replaceWith(fromp); selp->deleteTree(); selp=NULL; did=1; } // NodeSel doesn't include AstSel.... if (AstSel* selp = nodep->sensp()->castSel()) { AstNode* fromp = selp->fromp()->unlinkFrBack(); selp->replaceWith(fromp); selp->deleteTree(); selp=NULL; did=1; } if (AstNodePreSel* selp = nodep->sensp()->castNodePreSel()) { AstNode* fromp = selp->lhsp()->unlinkFrBack(); selp->replaceWith(fromp); selp->deleteTree(); selp=NULL; did=1; } } } if (!nodep->sensp()->castNodeVarRef()) { if (debug()) nodep->dumpTree(cout,"-tree: "); nodep->v3error("Unsupported: Complex statement in sensitivity list"); } } virtual void visit(AstSenGate* nodep, AstNUser*) { nodep->v3fatalSrc("SenGates shouldn't be in tree yet"); } virtual void visit(AstNodePreSel* nodep, AstNUser*) { if (!nodep->attrp()) { nodep->iterateChildren(*this); // Constification may change the fromp() to a constant, which will loose 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); AstNodeVarRef* varrefp = basefromp->castNodeVarRef(); // Maybe varxref - so need to clone if (!varrefp) nodep->v3fatalSrc("Illegal bit select; no signal being extracted from"); nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE, varrefp->cloneTree(false))); } } virtual void visit(AstCaseItem* nodep, AstNUser*) { // 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 nodep->iterateChildren(*this); if (!nodep->user2() && nodep->isDefault() && nodep->nextp()) { nodep->user2(true); AstNode* nextp = nodep->nextp(); nodep->unlinkFrBack(); nextp->addNext(nodep); } } virtual void visit(AstPragma* nodep, AstNUser*) { if (nodep->pragType() == AstPragmaType::PUBLIC_MODULE) { if (!m_modp) nodep->v3fatalSrc("PUBLIC_MODULE not under a module\n"); m_modp->modPublic(true); nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL; } else if (nodep->pragType() == AstPragmaType::PUBLIC_TASK) { if (!m_ftaskp) nodep->v3fatalSrc("PUBLIC_TASK not under a task\n"); m_ftaskp->taskPublic(true); m_modp->modPublic(true); // Need to get to the task... nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL; } 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); nodep=NULL; } } else { nodep->iterateChildren(*this); } } void expectFormat(AstNode* nodep, const string& format, AstNode* argp, bool isScan) { // Check display arguments bool inPct = false; for (const char* inp = format.c_str(); *inp; inp++) { char ch = tolower(*inp); // Breaks with iterators... if (!inPct && ch=='%') { inPct = true; } else if (inPct) { inPct = false; switch (tolower(ch)) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': inPct = true; break; case '%': break; // %% - just output a % case 'm': // %m - auto insert "name" if (isScan) nodep->v3error("Unsupported: %m in $fscanf"); break; default: // Most operators, just move to next argument if (!V3Number::displayedFmtLegal(ch)) { nodep->v3error("Unknown $display format code: %"<v3error("Missing arguments for $display format"); } else { argp = argp->nextp(); } } break; } // switch } } if (argp) { argp->v3error("Extra arguments for $display format"); } } 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, AstNUser*) { nodep->iterateChildren(*this); expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); } virtual void visit(AstFClose* nodep, AstNUser*) { nodep->iterateChildren(*this); expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); } virtual void visit(AstFEof* nodep, AstNUser*) { nodep->iterateChildren(*this); expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); } virtual void visit(AstFFlush* nodep, AstNUser*) { nodep->iterateChildren(*this); if (nodep->filep()) { expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); } } virtual void visit(AstFGetC* nodep, AstNUser*) { nodep->iterateChildren(*this); expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); } virtual void visit(AstFGetS* nodep, AstNUser*) { nodep->iterateChildren(*this); expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); } virtual void visit(AstFScanF* nodep, AstNUser*) { nodep->iterateChildren(*this); expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); expectFormat(nodep, nodep->text(), nodep->exprsp(), true); } virtual void visit(AstSScanF* nodep, AstNUser*) { nodep->iterateChildren(*this); expectFormat(nodep, nodep->text(), nodep->exprsp(), true); } void expectNodeDisplay(AstNodeDisplay* nodep) { // AstSFormat also handled here expectFormat(nodep, nodep->text(), nodep->exprsp(), false); if ((nodep->castDisplay() && nodep->castDisplay()->displayType().needScopeTracking()) || nodep->formatScopeTracking()) { nodep->scopeNamep(new AstScopeName(nodep->fileline())); } } virtual void visit(AstNodeDisplay* nodep, AstNUser*) { nodep->iterateChildren(*this); expectNodeDisplay(nodep); } virtual void visit(AstDisplay* nodep, AstNUser* vup) { nodep->iterateChildren(*this); expectNodeDisplay(nodep); if (nodep->filep()) expectDescriptor(nodep, nodep->filep()->castNodeVarRef()); if (!m_assertp && (nodep->displayType() == AstDisplayType::INFO || nodep->displayType() == AstDisplayType::WARNING || nodep->displayType() == AstDisplayType::ERROR || nodep->displayType() == AstDisplayType::FATAL)) { nodep->v3error(nodep->verilogKwd()+" only allowed under an assertion."); } } virtual void visit(AstUdpTable* nodep, AstNUser*) { UINFO(5,"UDPTABLE "<stmtsp(); stmtp; stmtp=stmtp->nextp()) { if (AstVar* varp = stmtp->castVar()) { if (varp->isInput()) { } else if (varp->isOutput()) { 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); nodep=NULL; } virtual void visit(AstScCtor* nodep, AstNUser*) { // Constructor info means the module must remain public m_modp->modPublic(true); nodep->iterateChildren(*this); } virtual void visit(AstScDtor* nodep, AstNUser*) { // Destructor info means the module must remain public m_modp->modPublic(true); nodep->iterateChildren(*this); } virtual void visit(AstScInt* nodep, AstNUser*) { // Special class info means the module must remain public m_modp->modPublic(true); nodep->iterateChildren(*this); } virtual void visit(AstNode* nodep, AstNUser*) { // Default: Just iterate nodep->iterateChildren(*this); } public: // CONSTUCTORS LinkResolveVisitor(AstNetlist* rootp) { m_ftaskp = NULL; m_modp = NULL; m_assertp = NULL; m_senitemCvtNum = 0; // rootp->accept(*this); } 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 static int debug() { static int level = -1; if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); return level; } // VISITs virtual void visit(AstNetlist* nodep, AstNUser*) { // Iterate modules backwards, in bottom-up order. nodep->iterateChildrenBackwards(*this); } virtual void visit(AstNodeModule* nodep, AstNUser*) { m_modp = nodep; nodep->iterateChildren(*this); m_modp = NULL; } virtual void visit(AstCell* nodep, AstNUser*) { // Parent module inherits child's publicity if (nodep->modp()->modPublic()) m_modp->modPublic(true); //** No iteration for speed } virtual void visit(AstNodeMath* nodep, AstNUser*) { // Speedup } virtual void visit(AstNode* nodep, AstNUser*) { // Default: Just iterate nodep->iterateChildren(*this); } public: // CONSTUCTORS LinkBotupVisitor(AstNetlist* rootp) { m_modp = NULL; // rootp->accept(*this); } virtual ~LinkBotupVisitor() {} }; //###################################################################### // Link class functions void V3LinkResolve::linkResolve(AstNetlist* rootp) { UINFO(4,__FUNCTION__<<": "<