diff --git a/include/verilated_trace.h b/include/verilated_trace.h index 471db6b99..0c2ca03f4 100644 --- a/include/verilated_trace.h +++ b/include/verilated_trace.h @@ -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 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 m_callbacks; ///< Routines to perform dumping + std::vector m_initCbs; ///< Routines to initialize traciong + std::vector m_fullCbs; ///< Routines to perform full dump + std::vector m_chgCbs; ///< Routines to perform incremental dump + std::vector 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& 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(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; diff --git a/include/verilated_trace_imp.cpp b/include/verilated_trace_imp.cpp index 78dc19a11..c9e8c426f 100644 --- a/include/verilated_trace_imp.cpp +++ b/include/verilated_trace_imp.cpp @@ -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::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::VerilatedTrace() template <> VerilatedTrace::~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::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::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::dump(vluint64_t timeui) { // Non-hot path internal interface to Verilator generated code template <> -void VerilatedTrace::addCallback(callback_t initcb, callback_t fullcb, - callback_t changecb, - void* userthis) VL_MT_UNSAFE_ONE { +void VerilatedTrace::addCallbackRecord(std::vector& 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::addInitCb(initCb_t cb, void* userp) { + CallbackRecord cbr(cb, userp); + addCallbackRecord(m_initCbs, cbr); +} +template <> void VerilatedTrace::addFullCb(dumpCb_t cb, void* userp) { + CallbackRecord cbr(cb, userp); + addCallbackRecord(m_fullCbs, cbr); +} +template <> void VerilatedTrace::addChgCb(dumpCb_t cb, void* userp) { + CallbackRecord cbr(cb, userp); + addCallbackRecord(m_chgCbs, cbr); +} +template <> void VerilatedTrace::addCleanupCb(dumpCb_t cb, void* userp) { + CallbackRecord cbr(cb, userp); + addCallbackRecord(m_cleanupCbs, cbr); } //========================================================================= diff --git a/src/V3Ast.h b/src/V3Ast.h index c99d7c235..19103e471 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -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(_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; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index c42c9175a..d3e31e846 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -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 { diff --git a/src/V3Cdc.cpp b/src/V3Cdc.cpp index 358160c49..15bcff4c7 100644 --- a/src/V3Cdc.cpp +++ b/src/V3Cdc.cpp @@ -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 {} diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index f7f9be2e9..da56681cf 100644 --- a/src/V3EmitC.cpp +++ b/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()); diff --git a/src/V3EmitCBase.h b/src/V3EmitCBase.h index 8e98efafe..94a5e2d8e 100644 --- a/src/V3EmitCBase.h +++ b/src/V3EmitCBase.h @@ -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()) { diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index e633a2c1f..b7c66ba1d 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -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 = ','; diff --git a/src/V3Gate.cpp b/src/V3Gate.cpp index 2c843c0c9..3b62f0e73 100644 --- a/src/V3Gate.cpp +++ b/src/V3Gate.cpp @@ -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; diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index 39c653ade..fdc701ebd 100644 --- a/src/V3Trace.cpp +++ b/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 ActCodeSet; + // For activity set, what traces apply + typedef std::multimap TraceVec; + // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -216,7 +206,7 @@ private: itp = itp->verticesNextp()) { if (const TraceTraceVertex* const vvertexp = dynamic_cast(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(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(dupincp->user1u().toGraphVertex()); + = dynamic_cast(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 ActCodeSet; - // For activity set, what traces apply - typedef std::multimap 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(itp)) { + if (const TraceTraceVertex* const vtxp = dynamic_cast(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(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::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::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(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() { diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index 3e52443cf..e404efda3 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -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;