verilator/src/V3Split.cpp
2013-01-01 09:42:59 -05:00

587 lines
22 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Break always into separate statements to reduce temps
//
// Code available from: http://www.veripool.org/verilator
//
//*************************************************************************
//
// Copyright 2003-2013 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.
//
//*************************************************************************
// 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->isPure()
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include <cstdio>
#include <cstdarg>
#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
// Do not make accessor for nodep(), It may change due to
// reordering a lower block, but we don't repair it
virtual string name() const {
if (m_nodep->name() == "") {
return cvtToStr((void*)m_nodep);
} else {
return m_nodep->name();
}
}
};
class SplitLogicVertex : public SplitNodeVertex {
uint32_t m_splitColor; // Copied from color() when determined
public:
SplitLogicVertex(V3Graph* graphp, AstNode* nodep)
: SplitNodeVertex(graphp,nodep) {}
void splitColor(uint32_t flag) { m_splitColor=flag; }
uint32_t splitColor() const { return m_splitColor; }
virtual ~SplitLogicVertex() {}
virtual string dotColor() const { return "yellow"; }
};
class SplitVarStdVertex : public SplitNodeVertex {
public:
SplitVarStdVertex(V3Graph* graphp, AstNode* nodep)
: SplitNodeVertex(graphp,nodep) {}
virtual ~SplitVarStdVertex() {}
virtual string dotColor() const { return "skyblue"; }
};
class SplitVarPostVertex : public SplitNodeVertex {
public:
SplitVarPostVertex(V3Graph* graphp, AstNode* nodep)
: SplitNodeVertex(graphp,nodep) {}
virtual ~SplitVarPostVertex() {}
virtual string name() const { return (string)"POST "+SplitNodeVertex::name(); }
virtual string dotColor() const { return "CadetBlue"; }
};
//######################################################################
// Edge types
class SplitEdge : public V3GraphEdge {
uint32_t m_ignoreInStep; // Step number that if set to, causes this edge to be ignored
static uint32_t s_stepNum; // Global step number
protected:
enum { WEIGHT_NORMAL=10 };
SplitEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top,
int weight, bool cutable=CUTABLE)
: V3GraphEdge(graphp, fromp, top, weight, cutable)
,m_ignoreInStep(0) {}
virtual ~SplitEdge() {}
public:
// Iterator for graph functions
static void incrementStep() { ++s_stepNum; }
bool ignoreThisStep() const { return m_ignoreInStep == s_stepNum; }
void setIgnoreThisStep() { m_ignoreInStep = s_stepNum; }
virtual bool followScoreboard() const = 0;
static bool followScoreboard(const V3GraphEdge* edgep) {
const SplitEdge* oedgep = dynamic_cast<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::user1p -> 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)
AstUser1InUse m_inuser1;
AstUser2InUse m_inuser2;
AstUser3InUse m_inuser3;
AstUser4InUse m_inuser4;
// TYPES
typedef vector<SplitLogicVertex*> 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
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
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
void scoreboardClear() {
//VV***** We reset user1p() and user2p on each block!!!
m_inDly = false;
m_graph.clear();
m_stmtStackps.clear();
m_pliVertexp = NULL;
m_noReorderWhy = "";
AstNode::user1ClearTree();
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
// 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
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();
if (color >= numVertexes) nextp->v3fatalSrc("More colors than vertexes!\n");
if (!color) nextp->v3fatalSrc("No node color assigned\n");
if (lastOfColor[color]) {
new SplitStrictEdge(&m_graph, lastOfColor[color], vvertexp);
}
lastOfColor[color] = vvertexp;
}
// And a real ordering to get the statements into something reasonable
// We don't care if there's cutable violations here...
// Non-cutable violations should be impossible; as those edges are program-order
if (debug()>=9) m_graph.dumpDotFilePrefixed((string)"splitg_preo", false);
m_graph.acyclic(&SplitEdge::followCyclic);
m_graph.rank(&SplitEdge::followCyclic); // Or order(), but that's more expensive
if (debug()>=9) m_graph.dumpDotFilePrefixed((string)"splitg_opt", false);
}
void reorderBlock(AstNode* nodep) {
// Reorder statements in the completed graph
AstAlways* splitAlwaysp = nodep->backp()->castAlways();
// Map the rank numbers into nodes they associate with
typedef multimap<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
AstNode* firstp = nodep; // We may reorder, and nodep is no longer first.
void* oldBlockUser3 = nodep->user3p(); // May be overloaded in below loop, save it
nodep->user3p(NULL);
if (!nodep->firstAbovep()) nodep->v3fatalSrc("Node passed is in next list; should have processed all list at once");
// Process it
if (!nodep->nextp()) {
// Just one, so can't reorder. Just look for more blocks/statements.
nodep->accept(*this);
} else {
UINFO(9," processBlock "<<nodep<<endl);
// Process block and followers
scanBlock(nodep);
if (m_noReorderWhy != "") { // Jump or something nasty
UINFO(9," NoReorderBlock because "<<m_noReorderWhy<<endl);
} else {
// Reorder statements in this block
cleanupBlockGraph(nodep);
reorderBlock(nodep);
// Delete old vertexes and edges only applying to this block
while (firstp->backp()->nextp()==firstp) firstp = firstp->backp(); // Walk back to first in list
for (AstNode* nextp=firstp; nextp; nextp=nextp->nextp()) {
SplitLogicVertex* vvertexp = (SplitLogicVertex*)nextp->user3p();
vvertexp->unlinkDelete(&m_graph);
}
}
}
// Again, nodep may no longer be first.
firstp->user3p(oldBlockUser3);
}
// VISITORS
virtual void visit(AstAlways* nodep, AstNUser*) {
UINFO(4," ALW "<<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->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: "<<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);
}
}
}
}
}
}
virtual void visit(AstJumpGo* nodep, AstNUser*) {
// Jumps will disable reordering at all levels
// This is overly pessimistic; we could treat jumps as barriers, and
// reorder everything between jumps/labels, however jumps are rare
// in always, so the performance gain probably isn't worth the work.
UINFO(9," NoReordering "<<nodep<<endl);
m_noReorderWhy = "JumpGo";
nodep->iterateChildren(*this);
}
//--------------------
// Default
virtual void visit(AstNode* nodep, AstNUser*) {
// **** SPECIAL default type that sets PLI_ORDERING
if (!m_stmtStackps.empty() && !nodep->isPure()) {
UINFO(9," NotSplittable "<<nodep<<endl);
scoreboardPli();
}
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
SplitVisitor(AstNetlist* 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);
}