2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Waves tracing
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2020-03-21 16:24:24 +01:00
|
|
|
// Copyright 2003-2020 by Wilson Snyder. This program is free software; you
|
|
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 23:07:57 +02:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Trace's Transformations:
|
|
|
|
|
// Pass 1:
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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
|
2006-08-26 13:35:28 +02:00
|
|
|
// Pass 2:
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
2019-05-19 22:13:13 +02:00
|
|
|
// For each CFUNC with unique callReason
|
|
|
|
|
// Make vertex
|
|
|
|
|
// For each var it sets, make vertex and edge from cfunc vertex
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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
|
2008-06-10 03:25:10 +02:00
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
|
2006-12-18 20:20:45 +01:00
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3Trace.h"
|
|
|
|
|
#include "V3EmitCBase.h"
|
|
|
|
|
#include "V3Graph.h"
|
|
|
|
|
#include "V3Hashed.h"
|
|
|
|
|
#include "V3Stats.h"
|
|
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include <map>
|
|
|
|
|
#include <set>
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Graph vertexes
|
|
|
|
|
|
|
|
|
|
class TraceActivityVertex : public V3GraphVertex {
|
2020-04-14 04:51:35 +02:00
|
|
|
AstNode* m_insertp; // Insert before this statement
|
|
|
|
|
vlsint32_t m_activityCode;
|
|
|
|
|
bool m_activityCodeValid;
|
|
|
|
|
bool m_slow; // If always slow, we can use the same code
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2020-04-14 04:51:35 +02:00
|
|
|
enum { ACTIVITY_NEVER = ((1UL << 31) - 1) };
|
|
|
|
|
enum { ACTIVITY_ALWAYS = ((1UL << 31) - 2) };
|
|
|
|
|
enum { ACTIVITY_SLOW = 0 };
|
2006-08-26 13:35:28 +02:00
|
|
|
TraceActivityVertex(V3Graph* graphp, AstNode* nodep, bool slow)
|
2020-04-14 04:51:35 +02:00
|
|
|
: V3GraphVertex(graphp)
|
|
|
|
|
, m_insertp(nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_activityCode = 0;
|
|
|
|
|
m_activityCodeValid = false;
|
|
|
|
|
m_slow = slow;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
TraceActivityVertex(V3Graph* graphp, vlsint32_t code)
|
2020-04-14 04:51:35 +02:00
|
|
|
: V3GraphVertex(graphp)
|
|
|
|
|
, m_insertp(NULL) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_activityCode = code;
|
|
|
|
|
m_activityCodeValid = true;
|
|
|
|
|
m_slow = false;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
virtual ~TraceActivityVertex() {}
|
2017-11-28 02:11:34 +01:00
|
|
|
// ACCESSORS
|
2006-08-26 13:35:28 +02:00
|
|
|
AstNode* insertp() const {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!m_insertp) v3fatalSrc("Null insertp; probably called on a special always/slow.");
|
|
|
|
|
return m_insertp;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
virtual string name() const {
|
2020-04-14 04:51:35 +02:00
|
|
|
if (activityAlways()) {
|
|
|
|
|
return "*ALWAYS*";
|
|
|
|
|
} else {
|
|
|
|
|
return (string(slow() ? "*SLOW* " : "")) + insertp()->name();
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-04-14 04:51:35 +02:00
|
|
|
virtual string dotColor() const { return slow() ? "yellowGreen" : "green"; }
|
2006-08-26 13:35:28 +02:00
|
|
|
bool activityCodeValid() const { return m_activityCodeValid; }
|
|
|
|
|
vlsint32_t activityCode() const { return m_activityCode; }
|
2020-04-14 04:51:35 +02:00
|
|
|
bool activityAlways() const { return activityCode() == ACTIVITY_ALWAYS; }
|
|
|
|
|
void activityCode(vlsint32_t code) {
|
|
|
|
|
m_activityCode = code;
|
|
|
|
|
m_activityCodeValid = true;
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
bool slow() const { return m_slow; }
|
2020-04-14 04:51:35 +02:00
|
|
|
void slow(bool flag) {
|
|
|
|
|
if (!flag) m_slow = false;
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class TraceCFuncVertex : public V3GraphVertex {
|
2019-05-19 22:13:13 +02:00
|
|
|
AstCFunc* m_nodep;
|
2020-04-14 04:51:35 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
|
|
|
|
TraceCFuncVertex(V3Graph* graphp, AstCFunc* nodep)
|
2020-04-14 04:51:35 +02:00
|
|
|
: V3GraphVertex(graphp)
|
|
|
|
|
, m_nodep(nodep) {}
|
2006-08-26 13:35:28 +02:00
|
|
|
virtual ~TraceCFuncVertex() {}
|
2017-11-28 02:11:34 +01:00
|
|
|
// ACCESSORS
|
2006-08-26 13:35:28 +02:00
|
|
|
AstCFunc* nodep() const { return m_nodep; }
|
|
|
|
|
virtual string name() const { return nodep()->name(); }
|
|
|
|
|
virtual string dotColor() const { return "yellow"; }
|
2018-07-15 00:45:06 +02:00
|
|
|
virtual FileLine* fileline() const { return nodep()->fileline(); }
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class TraceTraceVertex : public V3GraphVertex {
|
2019-05-19 22:13:13 +02:00
|
|
|
AstTraceInc* m_nodep; // TRACEINC this represents
|
2020-04-14 04:51:35 +02:00
|
|
|
// NULL, or other vertex with the real code() that duplicates this one
|
|
|
|
|
TraceTraceVertex* m_duplicatep;
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
|
|
|
|
TraceTraceVertex(V3Graph* graphp, AstTraceInc* nodep)
|
2020-04-14 04:51:35 +02:00
|
|
|
: V3GraphVertex(graphp)
|
|
|
|
|
, m_nodep(nodep)
|
|
|
|
|
, m_duplicatep(NULL) {}
|
2006-08-26 13:35:28 +02:00
|
|
|
virtual ~TraceTraceVertex() {}
|
2017-11-28 02:11:34 +01:00
|
|
|
// ACCESSORS
|
2006-08-26 13:35:28 +02:00
|
|
|
AstTraceInc* nodep() const { return m_nodep; }
|
|
|
|
|
virtual string name() const { return nodep()->declp()->name(); }
|
|
|
|
|
virtual string dotColor() const { return "red"; }
|
2018-07-15 00:45:06 +02:00
|
|
|
virtual FileLine* fileline() const { return nodep()->fileline(); }
|
2006-08-26 13:35:28 +02:00
|
|
|
TraceTraceVertex* duplicatep() const { return m_duplicatep; }
|
|
|
|
|
void duplicatep(TraceTraceVertex* dupp) {
|
2020-04-14 04:51:35 +02:00
|
|
|
UASSERT_OBJ(!duplicatep(), nodep(), "Assigning duplicatep() to already duplicated node");
|
2019-05-19 22:13:13 +02:00
|
|
|
m_duplicatep = dupp;
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class TraceVarVertex : public V3GraphVertex {
|
2020-04-14 04:51:35 +02:00
|
|
|
AstVarScope* m_nodep;
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
|
|
|
|
TraceVarVertex(V3Graph* graphp, AstVarScope* nodep)
|
2020-04-14 04:51:35 +02:00
|
|
|
: V3GraphVertex(graphp)
|
|
|
|
|
, m_nodep(nodep) {}
|
2006-08-26 13:35:28 +02:00
|
|
|
virtual ~TraceVarVertex() {}
|
2017-11-28 02:11:34 +01:00
|
|
|
// ACCESSORS
|
2006-08-26 13:35:28 +02:00
|
|
|
AstVarScope* nodep() const { return m_nodep; }
|
|
|
|
|
virtual string name() const { return nodep()->name(); }
|
|
|
|
|
virtual string dotColor() const { return "skyblue"; }
|
2018-07-15 00:45:06 +02:00
|
|
|
virtual FileLine* fileline() const { return nodep()->fileline(); }
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Trace state, as a visitor of each AstNode
|
|
|
|
|
|
|
|
|
|
class TraceVisitor : public EmitCBaseVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// V3Hashed
|
2019-05-19 22:13:13 +02:00
|
|
|
// Ast*::user4() // V3Hashed calculation
|
2008-06-10 03:25:10 +02:00
|
|
|
// Cleared entire netlist
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstCFunc::user1() // V3GraphVertex* for this node
|
|
|
|
|
// AstTraceInc::user1() // V3GraphVertex* for this node
|
|
|
|
|
// AstVarScope::user1() // V3GraphVertex* for this node
|
|
|
|
|
// AstCCall::user2() // bool; walked next list for other ccalls
|
|
|
|
|
// Ast*::user3() // TraceActivityVertex* for this node
|
2020-04-14 04:51:35 +02:00
|
|
|
AstUser1InUse m_inuser1;
|
|
|
|
|
AstUser2InUse m_inuser2;
|
|
|
|
|
AstUser3InUse m_inuser3;
|
|
|
|
|
// AstUser4InUse In V3Hashed
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// STATE
|
2020-04-14 04:51:35 +02:00
|
|
|
AstNodeModule* m_topModp; // Module to add variables to
|
2020-05-09 12:38:32 +02:00
|
|
|
AstScope* m_topScopep; // Scope to add variables to
|
2020-04-14 04:51:35 +02:00
|
|
|
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
|
|
|
|
|
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
|
2020-05-09 12:38:32 +02:00
|
|
|
TraceActivityVertex* const m_alwaysVtxp; // "Always trace" vertex
|
2020-04-14 04:51:35 +02:00
|
|
|
bool m_finding; // Pass one of algorithm?
|
|
|
|
|
int m_funcNum; // Function number being built
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2019-10-05 13:54:14 +02:00
|
|
|
VDouble0 m_statChgSigs; // Statistic tracking
|
|
|
|
|
VDouble0 m_statUniqSigs; // Statistic tracking
|
|
|
|
|
VDouble0 m_statUniqCodes; // Statistic tracking
|
2008-11-17 23:13:57 +01:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// METHODS
|
2018-05-14 12:50:47 +02:00
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
2009-01-21 22:56:50 +01:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
void detectDuplicates() {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(9, "Finding duplicates\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Note uses user4
|
|
|
|
|
V3Hashed hashed; // Duplicate code detection
|
|
|
|
|
// Hash all of the values the traceIncs need
|
2020-05-09 12:38:32 +02:00
|
|
|
for (const V3GraphVertex* itp = m_graph.verticesBeginp(); itp;
|
|
|
|
|
itp = itp->verticesNextp()) {
|
|
|
|
|
if (const TraceTraceVertex* const vvertexp
|
|
|
|
|
= dynamic_cast<const TraceTraceVertex*>(itp)) {
|
|
|
|
|
const AstTraceInc* const nodep = vvertexp->nodep();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->valuep()) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(nodep->valuep()->backp() == nodep, nodep,
|
|
|
|
|
"Trace duplicate back needs consistency,"
|
|
|
|
|
" so we can map duplicates back to TRACEINCs");
|
2019-05-19 22:13:13 +02:00
|
|
|
hashed.hash(nodep->valuep());
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(8, " Hashed " << std::hex << hashed.nodeHash(nodep->valuep()) << " "
|
|
|
|
|
<< nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
|
|
|
|
|
// Just keep one node in the map and point all duplicates to this node
|
|
|
|
|
if (hashed.findDuplicate(nodep->valuep()) == hashed.end()) {
|
|
|
|
|
hashed.hashAndInsert(nodep->valuep());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Find if there are any duplicates
|
2020-04-14 04:51:35 +02:00
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
2020-05-09 12:38:32 +02:00
|
|
|
if (TraceTraceVertex* const vvertexp = dynamic_cast<TraceTraceVertex*>(itp)) {
|
|
|
|
|
AstTraceInc* const nodep = vvertexp->nodep();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->valuep() && !vvertexp->duplicatep()) {
|
|
|
|
|
V3Hashed::iterator dupit = hashed.findDuplicate(nodep->valuep());
|
|
|
|
|
if (dupit != hashed.end()) {
|
2020-05-09 12:38:32 +02:00
|
|
|
const AstTraceInc* const dupincp
|
|
|
|
|
= VN_CAST_CONST(hashed.iteratorNodep(dupit)->backp(), TraceInc);
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(dupincp, nodep, "Trace duplicate of wrong type");
|
2020-05-09 12:38:32 +02:00
|
|
|
TraceTraceVertex* const dupvertexp
|
2019-05-19 22:13:13 +02:00
|
|
|
= dynamic_cast<TraceTraceVertex*>(dupincp->user1u().toGraphVertex());
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(8, " Orig " << nodep << endl);
|
|
|
|
|
UINFO(8, " dup " << dupincp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Mark the hashed node as the original and our
|
|
|
|
|
// iterating node as duplicated
|
|
|
|
|
vvertexp->duplicatep(dupvertexp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
hashed.clear();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void graphSimplify() {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Remove all variable nodes
|
2020-04-14 04:51:35 +02:00
|
|
|
for (V3GraphVertex *nextp, *itp = m_graph.verticesBeginp(); itp; itp = nextp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nextp = itp->verticesNextp();
|
2020-05-09 12:38:32 +02:00
|
|
|
if (TraceVarVertex* const vvertexp = dynamic_cast<TraceVarVertex*>(itp)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
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
|
2020-04-14 04:51:35 +02:00
|
|
|
for (V3GraphVertex *nextp, *itp = m_graph.verticesBeginp(); itp; itp = nextp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nextp = itp->verticesNextp();
|
2020-05-09 12:38:32 +02:00
|
|
|
if (TraceCFuncVertex* const vvertexp = dynamic_cast<TraceCFuncVertex*>(itp)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
vvertexp->rerouteEdges(&m_graph);
|
|
|
|
|
vvertexp->unlinkDelete(&m_graph);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove multiple variables connecting funcs to traces
|
|
|
|
|
m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
|
|
|
|
|
|
|
|
|
|
// If there are any edges from a always, keep only the always
|
2020-05-09 12:38:32 +02:00
|
|
|
for (const V3GraphVertex* itp = m_graph.verticesBeginp(); itp;
|
|
|
|
|
itp = itp->verticesNextp()) {
|
|
|
|
|
if (const TraceTraceVertex* const vvertexp
|
|
|
|
|
= dynamic_cast<const TraceTraceVertex*>(itp)) {
|
|
|
|
|
// Search for the incoming always edge
|
|
|
|
|
const V3GraphEdge* alwaysEdgep = NULL;
|
|
|
|
|
for (const V3GraphEdge* edgep = vvertexp->inBeginp(); edgep;
|
|
|
|
|
edgep = edgep->inNextp()) {
|
|
|
|
|
const TraceActivityVertex* const actVtxp
|
|
|
|
|
= dynamic_cast<const TraceActivityVertex*>(edgep->fromp());
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(actVtxp, vvertexp->nodep(),
|
|
|
|
|
"Tracing a node with FROM non activity");
|
2019-05-19 22:13:13 +02:00
|
|
|
if (actVtxp->activityAlways()) {
|
|
|
|
|
alwaysEdgep = edgep;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-09 12:38:32 +02:00
|
|
|
// If always edge exists, remove all other edges
|
2019-05-19 22:13:13 +02:00
|
|
|
if (alwaysEdgep) {
|
2020-04-14 04:51:35 +02:00
|
|
|
for (V3GraphEdge *nextp, *edgep = vvertexp->inBeginp(); edgep; edgep = nextp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nextp = edgep->inNextp();
|
2020-04-14 04:51:35 +02:00
|
|
|
if (edgep != alwaysEdgep) edgep->unlinkDelete();
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Activity points with no outputs can be removed
|
2020-04-14 04:51:35 +02:00
|
|
|
for (V3GraphVertex *nextp, *itp = m_graph.verticesBeginp(); itp; itp = nextp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nextp = itp->verticesNextp();
|
2020-05-09 12:38:32 +02:00
|
|
|
if (TraceActivityVertex* const vvertexp = dynamic_cast<TraceActivityVertex*>(itp)) {
|
2020-04-14 04:51:35 +02:00
|
|
|
if (!vvertexp->outBeginp()) { vvertexp->unlinkDelete(&m_graph); }
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void assignActivity() {
|
2020-05-09 12:38:32 +02:00
|
|
|
// Select activity numbers and put into each activity vertex
|
2018-07-23 02:54:28 +02:00
|
|
|
m_activityNumber = 1; // Note 0 indicates "slow"
|
2020-04-14 04:51:35 +02:00
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
2020-05-09 12:38:32 +02:00
|
|
|
if (TraceActivityVertex* const vvertexp = dynamic_cast<TraceActivityVertex*>(itp)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!vvertexp->activityCodeValid()) {
|
|
|
|
|
if (vvertexp->slow()) {
|
|
|
|
|
// If all functions in the calls are slow, we'll share the same code.
|
|
|
|
|
// This makes us need less activityNumbers and so speeds up the fast path.
|
|
|
|
|
vvertexp->activityCode(TraceActivityVertex::ACTIVITY_SLOW);
|
|
|
|
|
} else {
|
2018-07-23 02:54:28 +02:00
|
|
|
vvertexp->activityCode(m_activityNumber++);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-05-09 12:38:32 +02:00
|
|
|
FileLine* const flp = m_chgFuncp->fileline();
|
|
|
|
|
|
2018-07-23 02:54:28 +02:00
|
|
|
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.
|
2020-05-09 12:38:32 +02:00
|
|
|
AstNodeDType* const newScalarDtp = new AstBasicDType(flp, VFlagLogicPacked(), 1);
|
2018-07-23 02:54:28 +02:00
|
|
|
v3Global.rootp()->typeTablep()->addTypesp(newScalarDtp);
|
2020-05-09 12:38:32 +02:00
|
|
|
AstRange* const newArange
|
|
|
|
|
= new AstRange(flp, VNumRange(m_activityNumber - 1, 0, false));
|
|
|
|
|
AstNodeDType* const newArrDtp = new AstUnpackArrayDType(flp, newScalarDtp, newArange);
|
2018-07-23 02:54:28 +02:00
|
|
|
v3Global.rootp()->typeTablep()->addTypesp(newArrDtp);
|
2020-05-09 12:38:32 +02:00
|
|
|
newvarp = new AstVar(flp, AstVarType::MODULETEMP, "__Vm_traceActivity", newArrDtp);
|
2018-07-23 02:54:28 +02:00
|
|
|
} else {
|
2019-12-09 03:36:38 +01:00
|
|
|
// For tighter code; round to next word point.
|
2020-05-09 12:38:32 +02:00
|
|
|
const int activityBits = VL_WORDS_I(m_activityNumber) * VL_EDATASIZE;
|
|
|
|
|
newvarp = new AstVar(flp, AstVarType::MODULETEMP, "__Vm_traceActivity",
|
|
|
|
|
VFlagBitPacked(), activityBits);
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
m_topModp->addStmtp(newvarp);
|
2020-05-09 12:38:32 +02:00
|
|
|
AstVarScope* const newvscp = new AstVarScope(flp, m_topScopep, newvarp);
|
|
|
|
|
m_topScopep->addVarp(newvscp);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_activityVscp = newvscp;
|
|
|
|
|
|
|
|
|
|
// Insert activity setter
|
2020-05-09 12:38:32 +02:00
|
|
|
for (const V3GraphVertex* itp = m_graph.verticesBeginp(); itp;
|
|
|
|
|
itp = itp->verticesNextp()) {
|
|
|
|
|
if (const TraceActivityVertex* const vvertexp
|
|
|
|
|
= dynamic_cast<const TraceActivityVertex*>(itp)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!vvertexp->activityAlways()) {
|
2020-05-09 12:38:32 +02:00
|
|
|
FileLine* const fl = vvertexp->insertp()->fileline();
|
|
|
|
|
const uint32_t acode = vvertexp->activityCode();
|
2020-04-14 04:51:35 +02:00
|
|
|
vvertexp->insertp()->addNextHere(
|
|
|
|
|
new AstAssign(fl, selectActivity(fl, acode, true),
|
|
|
|
|
new AstConst(fl, AstConst::LogicTrue())));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2018-07-23 02:54:28 +02:00
|
|
|
AstNode* selectActivity(FileLine* flp, uint32_t acode, bool lvalue) {
|
|
|
|
|
if (v3Global.opt.mtasks()) {
|
2020-04-14 04:51:35 +02:00
|
|
|
return new AstArraySel(flp, new AstVarRef(flp, m_activityVscp, lvalue), acode);
|
2018-07-23 02:54:28 +02:00
|
|
|
} else {
|
2020-04-14 04:51:35 +02:00
|
|
|
return new AstSel(flp, new AstVarRef(flp, m_activityVscp, lvalue), acode, 1);
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-17 23:13:57 +01:00
|
|
|
AstCFunc* newCFunc(AstCFuncType type, const string& name, AstCFunc* basep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
AstCFunc* funcp = new AstCFunc(basep->fileline(), name, basep->scopep());
|
|
|
|
|
funcp->slow(basep->slow());
|
2020-04-14 04:51:35 +02:00
|
|
|
funcp->argTypes(EmitCBaseVisitor::symClassVar() + ", " + v3Global.opt.traceClassBase()
|
|
|
|
|
+ "* vcdp, uint32_t code");
|
2019-05-19 22:13:13 +02:00
|
|
|
funcp->funcType(type);
|
|
|
|
|
funcp->symProlog(true);
|
|
|
|
|
basep->addNext(funcp);
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(5, " Newfunc " << funcp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
return funcp;
|
2008-11-17 23:13:57 +01:00
|
|
|
}
|
2020-05-09 12:38:32 +02:00
|
|
|
|
2008-11-17 23:13:57 +01:00
|
|
|
AstCFunc* newCFuncSub(AstCFunc* basep, AstNode* callfromp) {
|
2020-04-14 04:51:35 +02:00
|
|
|
string name = basep->name() + "__" + cvtToStr(++m_funcNum);
|
2019-05-19 22:13:13 +02:00
|
|
|
AstCFunc* funcp = NULL;
|
2020-04-14 04:51:35 +02:00
|
|
|
if (basep->funcType() == AstCFuncType::TRACE_FULL) {
|
2019-05-19 22:13:13 +02:00
|
|
|
funcp = newCFunc(AstCFuncType::TRACE_FULL_SUB, name, basep);
|
2020-04-14 04:51:35 +02:00
|
|
|
} else if (basep->funcType() == AstCFuncType::TRACE_CHANGE) {
|
2019-05-19 22:13:13 +02:00
|
|
|
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");
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(callfromp, CFunc)) {
|
|
|
|
|
VN_CAST(callfromp, CFunc)->addStmtsp(callp);
|
|
|
|
|
} else if (VN_IS(callfromp, If)) {
|
|
|
|
|
VN_CAST(callfromp, If)->addIfsp(callp);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
callfromp->v3fatalSrc("Unknown caller node type"); // Where to add it??
|
|
|
|
|
}
|
|
|
|
|
return funcp;
|
2008-11-17 23:13:57 +01:00
|
|
|
}
|
2020-05-09 12:38:32 +02:00
|
|
|
|
|
|
|
|
uint32_t assignDeclCode(AstTraceDecl* nodep) {
|
|
|
|
|
if (!nodep->code()) {
|
|
|
|
|
nodep->code(m_code);
|
|
|
|
|
m_code += nodep->codeInc();
|
|
|
|
|
m_statUniqCodes += nodep->codeInc();
|
|
|
|
|
++m_statUniqSigs;
|
|
|
|
|
}
|
|
|
|
|
return nodep->code();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstTraceInc* assignTraceCode(const TraceTraceVertex* vvertexp, bool needChg) {
|
|
|
|
|
// Assign trace code, add to tree, return node for change tree or null
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-17 23:13:57 +01:00
|
|
|
void addToChgSub(AstNode* underp, AstNode* stmtsp) {
|
2020-04-14 04:51:35 +02:00
|
|
|
if (!m_chgSubFuncp || (m_chgSubParentp != underp)
|
2019-05-19 22:13:13 +02:00
|
|
|
|| (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();
|
2008-11-17 23:13:57 +01:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
void putTracesIntoTree() {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Form a sorted list of the traces we are interested in
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(9, "Making trees\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-05-09 12:38:32 +02:00
|
|
|
// All activity numbers applying to a given trace
|
|
|
|
|
typedef std::set<uint32_t> ActCodeSet;
|
|
|
|
|
// For activity set, what traces apply
|
|
|
|
|
typedef std::multimap<ActCodeSet, const TraceTraceVertex*> TraceVec;
|
2019-05-19 22:13:13 +02:00
|
|
|
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.
|
2020-05-09 12:38:32 +02:00
|
|
|
for (const V3GraphVertex* itp = m_graph.verticesBeginp(); itp;
|
|
|
|
|
itp = itp->verticesNextp()) {
|
|
|
|
|
if (const TraceTraceVertex* const vvertexp
|
|
|
|
|
= dynamic_cast<const TraceTraceVertex*>(itp)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
ActCodeSet actset;
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(9, " Add to sort: " << vvertexp << endl);
|
|
|
|
|
if (debug() >= 9) vvertexp->nodep()->dumpTree(cout, "- trnode: ");
|
2020-05-09 12:38:32 +02:00
|
|
|
for (const V3GraphEdge* edgep = vvertexp->inBeginp(); edgep;
|
|
|
|
|
edgep = edgep->inNextp()) {
|
|
|
|
|
const TraceActivityVertex* const cfvertexp
|
|
|
|
|
= dynamic_cast<const TraceActivityVertex*>(edgep->fromp());
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(cfvertexp, vvertexp->nodep(),
|
|
|
|
|
"Should have been function pointing to this trace");
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(9, " Activity: " << cfvertexp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (cfvertexp->activityAlways()) {
|
|
|
|
|
// If code 0, we always trace; ignore other codes
|
|
|
|
|
actset.clear();
|
|
|
|
|
actset.insert(TraceActivityVertex::ACTIVITY_ALWAYS);
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
2020-05-09 12:38:32 +02:00
|
|
|
actset.insert(cfvertexp->activityCode());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// If a trace doesn't have activity, it's constant, and we
|
|
|
|
|
// don't need to track changes on it.
|
|
|
|
|
// We put constants and non-changers last, as then the
|
|
|
|
|
// prevvalue vector is more compacted
|
|
|
|
|
if (actset.empty()) actset.insert(TraceActivityVertex::ACTIVITY_NEVER);
|
|
|
|
|
traces.insert(make_pair(actset, vvertexp));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Our keys are now sorted to have same activity number adjacent,
|
|
|
|
|
// then by trace order. (Better would be execution order for cache efficiency....)
|
|
|
|
|
// Last are constants and non-changers, as then the last value vector is more compact
|
|
|
|
|
|
|
|
|
|
// Put TRACEs back into the tree
|
|
|
|
|
const ActCodeSet* lastactp = NULL;
|
|
|
|
|
AstNode* ifnodep = NULL;
|
2020-04-14 04:51:35 +02:00
|
|
|
for (TraceVec::iterator it = traces.begin(); it != traces.end(); ++it) {
|
2019-05-19 22:13:13 +02:00
|
|
|
const ActCodeSet& actset = it->first;
|
2020-05-09 12:38:32 +02:00
|
|
|
const TraceTraceVertex* const vvertexp = it->second;
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(9, " Done sort: " << vvertexp << endl);
|
2020-05-09 12:38:32 +02:00
|
|
|
// 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);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (addp) { // Else no activity or duplicate
|
2020-05-09 12:38:32 +02:00
|
|
|
UASSERT_OBJ(needChg, addp, "needChg=0 and shouldn't need to add.");
|
|
|
|
|
if (actset.count(TraceActivityVertex::ACTIVITY_ALWAYS) != 0) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Must always set it; add to base of function
|
|
|
|
|
addToChgSub(m_chgFuncp, addp);
|
|
|
|
|
} else if (lastactp && actset == *lastactp && ifnodep) {
|
|
|
|
|
// Add to last statement we built
|
|
|
|
|
addToChgSub(ifnodep, addp);
|
|
|
|
|
} else {
|
|
|
|
|
// Build a new IF statement
|
|
|
|
|
FileLine* fl = addp->fileline();
|
|
|
|
|
AstNode* condp = NULL;
|
2020-04-14 04:51:35 +02:00
|
|
|
for (ActCodeSet::const_iterator csit = actset.begin(); csit != actset.end();
|
|
|
|
|
++csit) {
|
2019-05-19 22:13:13 +02:00
|
|
|
uint32_t acode = *csit;
|
2018-07-23 02:54:28 +02:00
|
|
|
AstNode* selp = selectActivity(fl, acode, false);
|
2020-04-14 04:51:35 +02:00
|
|
|
if (condp)
|
|
|
|
|
condp = new AstOr(fl, condp, selp);
|
|
|
|
|
else
|
|
|
|
|
condp = selp;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
AstIf* ifp = new AstIf(fl, condp, NULL, NULL);
|
2019-10-05 13:54:14 +02:00
|
|
|
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_chgFuncp->addStmtsp(ifp);
|
|
|
|
|
lastactp = &actset;
|
|
|
|
|
ifnodep = ifp;
|
2008-11-17 23:13:57 +01:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
addToChgSub(ifnodep, addp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Set in initializer
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Clear activity after tracing completes
|
2020-05-09 12:38:32 +02:00
|
|
|
FileLine*const fl = m_chgFuncp->fileline();
|
2018-07-23 02:54:28 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2020-04-14 04:51:35 +02:00
|
|
|
AstNode* clrp = new AstAssign(
|
|
|
|
|
fl, new AstVarRef(fl, m_activityVscp, true),
|
|
|
|
|
new AstConst(fl, AstConst::WidthedValue(), m_activityVscp->width(), 0));
|
2018-07-23 02:54:28 +02:00
|
|
|
m_fullFuncp->addFinalsp(clrp->cloneTree(true));
|
|
|
|
|
m_chgFuncp->addFinalsp(clrp);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TraceCFuncVertex* getCFuncVertexp(AstCFunc* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
TraceCFuncVertex* vertexp
|
|
|
|
|
= dynamic_cast<TraceCFuncVertex*>(nodep->user1u().toGraphVertex());
|
|
|
|
|
if (!vertexp) {
|
|
|
|
|
vertexp = new TraceCFuncVertex(&m_graph, nodep);
|
|
|
|
|
nodep->user1p(vertexp);
|
|
|
|
|
}
|
|
|
|
|
return vertexp;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
TraceActivityVertex* getActivityVertexp(AstNode* nodep, bool slow) {
|
2019-05-19 22:13:13 +02:00
|
|
|
TraceActivityVertex* vertexp
|
|
|
|
|
= dynamic_cast<TraceActivityVertex*>(nodep->user3u().toGraphVertex());
|
|
|
|
|
if (!vertexp) {
|
|
|
|
|
vertexp = new TraceActivityVertex(&m_graph, nodep, slow);
|
|
|
|
|
nodep->user3p(vertexp);
|
|
|
|
|
}
|
|
|
|
|
vertexp->slow(slow);
|
|
|
|
|
return vertexp;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstNetlist* nodep) VL_OVERRIDE {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_code = 1; // Multiple TopScopes will require fixing how code#s
|
|
|
|
|
// are assigned as duplicate varscopes must result in the same tracing code#.
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Add vertexes for all TRACES, and edges from VARs each trace looks at
|
|
|
|
|
m_finding = false;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Add vertexes for all CFUNCs, and edges to VARs the func sets
|
|
|
|
|
m_finding = true;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_finding = false;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Detect and remove duplicate values
|
|
|
|
|
detectDuplicates();
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Simplify it
|
2020-04-14 04:51:35 +02:00
|
|
|
if (debug() >= 6) m_graph.dumpDotFilePrefixed("trace_pre");
|
2019-05-19 22:13:13 +02:00
|
|
|
graphSimplify();
|
2020-04-14 04:51:35 +02:00
|
|
|
if (debug() >= 6) m_graph.dumpDotFilePrefixed("trace_opt");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-05-09 12:38:32 +02:00
|
|
|
// Create the activity flags
|
2019-05-19 22:13:13 +02:00
|
|
|
assignActivity();
|
2020-05-09 12:38:32 +02:00
|
|
|
|
|
|
|
|
//
|
2019-05-19 22:13:13 +02:00
|
|
|
putTracesIntoTree();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstNodeModule* nodep) VL_OVERRIDE {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->isTop()) m_topModp = nodep;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstTopScope* nodep) VL_OVERRIDE {
|
2020-05-09 12:38:32 +02:00
|
|
|
AstScope* const scopep = nodep->scopep();
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(scopep, nodep, "No scope found on top level");
|
2020-05-09 12:38:32 +02:00
|
|
|
m_topScopep = scopep;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstCCall* nodep) VL_OVERRIDE {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(8, " CCALL " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!m_finding && !nodep->user2()) {
|
|
|
|
|
// See if there are other calls in same statement list;
|
|
|
|
|
// If so, all funcs might share the same activity code
|
2020-05-09 12:38:32 +02:00
|
|
|
TraceActivityVertex* const activityVtxp
|
|
|
|
|
= getActivityVertexp(nodep, nodep->funcp()->slow());
|
2020-04-14 04:51:35 +02:00
|
|
|
for (AstNode* nextp = nodep; nextp; nextp = nextp->nextp()) {
|
2020-05-09 12:38:32 +02:00
|
|
|
if (AstCCall* const ccallp = VN_CAST(nextp, CCall)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
ccallp->user2(true); // Processed
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(8, " SubCCALL " << ccallp << endl);
|
2020-05-09 12:38:32 +02:00
|
|
|
V3GraphVertex* const ccallFuncVtxp = getCFuncVertexp(ccallp->funcp());
|
2019-05-19 22:13:13 +02:00
|
|
|
activityVtxp->slow(ccallp->funcp()->slow());
|
2018-08-25 15:52:45 +02:00
|
|
|
new V3GraphEdge(&m_graph, activityVtxp, ccallFuncVtxp, 1);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstCFunc* nodep) VL_OVERRIDE {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(8, " CFUNC " << nodep << endl);
|
2020-05-09 12:38:32 +02:00
|
|
|
if (nodep->funcType() == AstCFuncType::TRACE_FULL) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_fullFuncp = nodep;
|
|
|
|
|
} else if (nodep->funcType() == AstCFuncType::TRACE_CHANGE) {
|
|
|
|
|
m_chgFuncp = nodep;
|
|
|
|
|
}
|
2020-05-09 12:38:32 +02:00
|
|
|
V3GraphVertex* const funcVtxp = getCFuncVertexp(nodep);
|
2020-04-14 04:51:35 +02:00
|
|
|
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()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Need a non-null place to remember to later add a statement; make one
|
2020-05-09 12:38:32 +02:00
|
|
|
if (!nodep->stmtsp()) {
|
2020-04-14 04:51:35 +02:00
|
|
|
nodep->addStmtsp(
|
|
|
|
|
new AstComment(nodep->fileline(), "Tracing activity check", true));
|
2020-05-09 12:38:32 +02:00
|
|
|
}
|
|
|
|
|
V3GraphVertex* const activityVtxp
|
|
|
|
|
= getActivityVertexp(nodep->stmtsp(), nodep->slow());
|
2018-08-25 15:52:45 +02:00
|
|
|
new V3GraphEdge(&m_graph, activityVtxp, funcVtxp, 1);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_funcp = nodep;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_funcp = NULL;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstTraceInc* nodep) VL_OVERRIDE {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(8, " TRACE " << nodep << endl);
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(!m_finding, nodep, "Traces should have been removed in prev step.");
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->unlinkFrBack();
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-05-09 12:38:32 +02:00
|
|
|
V3GraphVertex* const vertexp = new TraceTraceVertex(&m_graph, nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->user1p(vertexp);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_funcp && m_chgFuncp && m_fullFuncp, nodep, "Trace not under func");
|
2019-05-19 22:13:13 +02:00
|
|
|
m_tracep = nodep;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_tracep = NULL;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstVarRef* nodep) VL_OVERRIDE {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_tracep) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(nodep->varScopep(), nodep, "No var scope?");
|
|
|
|
|
UASSERT_OBJ(!nodep->lvalue(), nodep, "Lvalue in trace? Should be const.");
|
2019-05-19 22:13:13 +02:00
|
|
|
V3GraphVertex* varVtxp = nodep->varScopep()->user1u().toGraphVertex();
|
|
|
|
|
if (!varVtxp) {
|
|
|
|
|
varVtxp = new TraceVarVertex(&m_graph, nodep->varScopep());
|
|
|
|
|
nodep->varScopep()->user1p(varVtxp);
|
|
|
|
|
}
|
2020-05-09 12:38:32 +02:00
|
|
|
V3GraphVertex* const traceVtxp = m_tracep->user1u().toGraphVertex();
|
2019-05-19 22:13:13 +02:00
|
|
|
new V3GraphEdge(&m_graph, varVtxp, traceVtxp, 1);
|
2018-10-27 23:29:00 +02:00
|
|
|
if (nodep->varp()->isPrimaryInish() // Always need to trace primary inputs
|
|
|
|
|
|| nodep->varp()->isSigPublic()) { // Or ones user can change
|
|
|
|
|
new V3GraphEdge(&m_graph, m_alwaysVtxp, traceVtxp, 1);
|
|
|
|
|
}
|
2020-04-14 04:51:35 +02:00
|
|
|
} else if (m_funcp && m_finding && nodep->lvalue()) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(nodep->varScopep(), nodep, "No var scope?");
|
2020-05-09 12:38:32 +02:00
|
|
|
V3GraphVertex* const funcVtxp = getCFuncVertexp(m_funcp);
|
|
|
|
|
V3GraphVertex* const varVtxp = nodep->varScopep()->user1u().toGraphVertex();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (varVtxp) { // else we're not tracing this signal
|
|
|
|
|
new V3GraphEdge(&m_graph, funcVtxp, varVtxp, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
//--------------------
|
2020-04-04 14:31:14 +02:00
|
|
|
virtual void visit(AstNode* nodep) VL_OVERRIDE { iterateChildren(nodep); }
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-05-09 12:38:32 +02:00
|
|
|
explicit TraceVisitor(AstNetlist* nodep)
|
|
|
|
|
: m_alwaysVtxp(new TraceActivityVertex(&m_graph, TraceActivityVertex::ACTIVITY_ALWAYS)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_funcp = NULL;
|
|
|
|
|
m_tracep = NULL;
|
|
|
|
|
m_topModp = NULL;
|
2020-05-09 12:38:32 +02:00
|
|
|
m_topScopep = NULL;
|
2019-05-19 22:13:13 +02:00
|
|
|
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;
|
2018-07-23 02:54:28 +02:00
|
|
|
m_activityNumber = 0;
|
2018-06-15 00:59:24 +02:00
|
|
|
m_code = 0;
|
2019-05-19 22:13:13 +02:00
|
|
|
m_funcNum = 0;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
virtual ~TraceVisitor() {
|
2019-05-19 22:13:13 +02:00
|
|
|
V3Stats::addStat("Tracing, Unique changing signals", m_statChgSigs);
|
|
|
|
|
V3Stats::addStat("Tracing, Unique traced signals", m_statUniqSigs);
|
|
|
|
|
V3Stats::addStat("Tracing, Unique trace codes", m_statUniqCodes);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Trace class functions
|
|
|
|
|
|
|
|
|
|
void V3Trace::traceAll(AstNetlist* nodep) {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
|
|
|
|
{ TraceVisitor visitor(nodep); } // Destruct before checking
|
2017-09-18 04:52:57 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("trace", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|