// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Resolve module/signal name references // // Code available from: http://www.veripool.org/verilator // //************************************************************************* // // Copyright 2003-2012 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. // //************************************************************************* // LinkDot TRANSFORMATIONS: // Top-down traversal // Cells: // Make graph of cell hierarchy // Var/Funcs's: // Collect all names into symtable under appropriate cell // Top-down traversal // VarXRef/Func's: // Find appropriate named cell and link to var they reference //************************************************************************* // Top // a (LinkDotCellVertex->AstCell) // aa (LinkDotCellVertex->AstCell) // var (AstVar) -- in syms(), not a vertex // beg (LinkDotBeginVertex->AstBegin) -- can see "upper" a's symbol table // a__DOT__aa (LinkDotInlineVertex->AstCellInline) -- points to a.aa's symbol table // b (LinkDotCellVertex->AstCell) //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include #include #include #include #include #include #include "V3Global.h" #include "V3LinkDot.h" #include "V3SymTable.h" #include "V3Graph.h" #include "V3Ast.h" //###################################################################### class LinkDotGraph : public V3Graph { public: LinkDotGraph() {} virtual ~LinkDotGraph() {} virtual string dotRankDir() { return "LR"; } }; class LinkDotBaseVertex : public V3GraphVertex { typedef std::map NameVtxMap; // A point in the hierarchy, either inlined or real string m_symPrefix; // String to prefix symbols with NameVtxMap m_nameToVtxMap; // Lookup of name -> to vertexes public: LinkDotBaseVertex(V3Graph* graphp, const string& symPrefix) : V3GraphVertex(graphp), m_symPrefix(symPrefix) {} virtual ~LinkDotBaseVertex() {} virtual string modName() const = 0; virtual string cellName() const = 0; virtual V3SymTable& syms() = 0; string symPrefix() const { return m_symPrefix; } void insertSubcellName(LinkDotGraph* graphp, const string& name, LinkDotBaseVertex* toVertexp) { m_nameToVtxMap.insert(make_pair(name, toVertexp)); new V3GraphEdge(graphp, this, toVertexp, 1, false); } LinkDotBaseVertex* findSubcell(const string& name, const string& altname) { // Find a vertex under this one by name. // We could walk the edge top() list, but that would be O(n) for large lists of cells { NameVtxMap::iterator iter = m_nameToVtxMap.find(name); if (iter != m_nameToVtxMap.end()) return iter->second; } if (altname != "") { NameVtxMap::iterator iter = m_nameToVtxMap.find(altname); if (iter != m_nameToVtxMap.end()) return iter->second; } return NULL; } void errorScopes(AstNode* nodep) { if (!this) { // Silence if we messed it up and aren't debugging if (debug() || v3Global.opt.debugCheck()) nodep->v3fatalSrc("Void pointer; perhaps used null vxp instead of okVxp?"); return; } { string scopes; for (NameVtxMap::iterator it = m_nameToVtxMap.begin(); it!=m_nameToVtxMap.end(); ++it) { if (scopes != "") scopes += ", "; scopes += AstNode::prettyName(it->second->cellName()); } cerr<second->name()<modp()), m_nodep(nodep) {} LinkDotCellVertex(V3Graph* graphp, AstNodeModule* nodep) : LinkDotBaseVertex(graphp, ""), m_modp(nodep), m_nodep(NULL) {} virtual ~LinkDotCellVertex() {} AstNodeModule* modp() const { return m_modp; } // May be NULL AstCell* nodep() const { return m_nodep; } // Is NULL at TOP virtual V3SymTable& syms() { return m_syms; } // We need to use origName as parameters may have renamed the modname virtual string modName() const { return (modp() ? modp()->origName() : "*NULL*"); } virtual string cellName() const { return (nodep() ? nodep()->origName() : "*NULL*"); } virtual string name() const { return (string)("C:")+cellName()+" M:"+modName(); } }; class LinkDotInlineVertex : public LinkDotBaseVertex { // A fake point in the hierarchy, corresponding to an inlined module // This refrences to another vertex, and eventually resolves to a module with a prefix string m_basename; // Name with dotteds stripped AstCellInline* m_cellInlinep; // Inlined cell LinkDotCellVertex* m_symVxp; // Above cell so we can find real symbol table // // (Could walk graph to find it, but that's much slower.) public: LinkDotInlineVertex(V3Graph* graphp, AstCellInline* nodep, LinkDotCellVertex* symVxp, const string& basename) : LinkDotBaseVertex(graphp, nodep->name()+"__DOT__") , m_basename(basename), m_cellInlinep(nodep), m_symVxp(symVxp) {} virtual ~LinkDotInlineVertex() {} AstCellInline* cellInlinep() const { return m_cellInlinep; } // Search up through tree to find the real symbol table. virtual V3SymTable& syms() { return m_symVxp->syms(); } virtual string modName() const { return cellInlinep()->origModName(); } virtual string cellName() const { return m_basename; } virtual string name() const { return (string)("INL C:")+cellName()+" M:"+modName()+" P:"+symPrefix(); } virtual string dotColor() const { return "yellow"; } }; class LinkDotBeginVertex : public LinkDotBaseVertex { // A fake point in the hierarchy, corresponding to a begin block // After we remove begins these will go away // Note we use the symbol table of the parent, as we want to find variables there // However, cells walk the graph, so cells will appear under the begin itself AstBegin* m_nodep; // Relevant node LinkDotCellVertex* m_symVxp; // Above cell so we can find real symbol table // // (Could walk graph to find it, but that's much slower.) public: LinkDotBeginVertex(V3Graph* graphp, AstBegin* nodep, LinkDotCellVertex* symVxp) : LinkDotBaseVertex(graphp, nodep->name()+"__DOT__") , m_nodep(nodep), m_symVxp(symVxp) {} virtual ~LinkDotBeginVertex() {} // Search up through tree to find the real symbol table. virtual V3SymTable& syms() { return m_symVxp->syms(); } virtual string modName() const { return m_nodep->name(); } virtual string cellName() const { return m_nodep->name(); } virtual string name() const { return (string)("BEG C:")+cellName(); } virtual string dotColor() const { return "blue"; } }; //###################################################################### // LinkDot state, as a visitor of each AstNode class LinkDotState { private: // NODE STATE // Cleared on Netlist // AstNodeModule::user1p() -> LinkDotCellVertex*. Last cell that uses this module // AstVarScope::user2p() -> AstVarScope*. Base alias for this signal AstUser1InUse m_inuser1; AstUser2InUse m_inuser2; // TYPES typedef std::multimap NameScopeMap; // MEMBERS LinkDotGraph m_graph; // Graph of hierarchy NameScopeMap m_nameScopeMap; // Hash of scope referenced by non-pretty textual name bool m_forPrearray; // Compress cell__[array] refs bool m_forScopeCreation; // Remove VarXRefs for V3Scope public: static int debug() { static int level = -1; if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); return level; } // CONSTRUCTORS LinkDotState(bool forPrearray, bool forScopeCreation) { UINFO(4,__FUNCTION__<<": "<user1p(vxp); if (forScopeCreation()) m_nameScopeMap.insert(make_pair(scopename, vxp)); return vxp; } LinkDotCellVertex* insertCell(LinkDotBaseVertex* abovep, LinkDotCellVertex* cellVxp, AstCell* nodep, const string& scopename) { UINFO(9," INSERTcell "<modp()) nodep->modp()->user1p(vxp); abovep->insertSubcellName(&m_graph, nodep->origName(), vxp); if (abovep != cellVxp) { // If it's foo_DOT_bar, we need to be able to find it under that too. cellVxp->insertSubcellName(&m_graph, nodep->name(), vxp); } if (forScopeCreation()) m_nameScopeMap.insert(make_pair(scopename, vxp)); return vxp; } LinkDotInlineVertex* insertInline(LinkDotBaseVertex* abovep, LinkDotCellVertex* cellVxp, AstCellInline* nodep, const string& basename) { UINFO(9," INSERTcinl "<insertSubcellName(&m_graph, basename, vxp); if (abovep != cellVxp) { // If it's foo_DOT_bar, we need to be able to find it under that too. cellVxp->insertSubcellName(&m_graph, nodep->name(), vxp); } return vxp; } LinkDotBeginVertex* insertBegin(LinkDotBaseVertex* abovep, LinkDotCellVertex* cellVxp, AstBegin* nodep) { UINFO(9," INSERTbeg "<insertSubcellName(&m_graph, nodep->name(), vxp); return vxp; } void insertSym(LinkDotCellVertex* abovep, const string& name, AstNode* nodep) { UINFO(9," INSERTsym "<syms().insert(name, nodep); } bool existsModScope(AstNodeModule* nodep) { return nodep->user1p()!=NULL; } LinkDotCellVertex* findModScope(AstNodeModule* nodep) { LinkDotCellVertex* vxp = (LinkDotCellVertex*)(nodep->user1p()); if (!vxp) nodep->v3fatalSrc("Module never assigned a vertex"); return vxp; } LinkDotCellVertex* findScope(AstScope* nodep) { NameScopeMap::iterator iter = m_nameScopeMap.find(nodep->name()); if (iter == m_nameScopeMap.end()) { nodep->v3fatalSrc("Scope never assigned a vertex"); } return iter->second; } void dump() { if (debug()>=6) m_graph.dumpDotFilePrefixed("linkdot"); } private: LinkDotBaseVertex* parentOfCell(LinkDotBaseVertex* lowerVxp) { for (V3GraphEdge* edgep = lowerVxp->inBeginp(); edgep; edgep=edgep->inNextp()) { LinkDotBaseVertex* fromVxp = dynamic_cast(edgep->fromp()); return fromVxp; } return NULL; } public: LinkDotBaseVertex* findDotted(LinkDotBaseVertex* cellVxp, const string& dotname, string& baddot, LinkDotBaseVertex*& okVxp) { // Given a dotted hierarchy name, return where in scope it is // Note when dotname=="" we just fall through and return cellVxp UINFO(8," dottedFind "<findSubcell(ident, altIdent)) { cellVxp = findVxp; } // Check this module - cur modname else if (cellVxp->modName() == ident) {} // Check this module - cur cellname else if (cellVxp->cellName() == ident) {} else if (cellVxp->cellName() == altIdent) {} // Move up and check cellname + modname else { while (cellVxp) { cellVxp = parentOfCell(cellVxp); if (cellVxp) { UINFO(9,"\t\tUp to "<modName() == ident || cellVxp->cellName() == ident || cellVxp->cellName() == altIdent) { break; } else if (LinkDotBaseVertex* findVxp = cellVxp->findSubcell(ident, altIdent)) { cellVxp = findVxp; break; } } } if (!cellVxp) return NULL; // Not found } } else { // Searching for middle submodule, must be a cell name if (LinkDotBaseVertex* findVxp = cellVxp->findSubcell(ident, altIdent)) { cellVxp = findVxp; } else { return NULL; // Not found } } firstId = false; } return cellVxp; } AstNode* findSym(LinkDotBaseVertex* cellVxp, const string& dotname, string& baddot) { // Find symbol in given point in hierarchy // For simplicity cellVxp may be passed NULL result from findDotted if (!cellVxp) return NULL; UINFO(8,"\t\tfindSym "<symPrefix()=="") ? "" : " as ") <<((cellVxp->symPrefix()=="") ? "" : cellVxp->symPrefix()+dotname) <<" at "<syms().findIdFlat(cellVxp->symPrefix() + dotname); // Might be NULL if (!nodep) baddot = dotname; return nodep; } }; //====================================================================== class LinkDotFindVisitor : public AstNVisitor { private: // STATE LinkDotState* m_statep; // State to pass between visitors, including symbol table LinkDotCellVertex* m_cellVxp; // Vertex for current module LinkDotBaseVertex* m_inlineVxp; // Vertex for current module, possibly a fake inlined one string m_scope; // Scope text AstBegin* m_beginp; // Current Begin/end block int debug() { return LinkDotState::debug(); } // VISITs virtual void visit(AstNetlist* nodep, AstNUser*) { // Process $unit or other packages // Not needed - dotted references not allowed from inside packages //for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castNodeModule()) { // if (nodep->castPackage()) {}} // The first module in the list is always the top module (sorted before this is called). // This may not be the module with isTop() set, as early in the steps, // wrapTop may have not been created yet. AstNodeModule* topmodp = nodep->modulesp(); if (!topmodp) { nodep->v3error("No top level module found"); } else { UINFO(8,"Top Module: "<insertTopCell(topmodp, m_scope); m_inlineVxp = m_cellVxp; { topmodp->accept(*this); } m_scope = ""; m_cellVxp = NULL; m_inlineVxp = m_cellVxp; } } virtual void visit(AstNodeModule* nodep, AstNUser*) { // Called on top module from Netlist, other modules from the cell creating them, // and packages UINFO(8," "<iterateChildren(*this); } } virtual void visit(AstScope* nodep, AstNUser*) { if (!m_statep->forScopeCreation()) v3fatalSrc("Scopes should only exist right after V3Scope"); // Ignored. Processed in next step } virtual void visit(AstCell* nodep, AstNUser*) { UINFO(5," CELL under "<iterateChildren(*this); // Recurse in, preserving state string oldscope = m_scope; AstBegin* oldbeginp = m_beginp; LinkDotCellVertex* oldVxp = m_cellVxp; LinkDotBaseVertex* oldInlineVxp = m_inlineVxp; // Where do we add it? LinkDotBaseVertex* aboveVxp = m_inlineVxp; string origname = AstNode::dedotName(nodep->name()); string::size_type pos; if ((pos = origname.rfind(".")) != string::npos) { // Flattened, find what CellInline it should live under string scope = origname.substr(0,pos); string baddot; LinkDotBaseVertex* okVxp; aboveVxp = m_statep->findDotted(aboveVxp, scope, baddot, okVxp); if (!aboveVxp) nodep->v3fatalSrc("Can't find cell insertion point at '"<prettyName()); } { m_scope = m_scope+"."+nodep->name(); m_cellVxp = m_statep->insertCell(aboveVxp, m_cellVxp, nodep, m_scope); m_inlineVxp = m_cellVxp; m_beginp = NULL; if (nodep->modp()) nodep->modp()->accept(*this); } m_scope = oldscope; m_beginp = oldbeginp; m_cellVxp = oldVxp; m_inlineVxp = oldInlineVxp; } virtual void visit(AstCellInline* nodep, AstNUser*) { UINFO(5," CELLINLINE under "<name(); string::size_type pos; if ((pos=dottedname.rfind("__DOT__")) != string::npos) { string dotted = dottedname.substr(0, pos); string ident = dottedname.substr(pos+strlen("__DOT__")); string baddot; LinkDotBaseVertex* okVxp; aboveVxp = m_statep->findDotted(aboveVxp, dotted, baddot, okVxp); if (!aboveVxp) nodep->v3fatalSrc("Can't find cellinline insertion point at '"<prettyName()); m_statep->insertInline(aboveVxp, m_cellVxp, nodep, ident); } else { // No __DOT__, just directly underneath m_statep->insertInline(aboveVxp, m_cellVxp, nodep, nodep->name()); } } virtual void visit(AstBegin* nodep, AstNUser*) { UINFO(5," "<insertBegin(m_inlineVxp, m_cellVxp, nodep); nodep->stmtsp()->iterateAndNext(*this); } m_inlineVxp = oldVxp; m_beginp = oldbegin; // nodep->flatsp()->iterateAndNext(*this); } virtual void visit(AstNodeFTask* nodep, AstNUser*) { if (!m_beginp) { // For now, we don't support xrefs into functions inside begin blocks m_statep->insertSym(m_cellVxp, nodep->name(), nodep); } // No recursion, we don't want to pick up variables } virtual void visit(AstVar* nodep, AstNUser*) { if (!m_statep->forScopeCreation() && !m_beginp // For now, we don't support xrefs into begin blocks && !nodep->isFuncLocal()) { m_statep->insertSym(m_cellVxp, nodep->name(), nodep); } else { UINFO(9," Not allowing dot refs to: "<iterateChildren(*this); } public: // CONSTUCTORS LinkDotFindVisitor(AstNetlist* rootp, LinkDotState* statep) { UINFO(4,__FUNCTION__<<": "<accept(*this); } virtual ~LinkDotFindVisitor() {} }; //====================================================================== class LinkDotScopeVisitor : public AstNVisitor { private: // STATE LinkDotState* m_statep; // State to pass between visitors, including symbol table LinkDotCellVertex* m_cellVxp; // Vertex for current module int debug() { return LinkDotState::debug(); } // VISITs virtual void visit(AstScope* nodep, AstNUser*) { UINFO(8," SCOPE "<forScopeCreation()) v3fatalSrc("Scopes should only exist right after V3Scope"); // Using the CELL names, we created all hierarchy. We now need to match this Scope // up with the hierarchy created by the CELL names. m_cellVxp = m_statep->findScope(nodep); nodep->iterateChildren(*this); m_cellVxp = NULL; } virtual void visit(AstVarScope* nodep, AstNUser*) { if (!nodep->varp()->isFuncLocal()) { m_statep->insertSym(m_cellVxp, nodep->varp()->name(), nodep); } } virtual void visit(AstNodeFTask* nodep, AstNUser*) { m_statep->insertSym(m_cellVxp, nodep->name(), nodep); // No recursion, we don't want to pick up variables } virtual void visit(AstAssignAlias* nodep, AstNUser*) { // Track aliases created by V3Inline; if we get a VARXREF(aliased_from) // we'll need to replace it with a VARXREF(aliased_to) if (m_statep->forScopeCreation()) { if (debug()>=9) nodep->dumpTree(cout,"-\t\t\t\talias: "); AstVarScope* fromVscp = nodep->lhsp()->castVarRef()->varScopep(); AstVarScope* toVscp = nodep->rhsp()->castVarRef()->varScopep(); if (!fromVscp || !toVscp) nodep->v3fatalSrc("Bad alias scopes"); fromVscp->user2p(toVscp); } nodep->iterateChildren(*this); } // For speed, don't recurse things that can't have scope // Note we allow AstNodeStmt's as generates may be under them virtual void visit(AstCell*, AstNUser*) {} virtual void visit(AstVar*, AstNUser*) {} virtual void visit(AstNodeMath*, AstNUser*) {} virtual void visit(AstNode* nodep, AstNUser*) { // Default: Just iterate nodep->iterateChildren(*this); } public: // CONSTUCTORS LinkDotScopeVisitor(AstNetlist* rootp, LinkDotState* statep) { UINFO(4,__FUNCTION__<<": "<accept(*this); } virtual ~LinkDotScopeVisitor() {} }; //====================================================================== class LinkDotResolveVisitor : public AstNVisitor { private: // STATE LinkDotState* m_statep; // State, including dotted symbol table LinkDotCellVertex* m_cellVxp; // Vertex for current module int debug() { return LinkDotState::debug(); } // METHODS // VISITs virtual void visit(AstNodeModule* nodep, AstNUser*) { UINFO(8," "<existsModScope(nodep)) { UINFO(5,"Dead module for "<findModScope(nodep); } nodep->iterateChildren(*this); m_cellVxp = NULL; } virtual void visit(AstScope* nodep, AstNUser*) { UINFO(8," "<findScope(nodep); nodep->iterateChildren(*this); m_cellVxp = NULL; } virtual void visit(AstCellInline* nodep, AstNUser*) { if (m_statep->forScopeCreation()) { nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL; } } virtual void visit(AstVarXRef* nodep, AstNUser*) { // VarRef: Resolve its reference // We always link even if varp() is set, because the module we choose may change // due to creating new modules, flattening, etc. UINFO(8," "<varp(NULL); // Module that is not in hierarchy. We'll be dead code eliminating it later. } else { string baddot; LinkDotBaseVertex* okVxp; LinkDotBaseVertex* dotVxp = m_cellVxp; // Start search at current scope if (nodep->inlinedDots()!="") { // Correct for current scope string inl = AstNode::dedotName(nodep->inlinedDots()); dotVxp = m_statep->findDotted(dotVxp, inl, baddot, okVxp); if (!dotVxp) nodep->v3fatalSrc("Couldn't resolve inlined scope '"<inlinedDots()); } dotVxp = m_statep->findDotted(dotVxp, nodep->dotted(), baddot, okVxp); // Maybe NULL if (!m_statep->forScopeCreation()) { AstVar* varp = (m_statep->findSym(dotVxp, nodep->name(), baddot) ->castVar()); // maybe NULL nodep->varp(varp); UINFO(7," Resolved "<varp()) { nodep->v3error("Can't find definition of '"<dotted()+"."+nodep->prettyName()); okVxp->errorScopes(nodep); } } else { string baddot; AstVarScope* vscp = (m_statep->findSym(dotVxp, nodep->name(), baddot) ->castVarScope()); // maybe NULL if (!vscp) { nodep->v3error("Can't find varpin scope of '"<dotted()+"."+nodep->prettyName()); okVxp->errorScopes(nodep); } else { while (vscp->user2p()) { // If V3Inline aliased it, pick up the new signal UINFO(7," Resolved pre-alias "<user2p()->castNode()->castVarScope(); } // Convert the VarXRef to a VarRef, so we don't need later optimizations to deal with VarXRef. nodep->varp(vscp->varp()); nodep->varScopep(vscp); UINFO(7," Resolved "<fileline(), vscp, nodep->lvalue()); nodep->replaceWith(newvscp); nodep->deleteTree(); nodep=NULL; } } } } virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { UINFO(8," "<packagep()) { // References into packages don't care about cell hierarchy. } else if (!m_cellVxp) { UINFO(9,"Dead module for "<taskp(NULL); // Module that is not in hierarchy. We'll be dead code eliminating it later. } else if (nodep->dotted()=="" && nodep->taskp()) { // V3Link should have setup the links // Might be under a BEGIN we're not processing, so don't relink it } else { string baddot; LinkDotBaseVertex* okVxp; LinkDotBaseVertex* dotVxp = m_cellVxp; // Start search at current scope if (nodep->inlinedDots()!="") { // Correct for current scope string inl = AstNode::dedotName(nodep->inlinedDots()); UINFO(8,"\t\tInlined "<findDotted(dotVxp, inl, baddot, okVxp); if (!dotVxp) { okVxp->errorScopes(nodep); nodep->v3fatalSrc("Couldn't resolve inlined scope '"<inlinedDots()); } } dotVxp = m_statep->findDotted(dotVxp, nodep->dotted(), baddot, okVxp); // Maybe NULL AstNodeFTask* taskp = (m_statep->findSym(dotVxp, nodep->name(), baddot) ->castNodeFTask()); // maybe NULL nodep->taskp(taskp); UINFO(7," Resolved "<taskp()) { nodep->v3error("Can't find definition of '"<dotted()+"."+nodep->prettyName()); okVxp->errorScopes(nodep); } } nodep->iterateChildren(*this); } virtual void visit(AstNode* nodep, AstNUser*) { // Default: Just iterate nodep->iterateChildren(*this); } public: // CONSTUCTORS LinkDotResolveVisitor(AstNetlist* rootp, LinkDotState* statep) { UINFO(4,__FUNCTION__<<": "<accept(*this); } virtual ~LinkDotResolveVisitor() {} }; //###################################################################### // Link class functions void V3LinkDot::linkDotGuts(AstNetlist* rootp, bool prearray, bool scoped) { UINFO(2,__FUNCTION__<<": "<=5) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot.tree")); LinkDotState state (prearray,scoped); LinkDotFindVisitor visitor(rootp,&state); if (scoped) { // Well after the initial link when we're ready to operate on the flat design, // process AstScope's. This needs to be separate pass after whole hierarchy graph created. LinkDotScopeVisitor visitors(rootp,&state); } state.dump(); LinkDotResolveVisitor visitorb(rootp,&state); }