From ef3c7bb6a2f2635759245e27e90efec668d20abc Mon Sep 17 00:00:00 2001 From: John Coiner Date: Wed, 28 Feb 2018 06:58:41 -0500 Subject: [PATCH] Better optimize large always block splitting, bug1244. Signed-off-by: Wilson Snyder --- Changes | 2 + src/V3AstNodes.h | 8 + src/V3Split.cpp | 830 +++++++++++++++++++++++------- test_regress/t/t_alw_noreorder.pl | 29 ++ test_regress/t/t_alw_nosplit.pl | 23 + test_regress/t/t_alw_nosplit.v | 131 +++++ test_regress/t/t_alw_reorder.pl | 32 ++ test_regress/t/t_alw_reorder.v | 55 ++ test_regress/t/t_alw_split.pl | 2 +- test_regress/t/t_alw_split.v | 96 +--- 10 files changed, 944 insertions(+), 264 deletions(-) create mode 100644 test_regress/t/t_alw_noreorder.pl create mode 100644 test_regress/t/t_alw_nosplit.pl create mode 100644 test_regress/t/t_alw_nosplit.v create mode 100644 test_regress/t/t_alw_reorder.pl create mode 100644 test_regress/t/t_alw_reorder.v diff --git a/Changes b/Changes index cccde10c5..808fe968a 100644 --- a/Changes +++ b/Changes @@ -8,6 +8,8 @@ The contributors that suggested a given feature are shown in []. Thanks! ** Fix internals to make null-pointer-check clean. +*** Better optimize large always block splitting, bug1244. [John Coiner] + **** Fix internals to avoid 'using namespace std'. diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index baae03d5b..7bc7d7dad 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -5614,6 +5614,14 @@ public: AstNode* bodysp() const { return op1p(); } // op1= expressions to print }; +class AstSplitPlaceholder : public AstNode { +public: + // Dummy node used within V3Split; never exists outside of V3Split. + AstSplitPlaceholder(FileLine* filelinep) + : AstNode(filelinep) {} + ASTNODE_NODE_FUNCS(SplitPlaceholder) +}; + //###################################################################### // Right below top diff --git a/src/V3Split.cpp b/src/V3Split.cpp index a5bf32d6d..cca14de0e 100644 --- a/src/V3Split.cpp +++ b/src/V3Split.cpp @@ -17,18 +17,50 @@ // GNU General Public License for more details. // //************************************************************************* -// V3Split's Transformations: +// V3Split implements two separate transformations: +// splitAlwaysAll() splits large always blocks into smaller always blocks +// when possible (but does not change the order of statements relative +// to one another.) +// +// splitReorderAll() reorders statements within individual blocks +// to avoid delay vars when possible. It no longer splits always blocks. +// +// Both use a common base class, and common graph-building code to reflect +// data dependencies within an always block (the "scoreboard".) +// +// The scoreboard tracks data deps as follows: // -// 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. +// Then we split into separate ALWAYS blocks. // -// Furthermore, optionally +// The scoreboard includes innards of if/else nodes also. Splitting is no +// longer limited to top-level statements, we can split within if-else +// blocks. We want to be able to split this: +// +// always @ (...) begin +// if (reset) begin +// a <= 0; +// b <= 0; +// // ... ten thousand more +// end +// else begin +// a <= a_in; +// b <= b_in; +// // ... ten thousand more +// end +// end +// +// ...into a separate block for each of a, b, and so on. Even though this +// requires duplicating the conditional many times, it's usually +// better. Later modules (V3Gate, V3Order) run faster if they aren't +// handling enormous blocks with long lists of inputs and outputs. +// +// Furthermore, the optional reorder routine can optimize this: // NODEASSIGN/NODEIF/WHILE // S1: ASSIGN {v1} <= 0. // Duplicate of below // S2: ASSIGN {v1} <= {v0} @@ -67,15 +99,6 @@ //###################################################################### // Support classes -class SplitPliVertex : public V3GraphVertex { -public: - explicit 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: @@ -92,15 +115,23 @@ protected: return m_nodep->name(); } } +public: + virtual AstNode* nodep() const { return m_nodep; } +}; + +class SplitPliVertex : public SplitNodeVertex { +public: + explicit SplitPliVertex(V3Graph* graphp, AstNode* nodep) + : SplitNodeVertex(graphp, nodep) {} + virtual ~SplitPliVertex() {} + virtual string name() const { return "*PLI*"; } + virtual string dotColor() const { return "green"; } }; class SplitLogicVertex : public SplitNodeVertex { - uint32_t m_splitColor; // Copied from color() when determined public: SplitLogicVertex(V3Graph* graphp, AstNode* nodep) - : SplitNodeVertex(graphp,nodep), m_splitColor(0) {} - void splitColor(uint32_t flag) { m_splitColor=flag; } - uint32_t splitColor() const { return m_splitColor; } + : SplitNodeVertex(graphp,nodep) {} virtual ~SplitLogicVertex() {} virtual string dotColor() const { return "yellow"; } }; @@ -153,6 +184,9 @@ public: if (oedgep->ignoreThisStep()) return false; return true; } + virtual string dotStyle() const { + return ignoreThisStep() ? "dotted" : V3GraphEdge::dotStyle(); + } }; uint32_t SplitEdge::s_stepNum = 0; @@ -163,7 +197,6 @@ public: 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 { @@ -173,7 +206,6 @@ public: 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 { @@ -183,7 +215,6 @@ public: 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 { @@ -193,7 +224,6 @@ public: 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 { @@ -205,13 +235,12 @@ public: 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 { +class SplitReorderBaseVisitor : public AstNVisitor { private: // NODE STATE // AstVarScope::user1p -> Var SplitNodeVertex* for usage var, 0=not set yet @@ -223,11 +252,11 @@ private: AstUser3InUse m_inuser3; AstUser4InUse m_inuser4; +protected: // TYPES typedef std::vector VStack; // STATE - bool m_reorder; // Reorder statements vs. just splitting string m_noReorderWhy; // Reason we can't reorder VStack m_stmtStackps; // Current statements being tracked SplitPliVertex* m_pliVertexp; // Element specifying PLI ordering @@ -235,7 +264,17 @@ private: bool m_inDly; // Inside ASSIGNDLY V3Double0 m_statSplits; // Statistic tracking + // CONSTUCTORS +public: + SplitReorderBaseVisitor() { + scoreboardClear(); + } + virtual ~SplitReorderBaseVisitor() { + V3Stats::addStat("Optimizations, Split always", m_statSplits); + } + // METHODS +protected: static int debug() { static int level = -1; if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); @@ -255,12 +294,13 @@ private: AstNode::user4ClearTree(); } - void scoreboardPli() { +private: + void scoreboardPli(AstNode* nodep) { // Order all PLI statements with other PLI statements // This ensures $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 + m_pliVertexp = new SplitPliVertex(&m_graph, nodep); // m_graph.clear() will delete it } for (VStack::iterator it = m_stmtStackps.begin(); it != m_stmtStackps.end(); ++it) { // Both ways... @@ -281,6 +321,7 @@ private: m_stmtStackps.pop_back(); } +protected: void scanBlock(AstNode* nodep) { // Iterate across current block, making the scoreboard for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) { @@ -290,13 +331,150 @@ private: } } + void pruneDepsOnInputs() { + for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { + if (!vertexp->outBeginp() + && dynamic_cast(vertexp)) { + if (debug() >= 9) { + SplitVarStdVertex* stdp = (SplitVarStdVertex*)(vertexp); + UINFO(0, "Will prune deps on var "<nodep()<nodep()->dumpTree(cout, "- "); + } + for (V3GraphEdge* edgep = vertexp->inBeginp(); + edgep; edgep=edgep->inNextp()) { + SplitEdge* oedgep = dynamic_cast(edgep); + oedgep->setIgnoreThisStep(); + } + } + } + } + + virtual void makeRvalueEdges(SplitVarStdVertex* vstdp) = 0; + + // VISITORS + virtual void visit(AstAlways* nodep) = 0; + virtual void visit(AstNodeIf* nodep) = 0; + + // We don't do AstNodeFor/AstWhile loops, due to the standard question + // of what is before vs. after + + virtual void visit(AstAssignDly* nodep) { + m_inDly = true; + UINFO(4," ASSIGNDLY "<iterateChildren(*this); + m_inDly = false; + } + virtual void visit(AstVarRef* nodep) { + if (!m_stmtStackps.empty()) { + AstVarScope* vscp = nodep->varScopep(); + if (!vscp) nodep->v3fatalSrc("Not linked"); + if (!nodep->varp()->isConst()) { // Constant lookups can be ignored + // --- + // NOTE: Formerly at this location we would avoid + // splitting or reordering if the variable is public. + // + // However, it should be perfectly safe to split an + // always block containing a public variable. + // Neither operation should perturb PLI's view of + // the variable. + // + // Former code: + // + // if (nodep->varp()->isSigPublic()) { + // // Public signals shouldn't be changed, + // // pli code might be messing with them + // scoreboardPli(nodep); + // } + // --- + + // Create vertexes for variable + if (!vscp->user1p()) { + SplitVarStdVertex* vstdp = new SplitVarStdVertex(&m_graph, vscp); + vscp->user1p(vstdp); + } + SplitVarStdVertex* vstdp = (SplitVarStdVertex*) vscp->user1p(); + + // 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: "<iterateChildren(*this); + } + + //-------------------- + // Default + virtual void visit(AstNode* nodep) { + // **** SPECIAL default type that sets PLI_ORDERING + if (!m_stmtStackps.empty() && !nodep->isPure()) { + UINFO(9," NotSplittable "<iterateChildren(*this); + } + +private: + VL_UNCOPYABLE(SplitReorderBaseVisitor); +}; + +class ReorderVisitor : public SplitReorderBaseVisitor { + // CONSTRUCTORS +public: + explicit ReorderVisitor(AstNetlist* nodep) + : SplitReorderBaseVisitor() { + nodep->accept(*this); + } + + virtual ~ReorderVisitor() {} + + // METHODS +protected: + void makeRvalueEdges(SplitVarStdVertex* vstdp) { + for (VStack::iterator it = m_stmtStackps.begin(); it != m_stmtStackps.end(); ++it) { + new SplitRVEdge(&m_graph, *it, vstdp); + } + } + void cleanupBlockGraph(AstNode* nodep) { // Transform the graph into what we need UINFO(5, "ReorderBlock "<=9) { - m_graph.dumpDotFilePrefixed("splitg_nodup", false); + m_graph.dumpDotFilePrefixed("reorderg_nodup", false); //m_graph.dump(); cout<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(); - } - } - } - } + SplitEdge::incrementStep(); + pruneDepsOnInputs(); + + // For reordering this single block only, mark all logic + // vertexes not involved with this step as unimportant + for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { + 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 // This follows all step-relevant edges excluding PostEdges, which are done later m_graph.weaklyConnected(&SplitEdge::followScoreboard); // Add hard orderings between all nodes of same color, in the order they appeared - std::vector lastOfColor; lastOfColor.resize(numVertexes); - for (uint32_t i=0; i lastOfColor; for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) { SplitLogicVertex* vvertexp = (SplitLogicVertex*)nextp->user3p(); - vvertexp->splitColor(vvertexp->color()); - uint32_t color = vvertexp->splitColor(); - if (color >= numVertexes) nextp->v3fatalSrc("More colors than vertexes"); + uint32_t color = vvertexp->color(); if (!color) nextp->v3fatalSrc("No node color assigned"); if (lastOfColor[color]) { new SplitStrictEdge(&m_graph, lastOfColor[color], vvertexp); @@ -365,68 +535,39 @@ private: void reorderBlock(AstNode* nodep) { // Reorder statements in the completed graph - AstAlways* splitAlwaysp = VN_CAST(nodep->backp(), Always); // Map the rank numbers into nodes they associate with typedef std::multimap RankNodeMap; - typedef std::map ColorRankMap; - ColorRankMap colorRankMap; - uint32_t firstColor = 0; bool multiColors = false; + RankNodeMap rankMap; 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; - } - } + for (RankNodeMap::const_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(); - if (newListp) newListp = newListp->addNext(nextp); - else newListp = nextp; - } - if (splitAlwaysp) { - ++m_statSplits; - AstAlways* alwaysp = new AstAlways(nodep->fileline(), VAlwaysKwd::ALWAYS, NULL, NULL); - addAfterp->addNextHere(alwaysp); addAfterp=alwaysp; - alwaysp->addStmtp(newListp); - } else { - // Just reordering - replaceHandle.relink(newListp); - } - } - if (splitAlwaysp) { - pushDeletep(splitAlwaysp->unlinkFrBack()); - } + AstNode* newListp = NULL; + for (RankNodeMap::const_iterator it = rankMap.begin(); it != rankMap.end(); ++it) { + AstNode* nextp = it->second; + UINFO(6, " New order: "<unlinkFrBack(&replaceHandle); + else nextp->unlinkFrBack(); + if (newListp) newListp = newListp->addNext(nextp); + else newListp = nextp; + } + replaceHandle.relink(newListp); } // leaveAlone } @@ -465,7 +606,6 @@ private: firstp->user3p(oldBlockUser3); } - // VISITORS virtual void visit(AstAlways* nodep) { UINFO(4," ALW "<=9) nodep->dumpTree(cout," alwIn:: "); @@ -473,104 +613,408 @@ private: processBlock(nodep->bodysp()); if (debug()>=9) nodep->dumpTree(cout," alwOut: "); } + virtual void visit(AstNodeIf* nodep) { - if (!m_reorder) { - nodep->iterateChildren(*this); - } else { - UINFO(4," IF "<condp()->iterateAndNext(*this); - processBlock(nodep->ifsp()); - processBlock(nodep->elsesp()); - } + 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 +private: + VL_UNCOPYABLE(ReorderVisitor); +}; - virtual void visit(AstAssignDly* nodep) { - m_inDly = true; - UINFO(4," ASSIGNDLY "<iterateChildren(*this); - m_inDly = false; - } - virtual void visit(AstVarRef* nodep) { - 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(); - } +typedef vl_unordered_set ColorSet; +typedef vl_unordered_set AlwaysSet; - // Create vertexes for variable - if (!vscp->user1p()) { - SplitVarStdVertex* vstdp = new SplitVarStdVertex(&m_graph, vscp); - vscp->user1p(vstdp); - } - SplitVarStdVertex* vstdp = (SplitVarStdVertex*) vscp->user1p(); +class IfColorVisitor : public AstNVisitor { + // MEMBERS + ColorSet m_colors; // All colors in the original always block - // 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: "<iterateChildren(*this); - } + typedef std::vector IfStack; + IfStack m_ifStack; // Stack of nested if-statements we're currently processing - //-------------------- - // Default - virtual void visit(AstNode* nodep) { - // **** SPECIAL default type that sets PLI_ORDERING - if (!m_stmtStackps.empty() && !nodep->isPure()) { - UINFO(9," NotSplittable "<iterateChildren(*this); - } + typedef vl_unordered_map IfColorMap; + IfColorMap m_ifColors; // Map each if-statement to the set of colors (split blocks) + // that will get a copy of that if-statement + // CONSTRUCTORS public: - // CONSTUCTORS - SplitVisitor(AstNetlist* nodep, bool reorder) - : m_reorder(reorder) { - scoreboardClear(); - nodep->accept(*this); + // Visit through *nodep and map each AstNodeIf within to the set of + // colors it will participate in. Also find the whole set of colors. + explicit IfColorVisitor(AstAlways* nodep) { + nodep->accept(*this); } - virtual ~SplitVisitor() { - V3Stats::addStat("Optimizations, Split always", m_statSplits); + virtual ~IfColorVisitor() {} + + // METHODS + const ColorSet& colors() const { return m_colors; } + const ColorSet& colors(AstNodeIf* nodep) const { + IfColorMap::const_iterator it = m_ifColors.find(nodep); + if (it == m_ifColors.end()) nodep->v3fatalSrc("Unknown node in split color() map"); + return it->second; } + +protected: + virtual void visit(AstNodeIf* nodep) { + m_ifStack.push_back(nodep); + nodep->iterateChildren(*this); + m_ifStack.pop_back(); + } + + virtual void visit(AstNode* nodep) { + if (nodep->user3p()) { + SplitLogicVertex* vertexp = (SplitLogicVertex*)(nodep->user3p()); + uint32_t color = vertexp->color(); + m_colors.insert(color); + UINFO(8, " SVL " << vertexp << " has color " << color << "\n"); + + // Record that all containing ifs have this color. + for (IfStack::const_iterator it = m_ifStack.begin(); + it != m_ifStack.end(); ++it) { + m_ifColors[*it].insert(color); + } + } + nodep->iterateChildren(*this); + } + + static int debug() { + static int level = -1; + if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); + return level; + } +private: + VL_UNCOPYABLE(IfColorVisitor); +}; + +class OrderCheckNoIfVisitor : public AstNVisitor { +public: + explicit OrderCheckNoIfVisitor(AstNode* nodep) { + nodep->accept(*this); + } + virtual ~OrderCheckNoIfVisitor() {} + virtual void visit(AstNode* nodep) { + nodep->iterateChildren(*this); + } + virtual void visit(AstNodeIf* nodep) { + UASSERT(false, "Found unexpeceted if/else in OrderCheckNoIfVisitor!"); + } +private: + VL_UNCOPYABLE(OrderCheckNoIfVisitor); +}; + +class EmitSplitVisitor : public AstNVisitor { + // MEMBERS + AstAlways* m_origAlwaysp; // Block that *this will split + const IfColorVisitor* m_ifColorp; // Digest of results of prior coloring + + // Map each color to our current place within the color's new always + typedef vl_unordered_map LocMap; + LocMap m_addAfter; + + AlwaysSet* m_newBlocksp; // Split always blocks we have generated + + // CONSTRUCTORS +public: + // EmitSplitVisitor visits through always block *nodep + // and generates its split blocks, writing the split blocks + // into *newBlocksp. + EmitSplitVisitor(AstAlways* nodep, + const IfColorVisitor* ifColorp, + AlwaysSet* newBlocksp) + : m_origAlwaysp(nodep) + , m_ifColorp(ifColorp) + , m_newBlocksp(newBlocksp) { + UINFO(6, " splitting always " << nodep << endl); + } + + virtual ~EmitSplitVisitor() {} + + // METHODS + void go() { + // Create a new always for each color + const ColorSet& colors = m_ifColorp->colors(); + for (ColorSet::const_iterator color = colors.begin(); + color != colors.end(); ++color) { + // We don't need to clone m_origAlwaysp->sensesp() here; + // V3Activate already moved it to a parent node. + AstAlways* alwaysp = + new AstAlways(m_origAlwaysp->fileline(), VAlwaysKwd::ALWAYS, + NULL, NULL); + // Put a placeholder node into stmtp to track our position. + // We'll strip these out after the blocks are fully cloned. + AstSplitPlaceholder* placeholderp = makePlaceholderp(); + alwaysp->addStmtp(placeholderp); + m_addAfter[*color] = placeholderp; + m_newBlocksp->insert(alwaysp); + } + // Scan the body of the always. We'll handle if/else + // specially, everything else is a leaf node that we can + // just clone into one of the split always blocks. + m_origAlwaysp->bodysp()->iterateAndNext(*this); + } + +protected: + static int debug() { + static int level = -1; + if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); + return level; + } + + AstSplitPlaceholder* makePlaceholderp() { + return new AstSplitPlaceholder(m_origAlwaysp->fileline()); + } + + virtual void visit(AstNode* nodep) { + // Anything that's not an if/else we assume is a leaf. + // Actually, that's worth asserting... +#ifdef VL_DEBUG + OrderCheckNoIfVisitor noIf(nodep); +#endif + + // Each leaf must have a user3p + UASSERT(nodep->user3p(), "null user3p!"); + + // Clone the leaf into its new always block + SplitLogicVertex* vxp = (SplitLogicVertex*)(nodep->user3p()); + uint32_t color = vxp->color(); + AstNode* clonedp = nodep->cloneTree(false); + m_addAfter[color]->addNextHere(clonedp); + m_addAfter[color] = clonedp; + } + + virtual void visit(AstNodeIf* nodep) { + const ColorSet& colors = m_ifColorp->colors(nodep); + typedef vl_unordered_map CloneMap; + CloneMap clones; + + for (ColorSet::const_iterator color = colors.begin(); + color != colors.end(); ++color) { + // Clone this if into its set of split blocks + AstSplitPlaceholder* if_placeholderp = makePlaceholderp(); + AstSplitPlaceholder* else_placeholderp = makePlaceholderp(); + AstIf* clonep = + new AstIf(nodep->fileline(), + nodep->condp()->cloneTree(true), + if_placeholderp, + else_placeholderp); + AstIf* origp = VN_CAST(nodep, If); + if (origp) { + // Preserve pragmas from unique if's + // so assertions work properly + clonep->uniquePragma(origp->uniquePragma()); + clonep->unique0Pragma(origp->unique0Pragma()); + clonep->priorityPragma(origp->priorityPragma()); + } + clones[*color] = clonep; + m_addAfter[*color]->addNextHere(clonep); + m_addAfter[*color] = if_placeholderp; + } + + nodep->ifsp()->iterateAndNext(*this); + + for (ColorSet::const_iterator color = colors.begin(); + color != colors.end(); ++color) { + m_addAfter[*color] = clones[*color]->elsesp(); + } + + nodep->elsesp()->iterateAndNext(*this); + + for (ColorSet::const_iterator color = colors.begin(); + color != colors.end(); ++color) { + m_addAfter[*color] = clones[*color]; + } + } + +private: + VL_UNCOPYABLE(EmitSplitVisitor); +}; + +class RemovePlaceholdersVisitor : public AstNVisitor { + typedef vl_unordered_set NodeSet; + NodeSet m_removeSet; // placeholders to be removed +public: + explicit RemovePlaceholdersVisitor(AstNode* nodep) { + nodep->accept(*this); + for (NodeSet::const_iterator it = m_removeSet.begin(); + it != m_removeSet.end(); ++it) { + AstNode* np = *it; + np->unlinkFrBack(); // Without next + np->deleteTree(); VL_DANGLING(np); + } + } + virtual ~RemovePlaceholdersVisitor() {} + virtual void visit(AstNode* nodep) { + nodep->iterateChildren(*this); + } + virtual void visit(AstSplitPlaceholder* nodep) { + m_removeSet.insert(nodep); + } +private: + VL_UNCOPYABLE(RemovePlaceholdersVisitor); +}; + +class SplitVisitor : public SplitReorderBaseVisitor { +private: + // Keys are original always blocks pending delete, + // values are newly split always blocks pending insertion + // at the same position as the originals: + typedef vl_unordered_map ReplaceMap; + ReplaceMap m_replaceBlocks; + + // AstNodeIf* whose condition we're currently visiting + AstNode* m_curIfConditional; + + // CONSTRUCTORS +public: + explicit SplitVisitor(AstNetlist* nodep) + : SplitReorderBaseVisitor() + , m_curIfConditional(NULL) { + nodep->accept(*this); + + // Splice newly-split blocks into the tree. Remove placeholders + // from newly-split blocks. Delete the original always blocks + // that we're replacing. + for (ReplaceMap::iterator it = m_replaceBlocks.begin(); + it != m_replaceBlocks.end(); ++it) { + AstAlways* origp = it->first; + for (AlwaysSet::iterator addme = it->second.begin(); + addme != it->second.end(); ++addme) { + origp->addNextHere(*addme); + RemovePlaceholdersVisitor removePlaceholders(*addme); + } + origp->unlinkFrBack(); // Without next + origp->deleteTree(); VL_DANGLING(origp); + } + } + + virtual ~SplitVisitor() {} + + // METHODS +protected: + void makeRvalueEdges(SplitVarStdVertex* vstdp) { + // Each 'if' depends on rvalues in its own conditional ONLY, + // not rvalues in the if/else bodies. + for (VStack::const_iterator it = m_stmtStackps.begin(); it != m_stmtStackps.end(); ++it) { + AstNodeIf* ifNodep = VN_CAST((*it)->nodep(), NodeIf); + if (ifNodep && (m_curIfConditional != ifNodep)) { + continue; + } + new SplitRVEdge(&m_graph, *it, vstdp); + } + } + + void colorAlwaysGraph() { + // Color the graph to indicate subsets, each of which + // we can split into its own always block. + m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); + + // Some vars are primary inputs to the always block; prune + // edges on those vars. Reasoning: if two statements both depend + // on primary input A, it's ok to split these statements. Whereas + // if they both depend on locally-generated variable B, the statements + // must be kept together. + SplitEdge::incrementStep(); + pruneDepsOnInputs(); + + // For any 'if' node whose deps have all been pruned + // (meaning, its conditional expression only looks at primary + // inputs) prune all edges that depend on the 'if'. + for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { + SplitLogicVertex* logicp = dynamic_cast(vertexp); + if (!logicp) continue; + + AstNodeIf* ifNodep = VN_CAST(logicp->nodep(), NodeIf); + if (!ifNodep) continue; + + bool pruneMe = true; + for (V3GraphEdge* edgep = logicp->outBeginp(); + edgep; edgep = edgep->outNextp()) { + SplitEdge* oedgep = dynamic_cast(edgep); + if (!oedgep->ignoreThisStep()) { + // This if conditional depends on something we can't + // prune -- a variable generated in the current block. + pruneMe = false; + + // When we can't prune dependencies on the conditional, + // give a hint about why... + if (debug() >= 9) { + V3GraphVertex* vxp = oedgep->top(); + SplitNodeVertex* nvxp = dynamic_cast(vxp); + UINFO(0, "Cannot prune if-node due to edge "<nodep()<nodep()->dumpTree(cout, "- "); + } + + break; + } + } + + if (!pruneMe) continue; + + // This if can be split; prune dependencies on it. + for (V3GraphEdge* edgep = logicp->inBeginp(); + edgep; edgep = edgep->inNextp()) { + SplitEdge* oedgep = dynamic_cast(edgep); + oedgep->setIgnoreThisStep(); + } + } + + if (debug()>=9) { + m_graph.dumpDotFilePrefixed("splitg_nodup", false); + } + + // Weak coloring to determine what needs to remain grouped + // in a single always. This follows all edges excluding: + // - those we pruned above + // - PostEdges, which are done later + m_graph.weaklyConnected(&SplitEdge::followScoreboard); + } + + virtual void visit(AstAlways* nodep) { + // build the scoreboard + scoreboardClear(); + scanBlock(nodep->bodysp()); + + if (m_noReorderWhy != "") { + // We saw a jump or something else rare that we don't handle. + UINFO(9," NoSplitBlock because "< 1) { + // Counting original always blocks rather than newly-split + // always blocks makes it a little easier to use this stat to + // check the result of the t_alw_split test: + ++m_statSplits; + + // Visit through the original always block one more time, + // and emit the split always blocks into m_replaceBlocks: + EmitSplitVisitor emitSplit(nodep, &ifColor, + &(m_replaceBlocks[nodep])); + emitSplit.go(); + } + } + virtual void visit(AstNodeIf* nodep) { + UINFO(4," IF "<condp()->iterateAndNext(*this); + m_curIfConditional = NULL; + scanBlock(nodep->ifsp()); + scanBlock(nodep->elsesp()); + } +private: + VL_UNCOPYABLE(SplitVisitor); }; //###################################################################### @@ -578,11 +1022,11 @@ public: void V3Split::splitReorderAll(AstNetlist* nodep) { UINFO(2,__FUNCTION__<<": "<= 3); } void V3Split::splitAlwaysAll(AstNetlist* nodep) { UINFO(2,__FUNCTION__<<": "<= 6); + SplitVisitor visitor (nodep); + V3Global::dumpCheckGlobalTree("split", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/test_regress/t/t_alw_noreorder.pl b/test_regress/t/t_alw_noreorder.pl new file mode 100644 index 000000000..cce730af7 --- /dev/null +++ b/test_regress/t/t_alw_noreorder.pl @@ -0,0 +1,29 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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. + +$Self->{vlt} or $Self->skip("Verilator only test"); + +top_filename("t/t_alw_reorder.v"); +compile ( + verilator_flags2 => ["--stats -Or"], + ); + +file_grep ($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 0); +# Here we should see some dly vars since reorder is disabled. +# (Whereas our twin test, t_alw_reorder, should see no dly vars +# since it enables the reorder step.) +file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.cpp", qr/dly__t__DOT__v1/i); +file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.cpp", qr/dly__t__DOT__v2/i); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_alw_nosplit.pl b/test_regress/t/t_alw_nosplit.pl new file mode 100644 index 000000000..2fcd2aee3 --- /dev/null +++ b/test_regress/t/t_alw_nosplit.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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. + +compile ( + verilator_flags2 => ["--stats"], + ); + +if ($Self->{vlt}) { + file_grep ($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 0); +} + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_alw_nosplit.v b/test_regress/t/t_alw_nosplit.v new file mode 100644 index 000000000..971676e16 --- /dev/null +++ b/test_regress/t/t_alw_nosplit.v @@ -0,0 +1,131 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2018 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg [15:0] m_din; + + // We expect none of these blocks to split. + // Blocks that can split should go in t_alw_split.v instead. + + reg [15:0] b_split_1, b_split_2; + always @ (/*AS*/m_din) begin + b_split_1 = m_din; + b_split_2 = b_split_1; + end + + reg [15:0] c_split_1, c_split_2; + always @ (/*AS*/m_din) begin + c_split_1 = m_din; + c_split_2 = c_split_1; + c_split_1 = ~m_din; + end + + always @ (posedge clk) begin + $write(" foo %x", m_din); + $write(" bar %x\n", m_din); + end + + reg [15:0] e_split_1, e_split_2; + always @ (posedge clk) begin + e_split_1 = m_din; + e_split_2 = e_split_1; + end + + reg [15:0] f_split_1, f_split_2; + always @ (posedge clk) begin + f_split_2 = f_split_1; + f_split_1 = m_din; + end + + reg [15:0] l_split_1, l_split_2; + always @ (posedge clk) begin + l_split_2 <= l_split_1; + l_split_1 <= l_split_2 | m_din; + end + + reg [15:0] z_split_1, z_split_2; + always @ (posedge clk) begin + z_split_1 <= 0; + z_split_1 <= ~m_din; + end + always @ (posedge clk) begin + z_split_2 <= 0; + z_split_2 <= z_split_1; + end + + reg [15:0] h_split_1; + reg [15:0] h_split_2; + reg [15:0] h_foo; + always @ (posedge clk) begin +// $write(" cyc = %x m_din = %x\n", cyc, m_din); + h_foo = m_din; + if (cyc > 2) begin + // This conditional depends on non-primary-input foo. + // Its dependency on foo should not be pruned. As a result, + // the dependencies of h_split_1 and h_split_2 on this + // conditional will also not be pruned, making them all + // weakly connected such that they'll end up in the same graph + // and we can't split. + if (h_foo == 16'h0) begin + h_split_1 <= 16'h0; + h_split_2 <= 16'h0; + end + else begin + h_split_1 <= m_din; + h_split_2 <= ~m_din; + end + end + else begin + h_split_1 <= 16'h0; + h_split_2 <= 16'h0; + end + end // always @ (posedge clk) + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc<=cyc+1; + end + if (cyc==1) begin + m_din <= 16'hfeed; + end + if (cyc==4) begin + m_din <= 16'he11e; + if (!(b_split_1==16'hfeed && b_split_2==16'hfeed)) $stop; + if (!(c_split_1==16'h0112 && c_split_2==16'hfeed)) $stop; + if (!(e_split_1==16'hfeed && e_split_2==16'hfeed)) $stop; + if (!(f_split_1==16'hfeed && f_split_2==16'hfeed)) $stop; + if (!(z_split_1==16'h0112 && z_split_2==16'h0112)) $stop; + end + if (cyc==5) begin + m_din <= 16'he22e; + if (!(b_split_1==16'he11e && b_split_2==16'he11e)) $stop; + if (!(c_split_1==16'h1ee1 && c_split_2==16'he11e)) $stop; + // Two valid orderings, as we don't know which posedge clk gets evaled first + if (!(e_split_1==16'hfeed && e_split_2==16'hfeed) && !(e_split_1==16'he11e && e_split_2==16'he11e)) $stop; + if (!(f_split_1==16'hfeed && f_split_2==16'hfeed) && !(f_split_1==16'he11e && f_split_2==16'hfeed)) $stop; + if (!(z_split_1==16'h0112 && z_split_2==16'h0112)) $stop; + end + if (cyc==6) begin + m_din <= 16'he33e; + if (!(b_split_1==16'he22e && b_split_2==16'he22e)) $stop; + if (!(c_split_1==16'h1dd1 && c_split_2==16'he22e)) $stop; + // Two valid orderings, as we don't know which posedge clk gets evaled first + if (!(e_split_1==16'he11e && e_split_2==16'he11e) && !(e_split_1==16'he22e && e_split_2==16'he22e)) $stop; + if (!(f_split_1==16'he11e && f_split_2==16'hfeed) && !(f_split_1==16'he22e && f_split_2==16'he11e)) $stop; + if (!(z_split_1==16'h1ee1 && z_split_2==16'h0112)) $stop; + end + if (cyc==7) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule diff --git a/test_regress/t/t_alw_reorder.pl b/test_regress/t/t_alw_reorder.pl new file mode 100644 index 000000000..9d4beca3e --- /dev/null +++ b/test_regress/t/t_alw_reorder.pl @@ -0,0 +1,32 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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. + +$Self->{vlt} or $Self->skip("Verilator only test"); + +compile ( + verilator_flags2 => ["--stats"], + ); + +file_grep ($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 0); +# Important: if reorder succeeded, we should see no dly vars. +# Equally important: twin test t_alw_noreorder should see dly vars, +# is identical to this test except for disabling the reorder step. +foreach my $file ("$Self->{obj_dir}/$Self->{VM_PREFIX}.cpp", + "$Self->{obj_dir}/$Self->{VM_PREFIX}.h") { + file_grep_not($file, qr/dly__t__DOT__v1/i); + file_grep_not($file, qr/dly__t__DOT__v2/i); + file_grep_not($file, qr/dly__t__DOT__v3/i); +} + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_alw_reorder.v b/test_regress/t/t_alw_reorder.v new file mode 100644 index 000000000..7433bfbe7 --- /dev/null +++ b/test_regress/t/t_alw_reorder.v @@ -0,0 +1,55 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2018 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg [15:0] m_din; + + reg [15:0] v1; + reg [15:0] v2; + reg [15:0] v3; + integer nosplit; + + always @ (posedge clk) begin + // write needed so that V3Dead doesn't kill v0..v3 + $write(" values %x %x %x\n", v1, v2, v3); + + // Locally-set 'nosplit' will prevent the if from splitting + // in splitAlwaysAll(). This whole always block should still be + // intact when we call splitReorderAll() which is the subject + // of this test. + nosplit = cyc; + if (nosplit > 2) begin + /* S1 */ v1 <= 16'h0; + /* S2 */ v1 <= m_din; + /* S3 */ if (m_din == 16'h0) begin + /* X1 */ v2 <= v1; + /* X2 */ v3 <= v2; + end + end + + // We expect to swap S2 and S3, and to swap X1 and X2. + // We can check that this worked by the absense of dly vars + // in the generated output; if the reorder fails (or is disabled) + // we should see dly vars for v1 and v2. + end + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc<=cyc+1; + if (cyc==7) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule diff --git a/test_regress/t/t_alw_split.pl b/test_regress/t/t_alw_split.pl index bfa9e7547..63469761f 100755 --- a/test_regress/t/t_alw_split.pl +++ b/test_regress/t/t_alw_split.pl @@ -12,7 +12,7 @@ compile ( ); if ($Self->{vlt}) { - file_grep ($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 6); + file_grep ($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 3); } execute ( diff --git a/test_regress/t/t_alw_split.v b/test_regress/t/t_alw_split.v index 6f46c27ed..0088166e8 100644 --- a/test_regress/t/t_alw_split.v +++ b/test_regress/t/t_alw_split.v @@ -13,29 +13,15 @@ module t (/*AUTOARG*/ reg [15:0] m_din; - // OK + // We expect all these blocks should split; + // blocks that don't split should go in t_alw_nosplit.v + reg [15:0] a_split_1, a_split_2; always @ (/*AS*/m_din) begin a_split_1 = m_din; a_split_2 = m_din; end - // OK - reg [15:0] b_split_1, b_split_2; - always @ (/*AS*/m_din) begin - b_split_1 = m_din; - b_split_2 = b_split_1; - end - - // Not OK - reg [15:0] c_split_1, c_split_2; - always @ (/*AS*/m_din) begin - c_split_1 = m_din; - c_split_2 = c_split_1; - c_split_1 = ~m_din; - end - - // OK reg [15:0] d_split_1, d_split_2; always @ (posedge clk) begin d_split_1 <= m_din; @@ -43,44 +29,27 @@ module t (/*AUTOARG*/ d_split_1 <= ~m_din; end - // Not OK + reg [15:0] h_split_1; + reg [15:0] h_split_2; always @ (posedge clk) begin - $write(" foo %x", m_din); - $write(" bar %x\n", m_din); - end - - // Not OK - reg [15:0] e_split_1, e_split_2; - always @ (posedge clk) begin - e_split_1 = m_din; - e_split_2 = e_split_1; - end - - // Not OK - reg [15:0] f_split_1, f_split_2; - always @ (posedge clk) begin - f_split_2 = f_split_1; - f_split_1 = m_din; - end - - // Not Ok - reg [15:0] l_split_1, l_split_2; - always @ (posedge clk) begin - l_split_2 <= l_split_1; - l_split_1 <= l_split_2 | m_din; - end - - // OK - reg [15:0] z_split_1, z_split_2; - always @ (posedge clk) begin - z_split_1 <= 0; - z_split_1 <= ~m_din; - end - always @ (posedge clk) begin - z_split_2 <= 0; - z_split_2 <= z_split_1; +// $write(" cyc = %x m_din = %x\n", cyc, m_din); + if (cyc > 2) begin + if (m_din == 16'h0) begin + h_split_1 <= 16'h0; + h_split_2 <= 16'h0; + end + else begin + h_split_1 <= m_din; + h_split_2 <= ~m_din; + end + end + else begin + h_split_1 <= 16'h0; + h_split_2 <= 16'h0; + end end + // (The checker block is an exception, it won't split.) always @ (posedge clk) begin if (cyc!=0) begin cyc<=cyc+1; @@ -93,39 +62,26 @@ module t (/*AUTOARG*/ m_din <= 16'he11e; //$write(" A %x %x\n", a_split_1, a_split_2); if (!(a_split_1==16'hfeed && a_split_2==16'hfeed)) $stop; - if (!(b_split_1==16'hfeed && b_split_2==16'hfeed)) $stop; - if (!(c_split_1==16'h0112 && c_split_2==16'hfeed)) $stop; if (!(d_split_1==16'h0112 && d_split_2==16'h0112)) $stop; - if (!(e_split_1==16'hfeed && e_split_2==16'hfeed)) $stop; - if (!(f_split_1==16'hfeed && f_split_2==16'hfeed)) $stop; - if (!(z_split_1==16'h0112 && z_split_2==16'h0112)) $stop; + if (!(h_split_1==16'hfeed && h_split_2==16'h0112)) $stop; end if (cyc==5) begin m_din <= 16'he22e; if (!(a_split_1==16'he11e && a_split_2==16'he11e)) $stop; - if (!(b_split_1==16'he11e && b_split_2==16'he11e)) $stop; - if (!(c_split_1==16'h1ee1 && c_split_2==16'he11e)) $stop; if (!(d_split_1==16'h0112 && d_split_2==16'h0112)) $stop; - if (!(z_split_1==16'h0112 && z_split_2==16'h0112)) $stop; - // Two valid orderings, as we don't know which posedge clk gets evaled first - if (!(e_split_1==16'hfeed && e_split_2==16'hfeed) && !(e_split_1==16'he11e && e_split_2==16'he11e)) $stop; - if (!(f_split_1==16'hfeed && f_split_2==16'hfeed) && !(f_split_1==16'he11e && f_split_2==16'hfeed)) $stop; + if (!(h_split_1==16'hfeed && h_split_2==16'h0112)) $stop; end if (cyc==6) begin m_din <= 16'he33e; if (!(a_split_1==16'he22e && a_split_2==16'he22e)) $stop; - if (!(b_split_1==16'he22e && b_split_2==16'he22e)) $stop; - if (!(c_split_1==16'h1dd1 && c_split_2==16'he22e)) $stop; if (!(d_split_1==16'h1ee1 && d_split_2==16'h0112)) $stop; - if (!(z_split_1==16'h1ee1 && d_split_2==16'h0112)) $stop; - // Two valid orderings, as we don't know which posedge clk gets evaled first - if (!(e_split_1==16'he11e && e_split_2==16'he11e) && !(e_split_1==16'he22e && e_split_2==16'he22e)) $stop; - if (!(f_split_1==16'he11e && f_split_2==16'hfeed) && !(f_split_1==16'he22e && f_split_2==16'he11e)) $stop; + if (!(h_split_1==16'he11e && h_split_2==16'h1ee1)) $stop; end if (cyc==7) begin $write("*-* All Finished *-*\n"); $finish; end end - end + end // always @ (posedge clk) + endmodule