// $Id$ //************************************************************************* // DESCRIPTION: Verilator: Break always into separate statements to reduce temps // // Code available from: http://www.veripool.com/verilator // // AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli // //************************************************************************* // // Copyright 2003-2007 by Wilson Snyder. This program is free software; you can // redistribute it and/or modify it under the terms of either the GNU // General Public License or the Perl Artistic License. // // 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. // //************************************************************************* // V3Split's Transformations: // // Note this can be called multiple times. // ALWAYS // ASSIGN ({var} <= {cons}) // Record as generating var_DLY (independent of use of var), consumers // ASSIGN ({var} = {cons} // Record generator and consumer // Any var that is only consumed can be ignored. // Then we split into a separate ALWAYS block for each top level statement. // // Furthermore, optionally // NODEASSIGN/NODEIF/WHILE // S1: ASSIGN {v1} <= 0. // Duplicate of below // S2: ASSIGN {v1} <= {v0} // S3: IF (..., // X1: ASSIGN {v2} <= {v1} // X2: ASSIGN {v3} <= {v2} // We'd like to swap S2 and S3, and X1 and X2. // // Create a graph in split assignment order. // v3 -breakable-> v3Dly --> X2 --> v2 -brk-> v2Dly -> X1 -> v1 // Likewise on each "upper" statement vertex // v3Dly & v2Dly -> S3 -> v1 & v2 // v1 -brk-> v1Dly -> S2 -> v0 // v1Dly -> S1 -> {empty} // Multiple assignments to the same variable must remain in order // // Also vars must not be "public" and we also scoreboard nodep->isSplittable() // //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include #include #include #include #include #include #include "V3Global.h" #include "V3Split.h" #include "V3Stats.h" #include "V3Ast.h" #include "V3Graph.h" //###################################################################### // Support classes class SplitPliVertex : public V3GraphVertex { public: SplitPliVertex(V3Graph* graphp) : V3GraphVertex(graphp) {} virtual ~SplitPliVertex() {} virtual string name() const { return "*PLI*"; } virtual string dotColor() const { return "green"; } }; class SplitNodeVertex : public V3GraphVertex { AstNode* m_nodep; protected: SplitNodeVertex(V3Graph* graphp, AstNode* nodep) : V3GraphVertex(graphp), m_nodep(nodep) {} virtual ~SplitNodeVertex() {} // Accessors // Do not make accessor for nodep(), It may change due to // reordering a lower block, but we don't repair it virtual string name() const { if (m_nodep->name() == "") { return cvtToStr((void*)m_nodep); } else { return m_nodep->name(); } } }; class SplitLogicVertex : public SplitNodeVertex { uint32_t m_splitColor; // Copied from color() when determined public: SplitLogicVertex(V3Graph* graphp, AstNode* nodep) : SplitNodeVertex(graphp,nodep) {} void splitColor(uint32_t flag) { m_splitColor=flag; } uint32_t splitColor() const { return m_splitColor; } virtual ~SplitLogicVertex() {} virtual string dotColor() const { return "yellow"; } }; class SplitVarStdVertex : public SplitNodeVertex { public: SplitVarStdVertex(V3Graph* graphp, AstNode* nodep) : SplitNodeVertex(graphp,nodep) {} virtual ~SplitVarStdVertex() {} virtual string dotColor() const { return "skyblue"; } }; class SplitVarPostVertex : public SplitNodeVertex { public: SplitVarPostVertex(V3Graph* graphp, AstNode* nodep) : SplitNodeVertex(graphp,nodep) {} virtual ~SplitVarPostVertex() {} virtual string name() const { return (string)"POST "+SplitNodeVertex::name(); } virtual string dotColor() const { return "CadetBlue"; } }; //###################################################################### // Edge types class SplitEdge : public V3GraphEdge { uint32_t m_ignoreInStep; // Step number that if set to, causes this edge to be ignored static uint32_t s_stepNum; // Global step number protected: enum { WEIGHT_NORMAL=10 }; SplitEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight, bool cutable=CUTABLE) : V3GraphEdge(graphp, fromp, top, weight, cutable) ,m_ignoreInStep(0) {} virtual ~SplitEdge() {} public: // Iterator for graph functions static void incrementStep() { ++s_stepNum; } bool ignoreThisStep() const { return m_ignoreInStep == s_stepNum; } void setIgnoreThisStep() { m_ignoreInStep = s_stepNum; } virtual bool followScoreboard() const = 0; static bool followScoreboard(const V3GraphEdge* edgep) { const SplitEdge* oedgep = dynamic_cast(edgep); if (!oedgep) v3fatalSrc("Following edge of non-SplitEdge type"); if (oedgep->ignoreThisStep()) return false; return oedgep->followScoreboard(); } static bool followCyclic(const V3GraphEdge* edgep) { const SplitEdge* oedgep = dynamic_cast(edgep); if (!oedgep) v3fatalSrc("Following edge of non-SplitEdge type"); if (oedgep->ignoreThisStep()) return false; return true; } }; uint32_t SplitEdge::s_stepNum = 0; class SplitPostEdge : public SplitEdge { public: SplitPostEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL) {} virtual ~SplitPostEdge() {} virtual bool followScoreboard() const { return false; } virtual string dotColor() const { return "khaki"; } virtual string dotStyle() const { return ignoreThisStep()?"dotted":V3GraphEdge::dotStyle(); } }; class SplitLVEdge : public SplitEdge { public: SplitLVEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL) {} virtual ~SplitLVEdge() {} virtual bool followScoreboard() const { return true; } virtual string dotColor() const { return "yellowGreen"; } virtual string dotStyle() const { return ignoreThisStep()?"dotted":V3GraphEdge::dotStyle(); } }; class SplitRVEdge : public SplitEdge { public: SplitRVEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL) {} virtual ~SplitRVEdge() {} virtual bool followScoreboard() const { return true; } virtual string dotColor() const { return "green"; } virtual string dotStyle() const { return ignoreThisStep()?"dotted":V3GraphEdge::dotStyle(); } }; struct SplitScorebdEdge : public SplitEdge { public: SplitScorebdEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL) {} virtual ~SplitScorebdEdge() {} virtual bool followScoreboard() const { return true; } virtual string dotColor() const { return "blue"; } virtual string dotStyle() const { return ignoreThisStep()?"dotted":V3GraphEdge::dotStyle(); } }; struct SplitStrictEdge : public SplitEdge { // A strict order, based on the original statement order in the graph // The only non-cutable edge type public: SplitStrictEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL, NOT_CUTABLE) {} virtual ~SplitStrictEdge() {} virtual bool followScoreboard() const { return true; } virtual string dotColor() const { return "blue"; } virtual string dotStyle() const { return ignoreThisStep()?"dotted":V3GraphEdge::dotStyle(); } }; //###################################################################### // Split class functions class SplitVisitor : public AstNVisitor { private: // NODE STATE // AstVarScope::userp -> Var SplitNodeVertex* for usage var, 0=not set yet // AstVarScope::user2p -> Var SplitNodeVertex* for delayed assignment var, 0=not set yet // Ast*::user3p -> Statement SplitLogicVertex* (temporary only) // Ast*::user4 -> Current ordering number (reorderBlock usage) // TYPES typedef vector VStack; // STATE bool m_reorder; // Reorder statements vs. just splitting VStack m_stmtStackps; // Current statements being tracked SplitPliVertex* m_pliVertexp; // Element specifying PLI ordering V3Graph m_graph; // Scoreboard of var usages/dependencies bool m_inDly; // Inside ASSIGNDLY uint32_t m_stepNum; // Step number we need to ignore a edge in V3Double0 m_statSplits; // Statistic tracking //int debug() { return 9; } // METHODS void scoreboardClear() { //VV***** We reset userp() and user2p on each block!!! m_inDly = false; m_graph.clear(); m_stmtStackps.clear(); m_pliVertexp = NULL; AstNode::userClearTree(); AstNode::user2ClearTree(); AstNode::user3ClearTree(); AstNode::user4ClearTree(); } void scoreboardPli() { // Order all PLI statements with other PLI statements // This insures $display's and such remain in proper order // We don't prevent splitting out other non-pli statements, however. if (!m_pliVertexp) { m_pliVertexp = new SplitPliVertex(&m_graph); // m_graph.clear() will delete it } for (VStack::iterator it = m_stmtStackps.begin(); it != m_stmtStackps.end(); ++it) { // Both ways... new SplitScorebdEdge(&m_graph, *it, m_pliVertexp); new SplitScorebdEdge(&m_graph, m_pliVertexp, *it); } } void scoreboardPushStmt(AstNode* nodep) { //UINFO(9," push "<user3p()) nodep->v3fatalSrc("user3p should not be used; cleared in processBlock\n"); nodep->user3p(vertexp); } void scoreboardPopStmt() { //UINFO(9," pop"<nextp()) { scoreboardPushStmt(nextp); nextp->accept(*this); scoreboardPopStmt(); } } void cleanupBlockGraph(AstNode* nodep) { // Transform the graph into what we need UINFO(5, "ReorderBlock "<=9) { m_graph.dumpDotFilePrefixed("splitg_nodup", false); //m_graph.dump(); cout<nextp()) { SplitLogicVertex* vvertexp = (SplitLogicVertex*)nextp->user3p(); vvertexp->user(true); } // If a var vertex has only inputs, it's a input-only node, // and can be ignored for coloring **this block only** SplitEdge::incrementStep(); uint32_t numVertexes = 1; // As colors start at 1, not 0 for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { numVertexes++; if (!vertexp->outBeginp() && dynamic_cast(vertexp)) { for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { SplitEdge* oedgep = dynamic_cast(edgep); oedgep->setIgnoreThisStep(); } } // Mark all logic vertexes not involved with this step as unimportant if (SplitLogicVertex* vvertexp = dynamic_cast(vertexp)) { if (!vvertexp->user()) { for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { SplitEdge* oedgep = dynamic_cast(edgep); oedgep->setIgnoreThisStep(); } for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { SplitEdge* oedgep = dynamic_cast(edgep); oedgep->setIgnoreThisStep(); } } } } // Weak coloring to determine what needs to remain in order m_graph.weaklyConnected(&SplitEdge::followScoreboard); // Add hard orderings between all nodes of same color, in the order they appeared vector lastOfColor; lastOfColor.resize(numVertexes); for (uint32_t i=0; inextp()) { SplitLogicVertex* vvertexp = (SplitLogicVertex*)nextp->user3p(); vvertexp->splitColor(vvertexp->color()); uint32_t color = vvertexp->splitColor(); if (color >= numVertexes) nextp->v3fatalSrc("More colors than vertexes!\n"); if (!color) nextp->v3fatalSrc("No node color assigned\n"); if (lastOfColor[color]) { new SplitStrictEdge(&m_graph, lastOfColor[color], vvertexp); } lastOfColor[color] = vvertexp; } // And a real ordering to get the statements into something reasonable // We don't care if there's cutable violations here... // Non-cutable violations should be impossible; as those edges are program-order if (debug()>=9) m_graph.dumpDotFilePrefixed((string)"splitg_preo", false); m_graph.acyclic(&SplitEdge::followCyclic); m_graph.rank(&SplitEdge::followCyclic); // Or order(), but that's more expensive if (debug()>=9) m_graph.dumpDotFilePrefixed((string)"splitg_opt", false); } void reorderBlock(AstNode* nodep) { // Reorder statements in the completed graph AstAlways* splitAlwaysp = nodep->backp()->castAlways(); // Map the rank numbers into nodes they associate with typedef multimap RankNodeMap; typedef map ColorRankMap; ColorRankMap colorRankMap; uint32_t firstColor = 0; bool multiColors = false; int currOrder = 0; // Existing sequence number of assignment for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) { SplitLogicVertex* vvertexp = (SplitLogicVertex*)nextp->user3p(); if (!splitAlwaysp) vvertexp->splitColor(1); // All blocks remain as-is RankNodeMap& rankMap = colorRankMap[vvertexp->splitColor()]; rankMap.insert(make_pair(vvertexp->rank(), nextp)); if (firstColor && firstColor != vvertexp->splitColor()) multiColors = true; firstColor = vvertexp->splitColor(); nextp->user4(++currOrder); // Record current ordering } // If there was only one color, we don't need multiple always blocks if (!multiColors) splitAlwaysp = NULL; // Is the current ordering OK? bool leaveAlone=true; if (splitAlwaysp) leaveAlone=false; int newOrder = 0; // New sequence number of assignment for (ColorRankMap::iterator colorIt = colorRankMap.begin(); colorIt != colorRankMap.end(); ++colorIt) { RankNodeMap& rankMap = colorIt->second; for (RankNodeMap::iterator it = rankMap.begin(); it != rankMap.end(); ++it) { AstNode* nextp = it->second; if (++newOrder != nextp->user4()) leaveAlone=false; } } if (leaveAlone) { UINFO(6," No changes\n"); } else { AstNRelinker replaceHandle; // Where to add the list AstNode* addAfterp = splitAlwaysp; for (ColorRankMap::iterator colorIt = colorRankMap.begin(); colorIt != colorRankMap.end(); ++colorIt) { uint32_t color = colorIt->first; RankNodeMap& rankMap = colorIt->second; AstNode* newListp = NULL; for (RankNodeMap::iterator it = rankMap.begin(); it != rankMap.end(); ++it) { AstNode* nextp = it->second; UINFO(6, " Color="<unlinkFrBack(&replaceHandle); else nextp->unlinkFrBack(); newListp = newListp->addNext(nextp); } if (splitAlwaysp) { m_statSplits++; AstAlways* alwaysp = new AstAlways(newListp->fileline(), NULL, NULL); addAfterp->addNextHere(alwaysp); addAfterp=alwaysp; alwaysp->addStmtp(newListp); } else { // Just reordering replaceHandle.relink(newListp); } } if (splitAlwaysp) { pushDeletep(splitAlwaysp->unlinkFrBack()); } } // leaveAlone } void processBlock(AstNode* nodep) { if (!nodep) return; // Empty lists are ignorable // Pass the first node in a list of block items, we'll process them // Check there's >= 2 sub statements, else nothing to analyze // Save recursion state AstNode* firstp = nodep; // We may reorder, and nodep is no longer first. void* oldBlockUser3 = nodep->user3p(); // May be overloaded in below loop, save it nodep->user3p(NULL); if (!nodep->firstAbovep()) nodep->v3fatalSrc("Node passed is in next list; should have processed all list at once"); // Process it if (!nodep->nextp()) { // Just one, so can't reorder. Just look for more blocks/statements. nodep->accept(*this); } else { UINFO(9," processBlock "<backp()->nextp()==firstp) firstp = firstp->backp(); // Walk back to first in list for (AstNode* nextp=firstp; nextp; nextp=nextp->nextp()) { SplitLogicVertex* vvertexp = (SplitLogicVertex*)nextp->user3p(); vvertexp->unlinkDelete(&m_graph); } } // Again, nodep may no longer be first. firstp->user3p(oldBlockUser3); } // VISITORS virtual void visit(AstAlways* nodep, AstNUser*) { UINFO(4," ALW "<=9) nodep->dumpTree(cout," alwIn:: "); scoreboardClear(); processBlock(nodep->bodysp()); if (debug()>=9) nodep->dumpTree(cout," alwOut: "); } virtual void visit(AstNodeIf* nodep, AstNUser*) { if (!m_reorder) { nodep->iterateChildren(*this); } else { UINFO(4," IF "<condp()->iterateAndNext(*this); processBlock(nodep->ifsp()); processBlock(nodep->elsesp()); } } // We don't do AstNodeFor/AstWhile loops, due to the standard question // of what is before vs. after virtual void visit(AstAssignDly* nodep, AstNUser*) { m_inDly = true; UINFO(4," ASSIGNDLY "<iterateChildren(*this); m_inDly = false; } virtual void visit(AstVarRef* nodep, AstNUser*) { if (!m_stmtStackps.empty()) { AstVarScope* vscp = nodep->varScopep(); if (!vscp) nodep->v3fatalSrc("Not linked"); if (!nodep->varp()->isConst()) { // Constant lookups can be ignored if (nodep->varp()->isSigPublic()) { // Public signals shouldn't be changed, pli code might be messing with them scoreboardPli(); } // Create vertexes for variable if (!vscp->userp()) { SplitVarStdVertex* vstdp = new SplitVarStdVertex(&m_graph, vscp); vscp->userp(vstdp); } SplitVarStdVertex* vstdp = (SplitVarStdVertex*) vscp->userp(); // SPEEDUP: We add duplicate edges, that should be fixed if (m_inDly && nodep->lvalue()) { UINFO(4," VARREFDLY: "<user2p()) { SplitVarPostVertex* vpostp = new SplitVarPostVertex(&m_graph, vscp); vscp->user2p(vpostp); new SplitPostEdge(&m_graph, vstdp, vpostp); } SplitVarPostVertex* vpostp = (SplitVarPostVertex*)vscp->user2p(); // Add edges for (VStack::iterator it = m_stmtStackps.begin(); it != m_stmtStackps.end(); ++it) { new SplitLVEdge(&m_graph, vpostp, *it); } } else { // Nondelayed assignment if (nodep->lvalue()) { // Non-delay; need to maintain existing ordering with all consumers of the signal UINFO(4," VARREFLV: "<isSplittable()) { UINFO(9," NotSplittable "<iterateChildren(*this); } public: // CONSTUCTORS SplitVisitor(AstNode* nodep, bool reorder) : m_reorder(reorder) { scoreboardClear(); nodep->accept(*this); } virtual ~SplitVisitor() { V3Stats::addStat("Optimizations, Split always", m_statSplits); } }; //###################################################################### // Split class functions void V3Split::splitReorderAll(AstNetlist* nodep) { UINFO(2,__FUNCTION__<<": "<