// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Break always into sensitivity active domains // // Code available from: http://www.veripool.org/verilator // //************************************************************************* // // Copyright 2008-2018 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. // //************************************************************************* // V3ClkGater's Transformations: // // Look for clock gaters // Note there's overlap between this process and V3Split's // ALWAYS: Build graph // IF: Form vertex pointing to any IFs or ALWAYS above // VARREF: Form vertex pointing to IFs it is under // ASSIGN: If under normal assignment, disable optimization // FUTURE OPTIMIZE: If signal is set to itself, consider that OK for gating. // !splitable: Mark all VARREFs in this statement as comming from it // Optimize graph so if signal is referenced under multiple IF branches it moves up // Make ALWAYS for each new gating term, and move statements there //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include #include #include #include #include #include #include "V3Global.h" #include "V3Ast.h" #include "V3Stats.h" #include "V3Graph.h" #include "V3ClkGater.h" //###################################################################### // Base for debug class GaterBaseVisitor : public AstNVisitor { protected: VL_DEBUG_FUNC; // Declare debug() }; //###################################################################### // Support classes class GaterVarVertex; class GaterVertex : public V3GraphVertex { static uint32_t s_rankNum; public: explicit GaterVertex(V3Graph* graphp) : V3GraphVertex(graphp) { s_rankNum++; rank(s_rankNum); } virtual ~GaterVertex() {} virtual int typeNum() const = 0; static void clearRank() { s_rankNum=0; } virtual int sortCmp(const V3GraphVertex* rhsp) const; }; uint32_t GaterVertex::s_rankNum = 0; class GaterHeadVertex : public GaterVertex { public: explicit GaterHeadVertex(V3Graph* graphp) : GaterVertex(graphp) {} virtual ~GaterHeadVertex() {} virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent virtual string name() const { return "*HEAD*"; } virtual string dotColor() const { return "green"; } }; class GaterPliVertex : public GaterVertex { public: explicit GaterPliVertex(V3Graph* graphp) : GaterVertex(graphp) {} virtual ~GaterPliVertex() {} virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent virtual string name() const { return "*PLI*"; } virtual string dotColor() const { return "red"; } }; class GaterIfVertex : public GaterVertex { AstNodeIf* m_nodep; public: AstNodeIf* nodep() const { return m_nodep; } GaterIfVertex(V3Graph* graphp, AstNodeIf* nodep) : GaterVertex(graphp), m_nodep(nodep) { } virtual ~GaterIfVertex() {} virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent virtual string name() const { return cvtToStr((void*)m_nodep)+" {"+cvtToStr(m_nodep->fileline()->lineno())+"}"; } }; class GaterVarVertex : public GaterVertex { AstVarScope* m_nodep; public: AstVarScope* nodep() const { return m_nodep; } GaterVarVertex(V3Graph* graphp, AstVarScope* nodep) : GaterVertex(graphp), m_nodep(nodep) { } virtual ~GaterVarVertex() {} virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent virtual string name() const { return nodep()->name(); } virtual string dotColor() const { return "skyblue"; } }; //###################################################################### // Edge types class GaterEdge : public V3GraphEdge { uint32_t m_ifelse; // True branch of if public: // These are used as shift amounts into node's user() #define VU_DEFINE enum VarUsage { VU_NONE=0, VU_IF=1, VU_ELSE=2, VU_PLI=4, VU_MADE=8} VU_DEFINE; uint32_t ifelse() const { return m_ifelse; } bool ifelseTrue() const { return m_ifelse & VU_IF; } bool ifelseFalse() const { return m_ifelse & VU_ELSE; } bool ifelseBoth() const { return ifelseTrue() && ifelseFalse(); } GaterEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, uint32_t ifelse) : V3GraphEdge(graphp, fromp, top, 10, false), m_ifelse(ifelse) {} virtual ~GaterEdge() {} virtual string dotColor() const { return ((ifelse() & VU_PLI) ? "red" : (ifelseBoth() ? "blue" : (ifelseTrue() ? "green" : "darkgreen"))); } virtual int sortCmp(const V3GraphEdge* rEdgep) const { // Our master sort sorts by edges first, so we don't want to // consider the upstream vertex::sortCmp when sorting by edges. // We do want to order by something though; rank works if (fromp()->rank() < rEdgep->fromp()->rank()) return -1; if (fromp()->rank() > rEdgep->fromp()->rank()) return 1; // If same, resolve by ifelse const GaterEdge* crEdgep = static_cast(rEdgep); if (m_ifelse < crEdgep->m_ifelse) return -1; if (m_ifelse > crEdgep->m_ifelse) return 1; return 0; } }; int GaterVertex::sortCmp(const V3GraphVertex* rhsp) const { const GaterVertex* crhsp = static_cast(rhsp); // We really only care about ordering Var's together, but... // First put same type together if (typeNum() < crhsp->typeNum()) return -1; if (typeNum() > crhsp->typeNum()) return 1; // If variable, group by same input fanin // (know they're the same type based on above compare) if (dynamic_cast(this)) { // We've already sorted by edges, so just see if same tree // If this gets too slow, we could compute a hash up front V3GraphEdge* lEdgep = this->inBeginp(); V3GraphEdge* rEdgep = rhsp->inBeginp(); while (lEdgep && rEdgep) { const GaterEdge* clEdgep = static_cast(lEdgep); const GaterEdge* crEdgep = static_cast(rEdgep); if (lEdgep->fromp()->rank() < rEdgep->fromp()->rank()) return -1; if (lEdgep->fromp()->rank() > rEdgep->fromp()->rank()) return 1; if (clEdgep->ifelse() < crEdgep->ifelse()) return -1; if (clEdgep->ifelse() > crEdgep->ifelse()) return 1; lEdgep = lEdgep->inNextp(); rEdgep = rEdgep->inNextp(); } if (!lEdgep && !rEdgep) return 0; return lEdgep ? -1 : 1; } // Finally by rank of this vertex if (rank() < rhsp->rank()) return -1; if (rank() > rhsp->rank()) return 1; return 0; } //###################################################################### // Check for non-simple gating equations class GaterCondVisitor : public GaterBaseVisitor { private: // RETURN STATE bool m_isSimple; // Set false when we know it isn't simple // METHODS inline void okIterate(AstNode* nodep) { if (m_isSimple) iterateChildren(nodep); } // VISITORS virtual void visit(AstOr* nodep) { okIterate(nodep); } virtual void visit(AstAnd* nodep) { okIterate(nodep); } virtual void visit(AstNot* nodep) { okIterate(nodep); } virtual void visit(AstLogOr* nodep) { okIterate(nodep); } virtual void visit(AstLogAnd* nodep) { okIterate(nodep); } virtual void visit(AstLogNot* nodep) { okIterate(nodep); } virtual void visit(AstVarRef* nodep) { okIterate(nodep); } // Other possibilities are equals, etc // But, we don't want to get too complicated or it will take too much // effort to calculate the gater virtual void visit(AstNode* nodep) { m_isSimple = false; //iterateChildren(nodep); } public: // CONSTUCTORS explicit GaterCondVisitor(AstNode* nodep) { m_isSimple = true; iterate(nodep); } virtual ~GaterCondVisitor() {} // PUBLIC METHODS bool isSimple() const { return m_isSimple; } }; //###################################################################### // Check for non-simple gating equations class GaterBodyVisitor : public GaterBaseVisitor { // NODE STATE // Input state // AstVarScope::user2p() -> AstAlways* moving this variable to enum State { // This is used as a bitmask STATE_UNKNOWN=0, STATE_KEEP=1, // Seen a variable we need, keep this statement STATE_DELETE=2 // Seen a variable we need, delete this statement // 3=keep & delete }; bool m_original; // Deleting original statements, vs deleting new var statements AstNode* m_exprp; // New gater expression we are building bool m_cloning; // Clone this object 0=not sure yet, 1=do uint32_t m_state; // Parsing state // VISITORS virtual void visit(AstVarRef* nodep) { if (nodep->lvalue()) { AstVarScope* vscp = nodep->varScopep(); if (vscp->user2p() == m_exprp) { // This variable's block needs to move to the new always if (m_original) { UINFO(9," VARREF delete in old: "<unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); // Pass upwards we did delete m_state |= STATE_DELETE; } else { // Pass upwards we must keep m_state |= STATE_KEEP; } } virtual void visit(AstNode* nodep) { iterateChildren(nodep); } public: // CONSTUCTORS GaterBodyVisitor(AstAlways* nodep, AstNode* exprp, bool original) { m_exprp = exprp; m_original = original; m_state = STATE_UNKNOWN; m_cloning = false; if (debug()>=9) nodep->dumpTree(cout," GateBodyIn: "); iterateAndNextNull(nodep->bodysp()); if (debug()>=9) nodep->dumpTree(cout," GateBodyOut: "); // If there's no statements we shouldn't have had a resulting graph // vertex asking for this creation } virtual ~GaterBodyVisitor() {} }; //###################################################################### // Create clock gaters class GaterVisitor : public GaterBaseVisitor { // NODE STATE // Cleared on Always // AstVarScope::user1p() -> GaterVarVertex* // AstAlways::user4() -> bool. True indicates processed // Cleared on each new Always // AstVarScope::user2p() -> AstAlways* moving this variable to AstUser1InUse m_inuser1; AstUser2InUse m_inuser2; AstUser4InUse m_inuser4; // GRAPH STATE // Cleared on simplify // Vertex::user() -> VarUsage: Mark of which if/else edges are hit // TYPES VU_DEFINE; enum MiscConsts { IF_DEPTH_MAX = 4, // IFs deep we bother to analyze DOMAINS_MAX = 32 // Clock domains before avoiding O(N^2) blowup }; // MEMBERS string m_nonopt; // Reason block is not optimizable V3Double0 m_statGaters; // Statistic tracking V3Double0 m_statBits; // Statistic tracking bool m_directlyUnderAlw; // Immediately under Always or If int m_ifDepth; // Depth of IF statements int m_numIfs; // Number of IF statements V3Graph m_graph; // Scoreboard of var usages/dependencies GaterPliVertex* m_pliVertexp; // Element specifying PLI ordering GaterHeadVertex* m_headVertexp; // Top vertex V3GraphVertex* m_aboveVertexp; // Vertex above this point in tree uint32_t m_aboveTrue; // Vertex above this point is true branch AstVarScope* m_stmtVscp; // Current statement had variable assigned bool m_stmtInPli; // Current statement has PLI // METHODS void nonOptimizable(AstNode* nodep, const char* reasonp) { if (m_nonopt=="") { UINFO(9," Nonopt: "<user1p()); new GaterEdge(&m_graph, m_pliVertexp, varVtxp, VU_PLI); } m_stmtInPli = true; // Mark all followon variables too } // METHODS -- GRAPH stuff void simplifyGraph() { if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_init", false); // We now have all PLI variables with edges FROM the pli vertex simplifyPli(); if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_pli", false); // Any vertex that points along both true & false path to variable // can be simplfied so parent points to that vertex. Any vertex // that points to a (great...) grandparent of a variable can just // point to the edge. m_graph.userClearVertices(); // user() will contain VarUsage simplifyIfElseRecurse(m_headVertexp); if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_ifelse", false); m_graph.userClearVertices(); // user() will contain VarUsage simplifyGrandRecurse(m_headVertexp, 1); if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_grand", false); simplifyRemoveUngated(m_headVertexp); // Give all signals with same gating term same color graphColorSameFeeder(); if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_done", false); } void simplifyPli() { // For now, we'll not gate any logic with PLI lvariables in it. In // the future we may move PLI statements. One way to do so is to // all below pli VARs to go IfVertex -> PliVertex -> VarVertex then // they all must get colored the same. There may be a lot of // duplicated edges to the PLI; they'll need cleanup. All of the // later optimizations need to deal, and the GaterBodyVisitor needs // to know how to move them. if (m_pliVertexp) { // Follow PLI out edges to find all relevant variables for (V3GraphEdge* nextp,* edgep = m_pliVertexp->outBeginp(); edgep; edgep = nextp) { nextp = edgep->outNextp(); // We may edit the list if (GaterVarVertex* vVxp = dynamic_cast(edgep->top())) { vVxp->unlinkDelete(&m_graph); VL_DANGLING(vVxp); VL_DANGLING(edgep); } else { m_graph.dump(); v3fatalSrc("PLI vertex points to non-signal"); } } m_pliVertexp->unlinkDelete(&m_graph); m_pliVertexp = NULL; } } void simplifyIfElseRecurse(V3GraphVertex* vertexp) { // From bottom-up, propagate duplicate IF/ELSE branches to grandparent for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { simplifyIfElseRecurse(edgep->top()); } //UINFO(9,"IERecurse "<(vertexp)) { // Clear indications for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { edgep->top()->user(VU_NONE); } // Mark nodes on to/from side for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { V3GraphVertex* toVxp = edgep->top(); GaterEdge* cedgep = static_cast(edgep); // We may mark this twice in one pass - if so it's duplicated; no worries if (cedgep->ifelseTrue()) toVxp->user(toVxp->user() | VU_IF); if (cedgep->ifelseFalse()) toVxp->user(toVxp->user() | VU_ELSE); //UINFO(9," mark "<outBeginp(); edgep; edgep = nextp) { nextp = edgep->outNextp(); // We may edit the list V3GraphVertex* toVxp = edgep->top(); //UINFO(9," to "<user()<<" "<user() & VU_IF) && (toVxp->user() & VU_ELSE)) { edgep->unlinkDelete(); VL_DANGLING(edgep); if (!(toVxp->user() & VU_MADE)) { // Make an edge only once toVxp->user(toVxp->user() | VU_MADE); GaterEdge* inedgep = static_cast(vertexp->inBeginp()); V3GraphVertex* grandparent = inedgep->fromp(); new GaterEdge(&m_graph, grandparent, toVxp, inedgep->ifelse()); } } } } } void simplifyGrandRecurse(V3GraphVertex* vertexp, uint32_t depth) { // From top-down delete any vars that grandparents source // IE A -> B -> C -> VAR // \----------^ //UINFO(9,"GRecurse "<outBeginp(); edgep; edgep=nextp) { nextp = edgep->outNextp(); // We may edit the list if (GaterVarVertex* toVxp = dynamic_cast(edgep->top())) { if (toVxp->user() && toVxp->user() < depth) { // A recursion "above" us marked it, // Remove this edge, it's redundant with the upper edge edgep->unlinkDelete(); VL_DANGLING(edgep); } else { GaterEdge* cedgep = static_cast(edgep); if (cedgep->ifelseBoth()) { toVxp->user(depth); } } } } // Recurse for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { simplifyGrandRecurse(edgep->top(), depth+1); } // Clean our marks for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { V3GraphVertex* toVxp = edgep->top(); if (toVxp->user() && toVxp->user() < depth) { // A recursion "above" us marked it; don't mess with it } else { toVxp->user(0); // We marked it originally, so unmark now } } // Delete any If nodes with no children // Last, as want bottom-up cleanup for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) { nextp = edgep->outNextp(); // We may edit the list if (GaterIfVertex* toVxp = dynamic_cast(edgep->top())) { if (!toVxp->outBeginp()) { if (!nextp || nextp->top() != edgep->top()) { // Else next would disappear; we'll do it next loop toVxp->unlinkDelete(&m_graph); VL_DANGLING(toVxp); VL_DANGLING(edgep); } } } } } void simplifyRemoveUngated(V3GraphVertex* vertexp) { // Remove variables that are ungated // At this point, any variable under the head is ungated for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) { nextp = edgep->outNextp(); // We may edit the list if (GaterVarVertex* toVxp = dynamic_cast(edgep->top())) { if (!nextp || nextp->top() != edgep->top()) { // Else next would disappear; we'll do it next loop toVxp->unlinkDelete(&m_graph); VL_DANGLING(toVxp); VL_DANGLING(edgep); } } } } void graphColorSameFeeder() { // Assign same color to all destination vertices that have same // subgraph feeding into them // (I.E. all "from" nodes are common within each color) // We could hash, but instead it's faster to sort edges, so we know // the same edge list is always adjacent. The result is a // O(vertices*edges) loop, but we'd need that to hash too. if (debug()>9) { cout<<"PreColor:\n"; m_graph.dump(); } m_graph.sortEdges(); // Now sort vertices, so same inbound edge set ends up adjacent m_graph.sortVertices(); // Now walk and assign colors; same color is adjacent m_graph.clearColors(); m_graph.userClearEdges(); // Used by newExprFromGraph uint32_t color = 1; GaterVarVertex* lastVxp = NULL; for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { if (GaterVarVertex* vVxp = dynamic_cast(vertexp)) { if (!vVxp->inBeginp()) { // At this point, any variable not linked is an error // (It should have at least landed under the Head node) vVxp->nodep()->v3fatalSrc("Variable became stranded in clk gate detection"); } if (!lastVxp || vVxp->sortCmp(lastVxp)) { // Different sources for this new node color++; } vVxp->color(color); lastVxp = vVxp; } } if (debug()>9) { cout<<"PostColor:\n"; m_graph.dump(); } } AstNode* newExprFromGraph(GaterVarVertex* vertexp) { // Recurse backwards, then form equation on return path // We could use user()!=0, but zeroing it is slow, so instead we'll mark with a generation // We get equations like "a | (!a & b)" which obviously could be reduced here, // instead out of generality, there's a V3Const::matchOrAndNot that'll clean it up static uint32_t s_generation = 0; ++s_generation; nafgMarkRecurse(vertexp, s_generation); AstNode* nodep = nafgCreateRecurse(m_headVertexp, s_generation); if (debug()>=9) nodep->dumpTree(cout," GateExpr: "); return nodep; } void nafgMarkRecurse(V3GraphVertex* vertexp, uint32_t generation) { // Backwards mark user() on the path we recurse //UINFO(9," nafgMark: v "<<(void*)(vertexp)<<" "<name()<inBeginp(); edgep; edgep = edgep->inNextp()) { //UINFO(9," nafgMark: "<<(void*)(edgep)<<" "<name()<user(generation); nafgMarkRecurse(edgep->fromp(), generation); } } AstNode* nafgCreateRecurse(V3GraphVertex* vertexp, uint32_t generation) { // Forewards follow user() marked previously and build tree AstNode* nodep = NULL; // OR across all edges found at this level //UINFO(9," nafgEnter: v "<<(void*)(vertexp)<<" "<name()<outBeginp(); edgep; edgep = edgep->outNextp()) { if (edgep->user() == generation) { GaterEdge* cedgep = static_cast(edgep); AstNode* eqnp = NULL; //UINFO(9," nafgFollow: "<<(void*)(edgep)<<" "<name()<(edgep->fromp())) { // Just OR in all lower terms eqnp = nafgCreateRecurse(edgep->top(), generation); } else if (GaterIfVertex* cVxp = dynamic_cast(edgep->fromp())) { // Edges from IFs represent a real IF branch in the equation tree //UINFO(9," ifver "<<(void*)(edgep)<<" cc"<dotColor()<nodep()->condp()->cloneTree(true); if (!eqnp) cVxp->nodep()->v3fatalSrc("null condition"); if (cedgep->ifelseFalse()) { eqnp = new AstNot(eqnp->fileline(),eqnp); } // We need to AND this term onto whatever was found below it AstNode* belowp = nafgCreateRecurse(edgep->top(), generation); if (belowp) eqnp = new AstAnd(eqnp->fileline(),eqnp,belowp); } // Top level we could choose to make multiple gaters, or ORs under the gater // Right now we'll put OR lower down and let other optimizations deal if (nodep) nodep = new AstOr(nodep->fileline(), nodep, eqnp); else nodep = eqnp; //if (debug()>=9) nodep->dumpTree(cout," followExpr: "); } } //UINFO(9," nafgExit: "<<(void*)(vertexp)<<" "<name()<verticesNextp()) { if (GaterVarVertex* vVxp = dynamic_cast(vertexp)) { if (!lastExprp || lastColor != vVxp->color()) { lastColor = vVxp->color(); // Create the block we've just finished if (lastExprp) newAlwaysTree(nodep, lastExprp); // Duplicate below // New expression for this color lastExprp = newExprFromGraph(vVxp); } // Mark variable to move if (vVxp->nodep()->user2p()) vVxp->nodep()->v3fatalSrc("One variable got marked under two gaters"); vVxp->nodep()->user2p(lastExprp); m_statBits += vVxp->nodep()->width(); // Moving a wide bus counts more! // There shouldn't be two possibilities we want to // move to, IE {A,B} <= Z because we marked such // things as unoptimizable } } // Create the final block we've just finished if (lastExprp) newAlwaysTree(nodep, lastExprp); // Duplicate above } void newAlwaysTree(AstAlways* nodep, AstNode* exprp) { // Create new always with specified gating expression // Relevant vars are already marked with user2p // Should we put the gater under the always itself, // or make a new signal? // New signal: May cause replicated logic until we can combine // Need way to toggle signal on complicated pos/negedge // Under Always: More complicated, may not propagate logic with gateer // Untill we get better gate optimization, we'll go this route. //#define GATER_NOP #ifdef GATER_NOP // For testing only, don't clock gate but still make new always AstSenTree* sensesp = nodep->sensesp()->cloneTree(true); #else // Make a SenGate AstSenItem* oldsenitemsp = VN_CAST(nodep->sensesp()->sensesp(), SenItem); if (!oldsenitemsp) nodep->v3fatalSrc("SenTree doesn't have any SenItem under it"); AstSenTree* sensesp = new AstSenTree(nodep->fileline(), new AstSenGate(nodep->fileline(), oldsenitemsp->cloneTree(true), exprp)); #endif // Make new body; note clone will preserve user2p() indicating moving vars AstNode* bodyp = nodep->bodysp()->cloneTree(true); AstAlways* alwp = new AstAlways(nodep->fileline(), nodep->keyword(), sensesp, bodyp); alwp->user4(1); // No need to process the new always again! nodep->addNextHere(alwp); // Blow moved statements from old body GaterBodyVisitor(nodep,exprp,true); // Blow old statements from new body GaterBodyVisitor(alwp,exprp,false); ++m_statGaters; if (debug()>=9) alwp->dumpTree(cout," new: "); } // VISITORS virtual void visit(AstAlways* nodep) { if (debug()>=9) cout<user4SetOnce()) return; clear(); if (debug()>=9) nodep->dumpTree(cout," Alwin: "); // Look for constructs we can't optimize // Form graph with Vertices at each IF, and each Variable m_aboveTrue = VU_IF | VU_ELSE; iterateChildrenAlw(nodep, true); // Other reasons to not optimize if (!m_numIfs) nonOptimizable(nodep, "No if statements"); // Something to optimize, oh my! if (m_nonopt!="") { UINFO(5, " Gater non-opt: "<verticesNextp()) { if (GaterVarVertex* vVxp = dynamic_cast(vertexp)) { if (lastColor < vVxp->color()) { lastColor = vVxp->color(); } } } if (lastColor == 0) { // Nothing we moved! nonOptimizable(nodep, "Nothing moved"); } else if (lastColor > DOMAINS_MAX) { // Our move algorithm is fairly slow and if we're splitting // up too much it'll get really nasty. It's probably a bad // move for performance to split too much, anyhow, as the // number of gaters will result in calling many small c functions. nonOptimizable(nodep, "Too much moved"); } if (m_nonopt=="") { newAlwaysTrees(nodep); if (debug()>=9) nodep->dumpTree(cout," Gaterout: "); } } UINFO(5, " Gater done"<lvalue()) { AstVarScope* vscp = nodep->varScopep(); if (nodep->varp()->isSigPublic()) { // Public signals shouldn't be changed, pli code might be messing with them scoreboardPli(nodep); } // If another lvalue in this node, give up optimizing. // We could just not optimize this variable, but we've already marked the // other variable as optimizable, so we can instead pretend it's a PLI node. if (m_stmtVscp) { UINFO(5, " Multiple lvalues in one statement: "<user1p()); if (!vertexp) { vertexp = new GaterVarVertex(&m_graph, vscp); vscp->user1p(vertexp); } new GaterEdge(&m_graph, m_aboveVertexp, vertexp, m_aboveTrue); if (m_stmtInPli) { new GaterEdge(&m_graph, m_pliVertexp, vertexp, VU_PLI); } } } virtual void visit(AstNodeIf* nodep) { m_ifDepth++; bool allowGater = m_directlyUnderAlw && m_ifDepth <= IF_DEPTH_MAX; if (allowGater) { GaterCondVisitor condVisitor(nodep->condp()); if (!condVisitor.isSimple()) { // Don't clear optimization, simply drop this IF as part of the gating UINFO(5," IFnon-simple-condition: "<condp()); // directlyUnder stays as-is } { m_aboveVertexp = vertexp; // Vars will point at this edge m_aboveTrue = VU_IF; iterateAndNextNull(nodep->ifsp()); // directlyUnder stays as-is (true) } { m_aboveVertexp = vertexp; // Vars will point at this edge m_aboveTrue = VU_ELSE; iterateAndNextNull(nodep->elsesp()); // directlyUnder stays as-is (true) } m_aboveVertexp = lastabovep; m_aboveTrue = lasttrue; } m_ifDepth--; } virtual void visit(AstAssignDly* nodep) { // iterateChildrenAlw will detect this is a statement for us iterateChildrenAlw(nodep, false); } virtual void visit(AstNodeAssign* nodep) { // Note NOT AssignDly; handled above, We'll just mark this block as // not optimizable. // // A future alternative is to look for any lvalues that are also // variables in the sensitivity list, or rvalues in this block. If // any hit, disable optimization. Unlikely to be useful (For loops // being an exception, but they're already unrolled.) nonOptimizable(nodep, "Non-delayed assignment"); // No reason to iterate. } virtual void visit(AstSenItem* nodep) { if (!nodep->isClocked()) { nonOptimizable(nodep, "Non-clocked sensitivity"); } iterateChildrenAlw(nodep, false); } //-------------------- virtual void visit(AstNode* nodep) { if (m_nonopt=="") { // Else accelerate iterateChildrenAlw(nodep, false); } } inline void iterateChildrenAlw(AstNode* nodep, bool under) { // **** USE THIS INSTEAD OF iterateChildren! // Note If visitor doesn't call here; does it its own way bool lastdua = m_directlyUnderAlw; AstVarScope* lastvscp = m_stmtVscp; bool lastpli = m_stmtInPli; m_directlyUnderAlw = under; if (VN_IS(nodep, NodeStmt)) { // Restored below UINFO(9," Stmt: "<isPure() || nodep->isBrancher()) { // May also be a new statement (above if); if so we mark it immediately UINFO(9," NotPure "<= 3); }