diff --git a/include/verilated_fst_c.cpp b/include/verilated_fst_c.cpp index ed93bdf64b7..670b78b6b6f 100644 --- a/include/verilated_fst_c.cpp +++ b/include/verilated_fst_c.cpp @@ -104,6 +104,7 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) { fstWriterSetPackType(m_fst, FST_WR_PT_LZ4); fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref if (m_useFstWriterThread) fstWriterSetParallelMode(m_fst, 1); + constDump(true); // First dump must contain the const signals fullDump(true); // First dump must be full for fst m_curScope.clear(); diff --git a/include/verilated_trace.h b/include/verilated_trace.h index 7e8694b7057..6b053e7760b 100644 --- a/include/verilated_trace.h +++ b/include/verilated_trace.h @@ -212,11 +212,14 @@ class VerilatedTrace VL_NOT_FINAL { private: std::vector m_sigs_enabledVec; // Staging for m_sigs_enabledp std::vector m_initCbs; // Routines to initialize tracing + std::vector m_constCbs; // Routines to perform const dump + std::vector m_constOffloadCbs; // Routines to perform offloaded const dump std::vector m_fullCbs; // Routines to perform full dump std::vector m_fullOffloadCbs; // Routines to perform offloaded full dump std::vector m_chgCbs; // Routines to perform incremental dump std::vector m_chgOffloadCbs; // Routines to perform offloaded incremental dump std::vector m_cleanupCbs; // Routines to call at the end of dump + bool m_constDump = true; // Whether a const dump is required on the next call to 'dump' bool m_fullDump = true; // Whether a full dump is required on the next call to 'dump' uint32_t m_nextCode = 0; // Next code number to assign uint32_t m_numSignals = 0; // Number of distinct signals @@ -289,6 +292,7 @@ class VerilatedTrace VL_NOT_FINAL { uint32_t nextCode() const { return m_nextCode; } uint32_t numSignals() const { return m_numSignals; } uint32_t maxBits() const { return m_maxBits; } + void constDump(bool value) { m_constDump = value; } void fullDump(bool value) { m_fullDump = value; } double timeRes() const { return m_timeRes; } @@ -359,6 +363,8 @@ class VerilatedTrace VL_NOT_FINAL { void addModel(VerilatedModel*) VL_MT_SAFE_EXCLUDES(m_mutex); void addInitCb(initCb_t cb, void* userp) VL_MT_SAFE; + void addConstCb(dumpCb_t cb, void* userp) VL_MT_SAFE; + void addConstCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE; void addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE; void addFullCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE; void addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE; diff --git a/include/verilated_trace_imp.h b/include/verilated_trace_imp.h index 9f579212715..2d473e90502 100644 --- a/include/verilated_trace_imp.h +++ b/include/verilated_trace_imp.h @@ -610,6 +610,15 @@ void VerilatedTrace::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD } } + if (VL_UNLIKELY(m_constDump)) { + m_constDump = false; + if (offload()) { + runOffloadedCallbacks(m_constOffloadCbs); + } else { + runCallbacks(m_constCbs); + } + } + for (uint32_t i = 0; i < m_cleanupCbs.size(); ++i) { const CallbackRecord& cbr = m_cleanupCbs[i]; cbr.m_cleanupCb(cbr.m_userp, self()); @@ -695,6 +704,14 @@ void VerilatedTrace::addInitCb(initCb_t cb, void* userp) VL_ addCallbackRecord(m_initCbs, CallbackRecord{cb, userp}); } template <> +void VerilatedTrace::addConstCb(dumpCb_t cb, void* userp) VL_MT_SAFE { + addCallbackRecord(m_constCbs, CallbackRecord{cb, userp}); +} +template <> +void VerilatedTrace::addConstCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE { + addCallbackRecord(m_constOffloadCbs, CallbackRecord{cb, userp}); +} +template <> void VerilatedTrace::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE { addCallbackRecord(m_fullCbs, CallbackRecord{cb, userp}); } diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index b05967c1b53..fc0b438cb4e 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -164,6 +164,7 @@ void VerilatedVcd::openNextImp(bool incFilename) { } } m_isOpen = true; + constDump(true); // First dump must containt the const signals fullDump(true); // First dump must be full m_wroteBytes = 0; } diff --git a/src/V3Ast.h b/src/V3Ast.h index a17cc84e01c..93b8f0610f3 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1247,6 +1247,38 @@ inline std::ostream& operator<<(std::ostream& os, const VUseType& rhs) { return os << rhs.ascii(); } +//###################################################################### + +class VTraceType final { +public: + enum en : uint8_t { + CONST, // Const value dump (once at the beginning) + FULL, // Full value dump (always emitted) + CHANGE // Incremental value dump (emitted only if the value changed) + }; + enum en m_e; + VTraceType() + : m_e{CONST} {} + // cppcheck-suppress noExplicitConstructor + constexpr VTraceType(en _e) + : m_e{_e} {} + explicit VTraceType(int _e) + : m_e(static_cast(_e)) {} // Need () or GCC 4.8 false warning + constexpr operator en() const { return m_e; } + const char* ascii() const { + static const char* const names[] = {"CONST", "FULL", "CHANGE"}; + return names[m_e]; + } +}; +constexpr bool operator==(const VTraceType& lhs, const VTraceType& rhs) { + return lhs.m_e == rhs.m_e; +} +constexpr bool operator==(const VTraceType& lhs, VTraceType::en rhs) { return lhs.m_e == rhs; } +constexpr bool operator==(VTraceType::en lhs, const VTraceType& rhs) { return lhs == rhs.m_e; } +inline std::ostream& operator<<(std::ostream& os, const VTraceType& rhs) { + return os << rhs.ascii(); +} + // ###################################################################### class VCastable final { diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 61c66ba79c8..25508a23074 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -3243,14 +3243,14 @@ class AstTraceInc final : public AstNodeStmt { private: AstTraceDecl* m_declp; // Pointer to declaration const uint32_t m_baseCode; // Trace code base value in function containing this AstTraceInc - const bool m_full; // Is this a full vs incremental dump + const VTraceType m_traceType; // Is this a const/full/incremental dump public: - AstTraceInc(FileLine* fl, AstTraceDecl* declp, bool full, uint32_t baseCode = 0) + AstTraceInc(FileLine* fl, AstTraceDecl* declp, VTraceType traceType, uint32_t baseCode = 0) : ASTGEN_SUPER_TraceInc(fl) , m_declp{declp} , m_baseCode{baseCode} - , m_full{full} { + , m_traceType{traceType} { dtypeFrom(declp); this->valuep( declp->valuep()->cloneTree(true)); // TODO: maybe use reference to TraceDecl instead? @@ -3274,7 +3274,7 @@ class AstTraceInc final : public AstNodeStmt { bool isOutputter() override { return true; } // but isPure() true AstTraceDecl* declp() const { return m_declp; } - bool full() const { return m_full; } + VTraceType traceType() const { return m_traceType; } uint32_t baseCode() const { return m_baseCode; } }; class AstTracePopNamePrefix final : public AstNodeStmt { diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index 7ec3acee9f4..0012af4fa17 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -769,7 +769,7 @@ class EmitCTrace final : EmitCFunc { void emitTraceChangeOne(AstTraceInc* nodep, int arrayindex) { iterateAndNextConstNull(nodep->precondsp()); - const string func = nodep->full() ? "full" : "chg"; + const std::string func = nodep->traceType() == VTraceType::CHANGE ? "chg" : "full"; bool emitWidth = true; if (nodep->dtypep()->basicp()->isDouble()) { puts("bufp->" + func + "Double"); @@ -794,7 +794,9 @@ class EmitCTrace final : EmitCFunc { const uint32_t offset = (arrayindex < 0) ? 0 : (arrayindex * nodep->declp()->widthWords()); const uint32_t code = nodep->declp()->code() + offset; - puts(v3Global.opt.useTraceOffload() && !nodep->full() ? "(base+" : "(oldp+"); + puts(v3Global.opt.useTraceOffload() && nodep->traceType() == VTraceType::CHANGE + ? "(base+" + : "(oldp+"); puts(cvtToStr(code - nodep->baseCode())); puts(","); emitTraceValue(nodep, arrayindex); diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index 653ba654877..c8ec9762a32 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -30,7 +30,7 @@ // 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 +// numbers (codes), and construct the const, full and incremental trace // functions, together with all other trace support functions. // //************************************************************************* @@ -319,11 +319,10 @@ class TraceVisitor final : public VNVisitor { return activityNumber; } - void sortTraces(TraceVec& traces, uint32_t& nFullCodes, uint32_t& nChgCodes) { + void sortTraces(TraceVec& traces, uint32_t& nNonConstCodes) { // Populate sort structure traces.clear(); - nFullCodes = 0; - nChgCodes = 0; + nNonConstCodes = 0; for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { if (TraceTraceVertex* const vtxp = itp->cast()) { ActCodeSet actSet; @@ -347,11 +346,8 @@ class TraceVisitor final : public VNVisitor { || actSet.size() == 1, vtxp->nodep(), "Always active trace has further triggers"); // Count nodes - if (!vtxp->duplicatep()) { - const uint32_t inc = vtxp->nodep()->codeInc(); - nFullCodes += inc; - if (!actSet.empty()) nChgCodes += inc; - } + if (!vtxp->duplicatep() && !actSet.empty()) + nNonConstCodes += vtxp->nodep()->codeInc(); if (actSet.empty()) { // If a trace doesn't have activity, it's constant, and we // don't need to track changes on it. @@ -374,8 +370,7 @@ class TraceVisitor final : public VNVisitor { // Sort the traces by activity sets TraceVec traces; uint32_t unused1; - uint32_t unused2; - sortTraces(traces, unused1, unused2); + sortTraces(traces, unused1); // For each activity set with only a small number of signals, make those // signals always traced, as it's cheaper to check a few value changes @@ -482,54 +477,62 @@ class TraceVisitor final : public VNVisitor { } } - AstCFunc* newCFunc(bool full, AstCFunc* topFuncp, int& funcNump, uint32_t baseCode = 0) { + AstCFunc* newCFunc(VTraceType traceType, AstCFunc* topFuncp, unsigned funcNum, + uint32_t baseCode = 0) { // Create new function const bool isTopFunc = topFuncp == nullptr; - const string baseName = full && isTopFunc ? "trace_full_top_" - : full ? "trace_full_sub_" - : isTopFunc ? "trace_chg_top_" - : "trace_chg_sub_"; + std::string baseName = "trace_"; + if (traceType == VTraceType::CONST) { + baseName += "const_"; + } else if (traceType == VTraceType::FULL) { + baseName += "full_"; + } else { + baseName += "chg_"; + } + baseName += isTopFunc ? "top_" : "sub_"; FileLine* const flp = m_topScopep->fileline(); - AstCFunc* const funcp = new AstCFunc{flp, baseName + cvtToStr(funcNump++), m_topScopep}; + AstCFunc* const funcp = new AstCFunc{flp, baseName + cvtToStr(funcNum), m_topScopep}; funcp->isTrace(true); funcp->dontCombine(true); funcp->isLoose(true); - funcp->slow(full); + funcp->slow(traceType != VTraceType::CHANGE); funcp->isStatic(isTopFunc); // Add it to top scope m_topScopep->addBlocksp(funcp); const auto addInitStr = [funcp, flp](const string& str) -> void { funcp->addInitsp(new AstCStmt{flp, str}); }; + const std::string bufArg + = v3Global.opt.traceClassBase() + + "::" + (v3Global.opt.useTraceOffload() ? "OffloadBuffer" : "Buffer") + "* bufp"; if (isTopFunc) { // Top functions - funcp->argTypes("void* voidSelf, " + v3Global.opt.traceClassBase() - + "::" + (v3Global.opt.useTraceOffload() ? "OffloadBuffer" : "Buffer") - + "* bufp"); + funcp->argTypes("void* voidSelf, " + bufArg); addInitStr(EmitCBase::voidSelfAssign(m_topModp)); addInitStr(EmitCBase::symClassAssign()); // Add global activity check to change dump functions - if (!full) { // + if (traceType == VTraceType::CHANGE) { // addInitStr("if (VL_UNLIKELY(!vlSymsp->__Vm_activity)) return;\n"); } // Register function - if (full) { - m_regFuncp->addStmtsp(new AstText{flp, "tracep->addFullCb(", true}); + std::string str; + if (traceType == VTraceType::CONST) { + str = "tracep->addConstCb("; + } else if (traceType == VTraceType::FULL) { + str = "tracep->addFullCb("; } else { - m_regFuncp->addStmtsp(new AstText{flp, "tracep->addChgCb(", true}); + str = "tracep->addChgCb("; } + m_regFuncp->addStmtsp(new AstText{flp, str, true}); m_regFuncp->addStmtsp(new AstAddrOfCFunc{flp, funcp}); - m_regFuncp->addStmtsp(new AstText{flp, ", vlSelf", true}); - m_regFuncp->addStmtsp(new AstText{flp, ");\n", true}); + m_regFuncp->addStmtsp(new AstText{flp, ", vlSelf);\n", true}); } else { // Sub functions - funcp->argTypes(v3Global.opt.traceClassBase() - + "::" + +(v3Global.opt.useTraceOffload() ? "OffloadBuffer" : "Buffer") - + "* bufp"); + funcp->argTypes(bufArg); // Setup base references. Note in rare occasions we can end up with an empty trace // sub function, hence the VL_ATTR_UNUSED attributes. - if (full) { + if (traceType != VTraceType::CHANGE) { // Full dump sub function addInitStr("uint32_t* const oldp VL_ATTR_UNUSED = " "bufp->oldp(vlSymsp->__Vm_baseCode);\n"); @@ -557,104 +560,96 @@ class TraceVisitor final : public VNVisitor { return funcp; } - void createFullTraceFunction(const TraceVec& traces, uint32_t nAllCodes, - uint32_t parallelism) { + void createConstTraceFunctions(const TraceVec& traces) { const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace() : std::numeric_limits::max(); - int topFuncNum = 0; - int subFuncNum = 0; - auto it = traces.cbegin(); - while (it != traces.cend()) { - AstCFunc* topFuncp = nullptr; - AstCFunc* subFuncp = nullptr; - 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. - const AstTraceDecl* const canonDeclp = canonVtxp->nodep(); - UASSERT_OBJ(!canonVtxp->duplicatep(), canonDeclp, - "Canonical node is a duplicate"); - UASSERT_OBJ(canonDeclp->code() != 0, canonDeclp, - "Canonical node should have code assigned already"); - declp->code(canonDeclp->code()); - } else { - // This is a canonical trace node. Assign signal number and - // add a TraceInc node to the full dump function. - UASSERT_OBJ(declp->code() == 0, declp, - "Canonical node should not have code assigned yet"); - declp->code(m_code); - m_code += declp->codeInc(); - m_statUniqCodes += declp->codeInc(); - ++m_statUniqSigs; - - // Create top function if not yet created - if (!topFuncp) { topFuncp = newCFunc(/* full: */ true, nullptr, topFuncNum); } - - // Crate new sub function if required - if (!subFuncp || subStmts > splitLimit) { - subStmts = 0; - subFuncp = newCFunc(/* full: */ true, topFuncp, subFuncNum); - } + AstCFunc* const topFuncp = newCFunc(VTraceType::CONST, nullptr, 0); + unsigned subFuncNum = 0; + AstCFunc* subFuncp = nullptr; + int subStmts = 0; + for (auto it = traces.cbegin(); 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. + const AstTraceDecl* const canonDeclp = canonVtxp->nodep(); + UASSERT_OBJ(!canonVtxp->duplicatep(), canonDeclp, "Canonical node is a duplicate"); + UASSERT_OBJ(canonDeclp->code() != 0, canonDeclp, + "Canonical node should have code assigned already"); + declp->code(canonDeclp->code()); + continue; + } - // Add TraceInc node - AstTraceInc* const incp - = new AstTraceInc{declp->fileline(), declp, /* full: */ true}; - subFuncp->addStmtsp(incp); - subStmts += incp->nodeCount(); + // This is a canonical trace node. Assign trace code (signal number). + UASSERT_OBJ(declp->code() == 0, declp, + "Canonical node should not have code assigned yet"); + declp->code(m_code); + m_code += declp->codeInc(); + m_statUniqCodes += declp->codeInc(); + ++m_statUniqSigs; - // Track partitioning - nCodes += declp->codeInc(); + // If this is a const signal, add the AstTraceInc + const ActCodeSet& actSet = it->first; + if (actSet.count(TraceActivityVertex::ACTIVITY_NEVER)) { + // Crate new sub function if required + if (!subFuncp || subStmts > splitLimit) { + subStmts = 0; + subFuncp = newCFunc(VTraceType::CONST, topFuncp, subFuncNum); + ++subFuncNum; } - } - if (topFuncp) { // might be nullptr if all trailing entries were duplicates - UINFO(5, "trace_full_top" << topFuncNum - 1 << " codes: " << nCodes << "/" - << maxCodes << endl); + FileLine* const flp = declp->fileline(); + AstTraceInc* const incp = new AstTraceInc{flp, declp, VTraceType::CONST}; + subFuncp->addStmtsp(incp); + subStmts += incp->nodeCount(); } } } - void createChgTraceFunctions(const TraceVec& traces, uint32_t nAllCodes, - uint32_t parallelism) { + void createNonConstTraceFunctions(const TraceVec& traces, uint32_t nAllCodes, + uint32_t parallelism) { const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace() : std::numeric_limits::max(); - int topFuncNum = 0; - int subFuncNum = 0; + unsigned topFuncNum = 0; + unsigned subFuncNum = 0; TraceVec::const_iterator it = traces.begin(); while (it != traces.end()) { - AstCFunc* topFuncp = nullptr; - AstCFunc* subFuncp = nullptr; + AstCFunc* topFulFuncp = nullptr; + AstCFunc* topChgFuncp = nullptr; + AstCFunc* subFulFuncp = nullptr; + AstCFunc* subChgFuncp = nullptr; int subStmts = 0; - uint32_t maxCodes = (nAllCodes + parallelism - 1) / parallelism; - if (maxCodes < 1) maxCodes = 1; + const uint32_t maxCodes = std::max((nAllCodes + parallelism - 1) / parallelism, 1U); uint32_t nCodes = 0; const ActCodeSet* prevActSet = nullptr; AstIf* ifp = nullptr; uint32_t baseCode = 0; 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 + // This is a duplicate decl, no need to add it if (vtxp->duplicatep()) continue; const ActCodeSet& actSet = it->first; - // Traced value never changes, no need to add it to incremental dump + // Traced value never changes, no need to add it if (actSet.count(TraceActivityVertex::ACTIVITY_NEVER)) continue; AstTraceDecl* const declp = vtxp->nodep(); // Create top function if not yet created - if (!topFuncp) { topFuncp = newCFunc(/* full: */ false, nullptr, topFuncNum); } + if (!topFulFuncp) { + topFulFuncp = newCFunc(VTraceType::FULL, nullptr, topFuncNum); + topChgFuncp = newCFunc(VTraceType::CHANGE, nullptr, topFuncNum); + ++topFuncNum; + } // Create new sub function if required - if (!subFuncp || subStmts > splitLimit) { + if (!subFulFuncp || subStmts > splitLimit) { baseCode = declp->code(); subStmts = 0; - subFuncp = newCFunc(/* full: */ false, topFuncp, subFuncNum, baseCode); + subFulFuncp = newCFunc(VTraceType::FULL, topFulFuncp, subFuncNum, baseCode); + subChgFuncp = newCFunc(VTraceType::CHANGE, topChgFuncp, subFuncNum, baseCode); + ++subFuncNum; prevActSet = nullptr; ifp = nullptr; } @@ -674,24 +669,27 @@ class TraceVisitor final : public VNVisitor { } ifp = new AstIf{flp, condp}; if (!always) ifp->branchPred(VBranchPred::BP_UNLIKELY); - subFuncp->addStmtsp(ifp); + subChgFuncp->addStmtsp(ifp); subStmts += ifp->nodeCount(); prevActSet = &actSet; } - // Add TraceInc node - AstTraceInc* const incp - = new AstTraceInc{declp->fileline(), declp, /* full: */ false, baseCode}; - ifp->addThensp(incp); - subStmts += incp->nodeCount(); + // Add TraceInc nodes + FileLine* const flp = declp->fileline(); + AstTraceInc* const incFulp = new AstTraceInc{flp, declp, VTraceType::FULL}; + subFulFuncp->addStmtsp(incFulp); + AstTraceInc* const incChgp + = new AstTraceInc{flp, declp, VTraceType::CHANGE, baseCode}; + ifp->addThensp(incChgp); + + // Track splitting due to size + UASSERT_OBJ(incFulp->nodeCount() == incChgp->nodeCount(), declp, + "Should have equal cost"); + subStmts += incChgp->nodeCount(); // Track partitioning nCodes += declp->codeInc(); } - if (topFuncp) { // might be nullptr if all trailing entries were duplicates/constants - UINFO(5, "trace_chg_top" << topFuncNum - 1 << " codes: " << nCodes << "/" - << maxCodes << endl); - } } } @@ -743,11 +741,8 @@ class TraceVisitor final : public VNVisitor { TraceVec traces; // The sorted traces // 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. - uint32_t nFullCodes = 0; // Number of non-duplicate codes (need to go into full* dump) - uint32_t nChgCodes = 0; // Number of non-constant codes (need to go in to chg* dump) - sortTraces(traces, nFullCodes, nChgCodes); - - UINFO(5, "nFullCodes: " << nFullCodes << " nChgCodes: " << nChgCodes << endl); + uint32_t nNonConstCodes = 0; + sortTraces(traces, nNonConstCodes); // Our keys are now sorted to have same activity number adjacent, then // by trace order. (Better would be execution order for cache @@ -763,11 +758,11 @@ class TraceVisitor final : public VNVisitor { m_regFuncp->isLoose(true); m_topScopep->addBlocksp(m_regFuncp); - // Create the full dump functions, also allocates signal numbers - createFullTraceFunction(traces, nFullCodes, m_parallelism); + // Create the const dump functions. Also allocates trace codes. + createConstTraceFunctions(traces); - // Create the incremental dump functions - createChgTraceFunctions(traces, nChgCodes, m_parallelism); + // Create the full and incremental dump functions + createNonConstTraceFunctions(traces, nNonConstCodes, m_parallelism); // Remove refs to traced values from TraceDecl nodes, these have now moved under // TraceInc