diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index fdc701ebd..3303d5c71 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -52,9 +52,8 @@ // Graph vertexes class TraceActivityVertex : public V3GraphVertex { - AstNode* m_insertp; // Insert before this statement + AstNode* const m_insertp; vlsint32_t m_activityCode; - bool m_activityCodeValid; bool m_slow; // If always slow, we can use the same code public: enum { ACTIVITY_NEVER = ((1UL << 31) - 1) }; @@ -64,14 +63,12 @@ public: : V3GraphVertex(graphp) , m_insertp(nodep) { m_activityCode = 0; - m_activityCodeValid = false; m_slow = slow; } TraceActivityVertex(V3Graph* graphp, vlsint32_t code) : V3GraphVertex(graphp) , m_insertp(NULL) { m_activityCode = code; - m_activityCodeValid = true; m_slow = false; } virtual ~TraceActivityVertex() {} @@ -88,13 +85,9 @@ public: } } virtual string dotColor() const { return slow() ? "yellowGreen" : "green"; } - bool activityCodeValid() const { return m_activityCodeValid; } vlsint32_t activityCode() const { return m_activityCode; } bool activityAlways() const { return activityCode() == ACTIVITY_ALWAYS; } - void activityCode(vlsint32_t code) { - m_activityCode = code; - m_activityCodeValid = true; - } + void activityCode(vlsint32_t code) { m_activityCode = code; } bool slow() const { return m_slow; } void slow(bool flag) { if (!flag) m_slow = false; @@ -192,7 +185,7 @@ private: // All activity numbers applying to a given trace typedef std::set ActCodeSet; // For activity set, what traces apply - typedef std::multimap TraceVec; + typedef std::multimap TraceVec; // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -246,24 +239,27 @@ private: hashed.clear(); } - void graphSimplify() { - // Remove all variable nodes - for (V3GraphVertex *nextp, *itp = m_graph.verticesBeginp(); itp; itp = nextp) { - nextp = itp->verticesNextp(); - if (TraceVarVertex* const vvertexp = dynamic_cast(itp)) { - vvertexp->rerouteEdges(&m_graph); - vvertexp->unlinkDelete(&m_graph); + void graphSimplify(bool initial) { + if (initial) { + // Remove all variable nodes + for (V3GraphVertex *nextp, *itp = m_graph.verticesBeginp(); itp; itp = nextp) { + nextp = itp->verticesNextp(); + if (TraceVarVertex* const vvertexp = dynamic_cast(itp)) { + vvertexp->rerouteEdges(&m_graph); + vvertexp->unlinkDelete(&m_graph); + } } - } - // Remove multiple variables connecting funcs to traces - // We do this twice, as then we have fewer edges to multiply out in the below expansion. - m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); - // Remove all Cfunc nodes - for (V3GraphVertex *nextp, *itp = m_graph.verticesBeginp(); itp; itp = nextp) { - nextp = itp->verticesNextp(); - if (TraceCFuncVertex* const vvertexp = dynamic_cast(itp)) { - vvertexp->rerouteEdges(&m_graph); - vvertexp->unlinkDelete(&m_graph); + // Remove multiple variables connecting funcs to traces + // We do this twice, as then we have fewer edges to multiply out in the below + // expansion. + m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); + // Remove all Cfunc nodes + for (V3GraphVertex *nextp, *itp = m_graph.verticesBeginp(); itp; itp = nextp) { + nextp = itp->verticesNextp(); + if (TraceCFuncVertex* const vvertexp = dynamic_cast(itp)) { + vvertexp->rerouteEdges(&m_graph); + vvertexp->unlinkDelete(&m_graph); + } } } @@ -301,77 +297,154 @@ private: // Activity points with no outputs can be removed for (V3GraphVertex *nextp, *itp = m_graph.verticesBeginp(); itp; itp = nextp) { nextp = itp->verticesNextp(); - if (TraceActivityVertex* const vvertexp = dynamic_cast(itp)) { - if (!vvertexp->outBeginp()) { vvertexp->unlinkDelete(&m_graph); } + if (TraceActivityVertex* const vtxp = dynamic_cast(itp)) { + // Leave in the always vertex for later use. + if (vtxp != m_alwaysVtxp && !vtxp->outBeginp()) { vtxp->unlinkDelete(&m_graph); } } } } - 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" + uint32_t assignactivityNumbers() { + uint32_t activityNumber = 1; // Note 0 indicates "slow" for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { if (TraceActivityVertex* const vvertexp = dynamic_cast(itp)) { - if (!vvertexp->activityCodeValid()) { + if (vvertexp != m_alwaysVtxp) { if (vvertexp->slow()) { - // If all functions in the calls are slow, we'll share the same code. - // This makes us need less activityNumbers and so speeds up the fast path. vvertexp->activityCode(TraceActivityVertex::ACTIVITY_SLOW); } else { - vvertexp->activityCode(m_activityNumber++); + vvertexp->activityCode(activityNumber++); } } } } + return activityNumber; + } - FileLine* const flp = m_topScopep->fileline(); - - AstVar* newvarp; - if (v3Global.opt.mtasks()) { - // Create a vector of bytes, not bits, for the tracing vector, - // so that we can set them atomically without locking. - // - // TODO: It would be slightly faster to have a bit vector per - // chain of packed MTasks, but we haven't packed the MTasks yet. - // If we support fully threaded tracing in the future, it would - // make sense to improve this at that time. - AstNodeDType* const newScalarDtp = new AstBasicDType(flp, VFlagLogicPacked(), 1); - v3Global.rootp()->typeTablep()->addTypesp(newScalarDtp); - AstRange* const newArange - = new AstRange(flp, VNumRange(m_activityNumber - 1, 0, false)); - AstNodeDType* const newArrDtp = new AstUnpackArrayDType(flp, newScalarDtp, newArange); - v3Global.rootp()->typeTablep()->addTypesp(newArrDtp); - newvarp = new AstVar(flp, AstVarType::MODULETEMP, "__Vm_traceActivity", newArrDtp); - } else { - // For tighter code; round to next word point. - const int activityBits = VL_WORDS_I(m_activityNumber) * VL_EDATASIZE; - newvarp = new AstVar(flp, AstVarType::MODULETEMP, "__Vm_traceActivity", - VFlagBitPacked(), activityBits); + void sortTraces(TraceVec& traces, uint32_t& nFullCodes, uint32_t& nChgCodes) { + // Populate sort structure + traces.clear(); + nFullCodes = 0; + nChgCodes = 0; + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { + if (TraceTraceVertex* const vtxp = dynamic_cast(itp)) { + ActCodeSet actSet; + 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, vtxp->nodep(), + "Should have been function pointing to this trace"); + UINFO(9, " Activity: " << cfvertexp << endl); + if (cfvertexp->activityAlways()) { + // If code 0, we always trace; ignore other codes + actSet.insert(TraceActivityVertex::ACTIVITY_ALWAYS); + } else { + actSet.insert(cfvertexp->activityCode()); + } + } + UASSERT_OBJ(actSet.count(TraceActivityVertex::ACTIVITY_ALWAYS) == 0 + || 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 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, vtxp)); + } } + } + + void graphOptimize() { + // Assign initial activity numbers to activity vertices + assignactivityNumbers(); + + // Sort the traces by activity sets + TraceVec traces; + uint32_t unused1, unused2; + sortTraces(traces, unused1, unused2); + + // 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 + // than to test a lot of activity flags + TraceVec::iterator it = traces.begin(); + const TraceVec::iterator end = traces.end(); + while (it != end) { + TraceVec::iterator head = it; + // Count the value comparisons in all traces under this activity set + uint32_t valueComparisons = 0; + const ActCodeSet& actSet = it->first; + for (; it != end && it->first == actSet; ++it) { + valueComparisons += it->second->nodep()->codeInc(); + } + // Leave alone always changing, never changing and signals only set in slow code + if (actSet.count(TraceActivityVertex::ACTIVITY_ALWAYS)) continue; + if (actSet.count(TraceActivityVertex::ACTIVITY_NEVER)) continue; + if (actSet.count(TraceActivityVertex::ACTIVITY_SLOW) && actSet.size() == 1) continue; + // If the value comparisons are cheaper to perform than checking the + // activity flags make the signals always traced. Note this cost + // equation is heuristic. + if (valueComparisons < actSet.size() * 2) { + for (; head != it; ++head) { + new V3GraphEdge(&m_graph, m_alwaysVtxp, head->second, 1); + } + } + } + + graphSimplify(false); + } + + AstNode* selectActivity(FileLine* flp, uint32_t acode, bool lvalue) { + return new AstArraySel(flp, new AstVarRef(flp, m_activityVscp, lvalue), acode); + } + + void createActivityFlags() { + // Assign final activity numbers + m_activityNumber = assignactivityNumbers(); + + // Create an array of bytes, not a bit vector, as they can be set + // atomically by mtasks, and are cheaper to set (not need for + // read-modify-write on the C type), and the speed of the tracing code + // is the same on largish designs. + FileLine* const flp = m_topScopep->fileline(); + AstNodeDType* const newScalarDtp = new AstBasicDType(flp, VFlagLogicPacked(), 1); + v3Global.rootp()->typeTablep()->addTypesp(newScalarDtp); + AstRange* const newArange = new AstRange(flp, VNumRange(m_activityNumber - 1, 0, false)); + AstNodeDType* const newArrDtp = new AstUnpackArrayDType(flp, newScalarDtp, newArange); + v3Global.rootp()->typeTablep()->addTypesp(newArrDtp); + AstVar* const newvarp + = new AstVar(flp, AstVarType::MODULETEMP, "__Vm_traceActivity", newArrDtp); m_topModp->addStmtp(newvarp); AstVarScope* const newvscp = new AstVarScope(flp, m_topScopep, newvarp); m_topScopep->addVarp(newvscp); m_activityVscp = newvscp; - // Insert activity setter + // Insert activity setters for (const V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - if (const TraceActivityVertex* const vvertexp + if (const TraceActivityVertex* const vtxp = dynamic_cast(itp)) { - if (!vvertexp->activityAlways()) { - FileLine* const fl = vvertexp->insertp()->fileline(); - const uint32_t acode = vvertexp->activityCode(); - vvertexp->insertp()->addNextHere( - new AstAssign(fl, selectActivity(fl, acode, true), - new AstConst(fl, AstConst::LogicTrue()))); + if (!vtxp->activityAlways()) { + FileLine* const fl = vtxp->insertp()->fileline(); + const uint32_t acode = vtxp->activityCode(); + AstAssign* const setterp + = new AstAssign(fl, selectActivity(fl, acode, true), + new AstConst(fl, AstConst::LogicTrue())); + if (AstCCall* const callp = VN_CAST(vtxp->insertp(), CCall)) { + callp->addNextHere(setterp); + } else if (AstCFunc* const funcp = VN_CAST(vtxp->insertp(), CFunc)) { + funcp->addStmtsp(setterp); + } else { + vtxp->insertp()->v3fatalSrc("Bad trace activity vertex"); + } } } } @@ -437,49 +510,6 @@ private: } } - 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. - - // Populate sort structure - for (const V3GraphVertex* itp = m_graph.verticesBeginp(); itp; - itp = itp->verticesNextp()) { - if (const TraceTraceVertex* const vtxp = dynamic_cast(itp)) { - ActCodeSet actset; - 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, vtxp->nodep(), - "Should have been function pointing to this trace"); - UINFO(9, " Activity: " << cfvertexp << endl); - if (cfvertexp->activityAlways()) { - // If code 0, we always trace; ignore other codes - actset.clear(); - actset.insert(TraceActivityVertex::ACTIVITY_ALWAYS); - break; - } else { - 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, vtxp)); - } - } - } - void createFullTraceFunction(const TraceVec& traces, uint32_t nAllCodes, uint32_t parallelism, AstCFunc* regFuncp) { const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace() @@ -633,29 +663,33 @@ private: 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* const clrp = new AstAssign(fl, selectActivity(fl, i, true), - new AstConst(fl, AstConst::LogicFalse())); - cleanupFuncp->addStmtsp(clrp); - } - } else { - AstNode* const clrp = new AstAssign( - fl, new AstVarRef(fl, m_activityVscp, true), - new AstConst(fl, AstConst::WidthedValue(), m_activityVscp->width(), 0)); + for (uint32_t i = 0; i < m_activityNumber; ++i) { + AstNode* const clrp = new AstAssign(fl, selectActivity(fl, i, true), + new AstConst(fl, AstConst::LogicFalse())); cleanupFuncp->addStmtsp(clrp); } } void createTraceFunctions() { - // Form a sorted list of the traces we are interested in - UINFO(9, "Making trees\n"); + // Detect and remove duplicate values + detectDuplicates(); + // Simplify & optimize the graph + if (debug() >= 6) m_graph.dumpDotFilePrefixed("trace_pre"); + graphSimplify(true); + if (debug() >= 6) m_graph.dumpDotFilePrefixed("trace_simplified"); + graphOptimize(); + if (debug() >= 6) m_graph.dumpDotFilePrefixed("trace_optimized"); + + // Create the fine grained activity flags + createActivityFlags(); + + // Form a sorted list of the traces we are interested in 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-consant codes (need to go in to chg* dump) - - // Sort the traces sortTraces(traces, nFullCodes, nChgCodes); UINFO(5, "nFullCodes: " << nFullCodes << " nChgCodes: " << nChgCodes << endl); @@ -683,7 +717,8 @@ private: // Create the incremental dump functions createChgTraceFunctions(traces, nChgCodes, parallelism, regFuncp); - // Remove refs to traced values from TraceDecl nodes, these have now moved under TraceInc + // 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(); @@ -726,18 +761,6 @@ private: // Add vertexes for all CFUNCs, and edges to VARs the func sets m_finding = true; iterateChildren(nodep); - m_finding = false; - - // Detect and remove duplicate values - detectDuplicates(); - - // Simplify it - if (debug() >= 6) m_graph.dumpDotFilePrefixed("trace_pre"); - graphSimplify(); - if (debug() >= 6) m_graph.dumpDotFilePrefixed("trace_opt"); - - // Create the activity flags - assignActivity(); // Create the trace functions and insert them into the tree createTraceFunctions(); @@ -774,16 +797,10 @@ private: virtual void visit(AstCFunc* nodep) VL_OVERRIDE { UINFO(8, " CFUNC " << nodep << endl); V3GraphVertex* const funcVtxp = getCFuncVertexp(nodep); - if (!m_finding) { // If public, we need a unique activity code to allow for sets directly - // in this func + if (!m_finding) { // If public, we need a unique activity code to allow for sets + // directly in this func if (nodep->funcPublic() || nodep->dpiExport() || nodep == v3Global.rootp()->evalp()) { - // Need a non-null place to remember to later add a statement; make one - if (!nodep->stmtsp()) { - nodep->addStmtsp( - new AstComment(nodep->fileline(), "Tracing activity check", true)); - } - V3GraphVertex* const activityVtxp - = getActivityVertexp(nodep->stmtsp(), nodep->slow()); + V3GraphVertex* const activityVtxp = getActivityVertexp(nodep, nodep->slow()); new V3GraphEdge(&m_graph, activityVtxp, funcVtxp, 1); } } diff --git a/test_regress/t/t_order_clkinst.out b/test_regress/t/t_order_clkinst.out index 2c1d5771e..98e8e114b 100644 --- a/test_regress/t/t_order_clkinst.out +++ b/test_regress/t/t_order_clkinst.out @@ -72,5 +72,5 @@ b00000011 $ #35 0/ #40 -b00000100 $ 1/ +b00000100 $ diff --git a/test_regress/t/t_trace_timescale.out b/test_regress/t/t_trace_timescale.out index 208185cca..34791f485 100644 --- a/test_regress/t/t_trace_timescale.out +++ b/test_regress/t/t_trace_timescale.out @@ -67,5 +67,5 @@ b00000000000000000000000000001010 # #105 0+ #110 -b00000000000000000000000000001011 # 1+ +b00000000000000000000000000001011 # diff --git a/test_regress/t/t_trace_two_dump_cc.out b/test_regress/t/t_trace_two_dump_cc.out index c452d0e3f..46be29d26 100644 --- a/test_regress/t/t_trace_two_dump_cc.out +++ b/test_regress/t/t_trace_two_dump_cc.out @@ -100,6 +100,6 @@ b00000000000000000000000000001011 + 03 #110 #110 +13 b00000000000000000000000000001011 # b00000000000000000000000000001100 + -13 diff --git a/test_regress/t/t_trace_two_hdr_sc.out b/test_regress/t/t_trace_two_hdr_sc.out index 8f680510d..46befffe5 100644 --- a/test_regress/t/t_trace_two_hdr_sc.out +++ b/test_regress/t/t_trace_two_hdr_sc.out @@ -120,7 +120,7 @@ r0.8999999999999999 F 0> #100000 1# +1> b00000000000000000000000000001011 + b00000000000000000000000000001100 3 -1> r0.9999999999999999 F diff --git a/test_regress/t/t_trace_two_port_cc.out b/test_regress/t/t_trace_two_port_cc.out index 4764562aa..9858cf941 100644 --- a/test_regress/t/t_trace_two_port_cc.out +++ b/test_regress/t/t_trace_two_port_cc.out @@ -100,6 +100,6 @@ b00000000000000000000000000001011 + 03 #110 #110 +13 b00000000000000000000000000001011 # b00000000000000000000000000001100 + -13