verilator/src/V3Trace.cpp
2012-04-12 21:08:20 -04:00

715 lines
27 KiB
C++

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Waves tracing
//
// Code available from: http://www.veripool.org/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2012 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.
//
//*************************************************************************
// V3Trace's Transformations:
// Pass 1:
// For each TRACE
// Add to list of traces, Mark where traces came from, unlink it
// Look for duplicate values; if so, cross reference
// Make vertex for each var it references
// Pass 2:
// Go through _eval; if there's a call on the same flat statement list
// then all functions for that call can get the same activity code.
// Likewise, all _slow functions can get the same code.
// CFUNCs that are public need unique codes, as does _eval
//
// For each CFUNC with unique callReason
// Make vertex
// For each var it sets, make vertex and edge from cfunc vertex
//
// For each CFUNC in graph
// Add ASSIGN(SEL(__Vm_traceActivity,activityNumber++),1)
// Create __Vm_traceActivity vector
// Sort TRACEs by activityNumber(s) they come from (may be more than one)
// Each set of activityNumbers
// Add IF (SEL(__Vm_traceActivity,activityNumber),1)
// Add traces under that activity number.
// Assign trace codes:
// If from a VARSCOPE, record the trace->varscope map
// Else, assign trace codes to each variable
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include <cstdio>
#include <cstdarg>
#include <unistd.h>
#include <set>
#include <map>
#include "V3Global.h"
#include "V3Trace.h"
#include "V3EmitCBase.h"
#include "V3Graph.h"
#include "V3Hashed.h"
#include "V3Stats.h"
//######################################################################
// Graph vertexes
class TraceActivityVertex : public V3GraphVertex {
AstNode* m_insertp; // Insert before this statement
vlsint32_t m_activityCode;
bool m_activityCodeValid;
bool m_slow; // If always slow, we can use the same code
public:
enum { ACTIVITY_NEVER =((1UL<<31) - 1) };
enum { ACTIVITY_ALWAYS=((1UL<<31) - 2) };
enum { ACTIVITY_SLOW=0 };
TraceActivityVertex(V3Graph* graphp, AstNode* nodep, bool slow)
: V3GraphVertex(graphp), m_insertp(nodep) {
m_activityCode = 0;
m_activityCodeValid = false;
m_slow = slow;
}
TraceActivityVertex(V3Graph* graphp, vlsint32_t code)
: V3GraphVertex(graphp), m_insertp(NULL) {
m_activityCode = code;
m_activityCodeValid = true;
m_slow = false;
}
virtual ~TraceActivityVertex() {}
// Accessors
AstNode* insertp() const {
if (!m_insertp) v3fatalSrc("Null insertp; probably called on a special always/slow.");
return m_insertp;
}
virtual string name() const {
if (activityAlways()) return "*ALWAYS*";
else return ((string)(slow()?"*SLOW* ":""))+insertp()->name();
}
virtual string dotColor() const { return slow()?"yellowGreen":"green"; }
bool activityCodeValid() const { return m_activityCodeValid; }
vlsint32_t activityCode() const { return m_activityCode; }
bool activityAlways() const { return activityCode()==ACTIVITY_ALWAYS; }
void activityCode(vlsint32_t code) { m_activityCode=code; m_activityCodeValid=true;}
bool slow() const { return m_slow; }
void slow(bool flag) { if (!flag) m_slow=false; }
};
class TraceCFuncVertex : public V3GraphVertex {
AstCFunc* m_nodep;
public:
TraceCFuncVertex(V3Graph* graphp, AstCFunc* nodep)
: V3GraphVertex(graphp), m_nodep(nodep) {
}
virtual ~TraceCFuncVertex() {}
// Accessors
AstCFunc* nodep() const { return m_nodep; }
virtual string name() const { return nodep()->name(); }
virtual string dotColor() const { return "yellow"; }
};
class TraceTraceVertex : public V3GraphVertex {
AstTraceInc* m_nodep; // TRACEINC this represents
TraceTraceVertex* m_duplicatep; // NULL, or other vertex with the real code() that duplicates this one
public:
TraceTraceVertex(V3Graph* graphp, AstTraceInc* nodep)
: V3GraphVertex(graphp), m_nodep(nodep), m_duplicatep(NULL) {}
virtual ~TraceTraceVertex() {}
// Accessors
AstTraceInc* nodep() const { return m_nodep; }
virtual string name() const { return nodep()->declp()->name(); }
virtual string dotColor() const { return "red"; }
TraceTraceVertex* duplicatep() const { return m_duplicatep; }
void duplicatep(TraceTraceVertex* dupp) {
if (duplicatep()) nodep()->v3fatalSrc("Assigning duplicatep() to already duplicated node");
m_duplicatep=dupp; }
};
class TraceVarVertex : public V3GraphVertex {
AstVarScope* m_nodep;
public:
TraceVarVertex(V3Graph* graphp, AstVarScope* nodep)
: V3GraphVertex(graphp), m_nodep(nodep) {}
virtual ~TraceVarVertex() {}
// Accessors
AstVarScope* nodep() const { return m_nodep; }
virtual string name() const { return nodep()->name(); }
virtual string dotColor() const { return "skyblue"; }
};
//######################################################################
// Trace state, as a visitor of each AstNode
class TraceVisitor : public EmitCBaseVisitor {
private:
// NODE STATE
// V3Hashed
// Ast*::user4() // V3Hashed calculation
// Cleared entire netlist
// AstCFunc::user1() // V3GraphVertex* for this node
// AstTraceInc::user1() // V3GraphVertex* for this node
// AstVarScope::user1() // V3GraphVertex* for this node
// AstCCall::user2() // bool; walked next list for other ccalls
// Ast*::user3() // TraceActivityVertex* for this node
AstUser1InUse m_inuser1;
AstUser2InUse m_inuser2;
AstUser3InUse m_inuser3;
//AstUser4InUse In V3Hashed
// STATE
AstNodeModule* m_topModp; // Module to add variables to
AstScope* m_highScopep; // Scope to add variables to
AstCFunc* m_funcp; // C function adding to graph
AstTraceInc* m_tracep; // Trace function adding to graph
AstCFunc* m_initFuncp; // Trace function we add statements to
AstCFunc* m_fullFuncp; // Trace function we add statements to
AstCFunc* m_fullSubFuncp; // Trace function we add statements to (under full)
int m_fullSubStmts; // Statements under function being built
AstCFunc* m_chgFuncp; // Trace function we add statements to
AstCFunc* m_chgSubFuncp; // Trace function we add statements to (under full)
AstNode* m_chgSubParentp;// Which node has call to m_chgSubFuncp
int m_chgSubStmts; // Statements under function being built
AstVarScope* m_activityVscp; // Activity variable
uint32_t m_code; // Trace ident code# being assigned
V3Graph m_graph; // Var/CFunc tracking
TraceActivityVertex* m_alwaysVtxp; // "Always trace" vertex
bool m_finding; // Pass one of algorithm?
int m_funcNum; // Function number being built
V3Double0 m_statChgSigs; // Statistic tracking
V3Double0 m_statUniqSigs; // Statistic tracking
V3Double0 m_statUniqCodes;// Statistic tracking
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
void detectDuplicates() {
UINFO(9,"Finding duplicates\n");
// Note uses user4
V3Hashed hashed; // Duplicate code detection
// Hash all of the values the traceIncs need
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
if (TraceTraceVertex* vvertexp = dynamic_cast<TraceTraceVertex*>(itp)) {
AstTraceInc* nodep = vvertexp->nodep();
if (nodep->valuep()) {
if (nodep->valuep()->backp() != nodep) nodep->v3fatalSrc("Trace duplicate back needs consistency, so we can map duplicates back to TRACEINCs");
hashed.hashAndInsert(nodep->valuep());
UINFO(8, " Hashed "<<hex<<hashed.nodeHash(nodep->valuep())<<" "<<nodep<<endl);
}
}
}
// Find if there are any duplicates
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
if (TraceTraceVertex* vvertexp = dynamic_cast<TraceTraceVertex*>(itp)) {
AstTraceInc* nodep = vvertexp->nodep();
if (nodep->valuep() && !vvertexp->duplicatep()) {
V3Hashed::iterator dupit = hashed.findDuplicate(nodep->valuep());
if (dupit != hashed.end()) {
AstTraceInc* dupincp = hashed.iteratorNodep(dupit)->backp()->castTraceInc();
if (!dupincp) nodep->v3fatalSrc("Trace duplicate of wrong type");
TraceTraceVertex* dupvertexp = dynamic_cast<TraceTraceVertex*>(dupincp->user1p()->castGraphVertex());
UINFO(8," Orig "<<nodep<<endl);
UINFO(8," dup "<<dupincp<<endl);
// Mark the found node as a duplicate of the first node
// (Not vice-versa as we get the iterator for the found node)
dupvertexp->duplicatep(vvertexp);
// Remove node from comparison so don't hit it again
hashed.erase(dupit);
}
}
}
}
hashed.clear();
}
void graphSimplify() {
// Remove all variable nodes
for (V3GraphVertex* nextp, *itp = m_graph.verticesBeginp(); itp; itp=nextp) {
nextp = itp->verticesNextp();
if (TraceVarVertex* vvertexp = dynamic_cast<TraceVarVertex*>(itp)) {
vvertexp->rerouteEdges(&m_graph);
vvertexp->unlinkDelete(&m_graph);
}
}
// Remove multiple variables connecting funcs to traces
// We do this twice, as then we have fewer edges to multiply out in the below expansion.
m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
// Remove all Cfunc nodes
for (V3GraphVertex* nextp, *itp = m_graph.verticesBeginp(); itp; itp=nextp) {
nextp = itp->verticesNextp();
if (TraceCFuncVertex* vvertexp = dynamic_cast<TraceCFuncVertex*>(itp)) {
vvertexp->rerouteEdges(&m_graph);
vvertexp->unlinkDelete(&m_graph);
}
}
// Remove multiple variables connecting funcs to traces
m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
// If there are any edges from a always, keep only the always
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
if (TraceTraceVertex* vvertexp = dynamic_cast<TraceTraceVertex*>(itp)) {
V3GraphEdge* alwaysEdgep = NULL;
for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; edgep=edgep->inNextp()) {
TraceActivityVertex* actVtxp = dynamic_cast<TraceActivityVertex*>(edgep->fromp());
if (!actVtxp) vvertexp->nodep()->v3fatalSrc("Tracing a node with FROM non activity");
if (actVtxp->activityAlways()) {
alwaysEdgep = edgep;
break;
}
}
if (alwaysEdgep) {
for (V3GraphEdge* nextp, *edgep = vvertexp->inBeginp(); edgep; edgep=nextp) {
nextp = edgep->inNextp();
if (edgep!=alwaysEdgep) edgep->unlinkDelete();
}
}
}
}
// Activity points with no outputs can be removed
for (V3GraphVertex* nextp, *itp = m_graph.verticesBeginp(); itp; itp=nextp) {
nextp = itp->verticesNextp();
if (TraceActivityVertex* vvertexp = dynamic_cast<TraceActivityVertex*>(itp)) {
if (!vvertexp->outBeginp()) {
vvertexp->unlinkDelete(&m_graph);
}
}
}
}
void assignActivity() {
// Select activity numbers and put into each CFunc vertex
uint32_t activityNumber = 1; // Note 0 indicates "slow"
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
if (TraceActivityVertex* vvertexp = dynamic_cast<TraceActivityVertex*>(itp)) {
if (!vvertexp->activityCodeValid()) {
if (vvertexp->slow()) {
// If all functions in the calls are slow, we'll share the same code.
// This makes us need less activityNumbers and so speeds up the fast path.
vvertexp->activityCode(TraceActivityVertex::ACTIVITY_SLOW);
} else {
vvertexp->activityCode(activityNumber++);
}
}
}
}
// Insert global variable
if (!activityNumber) activityNumber++; // For simplicity, always create it
int activityBits = VL_WORDS_I(activityNumber)*VL_WORDSIZE; // For tighter code; round to next 32 bit point.
AstVar* newvarp = new AstVar (m_chgFuncp->fileline(), AstVarType::MODULETEMP,
"__Vm_traceActivity", VFlagBitPacked(), activityBits);
m_topModp->addStmtp(newvarp);
AstVarScope* newvscp = new AstVarScope(newvarp->fileline(), m_highScopep, newvarp);
m_highScopep->addVarp(newvscp);
m_activityVscp = newvscp;
// Insert activity setter
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
if (TraceActivityVertex* vvertexp = dynamic_cast<TraceActivityVertex*>(itp)) {
if (!vvertexp->activityAlways()) {
FileLine* fl = vvertexp->insertp()->fileline();
uint32_t acode = vvertexp->activityCode();
vvertexp->insertp()->addNextHere
(new AstAssign (fl,
new AstSel (fl, new AstVarRef(fl, m_activityVscp, true),
acode, 1),
new AstConst (fl, AstConst::LogicTrue())));
}
}
}
}
AstCFunc* newCFunc(AstCFuncType type, const string& name, AstCFunc* basep) {
AstCFunc* funcp = new AstCFunc(basep->fileline(), name, basep->scopep());
funcp->slow(basep->slow());
funcp->argTypes(EmitCBaseVisitor::symClassVar()
+", "+v3Global.opt.traceClassBase()+"* vcdp, uint32_t code");
funcp->funcType(type);
funcp->symProlog(true);
basep->addNext(funcp);
UINFO(5," Newfunc "<<funcp<<endl);
return funcp;
}
AstCFunc* newCFuncSub(AstCFunc* basep, AstNode* callfromp) {
string name = basep->name()+"__"+cvtToStr(++m_funcNum);
AstCFunc* funcp = NULL;
if (basep->funcType()==AstCFuncType::TRACE_FULL) {
funcp = newCFunc(AstCFuncType::TRACE_FULL_SUB, name, basep);
} else if (basep->funcType()==AstCFuncType::TRACE_CHANGE) {
funcp = newCFunc(AstCFuncType::TRACE_CHANGE_SUB, name, basep);
} else {
basep->v3fatalSrc("Strange base function type");
}
AstCCall* callp = new AstCCall(funcp->fileline(), funcp);
callp->argTypes("vlSymsp, vcdp, code");
if (callfromp->castCFunc()) {
callfromp->castCFunc()->addStmtsp(callp);
} else if (callfromp->castIf()) {
callfromp->castIf()->addIfsp(callp);
} else {
callfromp->v3fatalSrc("Unknown caller node type"); // Where to add it??
}
return funcp;
}
void addToChgSub(AstNode* underp, AstNode* stmtsp) {
if (!m_chgSubFuncp
|| (m_chgSubParentp != underp)
|| (m_chgSubStmts && v3Global.opt.outputSplitCTrace()
&& m_chgSubStmts > v3Global.opt.outputSplitCTrace())) {
m_chgSubFuncp = newCFuncSub(m_chgFuncp, underp);
m_chgSubParentp = underp;
m_chgSubStmts = 0;
}
m_chgSubFuncp->addStmtsp(stmtsp);
m_chgSubStmts += EmitCBaseCounterVisitor(stmtsp).count();
}
void putTracesIntoTree() {
// Form a sorted list of the traces we are interested in
UINFO(9,"Making trees\n");
typedef set<uint32_t> ActCodeSet; // All activity numbers applying to a given trace
typedef multimap<ActCodeSet,TraceTraceVertex*> TraceVec; // For activity set, what traces apply
TraceVec traces;
// Form sort structure
// If a trace doesn't have activity, it's constant, and we don't need to track changes on it.
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
if (TraceTraceVertex* vvertexp = dynamic_cast<TraceTraceVertex*>(itp)) {
ActCodeSet actset;
UINFO(9," Add to sort: "<<vvertexp<<endl);
if (debug()>=9) vvertexp->nodep()->dumpTree(cout,"- trnode: ");
for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; edgep=edgep->inNextp()) {
TraceActivityVertex* cfvertexp = dynamic_cast<TraceActivityVertex*>(edgep->fromp());
if (!cfvertexp) vvertexp->nodep()->v3fatalSrc("Should have been function pointing to this trace");
UINFO(9," Activity: "<<cfvertexp<<endl);
if (cfvertexp->activityAlways()) {
// If code 0, we always trace; ignore other codes
actset.clear();
actset.insert(TraceActivityVertex::ACTIVITY_ALWAYS);
break;
} else {
uint32_t acode = cfvertexp->activityCode();
if (actset.find(acode) == actset.end()) {
actset.insert(acode);
}
}
}
// If a trace doesn't have activity, it's constant, and we don't need to track changes on it.
// We put constants and non-changers last, as then the prevvalue vector is more compacted
if (actset.empty()) actset.insert(TraceActivityVertex::ACTIVITY_NEVER);
traces.insert(make_pair(actset, vvertexp));
}
}
// Our keys are now sorted to have same activity number adjacent,
// then by trace order. (Better would be execution order for cache efficiency....)
// Last are constants and non-changers, as then the last value vector is more compact
// Put TRACEs back into the tree
const ActCodeSet* lastactp = NULL;
AstNode* ifnodep = NULL;
for (TraceVec::iterator it = traces.begin(); it!=traces.end(); ++it) {
const ActCodeSet& actset = it->first;
TraceTraceVertex* vvertexp = it->second;
UINFO(9," Done sort: "<<vvertexp<<endl);
bool needChg = true;
if (actset.find(TraceActivityVertex::ACTIVITY_NEVER) != actset.end()) {
// No activity needed; it's a constant value or only set in initial block
needChg = false;
}
AstNode* addp = assignTraceCode(vvertexp, vvertexp->nodep(), needChg);
if (addp) { // Else no activity or duplicate
if (actset.find(TraceActivityVertex::ACTIVITY_NEVER) != actset.end()) {
vvertexp->nodep()->v3fatalSrc("If never, needChg=0 and shouldn't need to add.");
} else if (actset.find(TraceActivityVertex::ACTIVITY_ALWAYS) != actset.end()) {
// Must always set it; add to base of function
addToChgSub(m_chgFuncp, addp);
} else if (lastactp && actset == *lastactp && ifnodep) {
// Add to last statement we built
addToChgSub(ifnodep, addp);
} else {
// Build a new IF statement
FileLine* fl = addp->fileline();
AstNode* condp = NULL;
for (ActCodeSet::const_iterator csit = actset.begin(); csit!=actset.end(); ++csit) {
uint32_t acode = *csit;
AstNode* selp = new AstSel (fl, new AstVarRef(fl, m_activityVscp, false),
acode, 1);
if (condp) condp = new AstOr (fl, condp, selp);
else condp = selp;
}
AstIf* ifp = new AstIf (fl, condp, NULL, NULL);
ifp->branchPred(AstBranchPred::BP_UNLIKELY);
m_chgFuncp->addStmtsp(ifp);
lastactp = &actset;
ifnodep = ifp;
addToChgSub(ifnodep, addp);
}
}
}
// Set in initializer
// Clear activity after tracing completes
FileLine* fl = m_chgFuncp->fileline();
AstNode* clrp = new AstAssign (fl,
new AstVarRef(fl, m_activityVscp, true),
new AstConst(fl, V3Number(fl, m_activityVscp->width())));
m_fullFuncp->addFinalsp(clrp->cloneTree(true));
m_chgFuncp->addFinalsp(clrp);
}
uint32_t assignDeclCode(AstTraceDecl* nodep) {
if (!nodep->code()) {
nodep->code(m_code);
m_code += nodep->codeInc();
m_statUniqCodes += nodep->codeInc();
++m_statUniqSigs;
}
return nodep->code();
}
AstNode* assignTraceCode(TraceTraceVertex* vvertexp, AstTraceInc* nodep, bool needChg) {
// Assign trace code, add to tree, return node for change tree or null
// Look for identical copies
uint32_t codePreassigned = 0;
//if (debug()>=9) nodep->dumpTree(cout,"- assnnode: ");
// Find non-duplicated node; note some nodep's maybe null, as they were deleted below
TraceTraceVertex* dupvertexp = vvertexp;
while (dupvertexp->duplicatep()) {
dupvertexp = dupvertexp->duplicatep();
UINFO(9," dupOf "<<((void*)dupvertexp)<<" "<<((void*)dupvertexp->nodep())
<<" "<<dupvertexp<<endl);
}
if (dupvertexp != vvertexp) {
// It's an exact copy. We'll assign the code to the master on
// the first one we hit; the later ones will share the code.
codePreassigned = assignDeclCode(dupvertexp->nodep()->declp());
nodep->declp()->code(codePreassigned);
} else {
assignDeclCode(nodep->declp());
}
UINFO(8," Created code="<<nodep->declp()->code()
<<" "<<(codePreassigned?"[PREASS]":"")
<<" "<<(needChg?"[CHG]":"")<<" "<<nodep<<endl);
AstNode* incAddp = NULL;
if (!codePreassigned) {
// Add to trace cfuncs
if (needChg) {
++m_statChgSigs;
incAddp = nodep->cloneTree(true);
}
if (!m_fullSubFuncp
|| (m_fullSubStmts && v3Global.opt.outputSplitCTrace()
&& m_fullSubStmts > v3Global.opt.outputSplitCTrace())) {
m_fullSubFuncp = newCFuncSub(m_fullFuncp, m_fullFuncp);
m_fullSubStmts = 0;
}
m_fullSubFuncp->addStmtsp(nodep);
m_fullSubStmts += EmitCBaseCounterVisitor(nodep).count();
} else {
// Duplicates don't need a TraceInc
pushDeletep(nodep); nodep=NULL;
}
return incAddp;
}
TraceCFuncVertex* getCFuncVertexp(AstCFunc* nodep) {
TraceCFuncVertex* vertexp = dynamic_cast<TraceCFuncVertex*>(nodep->user1p()->castGraphVertex());
if (!vertexp) {
vertexp = new TraceCFuncVertex(&m_graph, nodep);
nodep->user1p(vertexp);
}
return vertexp;
}
TraceActivityVertex* getActivityVertexp(AstNode* nodep, bool slow) {
TraceActivityVertex* vertexp = dynamic_cast<TraceActivityVertex*>(nodep->user3p()->castGraphVertex());
if (!vertexp) {
vertexp = new TraceActivityVertex(&m_graph, nodep, slow);
nodep->user3p(vertexp);
}
vertexp->slow(slow);
return vertexp;
}
// VISITORS
virtual void visit(AstNetlist* nodep, AstNUser*) {
m_code = 1; // Multiple TopScopes will require fixing how code#s
// are assigned as duplicate varscopes must result in the same tracing code#.
// Make a always vertex
m_alwaysVtxp = new TraceActivityVertex(&m_graph, TraceActivityVertex::ACTIVITY_ALWAYS);
// Add vertexes for all TRACES, and edges from VARs each trace looks at
m_finding = false;
nodep->iterateChildren(*this);
// Add vertexes for all CFUNCs, and edges to VARs the func sets
m_finding = true;
nodep->iterateChildren(*this);
m_finding = false;
// Detect and remove duplicate values
detectDuplicates();
// Simplify it
if (debug()>=6) m_graph.dumpDotFilePrefixed("trace_pre");
graphSimplify();
if (debug()>=6) m_graph.dumpDotFilePrefixed("trace_opt");
// Create new TRACEINCs
assignActivity();
putTracesIntoTree();
}
virtual void visit(AstNodeModule* nodep, AstNUser*) {
if (nodep->isTop()) m_topModp = nodep;
nodep->iterateChildren(*this);
}
virtual void visit(AstTopScope* nodep, AstNUser*) {
AstScope* scopep = nodep->scopep();
if (!scopep) nodep->v3fatalSrc("No scope found on top level");
m_highScopep = scopep;
nodep->iterateChildren(*this);
}
virtual void visit(AstCCall* nodep, AstNUser*) {
UINFO(8," CCALL "<<nodep<<endl);
if (!m_finding && !nodep->user2()) {
// See if there are other calls in same statement list;
// If so, all funcs might share the same activity code
TraceActivityVertex* activityVtxp = getActivityVertexp(nodep, nodep->funcp()->slow());
for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) {
if (AstCCall* ccallp = nextp->castCCall()) {
ccallp->user2(true); // Processed
UINFO(8," SubCCALL "<<ccallp<<endl);
V3GraphVertex* ccallFuncVtxp = getCFuncVertexp(ccallp->funcp());
activityVtxp->slow(ccallp->funcp()->slow());
new V3GraphEdge (&m_graph, activityVtxp, ccallFuncVtxp, 1);
}
}
}
nodep->iterateChildren(*this);
}
virtual void visit(AstCFunc* nodep, AstNUser*) {
UINFO(8," CFUNC "<<nodep<<endl);
if (nodep->funcType() == AstCFuncType::TRACE_INIT) {
m_initFuncp = nodep;
} else if (nodep->funcType() == AstCFuncType::TRACE_FULL) {
m_fullFuncp = nodep;
} else if (nodep->funcType() == AstCFuncType::TRACE_CHANGE) {
m_chgFuncp = nodep;
}
V3GraphVertex* funcVtxp = getCFuncVertexp(nodep);
if (!m_finding) { // If public, we need a unique activity code to allow for sets directly in this func
if (nodep->funcPublic() || nodep->dpiExport()
|| nodep->name() == "_eval") {
// Need a non-null place to remember to later add a statement; make one
if (!nodep->stmtsp()) nodep->addStmtsp(new AstComment(nodep->fileline(), "Tracing activity check"));
V3GraphVertex* activityVtxp = getActivityVertexp(nodep->stmtsp(), nodep->slow());
new V3GraphEdge (&m_graph, activityVtxp, funcVtxp, 1);
}
}
m_funcp = nodep;
nodep->iterateChildren(*this);
m_funcp = NULL;
}
virtual void visit(AstTraceInc* nodep, AstNUser*) {
UINFO(8," TRACE "<<nodep<<endl);
if (m_finding) nodep->v3fatalSrc("Traces should have been removed in prev step.");
nodep->unlinkFrBack();
V3GraphVertex* vertexp = new TraceTraceVertex(&m_graph, nodep);
nodep->user1p(vertexp);
if (!m_funcp || (!m_chgFuncp || !m_fullFuncp)) nodep->v3fatalSrc("Trace not under func");
m_tracep = nodep;
nodep->iterateChildren(*this);
m_tracep = NULL;
}
virtual void visit(AstVarRef* nodep, AstNUser*) {
if (m_tracep) {
if (!nodep->varScopep()) nodep->v3fatalSrc("No var scope?");
if (nodep->lvalue()) nodep->v3fatalSrc("Lvalue in trace? Should be const.");
V3GraphVertex* varVtxp = nodep->varScopep()->user1p()->castGraphVertex();
if (!varVtxp) {
varVtxp = new TraceVarVertex(&m_graph, nodep->varScopep());
nodep->varScopep()->user1p(varVtxp);
}
V3GraphVertex* traceVtxp = m_tracep->user1p()->castGraphVertex();
new V3GraphEdge(&m_graph, varVtxp, traceVtxp, 1);
if (nodep->varp()->isPrimaryIn() // Always need to trace primary inputs
|| nodep->varp()->isSigPublic()) { // Or ones user can change
new V3GraphEdge(&m_graph, m_alwaysVtxp, traceVtxp, 1);
}
}
else if (m_funcp && m_finding && nodep->lvalue()) {
if (!nodep->varScopep()) nodep->v3fatalSrc("No var scope?");
V3GraphVertex* funcVtxp = getCFuncVertexp(m_funcp);
V3GraphVertex* varVtxp = nodep->varScopep()->user1p()->castGraphVertex();
if (varVtxp) { // else we're not tracing this signal
new V3GraphEdge(&m_graph, funcVtxp, varVtxp, 1);
}
}
}
//--------------------
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
TraceVisitor(AstNetlist* nodep) {
m_funcp = NULL;
m_tracep = NULL;
m_topModp = NULL;
m_highScopep = NULL;
m_finding = false;
m_activityVscp = NULL;
m_alwaysVtxp = NULL;
m_initFuncp = NULL;
m_fullFuncp = NULL;
m_fullSubFuncp = NULL;
m_fullSubStmts = 0;
m_chgFuncp = NULL;
m_chgSubFuncp = NULL;
m_chgSubParentp = NULL;
m_chgSubStmts = 0;
m_funcNum = 0;
nodep->accept(*this);
}
virtual ~TraceVisitor() {
V3Stats::addStat("Tracing, Unique changing signals", m_statChgSigs);
V3Stats::addStat("Tracing, Unique traced signals", m_statUniqSigs);
V3Stats::addStat("Tracing, Unique trace codes", m_statUniqCodes);
}
};
//######################################################################
// Trace class functions
void V3Trace::traceAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
TraceVisitor visitor (nodep);
}