2006-08-26 11:35:28 +00:00
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Break always into separate statements to reduce temps
|
|
|
|
|
//
|
2008-04-25 12:14:27 +00:00
|
|
|
|
// Code available from: http://www.veripool.org/verilator
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//
|
|
|
|
|
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2008-01-15 14:29:08 +00:00
|
|
|
|
// Copyright 2003-2008 by Wilson Snyder. This program is free software; you can
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// 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()
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
2006-12-18 19:20:45 +00:00
|
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2006-08-26 11:35:28 +00:00
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <vector>
|
|
|
|
|
#include <map>
|
|
|
|
|
|
|
|
|
|
#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
|
2006-12-18 18:20:13 +00:00
|
|
|
|
// Do not make accessor for nodep(), It may change due to
|
|
|
|
|
// reordering a lower block, but we don't repair it
|
2006-08-26 11:35:28 +00:00
|
|
|
|
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<const SplitEdge*>(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<const SplitEdge*>(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<SplitLogicVertex*> 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 "<<nodep<<endl);
|
|
|
|
|
SplitLogicVertex* vertexp = new SplitLogicVertex(&m_graph, nodep);
|
|
|
|
|
m_stmtStackps.push_back(vertexp);
|
|
|
|
|
if (nodep->user3p()) nodep->v3fatalSrc("user3p should not be used; cleared in processBlock\n");
|
|
|
|
|
nodep->user3p(vertexp);
|
|
|
|
|
}
|
|
|
|
|
void scoreboardPopStmt() {
|
|
|
|
|
//UINFO(9," pop"<<endl);
|
|
|
|
|
if (m_stmtStackps.empty()) v3fatalSrc("Stack underflow\n");
|
|
|
|
|
m_stmtStackps.pop_back();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void scanBlock(AstNode* nodep) {
|
|
|
|
|
// Iterate across current block, making the scoreboard
|
|
|
|
|
for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) {
|
|
|
|
|
scoreboardPushStmt(nextp);
|
|
|
|
|
nextp->accept(*this);
|
|
|
|
|
scoreboardPopStmt();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void cleanupBlockGraph(AstNode* nodep) {
|
|
|
|
|
// Transform the graph into what we need
|
|
|
|
|
UINFO(5, "ReorderBlock "<<nodep<<endl);
|
|
|
|
|
m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
|
|
|
|
|
|
|
|
|
|
if (debug()>=9) {
|
|
|
|
|
m_graph.dumpDotFilePrefixed("splitg_nodup", false);
|
|
|
|
|
//m_graph.dump(); cout<<endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mark all the logic for this step
|
|
|
|
|
// Vertex::m_user begin: true indicates logic for this step
|
|
|
|
|
m_graph.userClearVertices();
|
|
|
|
|
for (AstNode* nextp=nodep; nextp; nextp=nextp->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<SplitVarStdVertex*>(vertexp)) {
|
|
|
|
|
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) {
|
|
|
|
|
SplitEdge* oedgep = dynamic_cast<SplitEdge*>(edgep);
|
|
|
|
|
oedgep->setIgnoreThisStep();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Mark all logic vertexes not involved with this step as unimportant
|
|
|
|
|
if (SplitLogicVertex* vvertexp = dynamic_cast<SplitLogicVertex*>(vertexp)) {
|
|
|
|
|
if (!vvertexp->user()) {
|
|
|
|
|
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) {
|
|
|
|
|
SplitEdge* oedgep = dynamic_cast<SplitEdge*>(edgep);
|
|
|
|
|
oedgep->setIgnoreThisStep();
|
|
|
|
|
}
|
|
|
|
|
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
|
|
|
|
SplitEdge* oedgep = dynamic_cast<SplitEdge*>(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<SplitLogicVertex*> lastOfColor; lastOfColor.resize(numVertexes);
|
|
|
|
|
for (uint32_t i=0; i<numVertexes; i++) lastOfColor[i] = NULL;
|
|
|
|
|
for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) {
|
|
|
|
|
SplitLogicVertex* vvertexp = (SplitLogicVertex*)nextp->user3p();
|
|
|
|
|
vvertexp->splitColor(vvertexp->color());
|
|
|
|
|
uint32_t color = vvertexp->splitColor();
|
2007-11-30 22:12:53 +00:00
|
|
|
|
if (color >= numVertexes) nextp->v3fatalSrc("More colors than vertexes!\n");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
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...
|
2006-12-18 18:20:13 +00:00
|
|
|
|
// Non-cutable violations should be impossible; as those edges are program-order
|
|
|
|
|
if (debug()>=9) m_graph.dumpDotFilePrefixed((string)"splitg_preo", false);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
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<uint32_t,AstNode*> RankNodeMap;
|
|
|
|
|
typedef map<uint32_t,RankNodeMap> 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="<<color<<" New order: "<<nextp<<endl);
|
|
|
|
|
if (nextp == nodep && !splitAlwaysp) nodep->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
|
2006-12-18 18:20:13 +00:00
|
|
|
|
AstNode* firstp = nodep; // We may reorder, and nodep is no longer first.
|
2006-08-26 11:35:28 +00:00
|
|
|
|
void* oldBlockUser3 = nodep->user3p(); // May be overloaded in below loop, save it
|
|
|
|
|
nodep->user3p(NULL);
|
2007-01-31 15:44:36 +00:00
|
|
|
|
if (!nodep->firstAbovep()) nodep->v3fatalSrc("Node passed is in next list; should have processed all list at once");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Process it
|
|
|
|
|
if (!nodep->nextp()) {
|
|
|
|
|
// Just one, so can't reorder. Just look for more blocks/statements.
|
|
|
|
|
nodep->accept(*this);
|
|
|
|
|
} else {
|
|
|
|
|
UINFO(9," processBlock "<<nodep<<endl);
|
|
|
|
|
// Process block and followers
|
|
|
|
|
scanBlock(nodep);
|
|
|
|
|
// Reorder statements in this block
|
|
|
|
|
cleanupBlockGraph(nodep);
|
|
|
|
|
reorderBlock(nodep);
|
|
|
|
|
// Delete old vertexes and edges only applying to this block
|
2006-12-18 18:20:13 +00:00
|
|
|
|
while (firstp->backp()->nextp()==firstp) firstp = firstp->backp(); // Walk back to first in list
|
|
|
|
|
for (AstNode* nextp=firstp; nextp; nextp=nextp->nextp()) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
SplitLogicVertex* vvertexp = (SplitLogicVertex*)nextp->user3p();
|
|
|
|
|
vvertexp->unlinkDelete(&m_graph);
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-12-18 18:20:13 +00:00
|
|
|
|
// Again, nodep may no longer be first.
|
|
|
|
|
firstp->user3p(oldBlockUser3);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
|
|
|
|
virtual void visit(AstAlways* nodep, AstNUser*) {
|
|
|
|
|
UINFO(4," ALW "<<nodep<<endl);
|
|
|
|
|
if (debug()>=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 "<<nodep<<endl);
|
|
|
|
|
nodep->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 "<<nodep<<endl);
|
|
|
|
|
nodep->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: "<<nodep<<endl);
|
|
|
|
|
// Delayed variable is different from non-delayed variable
|
|
|
|
|
if (!vscp->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: "<<nodep<<endl);
|
|
|
|
|
for (VStack::iterator it = m_stmtStackps.begin(); it != m_stmtStackps.end(); ++it) {
|
|
|
|
|
new SplitLVEdge(&m_graph, vstdp, *it);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
UINFO(4," VARREF: "<<nodep<<endl);
|
|
|
|
|
for (VStack::iterator it = m_stmtStackps.begin(); it != m_stmtStackps.end(); ++it) {
|
|
|
|
|
new SplitRVEdge(&m_graph, *it, vstdp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
// Default
|
|
|
|
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
|
|
|
|
// **** SPECIAL default type that sets PLI_ORDERING
|
|
|
|
|
if (!m_stmtStackps.empty() && !nodep->isSplittable()) {
|
|
|
|
|
UINFO(9," NotSplittable "<<nodep<<endl);
|
|
|
|
|
scoreboardPli();
|
|
|
|
|
}
|
|
|
|
|
nodep->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__<<": "<<endl);
|
|
|
|
|
SplitVisitor visitor (nodep, true);
|
|
|
|
|
}
|
|
|
|
|
void V3Split::splitAlwaysAll(AstNetlist* nodep) {
|
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
|
|
|
|
SplitVisitor visitor (nodep, false);
|
|
|
|
|
}
|