//************************************************************************* // 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. // //************************************************************************* // NO EDITS: Don't replace or delete nodes, as the parser symbol table // has pointers into the ast tree. // // LINK TRANSFORMATIONS: // Top-down traversal // Cells: // Read module if needed // Link to module that instantiates it //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include #include #include #include #include #include #include "V3Global.h" #include "V3LinkCells.h" #include "V3SymTable.h" #include "V3Parse.h" #include "V3Ast.h" #include "V3Graph.h" //###################################################################### // Graph subclasses class LinkCellsGraph : public V3Graph { public: LinkCellsGraph() {} virtual ~LinkCellsGraph() {} virtual void loopsMessageCb(V3GraphVertex* vertexp); }; class LinkCellsVertex : public V3GraphVertex { AstNodeModule* m_modp; public: LinkCellsVertex(V3Graph* graphp, AstNodeModule* modp) : V3GraphVertex(graphp), m_modp(modp) {} virtual ~LinkCellsVertex() {} AstNodeModule* modp() const { return m_modp; } virtual string name() const { return modp()->name(); } }; class LibraryVertex : public V3GraphVertex { public: LibraryVertex(V3Graph* graphp) : V3GraphVertex(graphp) {} virtual ~LibraryVertex() {} virtual string name() const { return "*LIBRARY*"; } }; void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp) { if (LinkCellsVertex* vvertexp = dynamic_cast(vertexp)) { vvertexp->modp()->v3error("Recursive module (module instantiates itself): " <modp()->name()); V3Error::abortIfErrors(); } else { // Everything should match above, but... v3fatalSrc("Recursive instantiations"); } } //###################################################################### // Link state, as a visitor of each AstNode class LinkCellsVisitor : public AstNVisitor { private: // NODE STATE // Entire netlist: // AstNodeModule::user1p() // V3GraphVertex* Vertex describing this module // Allocated across all readFiles in V3Global::readFiles: // AstNode::user4p() // V3SymTable* Package and typedef symbol names AstUser1InUse m_inuser1; // STATE V3InFilter* m_filterp; // Parser filter // Below state needs to be preserved between each module call. AstNodeModule* m_modp; // Current module V3SymTable m_mods; // Symbol table of all module names LinkCellsGraph m_graph; // Linked graph of all cell interconnects LibraryVertex* m_libVertexp; // Vertex at root of all libraries V3GraphVertex* m_topVertexp; // Vertex of top module static int debug() { static int level = -1; if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); return level; } // METHODS V3GraphVertex* vertex(AstNodeModule* nodep) { // Return corresponding vertex for this module if (!nodep->user1p()) { nodep->user1p(new LinkCellsVertex(&m_graph, nodep)); } return (nodep->user1p()->castGraphVertex()); } // VISITs virtual void visit(AstNetlist* nodep, AstNUser*) { AstNode::user1ClearTree(); readModNames(); nodep->iterateChildren(*this); // Find levels in graph m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); m_graph.dumpDotFilePrefixed("linkcells"); m_graph.rank(); for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { if (LinkCellsVertex* vvertexp = dynamic_cast(itp)) { // +1 so we leave level 1 for the new wrapper we'll make in a moment AstNodeModule* modp = vvertexp->modp(); modp->level(vvertexp->rank()+1); if (vvertexp == m_topVertexp && modp->level() != 2) { v3error("Specified --top-module '"<name()); if (topMatch) { m_topVertexp = vertex(nodep); UINFO(2,"Link --top-module: "<inLibrary(false); // Safer to make sure it doesn't disappear } if (v3Global.opt.topModule()=="" ? nodep->inLibrary() // Library cells are lower : !topMatch) { // Any non-specified module is lower // Put under a fake vertex so that the graph ranking won't indicate // this is a top level module if (!m_libVertexp) m_libVertexp = new LibraryVertex(&m_graph); new V3GraphEdge(&m_graph, m_libVertexp, vertex(nodep), 1, false); } nodep->iterateChildren(*this); nodep->checkTree(); m_modp = NULL; } virtual void visit(AstCell* nodep, AstNUser*) { // Cell: Resolve its filename. If necessary, parse it. if (!nodep->modp()) { UINFO(4,"Link Cell: "<modName())->castNodeModule(); if (!modp) { // Read-subfile V3Parse parser (v3Global.rootp(), m_filterp); parser.parseFile(nodep->fileline(), nodep->modName(), false); V3Error::abortIfErrors(); // We've read new modules, grab new pointers to their names readModNames(); // Check again modp = m_mods.findIdUpward(nodep->modName())->castNodeModule(); if (!modp) { nodep->v3error("Can't resolve module reference: "<modName()); } } if (modp) { nodep->modp(modp); // Track module depths, so can sort list from parent down to children new V3GraphEdge(&m_graph, vertex(m_modp), vertex(modp), 1, false); } } // Convert .* to list of pins bool pinStar = false; for (AstPin* nextp, *pinp = nodep->pinsp(); pinp; pinp=nextp) { nextp = pinp->nextp()->castPin(); if (pinp->dotStar()) { if (pinStar) pinp->v3error("Duplicate .* in a cell"); pinStar = true; // Done with this fake pin pinp->unlinkFrBack()->deleteTree(); pinp=NULL; } } if (nodep->modp() && pinStar) { // Note what pins exist UINFO(9," CELL .* connect "<pinsp(); pinp; pinp=pinp->nextp()->castPin()) { if (pinp->name()=="") pinp->v3error("Connect by position is illegal in .* connected cells"); if (!ports.findIdFlat(pinp->name())) { ports.insert(pinp->name(), pinp); } } // We search ports, rather than in/out declarations as they aren't resolved yet, // and it's easier to do it now than in V3Link when we'd need to repeat steps. for (AstNode* portnodep = nodep->modp()->stmtsp(); portnodep; portnodep=portnodep->nextp()) { if (AstPort* portp = portnodep->castPort()) { if (!ports.findIdFlat(portp->name())) { UINFO(9," need PORT "<fileline(),0,portp->name(), new AstVarRef(nodep->fileline(),portp->name(),false)); newp->svImplicit(true); nodep->addPinsp(newp); } } } } // Convert unnamed pins to pin number based assignments for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) { if (pinp->name()=="") pinp->name("__pinNumber"+cvtToStr(pinp->pinNum())); } for (AstPin* pinp = nodep->paramsp(); pinp; pinp=pinp->nextp()->castPin()) { if (pinp->name()=="") pinp->name("__paramNumber"+cvtToStr(pinp->pinNum())); } if (nodep->modp()) { nodep->iterateChildren(*this); } } // Accelerate the recursion // Must do statements to support Generates, math though... virtual void visit(AstNodeMath* nodep, AstNUser*) {} virtual void visit(AstNode* nodep, AstNUser*) { // Default: Just iterate nodep->iterateChildren(*this); } // METHODS void readModNames() { // Look at all modules, and store pointers to all module names for (AstNodeModule* nextp,* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nextp) { nextp = nodep->nextp()->castNodeModule(); AstNode* foundp = m_mods.findIdUpward(nodep->name()); if (foundp && foundp != nodep) { if (!(foundp->fileline()->warnIsOff(V3ErrorCode::MODDUP) || nodep->fileline()->warnIsOff(V3ErrorCode::MODDUP))) { nodep->v3warn(MODDUP,"Duplicate declaration of module: "<prettyName()); foundp->v3warn(MODDUP,"... Location of original declaration"); } nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL; } else if (!foundp) { m_mods.insert(nodep->name(), nodep); } } //if (debug()>=9) m_mods.dump(cout, "-syms: "); } public: // CONSTUCTORS LinkCellsVisitor(AstNetlist* rootp, V3InFilter* filterp) { m_filterp = filterp; m_modp = NULL; m_libVertexp = NULL; m_topVertexp = NULL; rootp->accept(*this); } virtual ~LinkCellsVisitor() {} }; //###################################################################### // Link class functions void V3LinkCells::link(AstNetlist* rootp, V3InFilter* filterp) { UINFO(4,__FUNCTION__<<": "<