2006-08-26 11:35:28 +00:00
|
|
|
|
// $Id$
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Block code ordering
|
|
|
|
|
//
|
|
|
|
|
// Code available from: http://www.veripool.com/verilator
|
|
|
|
|
//
|
|
|
|
|
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2007-01-02 22:06:40 +00:00
|
|
|
|
// Copyright 2003-2007 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.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Order's Transformations:
|
|
|
|
|
//
|
|
|
|
|
// Compute near optimal scheduling of always/wire statements
|
|
|
|
|
// Make a graph of the entire netlist
|
|
|
|
|
//
|
|
|
|
|
// Add master "*INPUTS*" vertex.
|
|
|
|
|
// For inputs on top level
|
|
|
|
|
// Add vertex for each input var.
|
|
|
|
|
// Add edge INPUTS->var_vertex
|
|
|
|
|
//
|
|
|
|
|
// For seq logic
|
|
|
|
|
// Add logic_sensitive_vertex for this list of SenItems
|
|
|
|
|
// Add edge for each sensitive_var->logic_sensitive_vertex
|
|
|
|
|
// For AssignPre's
|
|
|
|
|
// Add vertex for this logic
|
|
|
|
|
// Add edge logic_sensitive_vertex->logic_vertex
|
|
|
|
|
// Add edge logic_consumed_var_PREVAR->logic_vertex
|
|
|
|
|
// Add edge logic_vertex->logic_generated_var (same as if comb)
|
|
|
|
|
// Add edge logic_vertex->generated_var_PREORDER
|
|
|
|
|
// Cutable dependency to attempt to order dlyed
|
|
|
|
|
// assignments to avoid saving state, thus we prefer
|
|
|
|
|
// a <= b ... As the opposite order would
|
|
|
|
|
// b <= c ... require the old value of b.
|
|
|
|
|
// For Logic
|
|
|
|
|
// Add vertex for this logic
|
|
|
|
|
// Add edge logic_sensitive_vertex->logic_vertex
|
|
|
|
|
// Add edge logic_generated_var_PREORDER->logic_vertex
|
|
|
|
|
// This insures the AssignPre gets scheduled before this logic
|
|
|
|
|
// Add edge logic_vertex->consumed_var_PREVAR
|
|
|
|
|
// Add edge logic_vertex->consumed_var_POSTVAR
|
|
|
|
|
// Add edge logic_vertex->logic_generated_var (same as if comb)
|
|
|
|
|
// For AssignPost's
|
|
|
|
|
// Add vertex for this logic
|
|
|
|
|
// Add edge logic_sensitive_vertex->logic_vertex
|
|
|
|
|
// Add edge logic_consumed_var->logic_vertex (same as if comb)
|
|
|
|
|
// Add edge logic_vertex->logic_generated_var (same as if comb)
|
|
|
|
|
//
|
|
|
|
|
// For comb logic
|
|
|
|
|
// For comb logic
|
|
|
|
|
// Add vertex for this logic
|
|
|
|
|
// Add edge logic_consumed_var->logic_vertex
|
|
|
|
|
// Add edge logic_vertex->logic_generated_var
|
|
|
|
|
// Mark it cutable, as circular logic may require
|
|
|
|
|
// the generated signal to become a primary input again.
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
#ifdef NEW_ORDERING
|
|
|
|
|
// Determine nodes that form loops within combo logic
|
|
|
|
|
// (Strongly connected ignoring assign post/pre's)
|
|
|
|
|
// Make a subgraph for each loop of combo logic
|
|
|
|
|
// Break circular logic in each subgraph
|
|
|
|
|
//
|
|
|
|
|
// Determine nodes that form loops within a single eval call
|
|
|
|
|
// Make a subgraph for each loop of sequential logic
|
|
|
|
|
// Break circular logic in each subgraph
|
|
|
|
|
#endif
|
|
|
|
|
//
|
|
|
|
|
// Rank the graph starting at INPUTS (see V3Graph)
|
|
|
|
|
//
|
|
|
|
|
// Visit the graph's logic vertices in ranked order
|
|
|
|
|
// For all logic vertices with all inputs already ordered
|
|
|
|
|
// Make ordered block for this module
|
|
|
|
|
// For all ^^ in same domain
|
|
|
|
|
// Move logic to ordered activation
|
|
|
|
|
// When we have no more choices, we move to the next module
|
|
|
|
|
// and make a new block. Add that new activation block to the list of calls to make.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
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 <deque>
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <iomanip>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3File.h"
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
#include "V3Graph.h"
|
|
|
|
|
#include "V3List.h"
|
|
|
|
|
#include "V3SenTree.h"
|
|
|
|
|
#include "V3Stats.h"
|
2006-08-30 21:07:55 +00:00
|
|
|
|
#include "V3EmitCBase.h"
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
#include "V3Order.h"
|
|
|
|
|
#include "V3OrderGraph.h"
|
|
|
|
|
|
|
|
|
|
class OrderMoveDomScope;
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Functions for above graph classes
|
|
|
|
|
|
|
|
|
|
void OrderGraph::loopsVertexCb(V3GraphVertex* vertexp) {
|
|
|
|
|
if (debug()) cout<<"-Info-Loop: "<<vertexp<<" "<<endl;
|
|
|
|
|
if (OrderLogicVertex* vvertexp = dynamic_cast<OrderLogicVertex*>(vertexp)) {
|
|
|
|
|
cout<<V3Error::msgPrefix()<<" Example path: "<<vvertexp->nodep()->fileline()<<" "<<vvertexp->nodep()->typeName()<<endl;
|
|
|
|
|
}
|
|
|
|
|
if (OrderVarVertex* vvertexp = dynamic_cast<OrderVarVertex*>(vertexp)) {
|
|
|
|
|
cout<<V3Error::msgPrefix()<<" Example path: "<<vvertexp->varScp()->fileline()<<" "<<vvertexp->varScp()->prettyName()<<endl;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
|
|
|
|
class OrderMoveDomScope {
|
|
|
|
|
// Information stored for each unique loop, domain & scope trifecta
|
|
|
|
|
public:
|
|
|
|
|
V3ListEnt<OrderMoveDomScope*> m_readyDomScopeE;// List of next ready dom scope
|
|
|
|
|
V3List<OrderMoveVertex*> m_readyVertices; // Ready vertices with same domain & scope
|
|
|
|
|
private:
|
|
|
|
|
bool m_onReadyList; // True if DomScope is already on list of ready dom/scopes
|
|
|
|
|
AstSenTree* m_domainp; // Domain all vertices belong to
|
|
|
|
|
AstScope* m_scopep; // Scope all vertices belong to
|
|
|
|
|
OrderLoopId m_inLoop; // Loop member of
|
|
|
|
|
|
|
|
|
|
typedef pair<pair<OrderLoopId, AstSenTree*>, AstScope*> DomScopeKey;
|
|
|
|
|
typedef std::map<DomScopeKey, OrderMoveDomScope*> DomScopeMap;
|
|
|
|
|
static DomScopeMap s_dsMap; // Structure registered for each dom/scope pairing
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
OrderMoveDomScope(OrderLoopId inLoop, AstSenTree* domainp, AstScope* scopep)
|
|
|
|
|
: m_onReadyList(false), m_domainp(domainp), m_inLoop(inLoop) {}
|
|
|
|
|
OrderMoveDomScope* readyDomScopeNextp() const { return m_readyDomScopeE.nextp(); }
|
|
|
|
|
OrderLoopId inLoop() const { return m_inLoop; }
|
|
|
|
|
AstSenTree* domainp() const { return m_domainp; }
|
|
|
|
|
AstScope* scopep() const { return m_scopep; }
|
|
|
|
|
void ready(OrderVisitor* ovp); // Check the domScope is on ready list, add if not
|
|
|
|
|
void movedVertex(OrderVisitor* ovp, OrderMoveVertex* vertexp); // Mark one vertex as finished, remove from ready list if done
|
|
|
|
|
// STATIC MEMBERS (for lookup)
|
|
|
|
|
static void clear() {
|
2007-11-02 11:23:03 +00:00
|
|
|
|
for (DomScopeMap::iterator it=s_dsMap.begin(); it!=s_dsMap.end(); ++it) {
|
|
|
|
|
delete it->second;
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
s_dsMap.clear();
|
|
|
|
|
}
|
|
|
|
|
V3List<OrderMoveVertex*>& readyVertices() { return m_readyVertices; }
|
|
|
|
|
static OrderMoveDomScope* findCreate (OrderLoopId inLoop, AstSenTree* domainp, AstScope* scopep) {
|
|
|
|
|
const DomScopeKey key = make_pair(make_pair(inLoop,domainp),scopep);
|
|
|
|
|
DomScopeMap::iterator iter = s_dsMap.find(key);
|
|
|
|
|
if (iter != s_dsMap.end()) {
|
|
|
|
|
return iter->second;
|
|
|
|
|
} else {
|
|
|
|
|
OrderMoveDomScope* domScopep = new OrderMoveDomScope(inLoop, domainp, scopep);
|
|
|
|
|
s_dsMap.insert(make_pair(key, domScopep));
|
|
|
|
|
return domScopep;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
string name() const {
|
|
|
|
|
return (string("MDS:")
|
|
|
|
|
+" lp="+cvtToStr(inLoop())
|
|
|
|
|
+" d="+cvtToStr((void*)domainp())
|
|
|
|
|
+" s="+cvtToStr((void*)scopep()));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
OrderMoveDomScope::DomScopeMap OrderMoveDomScope::s_dsMap;
|
|
|
|
|
|
|
|
|
|
inline ostream& operator<< (ostream& lhs, const OrderMoveDomScope& rhs) {
|
|
|
|
|
lhs<<rhs.name();
|
|
|
|
|
return lhs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Order information stored under each AstNode::userp()...
|
|
|
|
|
|
|
|
|
|
// Types of vertex we can create
|
|
|
|
|
enum WhichVertex { WV_STD, WV_PRE, WV_PORD, WV_POST, WV_SETL,
|
|
|
|
|
WV_MAX};
|
|
|
|
|
|
|
|
|
|
class OrderUser {
|
|
|
|
|
// Stored in AstVarScope::userp, a list of all the various vertices
|
|
|
|
|
// that can exist for one given variable
|
|
|
|
|
private:
|
|
|
|
|
OrderVarVertex* m_vertexp[WV_MAX]; // Vertex of each type (if non NULL)
|
|
|
|
|
public:
|
|
|
|
|
// METHODS
|
|
|
|
|
OrderVarVertex* newVarUserVertex(V3Graph* graphp, AstScope* scopep,
|
|
|
|
|
AstVarScope* varscp, WhichVertex type, bool* createdp=NULL) {
|
|
|
|
|
if (type>=WV_MAX) varscp->v3fatalSrc("Bad Case\n");
|
|
|
|
|
OrderVarVertex* vertexp = m_vertexp[type];
|
|
|
|
|
if (!vertexp) {
|
|
|
|
|
UINFO(6,"New vertex "<<varscp<<endl);
|
|
|
|
|
if (createdp) *createdp=true;
|
|
|
|
|
switch (type) {
|
|
|
|
|
case WV_STD: vertexp = new OrderVarStdVertex (graphp, scopep, varscp); break;
|
|
|
|
|
case WV_PRE: vertexp = new OrderVarPreVertex (graphp, scopep, varscp); break;
|
|
|
|
|
case WV_PORD: vertexp = new OrderVarPordVertex (graphp, scopep, varscp); break;
|
|
|
|
|
case WV_POST: vertexp = new OrderVarPostVertex (graphp, scopep, varscp); break;
|
|
|
|
|
case WV_SETL: vertexp = new OrderVarSettleVertex(graphp, scopep, varscp); break;
|
|
|
|
|
default: varscp->v3fatalSrc("Bad Case\n");
|
|
|
|
|
}
|
|
|
|
|
m_vertexp[type] = vertexp;
|
|
|
|
|
} else {
|
|
|
|
|
if (createdp) *createdp=false;
|
|
|
|
|
}
|
|
|
|
|
return vertexp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
|
|
|
|
OrderUser() {
|
|
|
|
|
for (int i=0; i<WV_MAX; i++) m_vertexp[i]=NULL;
|
|
|
|
|
}
|
|
|
|
|
~OrderUser() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Order class functions
|
|
|
|
|
|
|
|
|
|
class OrderVisitor : public AstNVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// Forming graph:
|
|
|
|
|
// Entire Netlist:
|
|
|
|
|
// AstVarScope::userp -> OrderUser* for usage var
|
|
|
|
|
// AstActive::user3() -> uint clocks hash of sensitivity list
|
|
|
|
|
// {statement}Node::userp-> AstModule* statement is under
|
|
|
|
|
// USER5 Cleared on each Logic stmt
|
|
|
|
|
// AstVarScope::user5() -> VarUsage(gen/con/both). Where already encountered signal
|
|
|
|
|
// Ordering (user3/4/5 cleared between forming and ordering)
|
|
|
|
|
// AstScope::userp() -> AstModule*. Module this scope is under
|
|
|
|
|
// AstModule::user3() -> Number of routines created
|
|
|
|
|
|
|
|
|
|
//int debug() { return 9; }
|
|
|
|
|
|
|
|
|
|
// STATE
|
|
|
|
|
OrderGraph m_graph; // Scoreboard of var usages/dependencies
|
|
|
|
|
SenTreeFinder m_finder; // Find global sentree's and add them
|
|
|
|
|
AstSenTree* m_comboDomainp; // Combo activation tree
|
|
|
|
|
AstSenTree* m_deleteDomainp;// Delete this from tree
|
|
|
|
|
AstSenTree* m_settleDomainp;// Initial activation tree
|
|
|
|
|
OrderInputsVertex* m_inputsVxp; // Top level vertex all inputs point from
|
|
|
|
|
OrderSettleVertex* m_settleVxp; // Top level vertex all inputs point from
|
|
|
|
|
OrderLogicVertex* m_logicVxp; // Current statement being tracked, NULL=ignored
|
|
|
|
|
AstTopScope* m_topScopep; // Current top scope being processed
|
|
|
|
|
AstScope* m_scopetopp; // Scope under TOPSCOPE
|
|
|
|
|
AstModule* m_modp; // Current module
|
|
|
|
|
AstScope* m_scopep; // Current scope being processed
|
|
|
|
|
AstActive* m_activep; // Current activation block
|
|
|
|
|
bool m_inSenTree; // Underneath AstSenItem; any varrefs are clocks
|
|
|
|
|
bool m_inClocked; // Underneath clocked block
|
|
|
|
|
bool m_inPre; // Underneath AstAssignPre
|
|
|
|
|
bool m_inPost; // Underneath AstAssignPost
|
|
|
|
|
OrderLogicVertex* m_activeSenVxp; // Sensitivity vertex
|
|
|
|
|
deque<OrderUser*> m_orderUserps; // All created OrderUser's for later deletion.
|
|
|
|
|
// STATE... for inside process
|
|
|
|
|
OrderLoopId m_loopIdMax; // Maximum BeginLoop id number assigned
|
|
|
|
|
vector<OrderLoopEndVertex*> m_pmlLoopEndps; // processInsLoop: End vertex for each color
|
|
|
|
|
vector<OrderLoopBeginVertex*> m_pomLoopMoveps;// processMoveLoop: Loops next nodes are under
|
|
|
|
|
AstCFunc* m_pomNewFuncp; // Current function being created
|
2006-10-26 01:08:44 +00:00
|
|
|
|
int m_pomNewStmts; // Statements in function being created
|
2006-08-26 11:35:28 +00:00
|
|
|
|
V3Graph m_pomGraph; // Graph of logic elements to move
|
|
|
|
|
V3List<OrderMoveVertex*> m_pomWaiting; // List of nodes needing inputs to become ready
|
|
|
|
|
protected:
|
|
|
|
|
friend class OrderMoveDomScope;
|
|
|
|
|
V3List<OrderMoveDomScope*> m_pomReadyDomScope; // List of ready domain/scope pairs, by loopId
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// STATS
|
|
|
|
|
V3Double0 m_statCut[OrderVEdgeType::_ENUM_END]; // Count of each edge type cut
|
|
|
|
|
|
|
|
|
|
// TYPES
|
|
|
|
|
enum VarUsage { VU_NONE=0, VU_CON=1, VU_GEN=2 };
|
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
void iterateNewStmt(AstNode* nodep) {
|
|
|
|
|
if (m_scopep) {
|
|
|
|
|
UINFO(4," STMT "<<nodep<<endl);
|
|
|
|
|
//VV***** We reset user5p()
|
|
|
|
|
AstNode::user5ClearTree();
|
|
|
|
|
if (!m_activep || !m_activep->sensesp()) nodep->v3fatalSrc("NULL");
|
|
|
|
|
// If inside combo logic, ignore the domain, we'll assign one based on interconnect
|
|
|
|
|
AstSenTree* startDomainp = m_activep->sensesp();
|
|
|
|
|
if (startDomainp->hasCombo()) startDomainp=NULL;
|
|
|
|
|
m_logicVxp = new OrderLogicVertex(&m_graph, m_scopep, startDomainp, nodep);
|
|
|
|
|
if (m_activeSenVxp) {
|
|
|
|
|
// If in a clocked activation, add a link from the sensitivity to this block
|
|
|
|
|
// Add edge logic_sensitive_vertex->logic_vertex
|
|
|
|
|
new OrderEdge(&m_graph, m_activeSenVxp, m_logicVxp, WEIGHT_NORMAL);
|
|
|
|
|
}
|
|
|
|
|
nodep->userp(m_modp);
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_logicVxp = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OrderVarVertex* newVarUserVertex(AstVarScope* varscp, WhichVertex type, bool* createdp=NULL) {
|
|
|
|
|
if (!varscp->userp()) {
|
|
|
|
|
OrderUser* newup = new OrderUser();
|
|
|
|
|
m_orderUserps.push_back(newup);
|
|
|
|
|
varscp->userp(newup);
|
|
|
|
|
}
|
|
|
|
|
OrderUser* up = (OrderUser*)(varscp->userp());
|
|
|
|
|
return up->newVarUserVertex(&m_graph, m_scopep, varscp, type, createdp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
V3GraphEdge* findEndEdge(V3GraphVertex* vertexp, AstNode* errnodep, OrderLoopEndVertex*& evertexpr) {
|
|
|
|
|
// Given a vertex, find the end block corresponding to it
|
|
|
|
|
// Every vertex should have a pointer to the end block (one hopes)
|
|
|
|
|
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
|
|
|
|
if (OrderLoopEndVertex* evertexp = dynamic_cast<OrderLoopEndVertex*>(edgep->top())) {
|
|
|
|
|
evertexpr = evertexp;
|
|
|
|
|
return edgep;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
errnodep->v3fatalSrc("Loop-broken vertex doesn't have pointer to LoopEndVertex: "<<vertexp);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void process();
|
|
|
|
|
void processLoops(string stepName, V3EdgeFuncP edgeFuncp);
|
|
|
|
|
void processInsLoop();
|
|
|
|
|
void processInsLoopEdge(V3GraphEdge* edgep);
|
|
|
|
|
void processInsLoopNewEdge(V3GraphEdge* oldEdgep, V3GraphVertex* newFromp, V3GraphVertex* newTop);
|
|
|
|
|
OrderVarVertex* processInsLoopNewVar(OrderVarVertex* oldVertexp, bool& createdr);
|
|
|
|
|
void processBrokeLoop();
|
|
|
|
|
void processCircular();
|
|
|
|
|
void processSensitive();
|
|
|
|
|
void processDomains();
|
|
|
|
|
void processDomainsIterate(OrderEitherVertex* vertexp);
|
|
|
|
|
void processEdgeReport();
|
|
|
|
|
|
|
|
|
|
void processMove();
|
|
|
|
|
void processMoveClear();
|
|
|
|
|
void processMoveBuildGraph();
|
|
|
|
|
void processMoveBuildGraphIterate (OrderMoveVertex* moveVxp, V3GraphVertex* vertexp, int weightmin);
|
|
|
|
|
void processMovePrepScopes();
|
|
|
|
|
void processMovePrepReady();
|
|
|
|
|
void processMoveReadyOne(OrderMoveVertex* vertexp);
|
|
|
|
|
void processMoveDoneOne(OrderMoveVertex* vertexp);
|
|
|
|
|
void processMoveOne(OrderMoveVertex* vertexp, OrderMoveDomScope* domScopep, int level);
|
|
|
|
|
void processMoveLoopPush(OrderLoopBeginVertex* beginp);
|
|
|
|
|
void processMoveLoopPop(OrderLoopBeginVertex* beginp);
|
|
|
|
|
void processMoveLoopStmt(AstNode* newSubnodep);
|
|
|
|
|
OrderLoopId processMoveLoopCurrent();
|
|
|
|
|
|
|
|
|
|
string cfuncName(AstModule* modp, AstSenTree* domainp, AstScope* scopep, AstNode* forWhatp) {
|
|
|
|
|
modp->user3(1+modp->user3());
|
|
|
|
|
int funcnum = modp->user3();
|
|
|
|
|
string name = (domainp->hasCombo() ? "_combo"
|
|
|
|
|
: (domainp->hasInitial() ? "_initial"
|
|
|
|
|
: (domainp->hasSettle() ? "_settle"
|
|
|
|
|
: (domainp->isMulti() ? "_multiclk" : "_sequent"))));
|
|
|
|
|
name = name+"__"+scopep->nameDotless()+"__"+cvtToStr(funcnum);
|
|
|
|
|
if (v3Global.opt.profileCFuncs()) {
|
|
|
|
|
name += "__PROF__"+forWhatp->fileline()->profileFuncname();
|
|
|
|
|
}
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nodeMarkCircular(OrderVarVertex* vertexp, OrderEdge* edgep) {
|
|
|
|
|
AstVarScope* nodep = vertexp->varScp();
|
|
|
|
|
nodep->circular(true);
|
|
|
|
|
m_statCut[vertexp->type()]++;
|
|
|
|
|
if (edgep) m_statCut[edgep->type()]++;
|
|
|
|
|
if (vertexp->isClock()) {
|
|
|
|
|
// Seems obvious; no warning yet
|
|
|
|
|
//nodep->v3warn(GENCLK,"Signal unoptimizable: Generated clock: "<<nodep->prettyName());
|
|
|
|
|
} else if (nodep->varp()->isSigPublic()) {
|
|
|
|
|
nodep->v3warn(UNOPT,"Signal unoptimizable: Feedback to public clock or circular logic: "<<nodep->prettyName());
|
2007-01-26 21:53:03 +00:00
|
|
|
|
if (!nodep->fileline()->warnIsOff(V3ErrorCode::UNOPT)) {
|
|
|
|
|
nodep->fileline()->warnOff(V3ErrorCode::UNOPT, true); // Complain just once
|
|
|
|
|
// Give the user an example.
|
|
|
|
|
bool tempWeight = (edgep && edgep->weight()==0);
|
|
|
|
|
if (tempWeight) edgep->weight(1); // Else the below loop detect can't see the loop
|
|
|
|
|
m_graph.reportLoops(&OrderEdge::followComboConnected, vertexp); // calls OrderGraph::loopsVertexCb
|
|
|
|
|
if (tempWeight) edgep->weight(0);
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
} else {
|
|
|
|
|
// We don't use UNOPT, as there are lots of V2 places where it was needed, that aren't any more
|
|
|
|
|
// First v3warn not inside warnIsOff so we can see the suppressions with --debug
|
|
|
|
|
nodep->v3warn(UNOPTFLAT,"Signal unoptimizable: Feedback to clock or circular logic: "<<nodep->prettyName());
|
|
|
|
|
if (!nodep->fileline()->warnIsOff(V3ErrorCode::UNOPTFLAT)) {
|
|
|
|
|
nodep->fileline()->warnOff(V3ErrorCode::UNOPTFLAT, true); // Complain just once
|
|
|
|
|
// Give the user an example.
|
|
|
|
|
bool tempWeight = (edgep && edgep->weight()==0);
|
|
|
|
|
if (tempWeight) edgep->weight(1); // Else the below loop detect can't see the loop
|
|
|
|
|
m_graph.reportLoops(&OrderEdge::followComboConnected, vertexp); // calls OrderGraph::loopsVertexCb
|
|
|
|
|
if (tempWeight) edgep->weight(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
|
|
|
|
virtual void visit(AstNetlist* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
// We're finished, complete any unfinished topscopes
|
|
|
|
|
if (m_topScopep) { process(); m_topScopep=NULL; }
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstTopScope* nodep, AstNUser*) {
|
|
|
|
|
// Process the last thing we're finishing
|
|
|
|
|
if (m_topScopep) { process(); m_topScopep=NULL; }
|
|
|
|
|
UINFO(2," Loading tree...\n");
|
|
|
|
|
//VV***** We reset userp()
|
|
|
|
|
AstNode::userClearTree();
|
|
|
|
|
AstNode::user3ClearTree();
|
|
|
|
|
m_graph.clear();
|
|
|
|
|
m_activep = NULL;
|
|
|
|
|
m_topScopep = nodep;
|
2006-08-29 19:10:55 +00:00
|
|
|
|
m_scopetopp = nodep->scopep();
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Find sentree's
|
|
|
|
|
m_finder.main(m_topScopep);
|
|
|
|
|
// ProcessDomainsIterate will use these when it needs to move
|
|
|
|
|
// something to a combodomain. This saves a ton of find() operations.
|
2007-11-02 11:23:03 +00:00
|
|
|
|
AstSenTree* combp = new AstSenTree (nodep->fileline(), // Gets cloned() so ok if goes out of scope
|
|
|
|
|
new AstSenItem(nodep->fileline(), AstSenItem::Combo()));
|
|
|
|
|
m_comboDomainp = m_finder.getSenTree(nodep->fileline(), combp);
|
|
|
|
|
pushDeletep(combp); // Cleanup when done
|
|
|
|
|
AstSenTree* settlep = new AstSenTree (nodep->fileline(), // Gets cloned() so ok if goes out of scope
|
|
|
|
|
new AstSenItem(nodep->fileline(), AstSenItem::Settle()));
|
|
|
|
|
m_settleDomainp = m_finder.getSenTree(nodep->fileline(), settlep);
|
|
|
|
|
pushDeletep(settlep); // Cleanup when done
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Fake AstSenTree we set domainp to indicate needs deletion
|
|
|
|
|
m_deleteDomainp = new AstSenTree (nodep->fileline(),
|
|
|
|
|
new AstSenItem(nodep->fileline(), AstSenItem::Settle()));
|
2007-11-02 11:23:03 +00:00
|
|
|
|
pushDeletep(m_deleteDomainp); // Cleanup when done
|
2006-08-26 11:35:28 +00:00
|
|
|
|
UINFO(5," DeleteDomain = "<<m_deleteDomainp<<endl);
|
|
|
|
|
// Base vertices
|
|
|
|
|
m_activeSenVxp = NULL;
|
|
|
|
|
#ifdef NEW_ORDERING
|
|
|
|
|
m_inputsVxp = new OrderInputsVertex(&m_graph, m_comboDomainp);
|
|
|
|
|
m_settleVxp = new OrderSettleVertex(&m_graph, m_settleDomainp);
|
|
|
|
|
#else
|
|
|
|
|
m_inputsVxp = new OrderInputsVertex(&m_graph, NULL);
|
|
|
|
|
#endif
|
|
|
|
|
//
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
// Done topscope, erase extra user information
|
|
|
|
|
// userp passed to next process() operation
|
|
|
|
|
AstNode::user3ClearTree();
|
|
|
|
|
AstNode::user5ClearTree();
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstModule* nodep, AstNUser*) {
|
|
|
|
|
m_modp = nodep;
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstScope* nodep, AstNUser*) {
|
|
|
|
|
UINFO(4," SCOPE "<<nodep<<endl);
|
|
|
|
|
m_scopep = nodep;
|
|
|
|
|
m_logicVxp = NULL;
|
|
|
|
|
m_activeSenVxp = NULL;
|
|
|
|
|
nodep->userp(m_modp);
|
|
|
|
|
// Iterate
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_scopep = NULL;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstActive* nodep, AstNUser*) {
|
|
|
|
|
// Create required activation blocks and add to module
|
|
|
|
|
if (nodep->hasInitial()) return; // Ignore initials
|
|
|
|
|
UINFO(4," ACTIVE "<<nodep<<endl);
|
|
|
|
|
m_activep = nodep;
|
|
|
|
|
m_activeSenVxp = NULL;
|
|
|
|
|
m_inClocked = nodep->hasClocked();
|
|
|
|
|
// Compute hash of sensitivity list
|
|
|
|
|
V3Hash clocks;
|
|
|
|
|
if (nodep->sensesp()) {
|
|
|
|
|
for (AstSenItem* senp = nodep->sensesp()->sensesp(); senp; senp=senp->nextp()->castSenItem()) {
|
|
|
|
|
clocks = V3Hash(clocks, V3Hash(senp->edgeType()));
|
|
|
|
|
if (senp->varrefp()) {
|
|
|
|
|
clocks = V3Hash(clocks, V3Hash(senp->varrefp()->varScopep()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
nodep->user3(clocks.hshval());
|
|
|
|
|
// Grab the sensitivity list
|
|
|
|
|
if (nodep->sensesStorep()) nodep->v3fatalSrc("Senses should have been activeTop'ed to be global!");
|
|
|
|
|
nodep->sensesp()->accept(*this);
|
|
|
|
|
// Collect statements under it
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_activep = NULL;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstVarScope* nodep, AstNUser*) {
|
|
|
|
|
// Create links to all input signals
|
|
|
|
|
if (m_modp->isTop() && nodep->varp()->isInput()) {
|
|
|
|
|
OrderVarVertex* varVxp = newVarUserVertex(nodep, WV_STD);
|
|
|
|
|
new OrderEdge(&m_graph, m_inputsVxp, varVxp, WEIGHT_INPUT);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNodeVarRef* nodep, AstNUser*) {
|
|
|
|
|
if (m_scopep) {
|
|
|
|
|
AstVarScope* varscp = nodep->varScopep();
|
|
|
|
|
if (!varscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp\n");
|
|
|
|
|
if (m_inSenTree) {
|
|
|
|
|
// Add CLOCK dependency... This is a root of the tree we'll trace
|
|
|
|
|
if (nodep->lvalue()) nodep->v3fatalSrc("How can a sensitivity be setting a var?\n");
|
|
|
|
|
OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD);
|
|
|
|
|
varVxp->isClock(true);
|
|
|
|
|
new OrderEdge(&m_graph, varVxp, m_activeSenVxp, WEIGHT_MEDIUM);
|
|
|
|
|
} else {
|
|
|
|
|
if (!m_logicVxp) nodep->v3fatalSrc("Var ref not under a logic block\n");
|
|
|
|
|
// What new directions is this used
|
|
|
|
|
// We don't want to add extra edges if the logic block has many usages of same var
|
|
|
|
|
bool gen = false;
|
|
|
|
|
bool con = false;
|
|
|
|
|
if (nodep->lvalue()) {
|
|
|
|
|
gen = !(varscp->user5() & VU_GEN);
|
|
|
|
|
} else {
|
|
|
|
|
con = !(varscp->user5() & VU_CON);
|
|
|
|
|
if ((varscp->user5() & VU_GEN) && !m_inClocked) {
|
|
|
|
|
// Dangerous assumption:
|
|
|
|
|
// If a variable is used in the same activation which defines it first,
|
|
|
|
|
// consider it something like:
|
|
|
|
|
// foo = 1
|
|
|
|
|
// foo = foo + 1
|
|
|
|
|
// and still optimize. This is the rule verilog-mode assumes for /*AS*/
|
|
|
|
|
// Note this will break though:
|
|
|
|
|
// if (sometimes) foo = 1
|
|
|
|
|
// foo = foo + 1
|
|
|
|
|
con = false;
|
|
|
|
|
}
|
|
|
|
|
if (varscp->varp()->attrClockEn() && !m_inPre && !m_inPost && !m_inClocked) {
|
|
|
|
|
// clock_enable attribute: user's worring about it for us
|
|
|
|
|
con = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (gen) varscp->user5(varscp->user5() | VU_GEN);
|
|
|
|
|
if (con) varscp->user5(varscp->user5() | VU_CON);
|
|
|
|
|
// Add edges
|
|
|
|
|
if (!m_inClocked
|
|
|
|
|
|| m_inPost
|
|
|
|
|
) {
|
|
|
|
|
// Combo logic
|
|
|
|
|
if (
|
|
|
|
|
#ifdef NEW_ORDERING
|
|
|
|
|
m_activep && m_activep->hasSettle()
|
|
|
|
|
#else
|
|
|
|
|
0
|
|
|
|
|
#endif
|
|
|
|
|
) {
|
|
|
|
|
// Inside settlement; we use special variable names to prevent
|
|
|
|
|
// having extra logic to break arcs within
|
|
|
|
|
if (gen) {
|
|
|
|
|
// Add edge logic_vertex->logic_generated_var
|
|
|
|
|
bool created = false;
|
|
|
|
|
OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_SETL, &created);
|
|
|
|
|
new OrderComboCutEdge(&m_graph, m_logicVxp, varVxp);
|
|
|
|
|
#ifdef NEW_ORDERING
|
|
|
|
|
if (created) new OrderEdge(&m_graph, m_settleVxp, varVxp, WEIGHT_INPUT);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
if (con) {
|
|
|
|
|
// Add edge logic_consumed_var->logic_vertex
|
|
|
|
|
bool created = false;
|
|
|
|
|
OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_SETL, &created);
|
|
|
|
|
new OrderEdge(&m_graph, varVxp, m_logicVxp, WEIGHT_MEDIUM);
|
|
|
|
|
#ifdef NEW_ORDERING
|
|
|
|
|
if (created) new OrderEdge(&m_graph, m_settleVxp, varVxp, WEIGHT_INPUT);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
} else { // not settle and (combo or inPost)
|
|
|
|
|
if (gen) {
|
|
|
|
|
// Add edge logic_vertex->logic_generated_var
|
|
|
|
|
OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD);
|
|
|
|
|
if (m_inPost) {
|
|
|
|
|
new OrderPostCutEdge(&m_graph, m_logicVxp, varVxp);
|
|
|
|
|
} else {
|
|
|
|
|
new OrderComboCutEdge(&m_graph, m_logicVxp, varVxp);
|
|
|
|
|
}
|
|
|
|
|
// For m_inPost:
|
|
|
|
|
// Add edge consumed_var_POST->logic_vertex
|
|
|
|
|
// This prevents a consumer of the "early" value to be scheduled
|
|
|
|
|
// after we've changed to the next-cycle value
|
|
|
|
|
// ALWAYS do it:
|
|
|
|
|
// There maybe a wire a=b; between the two blocks
|
|
|
|
|
OrderVarVertex* postVxp = newVarUserVertex(varscp, WV_POST);
|
|
|
|
|
new OrderEdge(&m_graph, postVxp, m_logicVxp, WEIGHT_POST);
|
|
|
|
|
}
|
|
|
|
|
if (con) {
|
|
|
|
|
// Add edge logic_consumed_var->logic_vertex
|
|
|
|
|
OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD);
|
|
|
|
|
new OrderEdge(&m_graph, varVxp, m_logicVxp, WEIGHT_MEDIUM);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (m_inPre) {
|
|
|
|
|
// AstAssignPre logic
|
|
|
|
|
if (gen) {
|
|
|
|
|
// Add edge logic_vertex->generated_var_PREORDER
|
|
|
|
|
OrderVarVertex* ordVxp = newVarUserVertex(varscp, WV_PORD);
|
|
|
|
|
new OrderEdge(&m_graph, m_logicVxp, ordVxp, WEIGHT_NORMAL);
|
|
|
|
|
// Add edge logic_vertex->logic_generated_var (same as if comb)
|
|
|
|
|
OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD);
|
|
|
|
|
new OrderEdge(&m_graph, m_logicVxp, varVxp, WEIGHT_NORMAL);
|
|
|
|
|
}
|
|
|
|
|
if (con) {
|
|
|
|
|
// Add edge logic_consumed_var_PREVAR->logic_vertex
|
|
|
|
|
// This one is cutable (vs the producer) as there's only one of these, but many producers
|
|
|
|
|
OrderVarVertex* preVxp = newVarUserVertex(varscp, WV_PRE);
|
|
|
|
|
new OrderPreCutEdge(&m_graph, preVxp, m_logicVxp);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Seq logic
|
|
|
|
|
if (gen) {
|
|
|
|
|
// Add edge logic_generated_var_PREORDER->logic_vertex
|
|
|
|
|
OrderVarVertex* ordVxp = newVarUserVertex(varscp, WV_PORD);
|
|
|
|
|
new OrderEdge(&m_graph, ordVxp, m_logicVxp, WEIGHT_NORMAL);
|
|
|
|
|
// Add edge logic_vertex->logic_generated_var (same as if comb)
|
|
|
|
|
OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD);
|
|
|
|
|
new OrderEdge(&m_graph, m_logicVxp, varVxp, WEIGHT_NORMAL);
|
|
|
|
|
}
|
|
|
|
|
if (con) {
|
|
|
|
|
// Add edge logic_vertex->consumed_var_PREVAR
|
|
|
|
|
// Generation of 'pre' because we want to indicate it should be before AstAssignPre
|
|
|
|
|
OrderVarVertex* preVxp = newVarUserVertex(varscp, WV_PRE);
|
|
|
|
|
new OrderEdge(&m_graph, m_logicVxp, preVxp, WEIGHT_NORMAL);
|
|
|
|
|
// Add edge logic_vertex->consumed_var_POST
|
|
|
|
|
OrderVarVertex* postVxp = newVarUserVertex(varscp, WV_POST);
|
|
|
|
|
new OrderEdge(&m_graph, m_logicVxp, postVxp, WEIGHT_POST);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstSenTree* nodep, AstNUser*) {
|
|
|
|
|
// Having a node derived from the sentree isn't required for
|
|
|
|
|
// correctness, it mearly makes the graph better connected
|
|
|
|
|
// and improves graph algorithmic performance
|
|
|
|
|
if (m_scopep) { // Else TOPSCOPE's SENTREE list
|
|
|
|
|
m_inSenTree = true;
|
|
|
|
|
if (nodep->hasClocked()) {
|
|
|
|
|
if (!m_activeSenVxp) {
|
|
|
|
|
m_activeSenVxp = new OrderLogicVertex(&m_graph, m_scopep, nodep, m_activep);
|
|
|
|
|
}
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
m_inSenTree = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstAlways* nodep, AstNUser*) {
|
|
|
|
|
iterateNewStmt(nodep);
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstAlwaysPost* nodep, AstNUser*) {
|
|
|
|
|
m_inPost = true;
|
|
|
|
|
iterateNewStmt(nodep);
|
|
|
|
|
m_inPost = false;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstAssignAlias* nodep, AstNUser*) {
|
|
|
|
|
iterateNewStmt(nodep);
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstAssignW* nodep, AstNUser*) {
|
|
|
|
|
iterateNewStmt(nodep);
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstAssignPre* nodep, AstNUser*) {
|
|
|
|
|
m_inPre = true;
|
|
|
|
|
iterateNewStmt(nodep);
|
|
|
|
|
m_inPre = false;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstAssignPost* nodep, AstNUser*) {
|
|
|
|
|
m_inPost = true;
|
|
|
|
|
iterateNewStmt(nodep);
|
|
|
|
|
m_inPost = false;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstCFunc*, AstNUser*) {
|
|
|
|
|
// Ignore for now
|
|
|
|
|
// We should detect what variables are set in the function, and make
|
|
|
|
|
// settlement code for them, then set a global flag, so we call "settle"
|
|
|
|
|
// on the next evaluation loop.
|
|
|
|
|
}
|
|
|
|
|
//--------------------
|
|
|
|
|
// Default
|
|
|
|
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
|
|
|
|
OrderVisitor() {
|
|
|
|
|
m_topScopep = NULL;
|
|
|
|
|
m_scopetopp = NULL;
|
|
|
|
|
m_modp = NULL;
|
|
|
|
|
m_scopep = NULL;
|
|
|
|
|
m_activep = NULL;
|
|
|
|
|
m_inSenTree = false;
|
|
|
|
|
m_inClocked = false;
|
|
|
|
|
m_inPre = m_inPost = false;
|
|
|
|
|
m_comboDomainp = NULL;
|
|
|
|
|
m_deleteDomainp = NULL;
|
|
|
|
|
m_settleDomainp = NULL;
|
|
|
|
|
m_settleVxp = NULL;
|
|
|
|
|
m_inputsVxp = NULL;
|
|
|
|
|
m_loopIdMax = LOOPID_FIRST;
|
2006-10-26 01:08:44 +00:00
|
|
|
|
m_pomNewStmts = 0;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (debug()) m_graph.debug(5); // 3 is default if global debug; we want acyc debugging
|
|
|
|
|
}
|
|
|
|
|
virtual ~OrderVisitor() {
|
|
|
|
|
// Stats
|
|
|
|
|
for (int type=0; type<OrderVEdgeType::_ENUM_END; type++) {
|
|
|
|
|
if (double count = double(m_statCut[type])) {
|
|
|
|
|
V3Stats::addStat(string("Order, cut, ")+OrderVEdgeType(type).ascii(), count);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Destruction
|
|
|
|
|
for (deque<OrderUser*>::iterator it=m_orderUserps.begin(); it!=m_orderUserps.end(); ++it) {
|
|
|
|
|
delete *it;
|
|
|
|
|
}
|
|
|
|
|
m_graph.debug(V3Error::debugDefault());
|
|
|
|
|
}
|
|
|
|
|
void main(AstNode* nodep) {
|
|
|
|
|
nodep->accept(*this);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Pre-Loop elimination
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::processInsLoop() {
|
|
|
|
|
// Input is graph with color() reflecting the sub-graphs we need
|
|
|
|
|
// to loop remove. Take all the I/O from each subgraph and route
|
|
|
|
|
// through a new begin/end vertex
|
|
|
|
|
// Note we DON'T only do certain edges; we want all edges to be preserved,
|
|
|
|
|
// we'll select which are important later on.
|
|
|
|
|
|
|
|
|
|
uint32_t maxcolor = 0;
|
|
|
|
|
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
|
|
|
|
if (maxcolor <= vertexp->color()) maxcolor = vertexp->color() + 1;
|
|
|
|
|
if (OrderVarVertex* vvertexp = dynamic_cast<OrderVarVertex*>(vertexp)) {
|
|
|
|
|
vvertexp->pilNewVertexp(NULL); // Clear user-ish information before loop
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_pmlLoopEndps.clear();
|
|
|
|
|
m_pmlLoopEndps.reserve(maxcolor);
|
|
|
|
|
m_pmlLoopEndps.assign(maxcolor,NULL);
|
|
|
|
|
|
|
|
|
|
m_graph.userClearVertices(); // Vertex::user() // true if added as begin/end
|
|
|
|
|
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
|
|
|
|
if (uint32_t loopColor = vertexp->color()) {
|
|
|
|
|
OrderEitherVertex* evertexp = dynamic_cast<OrderEitherVertex*>(vertexp);
|
|
|
|
|
if (!evertexp) v3fatalSrc("Non either vertex found");
|
|
|
|
|
if (!vertexp->user()) {
|
|
|
|
|
UINFO(8," pil:nowInLoop lp="<<m_loopIdMax<<" waslp="
|
|
|
|
|
<<evertexp->inLoop()<<" "<<evertexp<<endl);
|
|
|
|
|
if (!m_pmlLoopEndps[loopColor]) {
|
|
|
|
|
AstUntilStable* untilp = new AstUntilStable(m_topScopep->fileline(), NULL, NULL);
|
|
|
|
|
m_topScopep->addStmtsp(untilp);
|
|
|
|
|
OrderLoopBeginVertex* beginp
|
|
|
|
|
= new OrderLoopBeginVertex(&m_graph, m_scopetopp,
|
|
|
|
|
evertexp->domainp(),
|
|
|
|
|
untilp,
|
|
|
|
|
m_loopIdMax, loopColor);
|
|
|
|
|
m_loopIdMax = (OrderLoopId)(m_loopIdMax+1);
|
|
|
|
|
UASSERT(LOOPID_MAX>m_loopIdMax, "loopid overflow "<<m_loopIdMax<<" "<<LOOPID_MAX);
|
|
|
|
|
OrderLoopEndVertex* endp = new OrderLoopEndVertex(&m_graph, beginp);
|
|
|
|
|
new OrderEdge(&m_graph, beginp, endp, WEIGHT_LOOPBE);
|
|
|
|
|
m_pmlLoopEndps[loopColor] = endp;
|
|
|
|
|
// Color edges to belong to subgraph
|
|
|
|
|
beginp->color(loopColor);
|
|
|
|
|
endp->color(loopColor);
|
|
|
|
|
beginp->user(1); // Added; don't iterate on it
|
|
|
|
|
endp->user(1); // Added; don't iterate on it
|
|
|
|
|
}
|
|
|
|
|
if (evertexp->inLoop()) {
|
|
|
|
|
// Adding node to loop, but already in loop...
|
|
|
|
|
// Ok if the nodes were a combo loop and now become a sequent loop,
|
|
|
|
|
// The combo loop will be "under" the sequent loop, so keep the combo #.
|
|
|
|
|
//UINFO(9, "Adding node to loop, but already in loop: "<<evertexp->name()<<endl);
|
|
|
|
|
} else {
|
|
|
|
|
// Make all nodes point from/to the begin/end nodes
|
|
|
|
|
OrderLoopEndVertex* endp = m_pmlLoopEndps[loopColor];
|
|
|
|
|
OrderLoopBeginVertex* beginp = endp->beginVertexp();
|
|
|
|
|
new OrderEdge(&m_graph, beginp, vertexp, WEIGHT_LOOPBE);
|
|
|
|
|
new OrderEdge(&m_graph, vertexp, endp, WEIGHT_LOOPBE);
|
|
|
|
|
evertexp->inLoop(beginp->loopId());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_graph.userClearEdges(); // Edge::user() // true if we added it
|
|
|
|
|
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
|
|
|
|
if (vertexp->color()) {
|
|
|
|
|
for (V3GraphEdge* nextp,* edgep = vertexp->outBeginp(); edgep; edgep = nextp) {
|
|
|
|
|
nextp = edgep->outNextp(); // Func may edit the list
|
|
|
|
|
if (edgep->weight()) {
|
|
|
|
|
processInsLoopEdge(edgep);
|
|
|
|
|
} else { // No purpose to this edge any longer
|
|
|
|
|
edgep->unlinkDelete(); edgep=NULL; // remove old edge
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (V3GraphEdge* nextp,* edgep = vertexp->inBeginp(); edgep; edgep = nextp) {
|
|
|
|
|
nextp = edgep->inNextp(); // Func may edit the list
|
|
|
|
|
if (edgep->weight()) {
|
|
|
|
|
processInsLoopEdge(edgep);
|
|
|
|
|
} else { // No purpose to this edge any longer
|
|
|
|
|
edgep->unlinkDelete(); edgep=NULL; // remove old edge
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Done with space
|
|
|
|
|
m_pmlLoopEndps.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::processInsLoopNewEdge(V3GraphEdge* oldEdgep, V3GraphVertex* newFromp, V3GraphVertex* newTop) {
|
|
|
|
|
// Create new edge based on the old edge's parameters
|
|
|
|
|
const OrderEdge* oedgep = dynamic_cast<const OrderEdge*>(oldEdgep);
|
|
|
|
|
V3GraphEdge* newEdgep = oedgep->clone(&m_graph, newFromp, newTop);
|
|
|
|
|
newEdgep->user(1); // New edge doesn't need to be changed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OrderVarVertex* OrderVisitor::processInsLoopNewVar(OrderVarVertex* oldVertexp, bool& createdr) {
|
|
|
|
|
// Create new VarVertex from given template
|
|
|
|
|
if (oldVertexp->pilNewVertexp()) {
|
|
|
|
|
createdr = false;
|
|
|
|
|
} else {
|
|
|
|
|
OrderVarVertex* newVertexp = oldVertexp->clone(&m_graph);
|
|
|
|
|
oldVertexp->pilNewVertexp(newVertexp);
|
|
|
|
|
createdr = true;
|
|
|
|
|
}
|
|
|
|
|
return oldVertexp->pilNewVertexp();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::processInsLoopEdge(V3GraphEdge* oldEdgep) {
|
|
|
|
|
if (!oldEdgep->user()) { // if not processed
|
|
|
|
|
oldEdgep->user(1); // mark as processed
|
|
|
|
|
if (oldEdgep->fromp()->color() == oldEdgep->top()->color()) {
|
|
|
|
|
return; // Doesn't cross subgraphs
|
|
|
|
|
} else {
|
|
|
|
|
V3GraphVertex* newFromp = oldEdgep->fromp();
|
|
|
|
|
V3GraphVertex* newTop = oldEdgep->top();
|
|
|
|
|
//UINFO(6, " pile: "<<newFromp<<" -> "<<newTop<<endl);
|
|
|
|
|
if (uint32_t fromId = oldEdgep->fromp()->color()) {
|
|
|
|
|
// Change to come from end block
|
|
|
|
|
OrderLoopEndVertex* endp = m_pmlLoopEndps[fromId];
|
|
|
|
|
newFromp = endp;
|
|
|
|
|
// If it goes from(to) a cutable VarVertex inside the Begin/End block,
|
|
|
|
|
// we can't loose the variable, as we might need to cut that variable out
|
|
|
|
|
// in the next pass of processLoops, and processBrokeLoops needs the var pointer.
|
|
|
|
|
// We'll make another VarVertex (dup of the one "inside" the loop)
|
|
|
|
|
// and point to it.
|
|
|
|
|
if (oldEdgep->cutable()) {
|
|
|
|
|
if (OrderVarVertex* vvFromp = dynamic_cast<OrderVarVertex*>(oldEdgep->fromp())) {
|
|
|
|
|
// end => newvarvtx -> {whatever}
|
|
|
|
|
bool created;
|
|
|
|
|
newFromp = processInsLoopNewVar(vvFromp, created/*ref*/);
|
|
|
|
|
if (created) processInsLoopNewEdge(oldEdgep, endp, newFromp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (uint32_t toId = oldEdgep->top()->color()) {
|
|
|
|
|
// Change to go to begin block
|
|
|
|
|
OrderLoopEndVertex* endp = m_pmlLoopEndps[toId];
|
|
|
|
|
OrderLoopBeginVertex* beginp = endp->beginVertexp();
|
|
|
|
|
newTop = beginp;
|
|
|
|
|
// Ditto above
|
|
|
|
|
if (oldEdgep->cutable()) {
|
|
|
|
|
if (OrderVarVertex* vvTop = dynamic_cast<OrderVarVertex*>(oldEdgep->top())) {
|
|
|
|
|
// oldfrom -> newvarvtx => begin
|
|
|
|
|
bool created;
|
|
|
|
|
newTop = processInsLoopNewVar(vvTop, created/*ref*/);
|
|
|
|
|
if (created) processInsLoopNewEdge(oldEdgep, newTop, beginp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// New edge with appropriate to/from
|
|
|
|
|
processInsLoopNewEdge(oldEdgep, newFromp, newTop);
|
|
|
|
|
oldEdgep->unlinkDelete(); oldEdgep=NULL; // remove old edge
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Pre-Loop elimination
|
|
|
|
|
|
|
|
|
|
// NEW_ORDERING
|
|
|
|
|
void OrderVisitor::processBrokeLoop() {
|
|
|
|
|
// Find those loops that were broken
|
|
|
|
|
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
|
|
|
|
// Above processInsLoopEdge makes sure there's always a OrderVarVertex on
|
|
|
|
|
// boundaries to/from LoopBegin/EndVertex'es
|
|
|
|
|
if (OrderVarVertex* vvertexp = dynamic_cast<OrderVarVertex*>(vertexp)) {
|
|
|
|
|
// Any cut edges?
|
|
|
|
|
bool anyCut = false;
|
|
|
|
|
for (V3GraphEdge* nextp,* edgep = vertexp->inBeginp(); edgep; edgep = nextp) {
|
|
|
|
|
nextp = edgep->inNextp(); // We may edit the list
|
|
|
|
|
if (edgep->weight()==0) { // was cut
|
|
|
|
|
anyCut = true;
|
|
|
|
|
edgep->unlinkDelete(); edgep=NULL; // remove old edge
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (V3GraphEdge* nextp,* edgep = vertexp->outBeginp(); edgep; edgep = nextp) {
|
|
|
|
|
nextp = edgep->outNextp(); // We may edit the list
|
|
|
|
|
if (edgep->weight()==0) { // was cut
|
|
|
|
|
anyCut = true;
|
|
|
|
|
edgep->unlinkDelete(); edgep=NULL; // remove old edge
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (anyCut) {
|
|
|
|
|
UINFO(6," pbl: Cut "<<vertexp->name()<<endl);
|
|
|
|
|
nodeMarkCircular(vvertexp, NULL);
|
|
|
|
|
OrderLoopEndVertex* endp = NULL; // Set below in findEndEdge
|
|
|
|
|
V3GraphEdge* endedgep = findEndEdge(vertexp, vvertexp->varScp(), endp/*ref*/);
|
|
|
|
|
// Add edge to graphically indicate change detect required
|
|
|
|
|
endedgep->unlinkDelete(); endedgep=NULL; // remove old edge
|
|
|
|
|
new OrderChangeDetEdge(&m_graph, vvertexp, endp);
|
|
|
|
|
// Add variable dependency to until loop
|
|
|
|
|
AstUntilStable* untilp = endp->beginVertexp()->untilp();
|
|
|
|
|
untilp->addStablesp(new AstVarRef(vvertexp->varScp()->fileline(), vvertexp->varScp(), false));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Circular detection
|
|
|
|
|
|
|
|
|
|
#ifndef NEW_ORDERING
|
|
|
|
|
void OrderVisitor::processCircular() {
|
|
|
|
|
// Take broken edges and add circular flags
|
|
|
|
|
// The change detect code will use this to force changedets
|
|
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
|
|
|
|
if (OrderVarStdVertex* vvertexp = dynamic_cast<OrderVarStdVertex*>(itp)) {
|
|
|
|
|
if (vvertexp->isClock() && !vvertexp->varScp()->varp()->isPrimaryIn()) {
|
|
|
|
|
// If a clock is generated internally, we need to do another loop
|
|
|
|
|
// through the entire evaluation. This fixes races; see t_clk_dpulse test.
|
|
|
|
|
UINFO(5,"Circular Clock "<<vvertexp<<endl);
|
|
|
|
|
nodeMarkCircular(vvertexp, NULL);
|
|
|
|
|
}
|
|
|
|
|
// Also mark any cut edges
|
|
|
|
|
for (V3GraphEdge* edgep = vvertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
|
|
|
|
if (edgep->weight()==0) { // was cut
|
|
|
|
|
OrderEdge* oedgep = dynamic_cast<OrderEdge*>(edgep);
|
|
|
|
|
if (!oedgep) vvertexp->varScp()->v3fatalSrc("Cuttable edge not of proper type");
|
|
|
|
|
nodeMarkCircular(vvertexp, oedgep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
|
|
|
|
if (edgep->weight()==0) { // was cut
|
|
|
|
|
OrderEdge* oedgep = dynamic_cast<OrderEdge*>(edgep);
|
|
|
|
|
if (!oedgep) vvertexp->varScp()->v3fatalSrc("Cuttable edge not of proper type");
|
|
|
|
|
nodeMarkCircular(vvertexp, oedgep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::processSensitive() {
|
|
|
|
|
// Sc sensitives are required on all inputs that go to a combo
|
|
|
|
|
// block. (Not inputs that go only to clocked blocks.)
|
|
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
|
|
|
|
if (OrderVarStdVertex* vvertexp = dynamic_cast<OrderVarStdVertex*>(itp)) {
|
|
|
|
|
if (vvertexp->varScp()->varp()->isInput()) {
|
|
|
|
|
//UINFO(0," scsen "<<vvertexp<<endl);
|
|
|
|
|
for (V3GraphEdge* edgep = vvertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
|
|
|
|
if (OrderEitherVertex* toVertexp = dynamic_cast<OrderEitherVertex*>(edgep->top())) {
|
|
|
|
|
if (edgep->weight() && toVertexp->domainp()) {
|
|
|
|
|
//UINFO(0," "<<toVertexp->domainp()<<endl);
|
|
|
|
|
if (toVertexp->domainp()->hasCombo()) {
|
|
|
|
|
vvertexp->varScp()->varp()->scSensitive(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::processDomains() {
|
|
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
|
|
|
|
OrderEitherVertex* vertexp = dynamic_cast<OrderEitherVertex*>(itp);
|
|
|
|
|
UASSERT(vertexp, "Null or vertex not derived from EitherVertex\n");
|
|
|
|
|
processDomainsIterate(vertexp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::processDomainsIterate(OrderEitherVertex* vertexp) {
|
|
|
|
|
// The graph routines have already sorted the vertexes and edges into best->worst order
|
|
|
|
|
// Assign clock domains to each signal.
|
|
|
|
|
// Sequential logic is forced into the same sequential domain.
|
|
|
|
|
// Combo logic may be pushed into a seq domain if all its inputs are the same domain,
|
|
|
|
|
// else, if all inputs are from flops, it's end-of-sequential code
|
|
|
|
|
// else, it's full combo code
|
|
|
|
|
if (!vertexp->inLoop()) vertexp->inLoop(LOOPID_NOTLOOPED);
|
|
|
|
|
if (vertexp->domainp()) return; // Already processed, or sequential logic
|
|
|
|
|
UINFO(5," pdi: "<<vertexp<<endl);
|
|
|
|
|
OrderVarVertex* vvertexp = dynamic_cast<OrderVarVertex*>(vertexp);
|
|
|
|
|
AstSenTree* domainp = NULL;
|
|
|
|
|
UASSERT(m_comboDomainp, "not preset");
|
|
|
|
|
#ifndef NEW_ORDERING // New ordering set the input node as combo, so this happens automatically
|
|
|
|
|
if (vvertexp && vvertexp->varScp()->varp()->isInput()) {
|
|
|
|
|
domainp = m_comboDomainp;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
if (vvertexp && vvertexp->varScp()->isCircular()) {
|
|
|
|
|
domainp = m_comboDomainp;
|
|
|
|
|
}
|
|
|
|
|
if (!domainp) {
|
|
|
|
|
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
|
|
|
|
OrderEitherVertex* fromVertexp = (OrderEitherVertex*)edgep->fromp();
|
|
|
|
|
if (edgep->weight()
|
|
|
|
|
#ifndef NEW_ORDERING
|
|
|
|
|
&& fromVertexp->domainMatters()
|
|
|
|
|
#endif
|
|
|
|
|
) {
|
|
|
|
|
UINFO(9," from d="<<(void*)fromVertexp->domainp()<<" "<<fromVertexp<<endl);
|
|
|
|
|
if (!domainp // First input to this vertex
|
|
|
|
|
|| domainp->hasSettle()) { // or, we can ignore being in the settle domain
|
|
|
|
|
domainp = fromVertexp->domainp();
|
|
|
|
|
}
|
|
|
|
|
else if (domainp->hasCombo()) {
|
|
|
|
|
// Once in combo, keep in combo; already as severe as we can get
|
|
|
|
|
}
|
|
|
|
|
else if (fromVertexp->domainp()->hasCombo()) {
|
|
|
|
|
// Any combo input means this vertex must remain combo
|
|
|
|
|
domainp = m_comboDomainp;
|
|
|
|
|
}
|
|
|
|
|
else if (fromVertexp->domainp()->hasSettle()) {
|
|
|
|
|
// Ignore that we have a constant (initial) input
|
|
|
|
|
}
|
|
|
|
|
else if (domainp != fromVertexp->domainp()) {
|
|
|
|
|
// Make a domain that merges the two domains
|
|
|
|
|
bool ddebug = debug()>=9;
|
|
|
|
|
if (ddebug) {
|
|
|
|
|
cout<<endl;
|
|
|
|
|
UINFO(0," conflicting domain "<<fromVertexp<<endl);
|
|
|
|
|
UINFO(0," dorig="<<domainp<<endl);
|
|
|
|
|
domainp->dumpTree(cout);
|
|
|
|
|
UINFO(0," d2 ="<<fromVertexp->domainp()<<endl);
|
|
|
|
|
fromVertexp->domainp()->dumpTree(cout);
|
|
|
|
|
}
|
|
|
|
|
AstSenTree* newtreep = domainp->cloneTree(false)->castSenTree();
|
|
|
|
|
AstSenItem* newtree2p = fromVertexp->domainp()->sensesp()->cloneTree(true)->castSenItem();
|
|
|
|
|
if (!newtree2p) fromVertexp->domainp()->v3fatalSrc("No senitem found under clocked domain");
|
|
|
|
|
newtreep->addSensesp(newtree2p);
|
|
|
|
|
newtreep->sortSenses(); // Remove duplicates
|
|
|
|
|
newtreep->multi(true); // Comment that it was made from 2 clock domains
|
|
|
|
|
domainp = m_finder.getSenTree(domainp->fileline(), newtreep);
|
|
|
|
|
if (ddebug) {
|
|
|
|
|
UINFO(0," dnew ="<<newtreep<<endl);
|
|
|
|
|
newtreep->dumpTree(cout);
|
|
|
|
|
UINFO(0," find ="<<domainp<<endl);
|
|
|
|
|
domainp->dumpTree(cout);
|
|
|
|
|
cout<<endl;
|
|
|
|
|
}
|
|
|
|
|
newtreep->deleteTree(); newtreep=NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} // next input edgep
|
|
|
|
|
// Default the domain
|
|
|
|
|
// This is a node which has only constant inputs, or is otherwise indeterminate.
|
|
|
|
|
// It should have already been copied into the settle domain. Presumably it has
|
|
|
|
|
// inputs which we never trigger, or nothing it's sensitive to, so we can rip it out.
|
|
|
|
|
if (!domainp && vertexp->scopep()) {
|
|
|
|
|
domainp = m_deleteDomainp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//
|
|
|
|
|
vertexp->domainp(domainp);
|
|
|
|
|
if (vertexp->domainp()) {
|
|
|
|
|
UINFO(5," done d="<<(void*)vertexp->domainp()
|
|
|
|
|
<<(vertexp->domainp()->hasCombo()?" [COMB]":"")
|
|
|
|
|
<<(vertexp->domainp()->isMulti()?" [MULT]":"")
|
|
|
|
|
<<" "<<vertexp<<endl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Move graph construction
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::processEdgeReport() {
|
|
|
|
|
// Make report of all signal names and what clock edges they have
|
|
|
|
|
string filename = v3Global.debugFilename("order_edges.txt");
|
|
|
|
|
const auto_ptr<ofstream> logp (V3File::new_ofstream(filename));
|
|
|
|
|
if (logp->fail()) v3fatalSrc("Can't write "<<filename);
|
|
|
|
|
|
|
|
|
|
deque<string> report;
|
|
|
|
|
|
|
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
|
|
|
|
if (OrderVarVertex* vvertexp = dynamic_cast<OrderVarVertex*>(itp)) {
|
|
|
|
|
string name (vvertexp->varScp()->prettyName());
|
|
|
|
|
if (dynamic_cast<OrderVarPreVertex*>(itp)) name += " {PRE}";
|
|
|
|
|
else if (dynamic_cast<OrderVarPostVertex*>(itp)) name += " {POST}";
|
|
|
|
|
else if (dynamic_cast<OrderVarPordVertex*>(itp)) name += " {PORD}";
|
|
|
|
|
else if (dynamic_cast<OrderVarSettleVertex*>(itp)) name += " {STL}";
|
|
|
|
|
ostringstream os;
|
|
|
|
|
os.setf(ios::left);
|
|
|
|
|
os<<" "<<setw(50)<<name<<" ";
|
|
|
|
|
AstSenTree* sentreep = vvertexp->domainp();
|
|
|
|
|
if (sentreep) {
|
|
|
|
|
bool first = true;
|
|
|
|
|
for (AstSenItem* itemp=sentreep->sensesp(); itemp; itemp=itemp->nextp()->castSenItem()) {
|
|
|
|
|
if (first) os<<"@( "; else os<<", ";
|
|
|
|
|
first = false;
|
|
|
|
|
os<<itemp->edgeType().verilogKwd();
|
|
|
|
|
if (itemp->varrefp()) {
|
|
|
|
|
os<<" "+itemp->varrefp()->prettyName();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!first) os<<" )";
|
|
|
|
|
}
|
|
|
|
|
report.push_back(os.str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*logp<<"Signals and their clock domains:"<<endl;
|
|
|
|
|
sort(report.begin(), report.end());
|
|
|
|
|
for (deque<string>::iterator it=report.begin(); it!=report.end(); ++it) {
|
|
|
|
|
*logp<<(*it)<<endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Move graph construction
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::processMoveClear() {
|
|
|
|
|
OrderMoveDomScope::clear();
|
|
|
|
|
m_pomWaiting.reset();
|
|
|
|
|
m_pomReadyDomScope.reset();
|
|
|
|
|
m_pomGraph.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::processMoveBuildGraph() {
|
|
|
|
|
// Build graph of only vertices
|
|
|
|
|
UINFO(5," MoveBuildGraph\n");
|
|
|
|
|
processMoveClear();
|
|
|
|
|
m_pomGraph.userClearVertices(); // Vertex::user() // OrderMoveVertex*, last edge added or NULL for none
|
|
|
|
|
|
|
|
|
|
// For each logic node, make a graph node
|
|
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
|
|
|
|
if (OrderLogicVertex* lvertexp = dynamic_cast<OrderLogicVertex*>(itp)) {
|
|
|
|
|
OrderMoveVertex* moveVxp = new OrderMoveVertex(&m_pomGraph, lvertexp);
|
|
|
|
|
moveVxp->m_pomWaitingE.pushBack(m_pomWaiting, moveVxp);
|
|
|
|
|
// Cross link so we can find it later
|
|
|
|
|
lvertexp->moveVxp(moveVxp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Build edges between logic vertices
|
|
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
|
|
|
|
if (OrderLogicVertex* lvertexp = dynamic_cast<OrderLogicVertex*>(itp)) {
|
|
|
|
|
OrderMoveVertex* moveVxp = lvertexp->moveVxp();
|
|
|
|
|
processMoveBuildGraphIterate(moveVxp, lvertexp, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::processMoveBuildGraphIterate (OrderMoveVertex* moveVxp, V3GraphVertex* vertexp, int weightmin) {
|
|
|
|
|
// Search forward from given logic vertex, making new edges based on moveVxp
|
|
|
|
|
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
|
|
|
|
if (edgep->weight()!=0) { // was cut
|
|
|
|
|
int weight = weightmin;
|
|
|
|
|
if (weight==0 || weight>edgep->weight()) weight=edgep->weight();
|
|
|
|
|
if (OrderLogicVertex* toLVertexp = dynamic_cast<OrderLogicVertex*>(edgep->top())) {
|
|
|
|
|
// Path from vertexp to a logic vertex; new edge
|
|
|
|
|
// Note we use the last edge's weight, not some function of multiple edges
|
|
|
|
|
new OrderEdge(&m_pomGraph, moveVxp, toLVertexp->moveVxp(), weight);
|
|
|
|
|
}
|
|
|
|
|
else { // Keep hunting forward for a logic node
|
|
|
|
|
processMoveBuildGraphIterate(moveVxp, edgep->top(), weight);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Moving
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::processMove() {
|
|
|
|
|
// The graph routines have already sorted the vertexes and edges into best->worst order
|
|
|
|
|
// Make a new waiting graph with only OrderLogicVertex's
|
|
|
|
|
// (Order is preserved in the recreation so the sorting is preserved)
|
|
|
|
|
// Move any node with all inputs ready to a "ready" graph mapped by domain and then scope
|
|
|
|
|
// While waiting graph ! empty (and also known: something in ready graph)
|
|
|
|
|
// For all scopes in domain of top ready vertex
|
|
|
|
|
// For all vertexes in domain&scope of top ready vertex
|
|
|
|
|
// Make ordered activation block for this module
|
|
|
|
|
// Add that new activation to the list of calls to make.
|
|
|
|
|
// Move logic to ordered active
|
|
|
|
|
// Any children that have all inputs now ready move from waiting->ready graph
|
|
|
|
|
// (This may add nodes the for loop directly above needs to detext)
|
|
|
|
|
processMovePrepScopes();
|
|
|
|
|
processMovePrepReady();
|
|
|
|
|
|
|
|
|
|
// New domain... another loop
|
|
|
|
|
UINFO(5," MoveIterate\n");
|
|
|
|
|
#ifdef NEW_ORDERING
|
|
|
|
|
OrderMoveDomScope* domScopep = NULL; // Currently active domain/scope
|
|
|
|
|
while (!m_pomReadyDomScope.empty()) {
|
|
|
|
|
// Always need to reamin in same loop construct
|
|
|
|
|
OrderLoopId curLoop = processMoveLoopCurrent();
|
|
|
|
|
// Scan list to find search candidates
|
|
|
|
|
OrderMoveDomScope* loopHuntp = NULL; // Found domscope under same loop
|
|
|
|
|
OrderMoveDomScope* domHuntp = NULL; // Found domscope under same domain
|
|
|
|
|
OrderMoveDomScope* scopeHuntp = NULL; // Found domscope under same scope
|
|
|
|
|
if (domScopep) { UINFO(6," MoveSearch: loop="<<curLoop<<" "<<*domScopep<<endl); }
|
|
|
|
|
else { UINFO(6," MoveSearch: loop="<<curLoop<<" NULL"<<endl); }
|
|
|
|
|
for (OrderMoveDomScope* huntp = m_pomReadyDomScope.begin(); huntp; huntp = huntp->readyDomScopeNextp()) {
|
|
|
|
|
if (huntp->inLoop() == curLoop) {
|
|
|
|
|
if (!loopHuntp) loopHuntp = huntp;
|
|
|
|
|
if (domScopep && huntp->domainp() == domScopep->domainp()) {
|
|
|
|
|
if (!domHuntp) domHuntp = huntp;
|
|
|
|
|
if (domScopep && huntp->scopep() == domScopep->scopep()) {
|
|
|
|
|
if (!scopeHuntp) scopeHuntp = huntp;
|
|
|
|
|
break; // Exact match; all we can hope for
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Recompute the next domScopep to process
|
|
|
|
|
if (scopeHuntp) {
|
|
|
|
|
domScopep = scopeHuntp;
|
|
|
|
|
UINFO(6," MoveIt: SameScope "<<*domScopep<<endl);
|
|
|
|
|
} else if (domHuntp) { // No exact scope matches, try only matching the domain
|
|
|
|
|
domScopep = domHuntp;
|
|
|
|
|
UINFO(6," MoveIt: SameDomain "<<*domScopep<<endl);
|
|
|
|
|
} else if (loopHuntp) { // No exact scope or domain matches, only match the loop
|
|
|
|
|
domScopep = loopHuntp;
|
|
|
|
|
UINFO(6," MoveIt: SameLoop "<<*domScopep<<endl);
|
|
|
|
|
} else { // else we're hopefully all done
|
|
|
|
|
if (curLoop != LOOPID_NOTLOOPED)
|
|
|
|
|
domScopep->domainp()->v3fatalSrc("Can't find more nodes like "<<*domScopep); // Should be at least a "end loop"
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// Work on all vertices in this loop/domain/scope
|
|
|
|
|
OrderMoveVertex* topVertexp = domScopep->readyVertices().begin();
|
|
|
|
|
UASSERT(topVertexp, "domScope on ready list without any nodes ready under it");
|
|
|
|
|
m_pomNewFuncp = NULL;
|
|
|
|
|
while (OrderMoveVertex* vertexp = domScopep->readyVertices().begin()) {
|
|
|
|
|
processMoveOne(vertexp, domScopep, 1);
|
|
|
|
|
if (curLoop != processMoveLoopCurrent()) break; // Hit a LoopBegin/end, change loop
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!m_pomWaiting.empty()) {
|
|
|
|
|
OrderMoveVertex* vertexp = m_pomWaiting.begin();
|
|
|
|
|
vertexp->logicp()->nodep()->v3fatalSrc("Didn't converge; nodes waiting, none ready, perhaps some input activations lost: "<<vertexp<<endl);
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
while (!m_pomReadyDomScope.empty()) {
|
|
|
|
|
// Start with top node on ready list's domain & scope
|
|
|
|
|
OrderMoveDomScope* domScopep = m_pomReadyDomScope.begin();
|
|
|
|
|
OrderMoveVertex* topVertexp = domScopep->readyVertices().begin();
|
|
|
|
|
UASSERT(topVertexp, "domScope on ready list without any nodes ready under it");
|
|
|
|
|
// Work on all scopes ready inside this domain
|
|
|
|
|
while (domScopep) {
|
|
|
|
|
UINFO(6," MoveDomain l="<<domScopep->domainp()<<endl);
|
|
|
|
|
// Process all nodes ready under same domain & scope
|
|
|
|
|
m_pomNewFuncp = NULL;
|
|
|
|
|
while (OrderMoveVertex* vertexp = domScopep->readyVertices().begin()) {
|
|
|
|
|
processMoveOne(vertexp, domScopep, 1);
|
|
|
|
|
}
|
|
|
|
|
// Done with scope/domain pair, pick new scope under same domain, or NULL if none left
|
|
|
|
|
OrderMoveDomScope* domScopeNextp = NULL;
|
|
|
|
|
for (OrderMoveDomScope* huntp = m_pomReadyDomScope.begin();
|
|
|
|
|
huntp; huntp = huntp->readyDomScopeNextp()) {
|
|
|
|
|
if (huntp->domainp() == domScopep->domainp()) {
|
|
|
|
|
domScopeNextp = huntp;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
domScopep = domScopeNextp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
UASSERT (m_pomWaiting.empty(), "Didn't converge; nodes waiting, none ready, perhaps some input activations lost.");
|
|
|
|
|
#endif
|
|
|
|
|
// Cleanup memory
|
|
|
|
|
processMoveClear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::processMovePrepScopes() {
|
|
|
|
|
UINFO(5," MovePrepScopes\n");
|
|
|
|
|
// Create a OrderMoveDomScope every domain/scope pairing
|
|
|
|
|
for (OrderMoveVertex* vertexp = m_pomWaiting.begin(); vertexp; vertexp=vertexp->pomWaitingNextp()) {
|
|
|
|
|
AstSenTree* domainp = vertexp->logicp()->domainp();
|
|
|
|
|
AstScope* scopep = vertexp->logicp()->scopep();
|
|
|
|
|
OrderLoopId inLoop = vertexp->logicp()->inLoop();
|
|
|
|
|
// Create the dom pairing for later lookup
|
|
|
|
|
OrderMoveDomScope* domScopep = OrderMoveDomScope::findCreate(inLoop, domainp, scopep);
|
|
|
|
|
vertexp->domScopep(domScopep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::processMovePrepReady() {
|
|
|
|
|
// Make list of ready nodes
|
|
|
|
|
UINFO(5," MovePrepReady\n");
|
|
|
|
|
for (OrderMoveVertex* vertexp = m_pomWaiting.begin(); vertexp; ) {
|
|
|
|
|
OrderMoveVertex* nextp = vertexp->pomWaitingNextp();
|
|
|
|
|
if (vertexp->isWait() && vertexp->inEmpty()) {
|
|
|
|
|
processMoveReadyOne(vertexp);
|
|
|
|
|
}
|
|
|
|
|
vertexp = nextp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::processMoveReadyOne(OrderMoveVertex* vertexp) {
|
|
|
|
|
// Recursive!
|
|
|
|
|
// Move one node from waiting to ready list
|
|
|
|
|
vertexp->setReady();
|
|
|
|
|
// Remove node from waiting list
|
|
|
|
|
vertexp->m_pomWaitingE.unlink(m_pomWaiting, vertexp);
|
|
|
|
|
// Add to ready list (indexed by domain and scope)
|
|
|
|
|
vertexp->m_readyVerticesE.pushBack(vertexp->domScopep()->m_readyVertices, vertexp);
|
|
|
|
|
vertexp->domScopep()->ready (this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::processMoveDoneOne(OrderMoveVertex* vertexp) {
|
|
|
|
|
// Move one node from ready to completion
|
|
|
|
|
vertexp->setMoved();
|
|
|
|
|
// Unlink from ready lists
|
|
|
|
|
vertexp->m_readyVerticesE.unlink(vertexp->domScopep()->m_readyVertices, vertexp);
|
|
|
|
|
vertexp->domScopep()->movedVertex (this, vertexp);
|
|
|
|
|
// Don't need to add it to another list, as we're done with it
|
|
|
|
|
// Mark our outputs as one closer to ready
|
|
|
|
|
for (V3GraphEdge* edgep = vertexp->outBeginp(), *nextp; edgep; edgep=nextp) {
|
|
|
|
|
nextp = edgep->outNextp();
|
|
|
|
|
OrderMoveVertex* toVertexp = (OrderMoveVertex*)edgep->top();
|
|
|
|
|
UINFO(9," Clear to "<<(toVertexp->inEmpty()?"[EMP] ":" ")
|
|
|
|
|
<<toVertexp<<endl);
|
|
|
|
|
// Delete this edge
|
|
|
|
|
edgep->unlinkDelete(); edgep=NULL;
|
|
|
|
|
if (toVertexp->inEmpty()) {
|
|
|
|
|
// If destination node now has all inputs resolved; recurse to move that vertex
|
|
|
|
|
// This is thus depth first (before width) which keeps the resulting executable's d-cache happy.
|
|
|
|
|
processMoveReadyOne(toVertexp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::processMoveOne(OrderMoveVertex* vertexp, OrderMoveDomScope* domScopep, int level) {
|
|
|
|
|
UASSERT(vertexp->domScopep() == domScopep, "Domain mismatch; list misbuilt?\n");
|
|
|
|
|
OrderLogicVertex* lvertexp = vertexp->logicp();
|
|
|
|
|
AstScope* scopep = lvertexp->scopep();
|
|
|
|
|
UINFO(5," POSmove l"<<setw(3)<<level<<" d="<<(void*)(lvertexp->domainp())
|
|
|
|
|
<<" s="<<(void*)(scopep)<<" "<<lvertexp<<endl);
|
|
|
|
|
AstSenTree* domainp = lvertexp->domainp();
|
|
|
|
|
AstNode* nodep = lvertexp->nodep();
|
|
|
|
|
AstModule* modp = scopep->userp()->castNode()->castModule(); UASSERT(modp,"NULL"); // Stashed by visitor func
|
|
|
|
|
if (nodep->castUntilStable()) {
|
|
|
|
|
#ifdef NEW_ORDERING
|
|
|
|
|
// Beginning of loop.
|
|
|
|
|
if (OrderLoopBeginVertex* beginp = dynamic_cast<OrderLoopBeginVertex*>(lvertexp)) {
|
|
|
|
|
m_pomNewFuncp = NULL; // Close out any old function
|
|
|
|
|
// Create new active record
|
|
|
|
|
string name = cfuncName(modp, domainp, scopep, nodep);
|
|
|
|
|
AstActive* callunderp = new AstActive(nodep->fileline(), name, domainp);
|
|
|
|
|
if (domainp == m_deleteDomainp) {
|
|
|
|
|
UINFO(6," Delete loop "<<beginp<<endl);
|
|
|
|
|
pushDeletep(callunderp);
|
|
|
|
|
} else {
|
|
|
|
|
processMoveLoopStmt(callunderp);
|
|
|
|
|
}
|
|
|
|
|
// Put loop under the activate
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
callunderp->addStmtsp(nodep);
|
|
|
|
|
// Remember we're in a loop
|
|
|
|
|
processMoveLoopPush(beginp);
|
|
|
|
|
}
|
|
|
|
|
else if (OrderLoopEndVertex* endp = dynamic_cast<OrderLoopEndVertex*>(lvertexp)) {
|
|
|
|
|
// Nodep is identical to OrderLoopBeginVertex's, so we don't move it
|
|
|
|
|
m_pomNewFuncp = NULL; // Close out any old function
|
|
|
|
|
processMoveLoopPop(endp->beginVertexp());
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
nodep->v3fatalSrc("AstUntilStable node isn't under a OrderLoop{End}Vertex.\n");
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
nodep->v3fatalSrc("Not implemented");
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
else if (nodep->castSenTree()) {
|
|
|
|
|
// Just ignore sensitivities, we'll deal with them when we move statements that need them
|
|
|
|
|
}
|
|
|
|
|
else { // Normal logic
|
|
|
|
|
// Make or borrow a CFunc to contain the new statements
|
2006-10-26 01:08:44 +00:00
|
|
|
|
if (v3Global.opt.profileCFuncs()
|
|
|
|
|
|| (v3Global.opt.outputSplitCFuncs()
|
|
|
|
|
&& v3Global.opt.outputSplitCFuncs() < m_pomNewStmts)) {
|
|
|
|
|
// Put every statement into a unique function to ease profiling or reduce function size
|
2006-08-26 11:35:28 +00:00
|
|
|
|
m_pomNewFuncp = NULL;
|
|
|
|
|
}
|
|
|
|
|
if (!m_pomNewFuncp && domainp != m_deleteDomainp) {
|
|
|
|
|
string name = cfuncName(modp, domainp, scopep, nodep);
|
|
|
|
|
m_pomNewFuncp = new AstCFunc(nodep->fileline(), name, scopep);
|
2006-08-30 21:07:55 +00:00
|
|
|
|
m_pomNewFuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
|
|
|
|
m_pomNewFuncp->symProlog(true);
|
2006-10-26 01:08:44 +00:00
|
|
|
|
m_pomNewStmts = 0;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (domainp->hasInitial() || domainp->hasSettle()) m_pomNewFuncp->slow(true);
|
|
|
|
|
scopep->addActivep(m_pomNewFuncp);
|
|
|
|
|
// Where will we be adding the call?
|
|
|
|
|
AstActive* callunderp = new AstActive(nodep->fileline(), name, domainp);
|
|
|
|
|
processMoveLoopStmt(callunderp);
|
|
|
|
|
// Add a top call to it
|
|
|
|
|
AstCCall* callp = new AstCCall(nodep->fileline(), m_pomNewFuncp);
|
2006-08-30 21:07:55 +00:00
|
|
|
|
callp->argTypes("vlSymsp");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
callunderp->addStmtsp(callp);
|
|
|
|
|
UINFO(6," New "<<m_pomNewFuncp<<endl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Move the logic to the function we're creating
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
if (domainp == m_deleteDomainp) {
|
|
|
|
|
UINFO(4," Ordering deleting pre-settled "<<nodep<<endl);
|
|
|
|
|
pushDeletep(nodep); nodep=NULL;
|
|
|
|
|
} else {
|
|
|
|
|
m_pomNewFuncp->addStmtsp(nodep);
|
2006-10-26 01:08:44 +00:00
|
|
|
|
if (v3Global.opt.outputSplitCFuncs()) {
|
|
|
|
|
// Add in the number of nodes we're adding
|
|
|
|
|
EmitCBaseCounterVisitor visitor(nodep);
|
|
|
|
|
m_pomNewStmts += visitor.count();
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
processMoveDoneOne (vertexp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void OrderVisitor::processMoveLoopPush(OrderLoopBeginVertex* beginp) {
|
|
|
|
|
UINFO(6," LoopPush "<<beginp<<endl);
|
|
|
|
|
m_pomLoopMoveps.push_back(beginp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void OrderVisitor::processMoveLoopPop(OrderLoopBeginVertex* beginp) {
|
|
|
|
|
UINFO(6," LoopPop "<<beginp<<endl);
|
|
|
|
|
if (m_pomLoopMoveps.empty()) beginp->nodep()->v3fatalSrc("processMoveLoopPop with no push'ed loops");
|
|
|
|
|
OrderLoopBeginVertex* topBeginp = m_pomLoopMoveps.back();
|
|
|
|
|
if (topBeginp != beginp) beginp->nodep()->v3fatalSrc("processMoveLoopPop had different vertex then one expected, got="<<topBeginp<<" exp="<<beginp<<endl);
|
|
|
|
|
m_pomLoopMoveps.pop_back();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void OrderVisitor::processMoveLoopStmt(AstNode* newSubnodep) {
|
|
|
|
|
if (m_pomLoopMoveps.empty()) {
|
|
|
|
|
// Not in any loops, statements go into main body
|
|
|
|
|
m_scopetopp->addActivep(newSubnodep);
|
|
|
|
|
} else {
|
|
|
|
|
// In a loop, put statements under appropriate loop body
|
|
|
|
|
OrderLoopBeginVertex* topBeginp = m_pomLoopMoveps.back();
|
|
|
|
|
topBeginp->untilp()->addBodysp(newSubnodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline OrderLoopId OrderVisitor::processMoveLoopCurrent() {
|
|
|
|
|
// Return loopID we're currently processing
|
|
|
|
|
if (m_pomLoopMoveps.empty()) {
|
|
|
|
|
return LOOPID_NOTLOOPED;
|
|
|
|
|
} else {
|
|
|
|
|
OrderLoopBeginVertex* topBeginp = m_pomLoopMoveps.back();
|
|
|
|
|
return topBeginp->loopId(); // Not inLoop, the begin is in the upper subloop.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void OrderMoveDomScope::ready(OrderVisitor* ovp) { // Check the domScope is on ready list, add if not
|
|
|
|
|
if (!m_onReadyList) {
|
|
|
|
|
m_onReadyList = true;
|
|
|
|
|
UASSERT (inLoop()!=0, "Loop# 0 is illegal, perhaps should be LOOPID_NOTLOOPED?");
|
|
|
|
|
m_readyDomScopeE.pushBack(ovp->m_pomReadyDomScope, this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void OrderMoveDomScope::movedVertex(OrderVisitor* ovp, OrderMoveVertex* vertexp) { // Mark one vertex as finished, remove from ready list if done
|
|
|
|
|
UASSERT(m_onReadyList, "Moving vertex from ready when nothing was on que as ready.");
|
|
|
|
|
if (m_readyVertices.empty()) { // Else more work to get to later
|
|
|
|
|
m_onReadyList = false;
|
|
|
|
|
m_readyDomScopeE.unlink(ovp->m_pomReadyDomScope, this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Top processing
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::processLoops(string stepName, V3EdgeFuncP edgeFuncp) {
|
|
|
|
|
UINFO(2," "<<stepName<<" Loop Detect...\n");
|
|
|
|
|
m_graph.stronglyConnected(edgeFuncp);
|
|
|
|
|
m_graph.dumpDotFilePrefixed((string)"orderg_"+stepName+"_strong", true);
|
|
|
|
|
|
|
|
|
|
UINFO(2," "<<stepName<<" Insert Loop Begin/Ends...\n");
|
|
|
|
|
processInsLoop();
|
|
|
|
|
if (debug()>=4) m_graph.dumpDotFilePrefixed((string)"orderg_"+stepName+"_loops", true);
|
|
|
|
|
|
|
|
|
|
// Remove loops from the graph
|
|
|
|
|
UINFO(2," "<<stepName<<" Acyclic...\n");
|
|
|
|
|
m_graph.acyclic(edgeFuncp);
|
|
|
|
|
if (debug()) m_graph.dumpDotFilePrefixed((string)"orderg_"+stepName+"_acyc", true);
|
|
|
|
|
|
|
|
|
|
// For any broken edges, add to change detect and remove edge
|
|
|
|
|
UINFO(2," "<<stepName<<" ProcessBrokeLoop...\n");
|
|
|
|
|
processBrokeLoop();
|
|
|
|
|
if (debug()>=4) m_graph.dumpDotFilePrefixed((string)"orderg_"+stepName+"_broke", true);
|
|
|
|
|
|
|
|
|
|
m_graph.makeEdgesNonCutable(edgeFuncp);
|
|
|
|
|
m_graph.dumpDotFilePrefixed((string)"orderg_"+stepName+"_done", true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OrderVisitor::process() {
|
|
|
|
|
// Dump data
|
|
|
|
|
m_graph.dumpDotFilePrefixed("orderg_pre");
|
|
|
|
|
|
|
|
|
|
#ifdef NEW_ORDERING
|
|
|
|
|
// Ignoring POST assignments and clocked statements, detect strongly connected components
|
|
|
|
|
// Split components into subgraphs
|
|
|
|
|
|
|
|
|
|
// Detect, loop begin/end, and acyc combo loops
|
|
|
|
|
UINFO(2," Combo loop elimination...\n");
|
|
|
|
|
processLoops("combo", &OrderEdge::followComboConnected);
|
|
|
|
|
|
|
|
|
|
// Detect, loop begin/end, and acyc post assigns
|
|
|
|
|
UINFO(2," Sequential loop elimination...\n");
|
|
|
|
|
processLoops("sequent", &OrderEdge::followSequentConnected);
|
|
|
|
|
|
|
|
|
|
// Detect and acyc any PRE assigns
|
|
|
|
|
// As breaking these does not cause any problems, we don't need to loop begin/end, etc.
|
|
|
|
|
UINFO(2," Pre Loop Detect & Acyc...\n");
|
|
|
|
|
m_graph.stronglyConnected(&V3GraphEdge::followAlwaysTrue);
|
|
|
|
|
if (debug()>=4) m_graph.dumpDotFilePrefixed("orderg_preasn_strong", true);
|
|
|
|
|
m_graph.acyclic(&V3GraphEdge::followAlwaysTrue);
|
|
|
|
|
m_graph.dumpDotFilePrefixed("orderg_preasn_done", true);
|
|
|
|
|
#else
|
|
|
|
|
// Break cycles
|
|
|
|
|
UINFO(2," Acyclic & Order...\n");
|
|
|
|
|
m_graph.acyclic(&V3GraphEdge::followAlwaysTrue);
|
|
|
|
|
m_graph.dumpDotFilePrefixed("orderg_acyc");
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Assign ranks so we know what to follow
|
|
|
|
|
// Then, sort vertices and edges by that ordering
|
|
|
|
|
m_graph.order();
|
|
|
|
|
m_graph.dumpDotFilePrefixed("orderg_order");
|
|
|
|
|
|
|
|
|
|
#ifndef NEW_ORDERING
|
|
|
|
|
UINFO(2," Process Circulars...\n");
|
|
|
|
|
processCircular(); // must be before processDomains
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Assign logic verticesto new domains
|
|
|
|
|
UINFO(2," Domains...\n");
|
|
|
|
|
processDomains();
|
|
|
|
|
m_graph.dumpDotFilePrefixed("orderg_domain");
|
|
|
|
|
|
|
|
|
|
if (debug() && v3Global.opt.dumpTree()) processEdgeReport();
|
|
|
|
|
|
|
|
|
|
UINFO(2," Construct Move Graph...\n");
|
|
|
|
|
processMoveBuildGraph();
|
|
|
|
|
if (debug()>=4) m_pomGraph.dumpDotFilePrefixed("ordermv_start"); // Different prefix (ordermv) as it's not the same graph
|
|
|
|
|
m_pomGraph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
|
|
|
|
|
m_pomGraph.dumpDotFilePrefixed("ordermv_simpl");
|
|
|
|
|
|
|
|
|
|
UINFO(2," Move...\n");
|
|
|
|
|
processMove();
|
|
|
|
|
|
|
|
|
|
// Any SC inputs feeding a combo domain must be marked, so we can make them sc_sensitive
|
|
|
|
|
UINFO(2," Sensitive...\n");
|
|
|
|
|
processSensitive(); // must be after processDomains
|
|
|
|
|
|
|
|
|
|
// Dump data
|
|
|
|
|
m_graph.dumpDotFilePrefixed("orderg_done");
|
|
|
|
|
if (0 && debug()) {
|
|
|
|
|
string dfilename = v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"_INT_order.tree";
|
|
|
|
|
const auto_ptr<ofstream> logp (V3File::new_ofstream(dfilename));
|
|
|
|
|
if (logp->fail()) v3fatalSrc("Can't write "<<dfilename);
|
|
|
|
|
m_graph.dump(*logp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Order class functions
|
|
|
|
|
|
|
|
|
|
void V3Order::orderAll(AstNetlist* nodep) {
|
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
|
|
|
|
OrderVisitor visitor;
|
|
|
|
|
visitor.main(nodep);
|
|
|
|
|
}
|