Refactor trace implementation to allow experimentation
The main goal of this patch is to enable splitting the full and incremental tracing functions into multiple functions, which can then be run in parallel at a later stage. It also simplifies further experimentation as all of the interesting trace code construction now happens in V3Trace. No functional change is intended by this patch, but there are some implementation changes in the generated code. Highlights: - Pass symbol table directly to trace callbacks for simplicity. - A new traceRegister function is generated which adds each trace function as an individual callback, which means we can have multiple callbacks for each trace function type. - A new traceCleanup function is generated which clears the activity flags, as the trace callbacks might be implemented as multiple functions. - Re-worked sub-function handling so there is no separate sub-function for each trace activity class. Sub-functions are generate when required by splitting. - traceFull/traceChg are now created in V3Trace rather than V3TraceDecl, this requires carrying the trace value tree in TraceDecl until it reaches V3Trace where the TraceInc nodes are created (previously a TraceInc was also created in V3TraceDecl which carries the value).
This commit is contained in:
parent
12b95f6b93
commit
900c023bb5
|
|
@ -105,8 +105,6 @@ public:
|
|||
};
|
||||
#endif
|
||||
|
||||
class VerilatedTraceCallInfo;
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTrace
|
||||
|
||||
|
|
@ -114,13 +112,34 @@ class VerilatedTraceCallInfo;
|
|||
// implementations in the format specific derived class, which must be passed
|
||||
// as the type parameter T_Derived
|
||||
template <class T_Derived> class VerilatedTrace {
|
||||
private:
|
||||
public:
|
||||
//=========================================================================
|
||||
// Generic tracing internals
|
||||
|
||||
typedef void (*initCb_t)(void*, T_Derived*, uint32_t); // Type of init callbacks
|
||||
typedef void (*dumpCb_t)(void*, T_Derived*); // Type of all but init callbacks
|
||||
|
||||
private:
|
||||
struct CallbackRecord {
|
||||
const union {
|
||||
initCb_t m_initCb; // The callback function
|
||||
dumpCb_t m_dumpCb; // The callback function
|
||||
};
|
||||
void* const m_userp; // The user pointer to pass to the callback (the symbol table)
|
||||
CallbackRecord(initCb_t cb, void* userp)
|
||||
: m_initCb(cb)
|
||||
, m_userp(userp) {}
|
||||
CallbackRecord(dumpCb_t cb, void* userp)
|
||||
: m_dumpCb(cb)
|
||||
, m_userp(userp) {}
|
||||
};
|
||||
|
||||
vluint32_t* m_sigs_oldvalp; ///< Old value store
|
||||
vluint64_t m_timeLastDump; ///< Last time we did a dump
|
||||
std::vector<VerilatedTraceCallInfo*> m_callbacks; ///< Routines to perform dumping
|
||||
std::vector<CallbackRecord> m_initCbs; ///< Routines to initialize traciong
|
||||
std::vector<CallbackRecord> m_fullCbs; ///< Routines to perform full dump
|
||||
std::vector<CallbackRecord> m_chgCbs; ///< Routines to perform incremental dump
|
||||
std::vector<CallbackRecord> m_cleanupCbs; ///< Routines to call at the end of dump
|
||||
bool m_fullDump; ///< Whether a full dump is required on the next call to 'dump'
|
||||
vluint32_t m_nextCode; ///< Next code number to assign
|
||||
vluint32_t m_numSignals; ///< Number of distinct signals
|
||||
|
|
@ -130,6 +149,8 @@ private:
|
|||
double m_timeRes; ///< Time resolution (ns/ms etc)
|
||||
double m_timeUnit; ///< Time units (ns/ms etc)
|
||||
|
||||
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord& cbRec);
|
||||
|
||||
// Equivalent to 'this' but is of the sub-type 'T_Derived*'. Use 'self()->'
|
||||
// to access duck-typed functions to avoid a virtual function call.
|
||||
T_Derived* self() { return static_cast<T_Derived*>(this); }
|
||||
|
|
@ -232,13 +253,13 @@ public:
|
|||
//=========================================================================
|
||||
// Non-hot path internal interface to Verilator generated code
|
||||
|
||||
typedef void (*callback_t)(T_Derived* tracep, void* userthis, vluint32_t code);
|
||||
void addInitCb(initCb_t cb, void* userp) VL_MT_UNSAFE_ONE;
|
||||
void addFullCb(dumpCb_t cb, void* userp) VL_MT_UNSAFE_ONE;
|
||||
void addChgCb(dumpCb_t cb, void* userp) VL_MT_UNSAFE_ONE;
|
||||
void addCleanupCb(dumpCb_t cb, void* userp) VL_MT_UNSAFE_ONE;
|
||||
|
||||
void changeThread() { m_assertOne.changeThread(); }
|
||||
|
||||
void addCallback(callback_t initcb, callback_t fullcb, callback_t changecb,
|
||||
void* userthis) VL_MT_UNSAFE_ONE;
|
||||
|
||||
void module(const std::string& name) VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
m_moduleName = name;
|
||||
|
|
|
|||
|
|
@ -73,31 +73,6 @@ static std::string doubleToTimescale(double value) {
|
|||
return valuestr; // Gets converted to string, so no ref to stack
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Internal callback routines for each module being traced.
|
||||
|
||||
// Each module that wishes to be traced registers a set of callbacks stored in
|
||||
// this class. When the trace file is being constructed, this class provides
|
||||
// the callback routines to be executed.
|
||||
class VerilatedTraceCallInfo {
|
||||
public: // This is in .cpp file so is not widely visible
|
||||
typedef VerilatedTrace<VL_DERIVED_T>::callback_t callback_t;
|
||||
|
||||
callback_t m_initcb; ///< Initialization Callback function
|
||||
callback_t m_fullcb; ///< Full Dumping Callback function
|
||||
callback_t m_changecb; ///< Incremental Dumping Callback function
|
||||
void* m_userthis; ///< User data pointer for callback
|
||||
vluint32_t m_code; ///< Starting code number (set later by traceInit)
|
||||
// CONSTRUCTORS
|
||||
VerilatedTraceCallInfo(callback_t icb, callback_t fcb, callback_t changecb, void* ut)
|
||||
: m_initcb(icb)
|
||||
, m_fullcb(fcb)
|
||||
, m_changecb(changecb)
|
||||
, m_userthis(ut)
|
||||
, m_code(1) {}
|
||||
~VerilatedTraceCallInfo() {}
|
||||
};
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
//=========================================================================
|
||||
// Buffer management
|
||||
|
|
@ -311,10 +286,6 @@ VerilatedTrace<VL_DERIVED_T>::VerilatedTrace()
|
|||
|
||||
template <> VerilatedTrace<VL_DERIVED_T>::~VerilatedTrace() {
|
||||
if (m_sigs_oldvalp) VL_DO_CLEAR(delete[] m_sigs_oldvalp, m_sigs_oldvalp = NULL);
|
||||
while (!m_callbacks.empty()) {
|
||||
delete m_callbacks.back();
|
||||
m_callbacks.pop_back();
|
||||
}
|
||||
#ifdef VL_TRACE_THREADED
|
||||
close();
|
||||
#endif
|
||||
|
|
@ -334,11 +305,12 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
|
|||
m_numSignals = 0;
|
||||
m_maxBits = 0;
|
||||
|
||||
// Call all initialize callbacks, which will call decl* for each signal.
|
||||
for (vluint32_t ent = 0; ent < m_callbacks.size(); ++ent) {
|
||||
VerilatedTraceCallInfo* cip = m_callbacks[ent];
|
||||
cip->m_code = nextCode();
|
||||
(cip->m_initcb)(self(), cip->m_userthis, cip->m_code);
|
||||
// Call all initialize callbacks, which will:
|
||||
// - Call decl* for each signal
|
||||
// - Store the base code
|
||||
for (vluint32_t i = 0; i < m_initCbs.size(); ++i) {
|
||||
const CallbackRecord& cbr = m_initCbs[i];
|
||||
cbr.m_initCb(cbr.m_userp, self(), nextCode());
|
||||
}
|
||||
|
||||
if (expectedCodes && nextCode() != expectedCodes) {
|
||||
|
|
@ -452,17 +424,22 @@ template <> void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) {
|
|||
// Run the callbacks
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
m_fullDump = false; // No more need for next dump to be full
|
||||
for (vluint32_t ent = 0; ent < m_callbacks.size(); ++ent) {
|
||||
VerilatedTraceCallInfo* cip = m_callbacks[ent];
|
||||
(cip->m_fullcb)(self(), cip->m_userthis, cip->m_code);
|
||||
for (vluint32_t i = 0; i < m_fullCbs.size(); ++i) {
|
||||
const CallbackRecord& cbr = m_fullCbs[i];
|
||||
cbr.m_dumpCb(cbr.m_userp, self());
|
||||
}
|
||||
} else {
|
||||
for (vluint32_t ent = 0; ent < m_callbacks.size(); ++ent) {
|
||||
VerilatedTraceCallInfo* cip = m_callbacks[ent];
|
||||
(cip->m_changecb)(self(), cip->m_userthis, cip->m_code);
|
||||
for (vluint32_t i = 0; i < m_chgCbs.size(); ++i) {
|
||||
const CallbackRecord& cbr = m_chgCbs[i];
|
||||
cbr.m_dumpCb(cbr.m_userp, self());
|
||||
}
|
||||
}
|
||||
|
||||
for (vluint32_t i = 0; i < m_cleanupCbs.size(); ++i) {
|
||||
const CallbackRecord& cbr = m_cleanupCbs[i];
|
||||
cbr.m_dumpCb(cbr.m_userp, self());
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
if (VL_LIKELY(bufferp)) {
|
||||
// Mark end of the trace buffer we just filled
|
||||
|
|
@ -481,17 +458,32 @@ template <> void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) {
|
|||
// Non-hot path internal interface to Verilator generated code
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::addCallback(callback_t initcb, callback_t fullcb,
|
||||
callback_t changecb,
|
||||
void* userthis) VL_MT_UNSAFE_ONE {
|
||||
void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
|
||||
CallbackRecord& cbRec) {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(timeLastDump() != 0)) {
|
||||
std::string msg = (std::string("Internal: ") + __FILE__ + "::" + __FUNCTION__
|
||||
+ " called with already open file");
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
}
|
||||
VerilatedTraceCallInfo* cip = new VerilatedTraceCallInfo(initcb, fullcb, changecb, userthis);
|
||||
m_callbacks.push_back(cip);
|
||||
cbVec.push_back(cbRec);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addInitCb(initCb_t cb, void* userp) {
|
||||
CallbackRecord cbr(cb, userp);
|
||||
addCallbackRecord(m_initCbs, cbr);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addFullCb(dumpCb_t cb, void* userp) {
|
||||
CallbackRecord cbr(cb, userp);
|
||||
addCallbackRecord(m_fullCbs, cbr);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addChgCb(dumpCb_t cb, void* userp) {
|
||||
CallbackRecord cbr(cb, userp);
|
||||
addCallbackRecord(m_chgCbs, cbr);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addCleanupCb(dumpCb_t cb, void* userp) {
|
||||
CallbackRecord cbr(cb, userp);
|
||||
addCallbackRecord(m_cleanupCbs, cbr);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
|
|
|
|||
|
|
@ -193,12 +193,14 @@ class AstCFuncType {
|
|||
public:
|
||||
enum en {
|
||||
FT_NORMAL,
|
||||
TRACE_REGISTER,
|
||||
TRACE_INIT,
|
||||
TRACE_INIT_SUB,
|
||||
TRACE_FULL,
|
||||
TRACE_FULL_SUB,
|
||||
TRACE_CHANGE,
|
||||
TRACE_CHANGE_SUB
|
||||
TRACE_CHANGE_SUB,
|
||||
TRACE_CLEANUP
|
||||
};
|
||||
enum en m_e;
|
||||
inline AstCFuncType()
|
||||
|
|
@ -210,10 +212,7 @@ public:
|
|||
: m_e(static_cast<en>(_e)) {}
|
||||
operator en() const { return m_e; }
|
||||
// METHODS
|
||||
bool isTrace() const {
|
||||
return (m_e == TRACE_INIT || m_e == TRACE_INIT_SUB || m_e == TRACE_FULL
|
||||
|| m_e == TRACE_FULL_SUB || m_e == TRACE_CHANGE || m_e == TRACE_CHANGE_SUB);
|
||||
}
|
||||
bool isTrace() const { return m_e != FT_NORMAL; }
|
||||
};
|
||||
inline bool operator==(const AstCFuncType& lhs, const AstCFuncType& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
|
|
|
|||
|
|
@ -4809,7 +4809,7 @@ class AstTraceDecl : public AstNodeStmt {
|
|||
// Trace point declaration
|
||||
// Separate from AstTraceInc; as a declaration can't be deleted
|
||||
// Parents: {statement list}
|
||||
// Children: none
|
||||
// Children: expression being traced
|
||||
private:
|
||||
uint32_t m_code; // Trace identifier code; converted to ASCII by trace routines
|
||||
const string m_showname; // Name of variable
|
||||
|
|
@ -4838,6 +4838,7 @@ public:
|
|||
, m_declDirection(varp->declDirection())
|
||||
, m_isScoped(isScoped) {
|
||||
dtypeFrom(valuep);
|
||||
addNOp1p(valuep);
|
||||
}
|
||||
virtual int instrCount() const { return 100; } // Large...
|
||||
ASTNODE_NODE_FUNCS(TraceDecl)
|
||||
|
|
@ -4856,20 +4857,25 @@ public:
|
|||
AstBasicDTypeKwd declKwd() const { return m_declKwd; }
|
||||
VDirection declDirection() const { return m_declDirection; }
|
||||
bool isScoped() const { return m_isScoped; }
|
||||
AstNode* valuep() const { return op1p(); }
|
||||
};
|
||||
|
||||
class AstTraceInc : public AstNodeStmt {
|
||||
// Trace point; incremental change detect and dump
|
||||
// Trace point dump
|
||||
// Parents: {statement list}
|
||||
// Children: incremental value
|
||||
// Children: op1: things to emit before this node,
|
||||
// op2: expression being traced (from decl)
|
||||
|
||||
private:
|
||||
AstTraceDecl* m_declp; // [After V3Trace] Pointer to declaration
|
||||
AstTraceDecl* m_declp; // Pointer to declaration
|
||||
const bool m_full; // Is this a full vs incremental dump
|
||||
public:
|
||||
AstTraceInc(FileLine* fl, AstTraceDecl* declp, AstNode* valuep)
|
||||
: ASTGEN_SUPER(fl) {
|
||||
AstTraceInc(FileLine* fl, AstTraceDecl* declp, bool full)
|
||||
: ASTGEN_SUPER(fl)
|
||||
, m_declp(declp)
|
||||
, m_full(full) {
|
||||
dtypeFrom(declp);
|
||||
m_declp = declp;
|
||||
addNOp2p(valuep);
|
||||
addOp2p(declp->valuep()->cloneTree(true));
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(TraceInc)
|
||||
virtual const char* broken() const {
|
||||
|
|
@ -4891,13 +4897,11 @@ public:
|
|||
virtual bool isOutputter() const { return true; }
|
||||
// but isPure() true
|
||||
// op1 = Statements before the value
|
||||
AstNode* precondsp() const {
|
||||
return op1p();
|
||||
} // op1 = prepare statements for condition (exec every loop)
|
||||
AstNode* precondsp() const { return op1p(); }
|
||||
void addPrecondsp(AstNode* newp) { addOp1p(newp); }
|
||||
// op2 = Value to trace
|
||||
AstTraceDecl* declp() const { return m_declp; } // Where defined
|
||||
AstNode* valuep() const { return op2p(); }
|
||||
AstTraceDecl* declp() const { return m_declp; }
|
||||
bool full() const { return m_full; }
|
||||
};
|
||||
|
||||
class AstActive : public AstNode {
|
||||
|
|
|
|||
|
|
@ -732,7 +732,7 @@ private:
|
|||
|
||||
// Ignores
|
||||
virtual void visit(AstInitial*) VL_OVERRIDE {}
|
||||
virtual void visit(AstTraceInc*) VL_OVERRIDE {}
|
||||
virtual void visit(AstTraceDecl*) VL_OVERRIDE {}
|
||||
virtual void visit(AstCoverToggle*) VL_OVERRIDE {}
|
||||
virtual void visit(AstNodeDType*) VL_OVERRIDE {}
|
||||
|
||||
|
|
|
|||
157
src/V3EmitC.cpp
157
src/V3EmitC.cpp
|
|
@ -3140,13 +3140,9 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
|
|||
emitIntFuncDecls(modp, true);
|
||||
|
||||
if (v3Global.opt.trace() && !VN_IS(modp, Class)) {
|
||||
ofp()->putsPrivate(false); // public:
|
||||
puts("static void " + protect("traceInit") + "(" + v3Global.opt.traceClassBase()
|
||||
+ "* vcdp, void* userthis, uint32_t code);\n");
|
||||
puts("static void " + protect("traceFull") + "(" + v3Global.opt.traceClassBase()
|
||||
+ "* vcdp, void* userthis, uint32_t code);\n");
|
||||
puts("static void " + protect("traceChg") + "(" + v3Global.opt.traceClassBase()
|
||||
+ "* vcdp, void* userthis, uint32_t code);\n");
|
||||
ofp()->putsPrivate(true); // private:
|
||||
puts("static void " + protect("traceInit") + "(void* userp, "
|
||||
+ v3Global.opt.traceClassBase() + "* tracep, uint32_t code) VL_ATTR_COLD;\n");
|
||||
}
|
||||
if (v3Global.opt.savable()) {
|
||||
ofp()->putsPrivate(false); // public:
|
||||
|
|
@ -3376,53 +3372,26 @@ class EmitCTrace : EmitCStmts {
|
|||
|
||||
puts("void " + topClassName() + "::trace(");
|
||||
puts(v3Global.opt.traceClassBase() + "C* tfp, int, int) {\n");
|
||||
puts("tfp->spTrace()->addCallback("
|
||||
"&"
|
||||
+ topClassName() + "::" + protect("traceInit") + ", &" + topClassName()
|
||||
+ "::" + protect("traceFull") + ", &" + topClassName() + "::" + protect("traceChg")
|
||||
+ ", this);\n");
|
||||
puts("tfp->spTrace()->addInitCb(&" + protect("traceInit") + ", __VlSymsp);\n");
|
||||
puts(protect("traceRegister") + "(tfp->spTrace());\n");
|
||||
puts("}\n");
|
||||
puts("\n");
|
||||
splitSizeInc(10);
|
||||
|
||||
puts("void " + topClassName() + "::" + protect("traceInit") + "("
|
||||
+ v3Global.opt.traceClassBase() + "* vcdp, void* userthis, uint32_t code) {\n");
|
||||
putsDecoration("// Callback from vcd->open()\n");
|
||||
puts(topClassName() + "* t = (" + topClassName() + "*)userthis;\n");
|
||||
puts(EmitCBaseVisitor::symClassVar() + " = t->__VlSymsp; // Setup global symbol table\n");
|
||||
puts("void " + topClassName() + "::" + protect("traceInit") + "(void* userp, "
|
||||
+ v3Global.opt.traceClassBase() + "* tracep, uint32_t code) {\n");
|
||||
putsDecoration("// Callback from tracep->open()\n");
|
||||
puts(symClassVar() + " = static_cast<" + symClassName() + "*>(userp);\n");
|
||||
puts("if (!Verilated::calcUnusedSigs()) {\n");
|
||||
puts("VL_FATAL_MT(__FILE__, __LINE__, __FILE__,\n");
|
||||
puts(" \"Turning on wave traces requires Verilated::traceEverOn(true) call "
|
||||
"before time 0.\");\n");
|
||||
puts("}\n");
|
||||
puts("vcdp->scopeEscape(' ');\n");
|
||||
puts("t->" + protect("traceInitThis") + "(vlSymsp, vcdp, code);\n");
|
||||
puts("vcdp->scopeEscape('.');\n"); // Restore so later traced files won't break
|
||||
puts("}\n");
|
||||
splitSizeInc(10);
|
||||
|
||||
puts("void " + topClassName() + "::" + protect("traceFull") + "("
|
||||
+ v3Global.opt.traceClassBase() + "* vcdp, void* userthis, uint32_t code) {\n");
|
||||
putsDecoration("// Callback from vcd->dump()\n");
|
||||
puts(topClassName() + "* t = (" + topClassName() + "*)userthis;\n");
|
||||
puts(EmitCBaseVisitor::symClassVar() + " = t->__VlSymsp; // Setup global symbol table\n");
|
||||
puts("t->" + protect("traceFullThis") + "(vlSymsp, vcdp, code);\n");
|
||||
puts("}\n");
|
||||
splitSizeInc(10);
|
||||
|
||||
puts("\n//======================\n\n");
|
||||
}
|
||||
|
||||
void emitTraceFast() {
|
||||
puts("\n//======================\n\n");
|
||||
|
||||
puts("void " + topClassName() + "::" + protect("traceChg") + "("
|
||||
+ v3Global.opt.traceClassBase() + "* vcdp, void* userthis, uint32_t code) {\n");
|
||||
putsDecoration("// Callback from vcd->dump()\n");
|
||||
puts(topClassName() + "* t = (" + topClassName() + "*)userthis;\n");
|
||||
puts(EmitCBaseVisitor::symClassVar() + " = t->__VlSymsp; // Setup global symbol table\n");
|
||||
puts("if (vlSymsp->getClearActivity()) {\n");
|
||||
puts("t->" + protect("traceChgThis") + "(vlSymsp, vcdp, code);\n");
|
||||
puts("}\n");
|
||||
puts("vlSymsp->__Vm_baseCode = code;\n");
|
||||
puts("tracep->module(vlSymsp->name());\n");
|
||||
puts("tracep->scopeEscape(' ');\n");
|
||||
puts(topClassName() + "::" + protect("traceInitTop") + "(vlSymsp, tracep);\n");
|
||||
puts("tracep->scopeEscape('.');\n"); // Restore so later traced files won't break
|
||||
puts("}\n");
|
||||
splitSizeInc(10);
|
||||
|
||||
|
|
@ -3430,21 +3399,21 @@ class EmitCTrace : EmitCStmts {
|
|||
}
|
||||
|
||||
bool emitTraceIsScBv(AstTraceInc* nodep) {
|
||||
const AstVarRef* varrefp = VN_CAST(nodep->valuep(), VarRef);
|
||||
const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef);
|
||||
if (!varrefp) return false;
|
||||
AstVar* varp = varrefp->varp();
|
||||
return varp->isSc() && varp->isScBv();
|
||||
}
|
||||
|
||||
bool emitTraceIsScBigUint(AstTraceInc* nodep) {
|
||||
const AstVarRef* varrefp = VN_CAST(nodep->valuep(), VarRef);
|
||||
const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef);
|
||||
if (!varrefp) return false;
|
||||
AstVar* varp = varrefp->varp();
|
||||
return varp->isSc() && varp->isScBigUint();
|
||||
}
|
||||
|
||||
bool emitTraceIsScUint(AstTraceInc* nodep) {
|
||||
const AstVarRef* varrefp = VN_CAST(nodep->valuep(), VarRef);
|
||||
const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef);
|
||||
if (!varrefp) return false;
|
||||
AstVar* varp = varrefp->varp();
|
||||
return varp->isSc() && varp->isScUint();
|
||||
|
|
@ -3452,15 +3421,15 @@ class EmitCTrace : EmitCStmts {
|
|||
|
||||
void emitTraceInitOne(AstTraceDecl* nodep, int enumNum) {
|
||||
if (nodep->dtypep()->basicp()->isDouble()) {
|
||||
puts("vcdp->declDouble");
|
||||
puts("tracep->declDouble");
|
||||
} else if (nodep->isWide()) {
|
||||
puts("vcdp->declArray");
|
||||
puts("tracep->declArray");
|
||||
} else if (nodep->isQuad()) {
|
||||
puts("vcdp->declQuad");
|
||||
puts("tracep->declQuad");
|
||||
} else if (nodep->bitRange().ranged()) {
|
||||
puts("vcdp->declBus");
|
||||
puts("tracep->declBus");
|
||||
} else {
|
||||
puts("vcdp->declBit");
|
||||
puts("tracep->declBit");
|
||||
}
|
||||
|
||||
puts("(c+" + cvtToStr(nodep->code()));
|
||||
|
|
@ -3574,10 +3543,10 @@ class EmitCTrace : EmitCStmts {
|
|||
putbs("\"" + constp->num().displayed(nodep, "%0b") + "\"");
|
||||
}
|
||||
puts("};\n");
|
||||
puts("vcdp->declDTypeEnum(" + cvtToStr(enumNum) + ", \"" + enump->prettyName()
|
||||
+ "\", " + cvtToStr(nvals) + ", " + cvtToStr(enump->widthMin()) + ", "
|
||||
+ protect("__VenumItemNames") + ", " + protect("__VenumItemValues")
|
||||
+ ");\n");
|
||||
puts("tracep->declDTypeEnum(" + cvtToStr(enumNum) + ", \""
|
||||
+ enump->prettyName() + "\", " + cvtToStr(nvals) + ", "
|
||||
+ cvtToStr(enump->widthMin()) + ", " + protect("__VenumItemNames") + ", "
|
||||
+ protect("__VenumItemValues") + ");\n");
|
||||
puts("}\n");
|
||||
}
|
||||
return enumNum;
|
||||
|
|
@ -3588,31 +3557,29 @@ class EmitCTrace : EmitCStmts {
|
|||
|
||||
void emitTraceChangeOne(AstTraceInc* nodep, int arrayindex) {
|
||||
iterateAndNextNull(nodep->precondsp());
|
||||
const bool full = (m_funcp->funcType() == AstCFuncType::TRACE_FULL
|
||||
|| m_funcp->funcType() == AstCFuncType::TRACE_FULL_SUB);
|
||||
const string func = full ? "full" : "chg";
|
||||
const string func = nodep->full() ? "full" : "chg";
|
||||
bool emitWidth = true;
|
||||
if (nodep->dtypep()->basicp()->isDouble()) {
|
||||
puts("vcdp->" + func + "Double");
|
||||
puts("tracep->" + func + "Double");
|
||||
emitWidth = false;
|
||||
} else if (nodep->isWide() || emitTraceIsScBv(nodep) || emitTraceIsScBigUint(nodep)) {
|
||||
puts("vcdp->" + func + "WData");
|
||||
puts("tracep->" + func + "WData");
|
||||
} else if (nodep->isQuad()) {
|
||||
puts("vcdp->" + func + "QData");
|
||||
puts("tracep->" + func + "QData");
|
||||
} else if (nodep->declp()->widthMin() > 16) {
|
||||
puts("vcdp->" + func + "IData");
|
||||
puts("tracep->" + func + "IData");
|
||||
} else if (nodep->declp()->widthMin() > 8) {
|
||||
puts("vcdp->" + func + "SData");
|
||||
puts("tracep->" + func + "SData");
|
||||
} else if (nodep->declp()->widthMin() > 1) {
|
||||
puts("vcdp->" + func + "CData");
|
||||
puts("tracep->" + func + "CData");
|
||||
} else {
|
||||
puts("vcdp->" + func + "Bit");
|
||||
puts("tracep->" + func + "Bit");
|
||||
emitWidth = false;
|
||||
}
|
||||
|
||||
const uint32_t offset = (arrayindex < 0) ? 0 : (arrayindex * nodep->declp()->widthWords());
|
||||
const uint32_t code = nodep->declp()->code() + offset;
|
||||
puts(v3Global.opt.trueTraceThreads() && !full ? "(base+" : "(oldp+");
|
||||
puts(v3Global.opt.trueTraceThreads() && !nodep->full() ? "(base+" : "(oldp+");
|
||||
puts(cvtToStr(code - m_baseCode));
|
||||
puts(",");
|
||||
emitTraceValue(nodep, arrayindex);
|
||||
|
|
@ -3620,8 +3587,7 @@ class EmitCTrace : EmitCStmts {
|
|||
puts(");\n");
|
||||
}
|
||||
void emitTraceValue(AstTraceInc* nodep, int arrayindex) {
|
||||
if (VN_IS(nodep->valuep(), VarRef)) {
|
||||
AstVarRef* varrefp = VN_CAST(nodep->valuep(), VarRef);
|
||||
if (AstVarRef* const varrefp = VN_CAST(nodep->valuep(), VarRef)) {
|
||||
AstVar* varp = varrefp->varp();
|
||||
puts("(");
|
||||
if (emitTraceIsScBigUint(nodep)) {
|
||||
|
|
@ -3682,30 +3648,37 @@ class EmitCTrace : EmitCStmts {
|
|||
puts(" ");
|
||||
puts(topClassName() + "::" + nodep->nameProtect() + "(" + cFuncArgs(nodep) + ") {\n");
|
||||
|
||||
if (nodep->symProlog()) puts(EmitCBaseVisitor::symTopAssign() + "\n");
|
||||
if (nodep->funcType() != AstCFuncType::TRACE_REGISTER) {
|
||||
puts(symClassVar() + " = static_cast<" + symClassName() + "*>(userp);\n");
|
||||
}
|
||||
|
||||
if (nodep->symProlog()) puts(symTopAssign() + "\n");
|
||||
|
||||
m_baseCode = -1;
|
||||
|
||||
if (nodep->funcType() == AstCFuncType::TRACE_FULL_SUB
|
||||
|| nodep->funcType() == AstCFuncType::TRACE_CHANGE_SUB) {
|
||||
const AstTraceInc* const stmtp = VN_CAST_CONST(nodep->stmtsp(), TraceInc);
|
||||
if (!stmtp) {
|
||||
nodep->stmtsp()->v3fatalSrc("Trace sub function should contain AstTraceInc");
|
||||
}
|
||||
m_baseCode = stmtp->declp()->code();
|
||||
if (v3Global.opt.trueTraceThreads()
|
||||
&& nodep->funcType() == AstCFuncType::TRACE_CHANGE_SUB) {
|
||||
puts("vluint32_t base = code+" + cvtToStr(m_baseCode) + ";\n");
|
||||
puts("if (false && vcdp && base) {} // Prevent unused\n");
|
||||
if (nodep->funcType() == AstCFuncType::TRACE_CHANGE_SUB) {
|
||||
const AstNode* const stmtp = nodep->stmtsp();
|
||||
const AstIf* const ifp = VN_CAST_CONST(stmtp, If);
|
||||
const AstTraceInc* const tracep
|
||||
= VN_CAST_CONST(ifp ? ifp->ifsp() : stmtp, TraceInc);
|
||||
// On rare occasions we can end up with an empty sub function
|
||||
m_baseCode = tracep ? tracep->declp()->code() : 0;
|
||||
if (v3Global.opt.trueTraceThreads()) {
|
||||
puts("const vluint32_t base = vlSymsp->__Vm_baseCode + " + cvtToStr(m_baseCode)
|
||||
+ ";\n");
|
||||
puts("if (false && tracep && base) {} // Prevent unused\n");
|
||||
} else {
|
||||
puts("vluint32_t* oldp = vcdp->oldp(code+" + cvtToStr(m_baseCode) + ");\n");
|
||||
puts("if (false && vcdp && oldp) {} // Prevent unused\n");
|
||||
puts("vluint32_t* const oldp = tracep->oldp(vlSymsp->__Vm_baseCode + "
|
||||
+ cvtToStr(m_baseCode) + ");\n");
|
||||
puts("if (false && oldp) {} // Prevent unused\n");
|
||||
}
|
||||
} else if (nodep->funcType() == AstCFuncType::TRACE_FULL_SUB) {
|
||||
m_baseCode = 0;
|
||||
puts("vluint32_t* const oldp = tracep->oldp(vlSymsp->__Vm_baseCode);\n");
|
||||
puts("if (false && oldp) {} // Prevent unused\n");
|
||||
} else if (nodep->funcType() == AstCFuncType::TRACE_INIT_SUB) {
|
||||
puts("int c = code;\n");
|
||||
puts("if (false && vcdp && c) {} // Prevent unused\n");
|
||||
} else {
|
||||
puts("if (false && vcdp) {} // Prevent unused\n");
|
||||
puts("const int c = vlSymsp->__Vm_baseCode;\n");
|
||||
puts("if (false && tracep && c) {} // Prevent unused\n");
|
||||
}
|
||||
|
||||
if (nodep->initsp()) {
|
||||
|
|
@ -3764,11 +3737,7 @@ public:
|
|||
// Put out the file
|
||||
newOutCFile(0);
|
||||
|
||||
if (m_slow) {
|
||||
emitTraceSlow();
|
||||
} else {
|
||||
emitTraceFast();
|
||||
}
|
||||
if (m_slow) { emitTraceSlow(); }
|
||||
|
||||
iterate(v3Global.rootp());
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ public:
|
|||
static string symClassName() { return v3Global.opt.prefix() + "_" + protect("_Syms"); }
|
||||
static string symClassVar() { return symClassName() + "* __restrict vlSymsp"; }
|
||||
static string symTopAssign() {
|
||||
return v3Global.opt.prefix() + "* __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;";
|
||||
return v3Global.opt.prefix() + "* const __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;";
|
||||
}
|
||||
static string funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp) {
|
||||
if (nodep->isConstructor()) {
|
||||
|
|
|
|||
|
|
@ -442,6 +442,7 @@ void EmitCSyms::emitSymHdr() {
|
|||
}
|
||||
if (v3Global.opt.trace()) {
|
||||
puts("bool __Vm_activity; ///< Used by trace routines to determine change occurred\n");
|
||||
puts("uint32_t __Vm_baseCode; ///< Used by trace routines when tracing multiple models\n");
|
||||
}
|
||||
puts("bool __Vm_didInit;\n");
|
||||
|
||||
|
|
@ -495,10 +496,6 @@ void EmitCSyms::emitSymHdr() {
|
|||
|
||||
puts("\n// METHODS\n");
|
||||
puts("inline const char* name() { return __Vm_namep; }\n");
|
||||
if (v3Global.opt.trace()) {
|
||||
puts("inline bool getClearActivity() { bool r=__Vm_activity; "
|
||||
"__Vm_activity=false; return r; }\n");
|
||||
}
|
||||
if (v3Global.opt.savable()) {
|
||||
puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n");
|
||||
puts("void " + protect("__Vdeserialize") + "(VerilatedDeserialize& os);\n");
|
||||
|
|
@ -627,7 +624,10 @@ void EmitCSyms::emitSymImp() {
|
|||
puts(" , __Vm_dumping(false)\n");
|
||||
puts(" , __Vm_dumperp(NULL)\n");
|
||||
}
|
||||
if (v3Global.opt.trace()) puts(" , __Vm_activity(false)\n");
|
||||
if (v3Global.opt.trace()) {
|
||||
puts(" , __Vm_activity(false)\n");
|
||||
puts(" , __Vm_baseCode(0)\n");
|
||||
}
|
||||
puts(" , __Vm_didInit(false)\n");
|
||||
puts(" // Setup submodule names\n");
|
||||
char comma = ',';
|
||||
|
|
|
|||
|
|
@ -516,8 +516,8 @@ private:
|
|||
virtual void visit(AstCoverToggle* nodep) VL_OVERRIDE {
|
||||
iterateNewStmt(nodep, "CoverToggle", "CoverToggle");
|
||||
}
|
||||
virtual void visit(AstTraceInc* nodep) VL_OVERRIDE {
|
||||
bool lastslow = m_inSlow;
|
||||
virtual void visit(AstTraceDecl* nodep) VL_OVERRIDE {
|
||||
const bool lastslow = m_inSlow;
|
||||
m_inSlow = true;
|
||||
iterateNewStmt(nodep, "Tracing", "Tracing");
|
||||
m_inSlow = lastslow;
|
||||
|
|
|
|||
551
src/V3Trace.cpp
551
src/V3Trace.cpp
|
|
@ -14,31 +14,24 @@
|
|||
//
|
||||
//*************************************************************************
|
||||
// V3Trace's Transformations:
|
||||
//
|
||||
// Examine whole design and build a graph describing which function call
|
||||
// may result in a write to a traced variable. This is done in 2 passes:
|
||||
//
|
||||
// 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
|
||||
// Add vertices for TraceDecl, CFunc, CCall and VarRef nodes, add
|
||||
// edges from CCall -> CFunc, VarRef -> TraceDecl, also add edges
|
||||
// for public entry points to CFuncs (these are like a spontaneous
|
||||
// call)
|
||||
//
|
||||
// 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
|
||||
// Add edges from CFunc -> VarRef being written
|
||||
//
|
||||
// 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
|
||||
// Finally:
|
||||
// Process graph to determine when traced variables can change, allocate
|
||||
// activity flags, insert nodes to set activity flags, allocate signal
|
||||
// numbers (codes), and construct the full and incremental trace
|
||||
// functions, together with all other trace support functions.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
|
|
@ -124,19 +117,19 @@ public:
|
|||
};
|
||||
|
||||
class TraceTraceVertex : public V3GraphVertex {
|
||||
AstTraceInc* m_nodep; // TRACEINC this represents
|
||||
AstTraceDecl* const m_nodep; // TRACEINC this represents
|
||||
// NULL, or other vertex with the real code() that duplicates this one
|
||||
TraceTraceVertex* m_duplicatep;
|
||||
|
||||
public:
|
||||
TraceTraceVertex(V3Graph* graphp, AstTraceInc* nodep)
|
||||
TraceTraceVertex(V3Graph* graphp, AstTraceDecl* 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(); }
|
||||
AstTraceDecl* nodep() const { return m_nodep; }
|
||||
virtual string name() const { return nodep()->name(); }
|
||||
virtual string dotColor() const { return "red"; }
|
||||
virtual FileLine* fileline() const { return nodep()->fileline(); }
|
||||
TraceTraceVertex* duplicatep() const { return m_duplicatep; }
|
||||
|
|
@ -171,7 +164,7 @@ private:
|
|||
// Ast*::user4() // V3Hashed calculation
|
||||
// Cleared entire netlist
|
||||
// AstCFunc::user1() // V3GraphVertex* for this node
|
||||
// AstTraceInc::user1() // V3GraphVertex* for this node
|
||||
// AstTraceDecl::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
|
||||
|
|
@ -184,26 +177,23 @@ private:
|
|||
AstNodeModule* m_topModp; // Module to add variables to
|
||||
AstScope* m_topScopep; // Scope to add variables to
|
||||
AstCFunc* m_funcp; // C function adding to graph
|
||||
AstTraceInc* m_tracep; // Trace function adding to graph
|
||||
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
|
||||
AstTraceDecl* m_tracep; // Trace function adding to graph
|
||||
AstVarScope* m_activityVscp; // Activity variable
|
||||
uint32_t m_activityNumber; // Count of fields in activity variable
|
||||
uint32_t m_code; // Trace ident code# being assigned
|
||||
V3Graph m_graph; // Var/CFunc tracking
|
||||
TraceActivityVertex* const m_alwaysVtxp; // "Always trace" vertex
|
||||
bool m_finding; // Pass one of algorithm?
|
||||
int m_funcNum; // Function number being built
|
||||
|
||||
VDouble0 m_statChgSigs; // Statistic tracking
|
||||
VDouble0 m_statUniqSigs; // Statistic tracking
|
||||
VDouble0 m_statUniqCodes; // Statistic tracking
|
||||
|
||||
// All activity numbers applying to a given trace
|
||||
typedef std::set<uint32_t> ActCodeSet;
|
||||
// For activity set, what traces apply
|
||||
typedef std::multimap<ActCodeSet, const TraceTraceVertex*> TraceVec;
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
|
|
@ -216,7 +206,7 @@ private:
|
|||
itp = itp->verticesNextp()) {
|
||||
if (const TraceTraceVertex* const vvertexp
|
||||
= dynamic_cast<const TraceTraceVertex*>(itp)) {
|
||||
const AstTraceInc* const nodep = vvertexp->nodep();
|
||||
const AstTraceDecl* const nodep = vvertexp->nodep();
|
||||
if (nodep->valuep()) {
|
||||
UASSERT_OBJ(nodep->valuep()->backp() == nodep, nodep,
|
||||
"Trace duplicate back needs consistency,"
|
||||
|
|
@ -235,17 +225,17 @@ private:
|
|||
// Find if there are any duplicates
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (TraceTraceVertex* const vvertexp = dynamic_cast<TraceTraceVertex*>(itp)) {
|
||||
AstTraceInc* const nodep = vvertexp->nodep();
|
||||
AstTraceDecl* const nodep = vvertexp->nodep();
|
||||
if (nodep->valuep() && !vvertexp->duplicatep()) {
|
||||
V3Hashed::iterator dupit = hashed.findDuplicate(nodep->valuep());
|
||||
if (dupit != hashed.end()) {
|
||||
const AstTraceInc* const dupincp
|
||||
= VN_CAST_CONST(hashed.iteratorNodep(dupit)->backp(), TraceInc);
|
||||
UASSERT_OBJ(dupincp, nodep, "Trace duplicate of wrong type");
|
||||
const AstTraceDecl* const dupDeclp
|
||||
= VN_CAST_CONST(hashed.iteratorNodep(dupit)->backp(), TraceDecl);
|
||||
UASSERT_OBJ(dupDeclp, nodep, "Trace duplicate of wrong type");
|
||||
TraceTraceVertex* const dupvertexp
|
||||
= dynamic_cast<TraceTraceVertex*>(dupincp->user1u().toGraphVertex());
|
||||
= dynamic_cast<TraceTraceVertex*>(dupDeclp->user1u().toGraphVertex());
|
||||
UINFO(8, " Orig " << nodep << endl);
|
||||
UINFO(8, " dup " << dupincp << endl);
|
||||
UINFO(8, " dup " << dupDeclp << endl);
|
||||
// Mark the hashed node as the original and our
|
||||
// iterating node as duplicated
|
||||
vvertexp->duplicatep(dupvertexp);
|
||||
|
|
@ -317,6 +307,14 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
AstNode* selectActivity(FileLine* flp, uint32_t acode, bool lvalue) {
|
||||
if (v3Global.opt.mtasks()) {
|
||||
return new AstArraySel(flp, new AstVarRef(flp, m_activityVscp, lvalue), acode);
|
||||
} else {
|
||||
return new AstSel(flp, new AstVarRef(flp, m_activityVscp, lvalue), acode, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void assignActivity() {
|
||||
// Select activity numbers and put into each activity vertex
|
||||
m_activityNumber = 1; // Note 0 indicates "slow"
|
||||
|
|
@ -334,7 +332,7 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
FileLine* const flp = m_chgFuncp->fileline();
|
||||
FileLine* const flp = m_topScopep->fileline();
|
||||
|
||||
AstVar* newvarp;
|
||||
if (v3Global.opt.mtasks()) {
|
||||
|
|
@ -379,144 +377,82 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
AstNode* selectActivity(FileLine* flp, uint32_t acode, bool lvalue) {
|
||||
if (v3Global.opt.mtasks()) {
|
||||
return new AstArraySel(flp, new AstVarRef(flp, m_activityVscp, lvalue), acode);
|
||||
} else {
|
||||
return new AstSel(flp, new AstVarRef(flp, m_activityVscp, lvalue), acode, 1);
|
||||
AstCFunc* newCFunc(AstCFuncType type, AstCFunc* callfromp, AstCFunc* regp, int& funcNump) {
|
||||
// Create new function
|
||||
string name;
|
||||
switch (type) {
|
||||
case AstCFuncType::TRACE_FULL: name = "traceFullTop"; break;
|
||||
case AstCFuncType::TRACE_FULL_SUB: name = "traceFullSub"; break;
|
||||
case AstCFuncType::TRACE_CHANGE: name = "traceChgTop"; break;
|
||||
case AstCFuncType::TRACE_CHANGE_SUB: name = "traceChgSub"; break;
|
||||
default: m_topScopep->v3fatalSrc("Bad trace function type");
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
name += cvtToStr(funcNump++);
|
||||
FileLine* const flp = m_topScopep->fileline();
|
||||
AstCFunc* const funcp = new AstCFunc(flp, name, m_topScopep);
|
||||
const string argTypes("void* userp, " + v3Global.opt.traceClassBase() + "* tracep");
|
||||
funcp->argTypes(argTypes);
|
||||
funcp->funcType(type);
|
||||
funcp->slow(type == AstCFuncType::TRACE_FULL || type == AstCFuncType::TRACE_FULL_SUB);
|
||||
funcp->symProlog(true);
|
||||
basep->addNext(funcp);
|
||||
UINFO(5, " Newfunc " << funcp << endl);
|
||||
funcp->declPrivate(true);
|
||||
// Add it to top scope
|
||||
m_topScopep->addActivep(funcp);
|
||||
// Add call to new function
|
||||
if (callfromp) {
|
||||
AstCCall* callp = new AstCCall(funcp->fileline(), funcp);
|
||||
callp->argTypes("userp, tracep");
|
||||
callfromp->addStmtsp(callp);
|
||||
}
|
||||
// Register function
|
||||
if (regp) {
|
||||
string registration = "tracep->add";
|
||||
if (type == AstCFuncType::TRACE_FULL) {
|
||||
registration += "Full";
|
||||
} else if (type == AstCFuncType::TRACE_CHANGE) {
|
||||
registration += "Chg";
|
||||
} else {
|
||||
funcp->v3fatal("Don't know how to register this type of function");
|
||||
}
|
||||
registration += "Cb(&" + protect(name) + ", __VlSymsp);\n";
|
||||
AstCStmt* const stmtp = new AstCStmt(flp, registration);
|
||||
regp->addStmtsp(stmtp);
|
||||
}
|
||||
// Add global activity check to TRACE_CHANGE functions
|
||||
if (type == AstCFuncType::TRACE_CHANGE) {
|
||||
funcp->addInitsp(
|
||||
new AstCStmt(flp, string("if (VL_UNLIKELY(!vlSymsp->__Vm_activity)) return;\n")));
|
||||
}
|
||||
// Done
|
||||
UINFO(5, " newCFunc " << 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 (VN_IS(callfromp, CFunc)) {
|
||||
VN_CAST(callfromp, CFunc)->addStmtsp(callp);
|
||||
} else if (VN_IS(callfromp, If)) {
|
||||
VN_CAST(callfromp, If)->addIfsp(callp);
|
||||
} else {
|
||||
callfromp->v3fatalSrc("Unknown caller node type"); // Where to add it??
|
||||
}
|
||||
return funcp;
|
||||
}
|
||||
|
||||
uint32_t assignDeclCode(AstTraceDecl* nodep) {
|
||||
void assignDeclCode(AstTraceDecl* nodep) {
|
||||
if (!nodep->code()) {
|
||||
nodep->code(m_code);
|
||||
m_code += nodep->codeInc();
|
||||
m_statUniqCodes += nodep->codeInc();
|
||||
++m_statUniqSigs;
|
||||
}
|
||||
return nodep->code();
|
||||
}
|
||||
|
||||
AstTraceInc* assignTraceCode(const TraceTraceVertex* vvertexp, bool needChg) {
|
||||
// Assign trace code, add to tree, return node for change tree or null
|
||||
void sortTraces(TraceVec& traces, uint32_t& nFullCodes, uint32_t& nChgCodes) {
|
||||
// We will split functions such that each have to dump roughly the same amount of data
|
||||
// for this we need to keep tack of the number of codes used by the trace functions.
|
||||
|
||||
AstTraceInc*const nodep = vvertexp->nodep();
|
||||
|
||||
// Find non-duplicated node;
|
||||
const TraceTraceVertex*const dupvertexp = vvertexp->duplicatep();
|
||||
if (dupvertexp) {
|
||||
UINFO(9, " dupOf " << cvtToHex(dupvertexp) << " " << cvtToHex(dupvertexp->nodep())
|
||||
<< " " << dupvertexp << endl);
|
||||
UASSERT_OBJ(!dupvertexp->duplicatep(), dupvertexp->nodep(),
|
||||
"Original node was marked as a duplicate");
|
||||
UASSERT_OBJ(dupvertexp != vvertexp, dupvertexp->nodep(),
|
||||
"Node was marked as duplicate of itself");
|
||||
// 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.
|
||||
const uint32_t code = assignDeclCode(dupvertexp->nodep()->declp());
|
||||
nodep->declp()->code(code);
|
||||
} else {
|
||||
assignDeclCode(nodep->declp());
|
||||
}
|
||||
UINFO(8, " Created code=" << nodep->declp()->code() << " "
|
||||
<< (dupvertexp ? "[PREASS]" : "") << " "
|
||||
<< (needChg ? "[CHG]" : "") << " " << nodep << endl);
|
||||
|
||||
AstTraceInc* incAddp = NULL;
|
||||
if (!dupvertexp) {
|
||||
// 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);
|
||||
}
|
||||
return incAddp;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
// All activity numbers applying to a given trace
|
||||
typedef std::set<uint32_t> ActCodeSet;
|
||||
// For activity set, what traces apply
|
||||
typedef std::multimap<ActCodeSet, const TraceTraceVertex*> TraceVec;
|
||||
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.
|
||||
// Populate sort structure
|
||||
for (const V3GraphVertex* itp = m_graph.verticesBeginp(); itp;
|
||||
itp = itp->verticesNextp()) {
|
||||
if (const TraceTraceVertex* const vvertexp
|
||||
= dynamic_cast<const TraceTraceVertex*>(itp)) {
|
||||
if (const TraceTraceVertex* const vtxp = dynamic_cast<const TraceTraceVertex*>(itp)) {
|
||||
ActCodeSet actset;
|
||||
UINFO(9, " Add to sort: " << vvertexp << endl);
|
||||
if (debug() >= 9) vvertexp->nodep()->dumpTree(cout, "- trnode: ");
|
||||
for (const V3GraphEdge* edgep = vvertexp->inBeginp(); edgep;
|
||||
UINFO(9, " Add to sort: " << vtxp << endl);
|
||||
if (debug() >= 9) vtxp->nodep()->dumpTree(cout, "- trnode: ");
|
||||
for (const V3GraphEdge* edgep = vtxp->inBeginp(); edgep;
|
||||
edgep = edgep->inNextp()) {
|
||||
const TraceActivityVertex* const cfvertexp
|
||||
= dynamic_cast<const TraceActivityVertex*>(edgep->fromp());
|
||||
UASSERT_OBJ(cfvertexp, vvertexp->nodep(),
|
||||
UASSERT_OBJ(cfvertexp, vtxp->nodep(),
|
||||
"Should have been function pointing to this trace");
|
||||
UINFO(9, " Activity: " << cfvertexp << endl);
|
||||
if (cfvertexp->activityAlways()) {
|
||||
|
|
@ -528,81 +464,236 @@ private:
|
|||
actset.insert(cfvertexp->activityCode());
|
||||
}
|
||||
}
|
||||
// Count nodes
|
||||
if (!vtxp->duplicatep()) {
|
||||
const uint32_t inc = vtxp->nodep()->codeInc();
|
||||
nFullCodes += inc;
|
||||
if (!actset.empty()) nChgCodes += inc;
|
||||
}
|
||||
// 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));
|
||||
traces.insert(make_pair(actset, vtxp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
void createFullTraceFunction(const TraceVec& traces, uint32_t nAllCodes, uint32_t parallelism,
|
||||
AstCFunc* regFuncp) {
|
||||
const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace()
|
||||
: std::numeric_limits<int>::max();
|
||||
|
||||
// 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;
|
||||
const TraceTraceVertex* const vvertexp = it->second;
|
||||
UINFO(9, " Done sort: " << vvertexp << endl);
|
||||
// Activity needed; it's not a constant value or only set in initial block
|
||||
const bool needChg = actset.count(TraceActivityVertex::ACTIVITY_NEVER) == 0;
|
||||
AstTraceInc* const addp = assignTraceCode(vvertexp, needChg);
|
||||
if (addp) { // Else no activity or duplicate
|
||||
UASSERT_OBJ(needChg, addp, "needChg=0 and shouldn't need to add.");
|
||||
if (actset.count(TraceActivityVertex::ACTIVITY_ALWAYS) != 0) {
|
||||
// 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);
|
||||
int topFuncNum = 0;
|
||||
int subFuncNum = 0;
|
||||
TraceVec::const_iterator it = traces.begin();
|
||||
while (it != traces.end()) {
|
||||
AstCFunc* topFuncp = NULL;
|
||||
AstCFunc* subFuncp = NULL;
|
||||
int subStmts = 0;
|
||||
const uint32_t maxCodes = (nAllCodes + parallelism - 1) / parallelism;
|
||||
uint32_t nCodes = 0;
|
||||
for (; nCodes < maxCodes && it != traces.end(); ++it) {
|
||||
const TraceTraceVertex* const vtxp = it->second;
|
||||
AstTraceDecl* const declp = vtxp->nodep();
|
||||
if (const TraceTraceVertex* const canonVtxp = vtxp->duplicatep()) {
|
||||
// This is a duplicate trace node. We will assign the signal
|
||||
// number to the canonical node, and emit this as an alias, so
|
||||
// no need to create a TraceInc node.
|
||||
AstTraceDecl* const canonDeclp = canonVtxp->nodep();
|
||||
UASSERT_OBJ(!canonVtxp->duplicatep(), canonDeclp,
|
||||
"Canonical node is a duplicate");
|
||||
assignDeclCode(canonDeclp);
|
||||
declp->code(canonDeclp->code());
|
||||
} 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 = selectActivity(fl, acode, false);
|
||||
if (condp)
|
||||
condp = new AstOr(fl, condp, selp);
|
||||
else
|
||||
condp = selp;
|
||||
}
|
||||
AstIf* ifp = new AstIf(fl, condp, NULL, NULL);
|
||||
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
m_chgFuncp->addStmtsp(ifp);
|
||||
lastactp = &actset;
|
||||
ifnodep = ifp;
|
||||
// This is a canonical trace node. Assign signal number and
|
||||
// add a TraceInc node to the full dump function.
|
||||
assignDeclCode(declp);
|
||||
|
||||
addToChgSub(ifnodep, addp);
|
||||
// Create top function if not yet created
|
||||
if (!topFuncp) {
|
||||
topFuncp = newCFunc(AstCFuncType::TRACE_FULL, NULL, regFuncp, topFuncNum);
|
||||
}
|
||||
|
||||
// Crate new sub function if required
|
||||
if (!subFuncp || subStmts > splitLimit) {
|
||||
subStmts = 0;
|
||||
subFuncp
|
||||
= newCFunc(AstCFuncType::TRACE_FULL_SUB, topFuncp, NULL, subFuncNum);
|
||||
}
|
||||
|
||||
// Add TraceInc node
|
||||
AstTraceInc* const incp = new AstTraceInc(declp->fileline(), declp, true);
|
||||
subFuncp->addStmtsp(incp);
|
||||
subStmts += EmitCBaseCounterVisitor(incp).count();
|
||||
|
||||
// Track partitioning
|
||||
nCodes += declp->codeInc();
|
||||
}
|
||||
}
|
||||
if (topFuncp) { // might be NULL if all trailing entries were duplicates
|
||||
UINFO(5, "traceFullTop" << topFuncNum - 1 << " codes: " << nCodes << "/"
|
||||
<< maxCodes << endl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set in initializer
|
||||
void createChgTraceFunctions(const TraceVec& traces, uint32_t nAllCodes, uint32_t parallelism,
|
||||
AstCFunc* regFuncp) {
|
||||
const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace()
|
||||
: std::numeric_limits<int>::max();
|
||||
int topFuncNum = 0;
|
||||
int subFuncNum = 0;
|
||||
TraceVec::const_iterator it = traces.begin();
|
||||
while (it != traces.end()) {
|
||||
AstCFunc* topFuncp = NULL;
|
||||
AstCFunc* subFuncp = NULL;
|
||||
int subStmts = 0;
|
||||
const uint32_t maxCodes = (nAllCodes + parallelism - 1) / parallelism;
|
||||
uint32_t nCodes = 0;
|
||||
const ActCodeSet* prevActSet = NULL;
|
||||
AstIf* ifp = NULL;
|
||||
for (; nCodes < maxCodes && it != traces.end(); ++it) {
|
||||
const TraceTraceVertex* const vtxp = it->second;
|
||||
// This is a duplicate decl, no need to add it to incremental dump
|
||||
if (vtxp->duplicatep()) continue;
|
||||
const ActCodeSet& actSet = it->first;
|
||||
// Traced value never changes, no need to add it to incremental dump
|
||||
if (actSet.count(TraceActivityVertex::ACTIVITY_NEVER)) continue;
|
||||
|
||||
// Clear activity after tracing completes
|
||||
FileLine*const fl = m_chgFuncp->fileline();
|
||||
// Create top function if not yet created
|
||||
if (!topFuncp) {
|
||||
topFuncp = newCFunc(AstCFuncType::TRACE_CHANGE, NULL, regFuncp, topFuncNum);
|
||||
}
|
||||
|
||||
// Crate new sub function if required
|
||||
if (!subFuncp || subStmts > splitLimit) {
|
||||
subStmts = 0;
|
||||
subFuncp
|
||||
= newCFunc(AstCFuncType::TRACE_CHANGE_SUB, topFuncp, NULL, subFuncNum);
|
||||
prevActSet = NULL;
|
||||
ifp = NULL;
|
||||
}
|
||||
|
||||
// If required, create the conditional node checking the activity flags
|
||||
if (!prevActSet || actSet != *prevActSet) {
|
||||
FileLine* const flp = m_topScopep->fileline();
|
||||
bool always = actSet.count(TraceActivityVertex::ACTIVITY_ALWAYS) != 0;
|
||||
AstNode* condp = NULL;
|
||||
if (always) {
|
||||
condp = new AstConst(flp, 1); // Always true, will be folded later
|
||||
} else {
|
||||
for (ActCodeSet::iterator it = actSet.begin(); it != actSet.end(); ++it) {
|
||||
const uint32_t actCode = *it;
|
||||
AstNode* const selp = selectActivity(flp, actCode, false);
|
||||
condp = condp ? new AstOr(flp, condp, selp) : selp;
|
||||
}
|
||||
}
|
||||
ifp = new AstIf(flp, condp, NULL, NULL);
|
||||
if (!always) { ifp->branchPred(VBranchPred::BP_UNLIKELY); }
|
||||
subFuncp->addStmtsp(ifp);
|
||||
subStmts += EmitCBaseCounterVisitor(ifp).count();
|
||||
prevActSet = &actSet;
|
||||
}
|
||||
|
||||
// Add TraceInc node
|
||||
AstTraceDecl* const declp = vtxp->nodep();
|
||||
AstTraceInc* const incp = new AstTraceInc(declp->fileline(), declp, false);
|
||||
ifp->addIfsp(incp);
|
||||
subStmts += EmitCBaseCounterVisitor(incp).count();
|
||||
|
||||
// Track partitioning
|
||||
nCodes += declp->codeInc();
|
||||
}
|
||||
if (topFuncp) { // might be NULL if all trailing entries were duplicates/constants
|
||||
UINFO(5, "traceChgTop" << topFuncNum - 1 << " codes: " << nCodes << "/" << maxCodes
|
||||
<< endl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void createCleanupFunction(AstCFunc* regFuncp) {
|
||||
FileLine* const fl = m_topScopep->fileline();
|
||||
AstCFunc* const cleanupFuncp = new AstCFunc(fl, "traceCleanup", m_topScopep);
|
||||
const string argTypes("void* userp, " + v3Global.opt.traceClassBase() + "* /*unused*/");
|
||||
cleanupFuncp->argTypes(argTypes);
|
||||
cleanupFuncp->funcType(AstCFuncType::TRACE_CLEANUP);
|
||||
cleanupFuncp->slow(false);
|
||||
cleanupFuncp->symProlog(true);
|
||||
cleanupFuncp->declPrivate(true);
|
||||
m_topScopep->addActivep(cleanupFuncp);
|
||||
|
||||
// Register it
|
||||
regFuncp->addStmtsp(new AstCStmt(
|
||||
fl, string("tracep->addCleanupCb(&" + protect("traceCleanup") + ", __VlSymsp);\n")));
|
||||
|
||||
// Clear global activity flag
|
||||
cleanupFuncp->addStmtsp(
|
||||
new AstCStmt(m_topScopep->fileline(), string("vlSymsp->__Vm_activity = false;\n")));
|
||||
|
||||
// Clear fine grained activity flags
|
||||
if (v3Global.opt.mtasks()) {
|
||||
for (uint32_t i = 0; i < m_activityNumber; ++i) {
|
||||
AstNode* clrp = new AstAssign(fl, selectActivity(fl, i, true),
|
||||
new AstConst(fl, AstConst::LogicFalse()));
|
||||
m_fullFuncp->addFinalsp(clrp->cloneTree(true));
|
||||
m_chgFuncp->addFinalsp(clrp);
|
||||
AstNode* const clrp = new AstAssign(fl, selectActivity(fl, i, true),
|
||||
new AstConst(fl, AstConst::LogicFalse()));
|
||||
cleanupFuncp->addStmtsp(clrp);
|
||||
}
|
||||
} else {
|
||||
AstNode* clrp = new AstAssign(
|
||||
AstNode* const clrp = new AstAssign(
|
||||
fl, new AstVarRef(fl, m_activityVscp, true),
|
||||
new AstConst(fl, AstConst::WidthedValue(), m_activityVscp->width(), 0));
|
||||
m_fullFuncp->addFinalsp(clrp->cloneTree(true));
|
||||
m_chgFuncp->addFinalsp(clrp);
|
||||
cleanupFuncp->addStmtsp(clrp);
|
||||
}
|
||||
}
|
||||
|
||||
void createTraceFunctions() {
|
||||
// Form a sorted list of the traces we are interested in
|
||||
UINFO(9, "Making trees\n");
|
||||
|
||||
TraceVec traces; // The sorted traces
|
||||
uint32_t nFullCodes = 0; // Number of non-duplicate codes (need to go into full* dump)
|
||||
uint32_t nChgCodes = 0; // Number of non-consant codes (need to go in to chg* dump)
|
||||
|
||||
// Sort the traces
|
||||
sortTraces(traces, nFullCodes, nChgCodes);
|
||||
|
||||
UINFO(5, "nFullCodes: " << nFullCodes << " nChgCodes: " << nChgCodes << endl);
|
||||
|
||||
// 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
|
||||
|
||||
// Create the trace registration function
|
||||
AstCFunc* const regFuncp
|
||||
= new AstCFunc(m_topScopep->fileline(), "traceRegister", m_topScopep);
|
||||
regFuncp->argTypes(v3Global.opt.traceClassBase() + "* tracep");
|
||||
regFuncp->funcType(AstCFuncType::TRACE_REGISTER);
|
||||
regFuncp->slow(true);
|
||||
regFuncp->isStatic(false);
|
||||
regFuncp->declPrivate(true);
|
||||
m_topScopep->addActivep(regFuncp);
|
||||
|
||||
const int parallelism = 1; // Note: will bump this later, code below works for any value
|
||||
|
||||
// Create the full dump functions, also allocates signal numbers
|
||||
createFullTraceFunction(traces, nFullCodes, parallelism, regFuncp);
|
||||
|
||||
// Create the incremental dump functions
|
||||
createChgTraceFunctions(traces, nChgCodes, parallelism, regFuncp);
|
||||
|
||||
// Remove refs to traced values from TraceDecl nodes, these have now moved under TraceInc
|
||||
for (TraceVec::iterator it = traces.begin(); it != traces.end(); ++it) {
|
||||
AstNode* const valuep = it->second->nodep()->valuep();
|
||||
valuep->unlinkFrBack();
|
||||
valuep->deleteTree();
|
||||
}
|
||||
|
||||
// Create the trace cleanup function clearing the activity flags
|
||||
createCleanupFunction(regFuncp);
|
||||
}
|
||||
|
||||
TraceCFuncVertex* getCFuncVertexp(AstCFunc* nodep) {
|
||||
TraceCFuncVertex* vertexp
|
||||
= dynamic_cast<TraceCFuncVertex*>(nodep->user1u().toGraphVertex());
|
||||
|
|
@ -628,7 +719,7 @@ private:
|
|||
m_code = 1; // Multiple TopScopes will require fixing how code#s
|
||||
// are assigned as duplicate varscopes must result in the same tracing code#.
|
||||
|
||||
// Add vertexes for all TRACES, and edges from VARs each trace looks at
|
||||
// Add vertexes for all TraceDecl, and edges from VARs each trace looks at
|
||||
m_finding = false;
|
||||
iterateChildren(nodep);
|
||||
|
||||
|
|
@ -648,8 +739,8 @@ private:
|
|||
// Create the activity flags
|
||||
assignActivity();
|
||||
|
||||
//
|
||||
putTracesIntoTree();
|
||||
// Create the trace functions and insert them into the tree
|
||||
createTraceFunctions();
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) VL_OVERRIDE {
|
||||
if (nodep->isTop()) m_topModp = nodep;
|
||||
|
|
@ -682,11 +773,6 @@ private:
|
|||
}
|
||||
virtual void visit(AstCFunc* nodep) VL_OVERRIDE {
|
||||
UINFO(8, " CFUNC " << nodep << endl);
|
||||
if (nodep->funcType() == AstCFuncType::TRACE_FULL) {
|
||||
m_fullFuncp = nodep;
|
||||
} else if (nodep->funcType() == AstCFuncType::TRACE_CHANGE) {
|
||||
m_chgFuncp = nodep;
|
||||
}
|
||||
V3GraphVertex* const funcVtxp = getCFuncVertexp(nodep);
|
||||
if (!m_finding) { // If public, we need a unique activity code to allow for sets directly
|
||||
// in this func
|
||||
|
|
@ -705,18 +791,17 @@ private:
|
|||
iterateChildren(nodep);
|
||||
m_funcp = NULL;
|
||||
}
|
||||
virtual void visit(AstTraceInc* nodep) VL_OVERRIDE {
|
||||
virtual void visit(AstTraceDecl* nodep) VL_OVERRIDE {
|
||||
UINFO(8, " TRACE " << nodep << endl);
|
||||
UASSERT_OBJ(!m_finding, nodep, "Traces should have been removed in prev step.");
|
||||
nodep->unlinkFrBack();
|
||||
if (!m_finding) {
|
||||
V3GraphVertex* const vertexp = new TraceTraceVertex(&m_graph, nodep);
|
||||
nodep->user1p(vertexp);
|
||||
|
||||
V3GraphVertex* const vertexp = new TraceTraceVertex(&m_graph, nodep);
|
||||
nodep->user1p(vertexp);
|
||||
|
||||
UASSERT_OBJ(m_funcp && m_chgFuncp && m_fullFuncp, nodep, "Trace not under func");
|
||||
m_tracep = nodep;
|
||||
iterateChildren(nodep);
|
||||
m_tracep = NULL;
|
||||
UASSERT_OBJ(m_funcp, nodep, "Trace not under func");
|
||||
m_tracep = nodep;
|
||||
iterateChildren(nodep);
|
||||
m_tracep = NULL;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) VL_OVERRIDE {
|
||||
if (m_tracep) {
|
||||
|
|
@ -755,16 +840,8 @@ public:
|
|||
m_topScopep = NULL;
|
||||
m_finding = false;
|
||||
m_activityVscp = NULL;
|
||||
m_fullFuncp = NULL;
|
||||
m_fullSubFuncp = NULL;
|
||||
m_fullSubStmts = 0;
|
||||
m_chgFuncp = NULL;
|
||||
m_chgSubFuncp = NULL;
|
||||
m_chgSubParentp = NULL;
|
||||
m_chgSubStmts = 0;
|
||||
m_activityNumber = 0;
|
||||
m_code = 0;
|
||||
m_funcNum = 0;
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~TraceVisitor() {
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@
|
|||
//
|
||||
//*************************************************************************
|
||||
// V3TraceDecl's Transformations:
|
||||
// Create trace CFUNCs
|
||||
// For each VARSCOPE
|
||||
// If appropriate type of signal, create a TRACE
|
||||
// Create trace init CFunc
|
||||
// For each VarScope
|
||||
// If appropriate type of signal, create a TraceDecl
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
|
|
@ -36,11 +36,10 @@ private:
|
|||
// NODE STATE
|
||||
|
||||
// STATE
|
||||
AstScope* m_scopetopp; // Current top scope
|
||||
AstScope* m_topScopep; // Current top scope
|
||||
AstCFunc* m_initFuncp; // Trace function being built
|
||||
AstCFunc* m_initSubFuncp; // Trace function being built (under m_init)
|
||||
int m_initSubStmts; // Number of statements in function
|
||||
AstCFunc* m_chgFuncp; // Trace function being built
|
||||
int m_funcNum; // Function number being built
|
||||
AstVarScope* m_traVscp; // Signal being trace constructed
|
||||
AstNode* m_traValuep; // Signal being traced's value to trace in it
|
||||
|
|
@ -69,39 +68,29 @@ private:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
AstCFunc* newCFunc(AstCFuncType type, const string& name, bool slow) {
|
||||
FileLine* const flp = m_scopetopp->fileline();
|
||||
AstCFunc* const funcp = new AstCFunc(flp, name, m_scopetopp);
|
||||
funcp->slow(slow);
|
||||
string argTypes;
|
||||
argTypes += EmitCBaseVisitor::symClassVar();
|
||||
argTypes += ", " + v3Global.opt.traceClassBase() + "* vcdp";
|
||||
argTypes += ", uint32_t code";
|
||||
AstCFunc* newCFunc(AstCFuncType type, const string& name) {
|
||||
FileLine* const flp = m_topScopep->fileline();
|
||||
AstCFunc* const funcp = new AstCFunc(flp, name, m_topScopep);
|
||||
string argTypes("void* userp, " + v3Global.opt.traceClassBase() + "* tracep");
|
||||
if (m_interface) argTypes += ", const char* scopep";
|
||||
funcp->argTypes(argTypes);
|
||||
funcp->funcType(type);
|
||||
funcp->symProlog(true);
|
||||
if (type == AstCFuncType::TRACE_INIT) {
|
||||
// Setup signal names
|
||||
const string str = "vcdp->module(vlSymsp->name());\n";
|
||||
funcp->addStmtsp(new AstCStmt(flp, str));
|
||||
}
|
||||
m_scopetopp->addActivep(funcp);
|
||||
funcp->slow(true);
|
||||
funcp->declPrivate(true);
|
||||
m_topScopep->addActivep(funcp);
|
||||
UINFO(5, " Newfunc " << funcp << endl);
|
||||
return funcp;
|
||||
}
|
||||
void callCFuncSub(AstCFunc* basep, AstCFunc* funcp, AstIntfRef* irp) {
|
||||
AstCCall* callp = new AstCCall(funcp->fileline(), funcp);
|
||||
callp->argTypes("vlSymsp, vcdp, code");
|
||||
callp->argTypes("userp, tracep");
|
||||
if (irp) callp->addArgsp(irp->unlinkFrBack());
|
||||
basep->addStmtsp(callp);
|
||||
}
|
||||
AstCFunc* newCFuncSub(AstCFunc* basep) {
|
||||
UASSERT_OBJ(basep->funcType() == AstCFuncType::TRACE_INIT
|
||||
|| basep->funcType() == AstCFuncType::TRACE_INIT_SUB,
|
||||
basep, "Strange base function type");
|
||||
const string name = basep->name() + "__" + cvtToStr(++m_funcNum);
|
||||
AstCFunc* const funcp = newCFunc(AstCFuncType::TRACE_INIT_SUB, name, basep->slow());
|
||||
const string name = "traceInitSub" + cvtToStr(m_funcNum++);
|
||||
AstCFunc* const funcp = newCFunc(AstCFuncType::TRACE_INIT_SUB, name);
|
||||
if (!m_interface) callCFuncSub(basep, funcp, NULL);
|
||||
return funcp;
|
||||
}
|
||||
|
|
@ -116,7 +105,7 @@ private:
|
|||
}
|
||||
AstTraceDecl* const declp
|
||||
= new AstTraceDecl(m_traVscp->fileline(), m_traShowname, m_traVscp->varp(),
|
||||
m_traValuep, bitRange, arrayRange, m_interface);
|
||||
m_traValuep->cloneTree(true), bitRange, arrayRange, m_interface);
|
||||
UINFO(9, "Decl " << declp << endl);
|
||||
|
||||
if (!m_interface && v3Global.opt.outputSplitCTrace()
|
||||
|
|
@ -127,10 +116,6 @@ private:
|
|||
|
||||
m_initSubFuncp->addStmtsp(declp);
|
||||
m_initSubStmts += EmitCBaseCounterVisitor(declp).count();
|
||||
|
||||
m_chgFuncp->addStmtsp(
|
||||
new AstTraceInc(m_traVscp->fileline(), declp, m_traValuep->cloneTree(true)));
|
||||
// The full version will get constructed in V3Trace
|
||||
}
|
||||
void addIgnore(const char* why) {
|
||||
++m_statIgnSigs;
|
||||
|
|
@ -140,12 +125,10 @@ private:
|
|||
|
||||
// VISITORS
|
||||
virtual void visit(AstTopScope* nodep) VL_OVERRIDE {
|
||||
m_scopetopp = nodep->scopep();
|
||||
// Make containers for TRACEDECLs first
|
||||
m_initFuncp = newCFunc(AstCFuncType::TRACE_INIT, "traceInitThis", true);
|
||||
/*fullFuncp*/ newCFunc(AstCFuncType::TRACE_FULL, "traceFullThis", true);
|
||||
m_chgFuncp = newCFunc(AstCFuncType::TRACE_CHANGE, "traceChgThis", false);
|
||||
//
|
||||
m_topScopep = nodep->scopep();
|
||||
// Create the trace init function
|
||||
m_initFuncp = newCFunc(AstCFuncType::TRACE_INIT, "traceInitTop");
|
||||
// Create initial sub function
|
||||
m_initSubFuncp = newCFuncSub(m_initFuncp);
|
||||
// And find variables
|
||||
iterateChildren(nodep);
|
||||
|
|
@ -254,10 +237,10 @@ private:
|
|||
}
|
||||
} else {
|
||||
// Unroll now, as have no other method to get right signal names
|
||||
AstNodeDType*const subtypep = nodep->subDTypep()->skipRefToEnump();
|
||||
AstNodeDType* const subtypep = nodep->subDTypep()->skipRefToEnump();
|
||||
for (int i = nodep->lsb(); i <= nodep->msb(); ++i) {
|
||||
const string oldShowname = m_traShowname;
|
||||
AstNode*const oldValuep = m_traValuep;
|
||||
AstNode* const oldValuep = m_traValuep;
|
||||
{
|
||||
m_traShowname += string("(") + cvtToStr(i) + string(")");
|
||||
m_traValuep = new AstArraySel(
|
||||
|
|
@ -281,7 +264,7 @@ private:
|
|||
// a much faster way to trace
|
||||
addTraceDecl(VNumRange(), nodep->width());
|
||||
} else {
|
||||
AstNodeDType*const subtypep = nodep->subDTypep()->skipRefToEnump();
|
||||
AstNodeDType* const subtypep = nodep->subDTypep()->skipRefToEnump();
|
||||
for (int i = nodep->lsb(); i <= nodep->msb(); ++i) {
|
||||
const string oldShowname = m_traShowname;
|
||||
AstNode* const oldValuep = m_traValuep;
|
||||
|
|
@ -356,11 +339,10 @@ private:
|
|||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit TraceDeclVisitor(AstNetlist* nodep) {
|
||||
m_scopetopp = NULL;
|
||||
m_topScopep = NULL;
|
||||
m_initFuncp = NULL;
|
||||
m_initSubFuncp = NULL;
|
||||
m_initSubStmts = 0;
|
||||
m_chgFuncp = NULL;
|
||||
m_funcNum = 0;
|
||||
m_traVscp = NULL;
|
||||
m_traValuep = NULL;
|
||||
|
|
|
|||
Loading…
Reference in New Issue