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
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2025-01-01 14:30:25 +01:00
|
|
|
// Copyright 2003-2025 by Wilson Snyder. This program is free software; you
|
2020-03-21 16:24:24 +01:00
|
|
|
// 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:
|
2020-05-09 00:42:34 +02:00
|
|
|
//
|
|
|
|
|
// 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:
|
|
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
// Pass 1:
|
2020-05-09 00:42:34 +02:00
|
|
|
// 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)
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
2020-05-09 00:42:34 +02:00
|
|
|
// Pass 2:
|
|
|
|
|
// Add edges from CFunc -> VarRef being written
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
2020-05-09 00:42:34 +02:00
|
|
|
// Finally:
|
|
|
|
|
// Process graph to determine when traced variables can change, allocate
|
|
|
|
|
// activity flags, insert nodes to set activity flags, allocate signal
|
2023-10-23 12:36:24 +02:00
|
|
|
// numbers (codes), and construct the const, full and incremental trace
|
2020-05-09 00:42:34 +02:00
|
|
|
// functions, together with all other trace support functions.
|
2008-06-10 03:25:10 +02:00
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Trace.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
|
|
|
|
#include "V3DupFinder.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3EmitCBase.h"
|
|
|
|
|
#include "V3Graph.h"
|
|
|
|
|
#include "V3Stats.h"
|
|
|
|
|
|
2021-01-11 17:23:54 +01:00
|
|
|
#include <limits>
|
2018-10-14 19:43:24 +02:00
|
|
|
#include <set>
|
|
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Graph vertexes
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class TraceActivityVertex final : public V3GraphVertex {
|
2023-09-01 00:00:53 +02:00
|
|
|
VL_RTTI_IMPL(TraceActivityVertex, V3GraphVertex)
|
2020-05-17 14:53:02 +02:00
|
|
|
AstNode* const m_insertp;
|
2022-03-27 21:27:40 +02:00
|
|
|
int32_t m_activityCode;
|
2020-04-14 04:51:35 +02:00
|
|
|
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-08-16 15:55:36 +02:00
|
|
|
: V3GraphVertex{graphp}
|
|
|
|
|
, m_insertp{nodep} {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_activityCode = 0;
|
|
|
|
|
m_slow = slow;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-03-27 21:27:40 +02:00
|
|
|
TraceActivityVertex(V3Graph* graphp, int32_t code)
|
2020-08-16 15:55:36 +02:00
|
|
|
: V3GraphVertex{graphp}
|
|
|
|
|
, m_insertp{nullptr} {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_activityCode = code;
|
|
|
|
|
m_slow = false;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~TraceActivityVertex() override = default;
|
2017-11-28 02:11:34 +01:00
|
|
|
// ACCESSORS
|
2006-08-26 13:35:28 +02:00
|
|
|
AstNode* insertp() const {
|
2024-01-06 00:00:06 +01:00
|
|
|
UASSERT(m_insertp, "Null insertp; probably called on a special always/slow");
|
2019-05-19 22:13:13 +02:00
|
|
|
return m_insertp;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
string name() const override {
|
2020-04-14 04:51:35 +02:00
|
|
|
if (activityAlways()) {
|
|
|
|
|
return "*ALWAYS*";
|
|
|
|
|
} else {
|
2022-08-30 05:05:52 +02:00
|
|
|
return std::string{slow() ? "*SLOW* " : ""} + insertp()->name();
|
2020-04-14 04:51:35 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
string dotColor() const override { return slow() ? "yellowGreen" : "green"; }
|
2022-03-27 21:27:40 +02:00
|
|
|
int32_t activityCode() const { return m_activityCode; }
|
2020-04-14 04:51:35 +02:00
|
|
|
bool activityAlways() const { return activityCode() == ACTIVITY_ALWAYS; }
|
2020-05-17 17:01:19 +02:00
|
|
|
bool activitySlow() const { return activityCode() == ACTIVITY_SLOW; }
|
2022-03-27 21:27:40 +02:00
|
|
|
void activityCode(int32_t code) { m_activityCode = code; }
|
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
|
|
|
};
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class TraceCFuncVertex final : public V3GraphVertex {
|
2023-09-01 00:00:53 +02:00
|
|
|
VL_RTTI_IMPL(TraceCFuncVertex, V3GraphVertex)
|
2021-11-13 19:50:44 +01:00
|
|
|
AstCFunc* const 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-08-16 15:55:36 +02:00
|
|
|
: V3GraphVertex{graphp}
|
|
|
|
|
, m_nodep{nodep} {}
|
2022-09-16 12:22:11 +02:00
|
|
|
~TraceCFuncVertex() override = default;
|
2017-11-28 02:11:34 +01:00
|
|
|
// ACCESSORS
|
2006-08-26 13:35:28 +02:00
|
|
|
AstCFunc* nodep() const { return m_nodep; }
|
2022-09-16 12:22:11 +02:00
|
|
|
string name() const override { return nodep()->name(); }
|
|
|
|
|
string dotColor() const override { return "yellow"; }
|
|
|
|
|
FileLine* fileline() const override { return nodep()->fileline(); }
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class TraceTraceVertex final : public V3GraphVertex {
|
2023-09-01 00:00:53 +02:00
|
|
|
VL_RTTI_IMPL(TraceTraceVertex, V3GraphVertex)
|
2020-05-09 00:42:34 +02:00
|
|
|
AstTraceDecl* const m_nodep; // TRACEINC this represents
|
2020-08-15 16:12:55 +02:00
|
|
|
// nullptr, or other vertex with the real code() that duplicates this one
|
2020-08-16 15:55:36 +02:00
|
|
|
TraceTraceVertex* m_duplicatep = nullptr;
|
2020-04-14 04:51:35 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2020-05-09 00:42:34 +02:00
|
|
|
TraceTraceVertex(V3Graph* graphp, AstTraceDecl* nodep)
|
2020-08-16 15:55:36 +02:00
|
|
|
: V3GraphVertex{graphp}
|
|
|
|
|
, m_nodep{nodep} {}
|
2022-09-16 12:22:11 +02:00
|
|
|
~TraceTraceVertex() override = default;
|
2017-11-28 02:11:34 +01:00
|
|
|
// ACCESSORS
|
2020-05-09 00:42:34 +02:00
|
|
|
AstTraceDecl* nodep() const { return m_nodep; }
|
2022-09-16 12:22:11 +02:00
|
|
|
string name() const override { return nodep()->name(); }
|
|
|
|
|
string dotColor() const override { return "red"; }
|
|
|
|
|
FileLine* fileline() const override { 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
|
|
|
};
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class TraceVarVertex final : public V3GraphVertex {
|
2023-09-01 00:00:53 +02:00
|
|
|
VL_RTTI_IMPL(TraceVarVertex, V3GraphVertex)
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVarScope* const m_nodep;
|
2020-04-14 04:51:35 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
|
|
|
|
TraceVarVertex(V3Graph* graphp, AstVarScope* nodep)
|
2020-08-16 15:55:36 +02:00
|
|
|
: V3GraphVertex{graphp}
|
|
|
|
|
, m_nodep{nodep} {}
|
2022-09-16 12:22:11 +02:00
|
|
|
~TraceVarVertex() override = default;
|
2017-11-28 02:11:34 +01:00
|
|
|
// ACCESSORS
|
2006-08-26 13:35:28 +02:00
|
|
|
AstVarScope* nodep() const { return m_nodep; }
|
2022-09-16 12:22:11 +02:00
|
|
|
string name() const override { return nodep()->name(); }
|
|
|
|
|
string dotColor() const override { return "skyblue"; }
|
|
|
|
|
FileLine* fileline() const override { return nodep()->fileline(); }
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Trace state, as a visitor of each AstNode
|
|
|
|
|
|
2023-03-18 17:05:29 +01:00
|
|
|
class TraceVisitor final : public VNVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
// NODE STATE
|
2021-05-21 02:41:46 +02:00
|
|
|
// V3Hasher in V3DupFinder
|
|
|
|
|
// Ast*::user4() // V3Hasher 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
|
2020-05-09 00:42:34 +02:00
|
|
|
// AstTraceDecl::user1() // V3GraphVertex* for this node
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstVarScope::user1() // V3GraphVertex* for this node
|
2022-10-12 11:19:21 +02:00
|
|
|
// AstStmtExpr::user2() // bool; walked next list for other ccalls
|
2019-05-19 22:13:13 +02:00
|
|
|
// Ast*::user3() // TraceActivityVertex* for this node
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser1InUse m_inuser1;
|
|
|
|
|
const VNUser2InUse m_inuser2;
|
|
|
|
|
const VNUser3InUse m_inuser3;
|
|
|
|
|
// VNUser4InUse In V3Hasher via V3DupFinder
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// STATE
|
2020-08-16 15:55:36 +02:00
|
|
|
AstNodeModule* m_topModp = nullptr; // Module to add variables to
|
2021-10-17 11:29:17 +02:00
|
|
|
AstScope* const m_topScopep = v3Global.rootp()->topScopep()->scopep(); // The top AstScope
|
2020-10-31 13:59:35 +01:00
|
|
|
AstCFunc* m_cfuncp = nullptr; // C function adding to graph
|
2021-06-22 14:50:21 +02:00
|
|
|
AstCFunc* m_regFuncp = nullptr; // Trace registration function
|
2024-08-29 02:45:51 +02:00
|
|
|
AstCFunc* m_actAllFuncp = nullptr; // Set all activity function
|
2020-08-16 15:55:36 +02:00
|
|
|
AstTraceDecl* m_tracep = nullptr; // Trace function adding to graph
|
|
|
|
|
AstVarScope* m_activityVscp = nullptr; // Activity variable
|
|
|
|
|
uint32_t m_activityNumber = 0; // Count of fields in activity variable
|
|
|
|
|
uint32_t m_code = 0; // Trace ident code# being assigned
|
2020-04-14 04:51:35 +02:00
|
|
|
V3Graph m_graph; // Var/CFunc tracking
|
2020-05-09 12:38:32 +02:00
|
|
|
TraceActivityVertex* const m_alwaysVtxp; // "Always trace" vertex
|
2020-08-16 15:55:36 +02:00
|
|
|
bool m_finding = false; // Pass one of algorithm?
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-05-29 20:08:39 +02:00
|
|
|
// Trace parallelism. Only VCD tracing can be parallelized at this time.
|
|
|
|
|
const uint32_t m_parallelism
|
|
|
|
|
= v3Global.opt.useTraceParallel() ? static_cast<uint32_t>(v3Global.opt.threads()) : 1;
|
|
|
|
|
|
2024-08-29 02:45:51 +02:00
|
|
|
VDouble0 m_statSetters; // Statistic tracking
|
|
|
|
|
VDouble0 m_statSettersSlow; // Statistic tracking
|
2019-10-05 13:54:14 +02:00
|
|
|
VDouble0 m_statUniqCodes; // Statistic tracking
|
2024-08-29 02:45:51 +02:00
|
|
|
VDouble0 m_statUniqSigs; // Statistic tracking
|
2008-11-17 23:13:57 +01:00
|
|
|
|
2020-05-09 00:42:34 +02:00
|
|
|
// All activity numbers applying to a given trace
|
2021-03-13 00:10:45 +01:00
|
|
|
using ActCodeSet = std::set<uint32_t>;
|
2020-05-09 00:42:34 +02:00
|
|
|
// For activity set, what traces apply
|
2021-03-13 00:10:45 +01:00
|
|
|
using TraceVec = std::multimap<ActCodeSet, TraceTraceVertex*>;
|
2020-05-09 00:42:34 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// METHODS
|
2009-01-21 22:56:50 +01:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
void detectDuplicates() {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "Finding duplicates");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Note uses user4
|
2021-05-21 02:41:46 +02:00
|
|
|
V3DupFinder dupFinder; // Duplicate code detection
|
2023-12-12 20:30:18 +01:00
|
|
|
// Hash all of the traced values and find if there are any duplicates
|
2024-03-26 00:06:25 +01:00
|
|
|
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
|
|
|
|
if (TraceTraceVertex* const vvertexp = vtx.cast<TraceTraceVertex>()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstTraceDecl* const nodep = vvertexp->nodep();
|
2023-12-12 20:30:18 +01:00
|
|
|
UASSERT_OBJ(!vvertexp->duplicatep(), nodep, "Should not be a duplicate");
|
|
|
|
|
const auto dupit = dupFinder.findDuplicate(nodep->valuep());
|
|
|
|
|
if (dupit == dupFinder.end()) {
|
|
|
|
|
dupFinder.insert(nodep->valuep());
|
|
|
|
|
} else {
|
|
|
|
|
const AstTraceDecl* const dupDeclp = VN_AS(dupit->second->backp(), TraceDecl);
|
|
|
|
|
UASSERT_OBJ(dupDeclp, nodep, "Trace duplicate of wrong type");
|
|
|
|
|
TraceTraceVertex* const dupvertexp
|
|
|
|
|
= dupDeclp->user1u().toGraphVertex()->cast<TraceTraceVertex>();
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " Orig " << nodep);
|
|
|
|
|
UINFO(8, " dup " << dupDeclp);
|
2023-12-12 20:30:18 +01:00
|
|
|
// Mark the hashed node as the original and our
|
|
|
|
|
// iterating node as duplicated
|
|
|
|
|
vvertexp->duplicatep(dupvertexp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-12 02:58:03 +02:00
|
|
|
if (dumpLevel() || debug() >= 9)
|
|
|
|
|
dupFinder.dumpFile(v3Global.debugFilename("trace") + ".hash", false);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-17 14:53:02 +02:00
|
|
|
void graphSimplify(bool initial) {
|
|
|
|
|
if (initial) {
|
|
|
|
|
// Remove all variable nodes
|
2024-03-26 00:06:25 +01:00
|
|
|
for (V3GraphVertex* const vtxp : m_graph.vertices().unlinkable()) {
|
|
|
|
|
if (TraceVarVertex* const vvertexp = vtxp->cast<TraceVarVertex>()) {
|
2020-05-17 14:53:02 +02:00
|
|
|
vvertexp->rerouteEdges(&m_graph);
|
|
|
|
|
vvertexp->unlinkDelete(&m_graph);
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-05-17 14:53:02 +02:00
|
|
|
// Remove multiple variables connecting funcs to traces
|
|
|
|
|
// We do this twice, as then we have fewer edges to multiply out in the below
|
|
|
|
|
// expansion.
|
2023-10-29 02:11:28 +02:00
|
|
|
m_graph.removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue);
|
2020-05-17 14:53:02 +02:00
|
|
|
// Remove all Cfunc nodes
|
2024-03-26 00:06:25 +01:00
|
|
|
for (V3GraphVertex* const vtxp : m_graph.vertices().unlinkable()) {
|
|
|
|
|
if (TraceCFuncVertex* const vvertexp = vtxp->cast<TraceCFuncVertex>()) {
|
2020-05-17 14:53:02 +02:00
|
|
|
vvertexp->rerouteEdges(&m_graph);
|
|
|
|
|
vvertexp->unlinkDelete(&m_graph);
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove multiple variables connecting funcs to traces
|
2023-10-29 02:11:28 +02:00
|
|
|
m_graph.removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue);
|
2019-05-19 22:13:13 +02:00
|
|
|
|
|
|
|
|
// If there are any edges from a always, keep only the always
|
2024-03-26 00:06:25 +01:00
|
|
|
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
|
|
|
|
if (TraceTraceVertex* const vvertexp = vtx.cast<TraceTraceVertex>()) {
|
2020-05-09 12:38:32 +02:00
|
|
|
// Search for the incoming always edge
|
2020-08-15 16:12:55 +02:00
|
|
|
const V3GraphEdge* alwaysEdgep = nullptr;
|
2024-03-26 00:06:25 +01:00
|
|
|
for (const V3GraphEdge& edge : vvertexp->inEdges()) {
|
2020-05-09 12:38:32 +02:00
|
|
|
const TraceActivityVertex* const actVtxp
|
2024-03-26 00:06:25 +01:00
|
|
|
= edge.fromp()->as<const TraceActivityVertex>();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (actVtxp->activityAlways()) {
|
2024-03-26 00:06:25 +01:00
|
|
|
alwaysEdgep = &edge;
|
2019-05-19 22:13:13 +02:00
|
|
|
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) {
|
2024-03-26 00:06:25 +01:00
|
|
|
for (V3GraphEdge* const edgep : vvertexp->inEdges().unlinkable()) {
|
|
|
|
|
if (edgep != alwaysEdgep) VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Activity points with no outputs can be removed
|
2024-03-26 00:06:25 +01:00
|
|
|
for (V3GraphVertex* const vtxp : m_graph.vertices().unlinkable()) {
|
|
|
|
|
if (TraceActivityVertex* const aVtxp = vtxp->cast<TraceActivityVertex>()) {
|
2020-05-17 14:53:02 +02:00
|
|
|
// Leave in the always vertex for later use.
|
2024-03-26 00:06:25 +01:00
|
|
|
if (aVtxp != m_alwaysVtxp && aVtxp->outEmpty()) {
|
|
|
|
|
VL_DO_DANGLING(aVtxp->unlinkDelete(&m_graph), aVtxp);
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-17 14:53:02 +02:00
|
|
|
uint32_t assignactivityNumbers() {
|
2020-05-17 17:01:19 +02:00
|
|
|
uint32_t activityNumber = 1; // Note 0 indicates "slow" only
|
2024-03-26 00:06:25 +01:00
|
|
|
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
|
|
|
|
if (TraceActivityVertex* const vvertexp = vtx.cast<TraceActivityVertex>()) {
|
2020-05-17 14:53:02 +02:00
|
|
|
if (vvertexp != m_alwaysVtxp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (vvertexp->slow()) {
|
|
|
|
|
vvertexp->activityCode(TraceActivityVertex::ACTIVITY_SLOW);
|
|
|
|
|
} else {
|
2020-05-17 14:53:02 +02:00
|
|
|
vvertexp->activityCode(activityNumber++);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-17 14:53:02 +02:00
|
|
|
return activityNumber;
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2023-10-23 12:36:24 +02:00
|
|
|
void sortTraces(TraceVec& traces, uint32_t& nNonConstCodes) {
|
2020-05-17 14:53:02 +02:00
|
|
|
// Populate sort structure
|
|
|
|
|
traces.clear();
|
2023-10-23 12:36:24 +02:00
|
|
|
nNonConstCodes = 0;
|
2024-03-26 00:06:25 +01:00
|
|
|
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
|
|
|
|
if (TraceTraceVertex* const vtxp = vtx.cast<TraceTraceVertex>()) {
|
2020-05-17 14:53:02 +02:00
|
|
|
ActCodeSet actSet;
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " Add to sort: " << vtxp);
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, vtxp->nodep(), "", "trnode");
|
2024-03-26 00:06:25 +01:00
|
|
|
for (const V3GraphEdge& edge : vtxp->inEdges()) {
|
2020-05-17 14:53:02 +02:00
|
|
|
const TraceActivityVertex* const cfvertexp
|
2024-03-26 00:06:25 +01:00
|
|
|
= edge.fromp()->cast<const TraceActivityVertex>();
|
2020-05-17 14:53:02 +02:00
|
|
|
UASSERT_OBJ(cfvertexp, vtxp->nodep(),
|
|
|
|
|
"Should have been function pointing to this trace");
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " Activity: " << cfvertexp);
|
2020-05-17 14:53:02 +02:00
|
|
|
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
|
2023-10-23 12:36:24 +02:00
|
|
|
if (!vtxp->duplicatep() && !actSet.empty())
|
|
|
|
|
nNonConstCodes += vtxp->nodep()->codeInc();
|
2020-05-17 17:01:19 +02:00
|
|
|
if (actSet.empty()) {
|
|
|
|
|
// If a trace doesn't have activity, it's constant, and we
|
|
|
|
|
// don't need to track changes on it.
|
|
|
|
|
actSet.insert(TraceActivityVertex::ACTIVITY_NEVER);
|
|
|
|
|
} else if (actSet.count(TraceActivityVertex::ACTIVITY_SLOW) && actSet.size() > 1) {
|
|
|
|
|
// If a trace depends on the slow flag as well as other
|
|
|
|
|
// flags, remove the dependency on the slow flag. We will
|
|
|
|
|
// make slow routines set all activity flags.
|
|
|
|
|
actSet.erase(TraceActivityVertex::ACTIVITY_SLOW);
|
|
|
|
|
}
|
2020-12-19 00:24:47 +01:00
|
|
|
traces.emplace(actSet, vtxp);
|
2020-05-17 14:53:02 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-09 12:38:32 +02:00
|
|
|
|
2020-05-17 14:53:02 +02:00
|
|
|
void graphOptimize() {
|
|
|
|
|
// Assign initial activity numbers to activity vertices
|
|
|
|
|
assignactivityNumbers();
|
|
|
|
|
|
|
|
|
|
// Sort the traces by activity sets
|
|
|
|
|
TraceVec traces;
|
2020-06-02 05:16:02 +02:00
|
|
|
uint32_t unused1;
|
2023-10-23 12:36:24 +02:00
|
|
|
sortTraces(traces, unused1);
|
2020-05-17 14:53:02 +02:00
|
|
|
|
|
|
|
|
// 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
|
2020-08-16 17:43:49 +02:00
|
|
|
auto it = traces.begin();
|
|
|
|
|
const auto end = traces.end();
|
2020-05-17 14:53:02 +02:00
|
|
|
while (it != end) {
|
2020-08-16 17:43:49 +02:00
|
|
|
auto head = it;
|
2020-05-17 17:01:19 +02:00
|
|
|
// Approximate the complexity of the value change check
|
|
|
|
|
uint32_t complexity = 0;
|
2020-05-17 14:53:02 +02:00
|
|
|
const ActCodeSet& actSet = it->first;
|
|
|
|
|
for (; it != end && it->first == actSet; ++it) {
|
2020-05-17 17:01:19 +02:00
|
|
|
if (!it->second->duplicatep()) {
|
|
|
|
|
uint32_t cost = 0;
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstTraceDecl* const declp = it->second->nodep();
|
2022-05-29 20:08:39 +02:00
|
|
|
// The number of comparisons required by bufp->chg*
|
2020-05-17 17:01:19 +02:00
|
|
|
cost += declp->isWide() ? declp->codeInc() : 1;
|
|
|
|
|
// Arrays are traced by element
|
|
|
|
|
cost *= declp->arrayRange().ranged() ? declp->arrayRange().elements() : 1;
|
|
|
|
|
// Note: Experiments factoring in the size of declp->valuep()
|
|
|
|
|
// showed no benefit in tracing speed, even for large trees,
|
2022-12-26 10:30:41 +01:00
|
|
|
// so we will leave those out for now.
|
2020-05-17 17:01:19 +02:00
|
|
|
complexity += cost;
|
|
|
|
|
}
|
2020-05-17 14:53:02 +02:00
|
|
|
}
|
|
|
|
|
// 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;
|
2020-05-17 17:01:19 +02:00
|
|
|
if (actSet.count(TraceActivityVertex::ACTIVITY_SLOW)) continue;
|
2020-05-17 14:53:02 +02:00
|
|
|
// If the value comparisons are cheaper to perform than checking the
|
|
|
|
|
// activity flags make the signals always traced. Note this cost
|
|
|
|
|
// equation is heuristic.
|
2020-05-17 17:01:19 +02:00
|
|
|
if (complexity <= actSet.size() * 2) {
|
2020-05-17 14:53:02 +02:00
|
|
|
for (; head != it; ++head) {
|
2022-11-20 23:40:38 +01:00
|
|
|
new V3GraphEdge{&m_graph, m_alwaysVtxp, head->second, 1};
|
2020-05-17 14:53:02 +02:00
|
|
|
}
|
|
|
|
|
}
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
2020-05-17 14:53:02 +02:00
|
|
|
|
|
|
|
|
graphSimplify(false);
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* selectActivity(FileLine* flp, uint32_t acode, const VAccess& access) {
|
2022-11-20 23:40:38 +01:00
|
|
|
return new AstArraySel(flp, new AstVarRef{flp, m_activityVscp, access}, acode);
|
2020-05-17 14:53:02 +02:00
|
|
|
}
|
|
|
|
|
|
2024-08-29 02:45:51 +02:00
|
|
|
AstNode* newActivitySetter(AstNode* insertp, uint32_t code) {
|
|
|
|
|
++m_statSetters;
|
2020-05-17 17:01:19 +02:00
|
|
|
FileLine* const fl = insertp->fileline();
|
2022-11-20 23:40:38 +01:00
|
|
|
AstAssign* const setterp = new AstAssign{fl, selectActivity(fl, code, VAccess::WRITE),
|
|
|
|
|
new AstConst{fl, AstConst::BitTrue{}}};
|
2024-08-29 02:45:51 +02:00
|
|
|
return setterp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstNode* newActivityAll(AstNode* insertp) {
|
|
|
|
|
++m_statSettersSlow;
|
|
|
|
|
if (!m_actAllFuncp) {
|
|
|
|
|
FileLine* const flp = m_topScopep->fileline();
|
|
|
|
|
AstCFunc* const funcp = new AstCFunc{flp, "__Vm_traceActivitySetAll", m_topScopep};
|
|
|
|
|
funcp->slow(true);
|
|
|
|
|
funcp->isStatic(false);
|
|
|
|
|
funcp->isLoose(true);
|
|
|
|
|
m_topScopep->addBlocksp(funcp);
|
|
|
|
|
for (uint32_t code = 0; code < m_activityNumber; ++code) {
|
|
|
|
|
AstNode* const setterp = newActivitySetter(insertp, code);
|
|
|
|
|
funcp->addStmtsp(setterp);
|
2022-10-20 12:28:55 +02:00
|
|
|
}
|
2024-08-29 02:45:51 +02:00
|
|
|
m_actAllFuncp = funcp;
|
2020-05-17 17:01:19 +02:00
|
|
|
}
|
2024-08-29 02:45:51 +02:00
|
|
|
AstCCall* const callp = new AstCCall{insertp->fileline(), m_actAllFuncp};
|
|
|
|
|
callp->dtypeSetVoid();
|
|
|
|
|
return callp->makeStmt();
|
2020-05-17 17:01:19 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-17 14:53:02 +02:00
|
|
|
void createActivityFlags() {
|
|
|
|
|
// Assign final activity numbers
|
|
|
|
|
m_activityNumber = assignactivityNumbers();
|
|
|
|
|
|
|
|
|
|
// Create an array of bytes, not a bit vector, as they can be set
|
2020-05-17 17:01:19 +02:00
|
|
|
// atomically by mtasks, and are cheaper to set (no need for
|
2020-05-17 14:53:02 +02:00
|
|
|
// 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();
|
2022-10-16 00:37:44 +02:00
|
|
|
AstNodeDType* const newScalarDtp = new AstBasicDType{flp, VFlagBitPacked{}, 1};
|
2020-05-17 14:53:02 +02:00
|
|
|
v3Global.rootp()->typeTablep()->addTypesp(newScalarDtp);
|
2020-12-07 03:13:56 +01:00
|
|
|
AstRange* const newArange
|
|
|
|
|
= new AstRange{flp, VNumRange{static_cast<int>(m_activityNumber) - 1, 0}};
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNodeDType* const newArrDtp = new AstUnpackArrayDType{flp, newScalarDtp, newArange};
|
2020-05-17 14:53:02 +02:00
|
|
|
v3Global.rootp()->typeTablep()->addTypesp(newArrDtp);
|
|
|
|
|
AstVar* const newvarp
|
2022-11-20 23:40:38 +01:00
|
|
|
= new AstVar{flp, VVarType::MODULETEMP, "__Vm_traceActivity", newArrDtp};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_topModp->addStmtsp(newvarp);
|
2022-11-20 23:40:38 +01:00
|
|
|
AstVarScope* const newvscp = new AstVarScope{flp, m_topScopep, newvarp};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_topScopep->addVarsp(newvscp);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_activityVscp = newvscp;
|
|
|
|
|
|
2020-05-17 14:53:02 +02:00
|
|
|
// Insert activity setters
|
2024-03-26 00:06:25 +01:00
|
|
|
for (const V3GraphVertex& vtx : m_graph.vertices()) {
|
|
|
|
|
if (const TraceActivityVertex* const vtxp = vtx.cast<const TraceActivityVertex>()) {
|
2024-08-29 02:45:51 +02:00
|
|
|
AstNode* setterp = nullptr;
|
2020-05-17 17:01:19 +02:00
|
|
|
if (vtxp->activitySlow()) {
|
2024-08-29 02:45:51 +02:00
|
|
|
setterp = newActivityAll(vtxp->insertp());
|
2020-05-17 17:01:19 +02:00
|
|
|
// Just set all flags in slow code as it should be rare.
|
|
|
|
|
// This will be rolled up into a loop by V3Reloop.
|
|
|
|
|
} else if (!vtxp->activityAlways()) {
|
2024-08-29 02:45:51 +02:00
|
|
|
setterp = newActivitySetter(vtxp->insertp(), vtxp->activityCode());
|
|
|
|
|
}
|
|
|
|
|
if (setterp) {
|
|
|
|
|
AstNode* const insertp = vtxp->insertp();
|
|
|
|
|
if (AstStmtExpr* const stmtp = VN_CAST(insertp, StmtExpr)) {
|
|
|
|
|
stmtp->addNextHere(setterp);
|
|
|
|
|
} else if (AstCFunc* const funcp = VN_CAST(insertp, CFunc)) {
|
|
|
|
|
// If there are awaits, insert the setter after each await
|
|
|
|
|
if (funcp->isCoroutine() && funcp->stmtsp()) {
|
|
|
|
|
funcp->stmtsp()->foreachAndNext([&](AstCAwait* awaitp) {
|
|
|
|
|
AstNode* stmtp = awaitp->backp();
|
|
|
|
|
while (VN_IS(stmtp, NodeExpr)) stmtp = stmtp->backp();
|
|
|
|
|
stmtp->addNextHere(setterp->cloneTree(false));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
funcp->addStmtsp(setterp);
|
|
|
|
|
} else {
|
|
|
|
|
insertp->v3fatalSrc("Bad trace activity vertex");
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2023-10-23 17:01:55 +02:00
|
|
|
AstCFunc* newCFunc(VTraceType traceType, AstCFunc* topFuncp, uint32_t funcNum,
|
2023-10-23 12:36:24 +02:00
|
|
|
uint32_t baseCode = 0) {
|
2020-05-09 00:42:34 +02:00
|
|
|
// Create new function
|
2021-06-22 14:50:21 +02:00
|
|
|
const bool isTopFunc = topFuncp == nullptr;
|
2023-10-23 17:01:55 +02:00
|
|
|
std::string funcName;
|
|
|
|
|
if (isTopFunc) {
|
|
|
|
|
if (traceType == VTraceType::CONSTANT) {
|
|
|
|
|
funcName = "trace_const";
|
|
|
|
|
} else if (traceType == VTraceType::FULL) {
|
|
|
|
|
funcName = "trace_full";
|
|
|
|
|
} else {
|
|
|
|
|
funcName = "trace_chg";
|
|
|
|
|
}
|
2023-10-23 12:36:24 +02:00
|
|
|
} else {
|
2023-10-23 17:01:55 +02:00
|
|
|
funcName = topFuncp->name();
|
|
|
|
|
funcName += "_sub";
|
2023-10-23 12:36:24 +02:00
|
|
|
}
|
2023-10-23 17:01:55 +02:00
|
|
|
funcName += "_";
|
|
|
|
|
funcName += cvtToStr(funcNum);
|
2021-06-22 14:50:21 +02:00
|
|
|
|
2020-05-09 00:42:34 +02:00
|
|
|
FileLine* const flp = m_topScopep->fileline();
|
2023-10-23 17:01:55 +02:00
|
|
|
AstCFunc* const funcp = new AstCFunc{flp, funcName, m_topScopep};
|
2021-06-22 14:50:21 +02:00
|
|
|
funcp->isTrace(true);
|
2021-06-13 15:33:11 +02:00
|
|
|
funcp->dontCombine(true);
|
2021-06-22 14:50:21 +02:00
|
|
|
funcp->isLoose(true);
|
2023-10-23 12:36:24 +02:00
|
|
|
funcp->slow(traceType != VTraceType::CHANGE);
|
2021-06-22 14:50:21 +02:00
|
|
|
funcp->isStatic(isTopFunc);
|
|
|
|
|
// Add it to top scope
|
2022-09-15 20:43:56 +02:00
|
|
|
m_topScopep->addBlocksp(funcp);
|
2023-10-23 12:36:24 +02:00
|
|
|
const std::string bufArg
|
|
|
|
|
= v3Global.opt.traceClassBase()
|
|
|
|
|
+ "::" + (v3Global.opt.useTraceOffload() ? "OffloadBuffer" : "Buffer") + "* bufp";
|
2021-06-13 15:33:11 +02:00
|
|
|
if (isTopFunc) {
|
2021-06-22 14:50:21 +02:00
|
|
|
// Top functions
|
2023-10-23 12:36:24 +02:00
|
|
|
funcp->argTypes("void* voidSelf, " + bufArg);
|
2025-10-21 17:37:32 +02:00
|
|
|
funcp->addStmtsp(new AstCStmt{flp, EmitCUtil::voidSelfAssign(m_topModp)});
|
|
|
|
|
funcp->addStmtsp(new AstCStmt{flp, EmitCUtil::symClassAssign()});
|
2021-06-22 14:50:21 +02:00
|
|
|
// Add global activity check to change dump functions
|
2023-10-23 12:36:24 +02:00
|
|
|
if (traceType == VTraceType::CHANGE) { //
|
2025-10-21 17:37:32 +02:00
|
|
|
funcp->addStmtsp(
|
|
|
|
|
new AstCStmt{flp, "if (VL_UNLIKELY(!vlSymsp->__Vm_activity)) return;"});
|
2021-06-22 14:50:21 +02:00
|
|
|
}
|
|
|
|
|
// Register function
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
AstCStmt* const cstmtp = new AstCStmt{flp};
|
|
|
|
|
m_regFuncp->addStmtsp(cstmtp);
|
2023-10-23 12:36:24 +02:00
|
|
|
if (traceType == VTraceType::CONSTANT) {
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
cstmtp->add("tracep->addConstCb(");
|
|
|
|
|
cstmtp->add(new AstAddrOfCFunc{flp, funcp});
|
|
|
|
|
cstmtp->add(", " + std::to_string(funcNum) + ", vlSelf);");
|
2023-10-23 12:36:24 +02:00
|
|
|
} else if (traceType == VTraceType::FULL) {
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
cstmtp->add("tracep->addFullCb(");
|
|
|
|
|
cstmtp->add(new AstAddrOfCFunc{flp, funcp});
|
|
|
|
|
cstmtp->add(", " + std::to_string(funcNum) + ", vlSelf);");
|
2021-06-22 14:50:21 +02:00
|
|
|
} else {
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
cstmtp->add("tracep->addChgCb(");
|
|
|
|
|
cstmtp->add(new AstAddrOfCFunc{flp, funcp});
|
|
|
|
|
cstmtp->add(", " + std::to_string(funcNum) + ", vlSelf);");
|
2021-06-22 14:50:21 +02:00
|
|
|
}
|
2021-06-13 15:33:11 +02:00
|
|
|
} else {
|
2021-06-22 14:50:21 +02:00
|
|
|
// Sub functions
|
2023-10-23 12:36:24 +02:00
|
|
|
funcp->argTypes(bufArg);
|
2021-06-22 14:50:21 +02:00
|
|
|
// Setup base references. Note in rare occasions we can end up with an empty trace
|
|
|
|
|
// sub function, hence the VL_ATTR_UNUSED attributes.
|
2023-10-23 12:36:24 +02:00
|
|
|
if (traceType != VTraceType::CHANGE) {
|
2021-06-22 14:50:21 +02:00
|
|
|
// Full dump sub function
|
2025-10-21 17:37:32 +02:00
|
|
|
funcp->addStmtsp(new AstCStmt{flp, //
|
|
|
|
|
"uint32_t* const oldp VL_ATTR_UNUSED = "
|
|
|
|
|
"bufp->oldp(vlSymsp->__Vm_baseCode);\n"});
|
2020-05-09 00:42:34 +02:00
|
|
|
} else {
|
2021-06-22 14:50:21 +02:00
|
|
|
// Change dump sub function
|
2022-05-29 20:08:39 +02:00
|
|
|
if (v3Global.opt.useTraceOffload()) {
|
2025-10-21 17:37:32 +02:00
|
|
|
funcp->addStmtsp(new AstCStmt{flp, //
|
|
|
|
|
"const uint32_t base VL_ATTR_UNUSED = "
|
|
|
|
|
"vlSymsp->__Vm_baseCode + "
|
|
|
|
|
+ cvtToStr(baseCode) + ";\n"});
|
|
|
|
|
funcp->addStmtsp(
|
|
|
|
|
new AstCStmt{flp, "(void)bufp; // Prevent unused variable warning\n"});
|
2021-06-22 14:50:21 +02:00
|
|
|
} else {
|
2025-10-21 17:37:32 +02:00
|
|
|
funcp->addStmtsp(new AstCStmt{flp, //
|
|
|
|
|
"uint32_t* const oldp VL_ATTR_UNUSED = "
|
|
|
|
|
"bufp->oldp(vlSymsp->__Vm_baseCode + "
|
|
|
|
|
+ cvtToStr(baseCode) + ");\n"});
|
2021-06-22 14:50:21 +02:00
|
|
|
}
|
2020-05-09 00:42:34 +02:00
|
|
|
}
|
2021-06-22 14:50:21 +02:00
|
|
|
// Add call to top function
|
2022-11-20 23:40:38 +01:00
|
|
|
AstCCall* const callp = new AstCCall{funcp->fileline(), funcp};
|
2022-10-12 11:19:21 +02:00
|
|
|
callp->dtypeSetVoid();
|
2022-05-29 20:08:39 +02:00
|
|
|
callp->argTypes("bufp");
|
2022-10-12 11:19:21 +02:00
|
|
|
topFuncp->addStmtsp(callp->makeStmt());
|
2020-05-09 00:42:34 +02:00
|
|
|
}
|
|
|
|
|
// Done
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(5, " newCFunc " << funcp);
|
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
|
|
|
|
2023-10-23 12:36:24 +02:00
|
|
|
void createConstTraceFunctions(const TraceVec& traces) {
|
2020-05-09 00:42:34 +02:00
|
|
|
const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace()
|
|
|
|
|
: std::numeric_limits<int>::max();
|
|
|
|
|
|
2023-10-23 12:36:24 +02:00
|
|
|
AstCFunc* const topFuncp = newCFunc(VTraceType::CONSTANT, nullptr, 0);
|
2023-10-23 17:01:55 +02:00
|
|
|
uint32_t subFuncNum = 0;
|
2023-10-23 12:36:24 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2020-05-09 00:42:34 +02:00
|
|
|
|
2023-10-23 12:36:24 +02:00
|
|
|
// 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);
|
2023-12-12 20:30:18 +01:00
|
|
|
const uint32_t codeInc = declp->codeInc();
|
|
|
|
|
m_code += codeInc;
|
|
|
|
|
m_statUniqCodes += codeInc;
|
2023-10-23 12:36:24 +02:00
|
|
|
++m_statUniqSigs;
|
2020-05-09 00:42:34 +02:00
|
|
|
|
2023-10-23 12:36:24 +02:00
|
|
|
// 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::CONSTANT, topFuncp, subFuncNum);
|
|
|
|
|
++subFuncNum;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2023-10-23 12:36:24 +02:00
|
|
|
FileLine* const flp = declp->fileline();
|
|
|
|
|
AstTraceInc* const incp = new AstTraceInc{flp, declp, VTraceType::CONSTANT};
|
|
|
|
|
subFuncp->addStmtsp(incp);
|
|
|
|
|
subStmts += incp->nodeCount();
|
2020-05-09 00:42:34 +02:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-05-09 00:42:34 +02:00
|
|
|
}
|
|
|
|
|
|
2023-10-23 12:36:24 +02:00
|
|
|
void createNonConstTraceFunctions(const TraceVec& traces, uint32_t nAllCodes,
|
|
|
|
|
uint32_t parallelism) {
|
2020-05-09 00:42:34 +02:00
|
|
|
const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace()
|
|
|
|
|
: std::numeric_limits<int>::max();
|
2023-10-23 17:01:55 +02:00
|
|
|
|
|
|
|
|
// pre-incremented, so starts at 0
|
|
|
|
|
uint32_t topFuncNum = std::numeric_limits<uint32_t>::max();
|
2020-05-09 00:42:34 +02:00
|
|
|
TraceVec::const_iterator it = traces.begin();
|
|
|
|
|
while (it != traces.end()) {
|
2023-10-23 12:36:24 +02:00
|
|
|
AstCFunc* topFulFuncp = nullptr;
|
|
|
|
|
AstCFunc* topChgFuncp = nullptr;
|
|
|
|
|
AstCFunc* subFulFuncp = nullptr;
|
|
|
|
|
AstCFunc* subChgFuncp = nullptr;
|
2023-10-23 17:01:55 +02:00
|
|
|
uint32_t subFuncNum = 0;
|
2020-05-09 00:42:34 +02:00
|
|
|
int subStmts = 0;
|
2023-10-23 12:36:24 +02:00
|
|
|
const uint32_t maxCodes = std::max((nAllCodes + parallelism - 1) / parallelism, 1U);
|
2020-05-09 00:42:34 +02:00
|
|
|
uint32_t nCodes = 0;
|
2020-08-15 16:12:55 +02:00
|
|
|
const ActCodeSet* prevActSet = nullptr;
|
|
|
|
|
AstIf* ifp = nullptr;
|
2021-06-22 14:50:21 +02:00
|
|
|
uint32_t baseCode = 0;
|
2020-05-09 00:42:34 +02:00
|
|
|
for (; nCodes < maxCodes && it != traces.end(); ++it) {
|
|
|
|
|
const ActCodeSet& actSet = it->first;
|
2023-10-23 12:36:24 +02:00
|
|
|
// Traced value never changes, no need to add it
|
2020-05-09 00:42:34 +02:00
|
|
|
if (actSet.count(TraceActivityVertex::ACTIVITY_NEVER)) continue;
|
|
|
|
|
|
2023-10-23 17:01:55 +02:00
|
|
|
const TraceTraceVertex* const vtxp = it->second;
|
2021-06-22 14:50:21 +02:00
|
|
|
AstTraceDecl* const declp = vtxp->nodep();
|
|
|
|
|
|
2023-10-23 17:01:55 +02:00
|
|
|
// This is a duplicate decl, no need to add it, but must set the
|
|
|
|
|
// function index to the same as the canonical node.
|
|
|
|
|
if (const TraceTraceVertex* const canonVtxp = vtxp->duplicatep()) {
|
|
|
|
|
declp->fidx(canonVtxp->nodep()->fidx());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-09 00:42:34 +02:00
|
|
|
// Create top function if not yet created
|
2023-10-23 12:36:24 +02:00
|
|
|
if (!topFulFuncp) {
|
2023-10-23 17:01:55 +02:00
|
|
|
++topFuncNum;
|
2023-10-23 12:36:24 +02:00
|
|
|
topFulFuncp = newCFunc(VTraceType::FULL, nullptr, topFuncNum);
|
|
|
|
|
topChgFuncp = newCFunc(VTraceType::CHANGE, nullptr, topFuncNum);
|
|
|
|
|
}
|
2020-05-09 00:42:34 +02:00
|
|
|
|
2021-06-22 14:50:21 +02:00
|
|
|
// Create new sub function if required
|
2023-10-23 12:36:24 +02:00
|
|
|
if (!subFulFuncp || subStmts > splitLimit) {
|
2021-06-22 14:50:21 +02:00
|
|
|
baseCode = declp->code();
|
2020-05-09 00:42:34 +02:00
|
|
|
subStmts = 0;
|
2023-10-23 12:36:24 +02:00
|
|
|
subFulFuncp = newCFunc(VTraceType::FULL, topFulFuncp, subFuncNum, baseCode);
|
|
|
|
|
subChgFuncp = newCFunc(VTraceType::CHANGE, topChgFuncp, subFuncNum, baseCode);
|
|
|
|
|
++subFuncNum;
|
2020-08-15 16:12:55 +02:00
|
|
|
prevActSet = nullptr;
|
|
|
|
|
ifp = nullptr;
|
2020-05-09 00:42:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If required, create the conditional node checking the activity flags
|
|
|
|
|
if (!prevActSet || actSet != *prevActSet) {
|
|
|
|
|
FileLine* const flp = m_topScopep->fileline();
|
2021-06-21 00:32:57 +02:00
|
|
|
const bool always = actSet.count(TraceActivityVertex::ACTIVITY_ALWAYS) != 0;
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* condp = nullptr;
|
2020-05-09 00:42:34 +02:00
|
|
|
if (always) {
|
2022-11-20 23:40:38 +01:00
|
|
|
condp = new AstConst{flp, 1}; // Always true, will be folded later
|
2020-05-09 00:42:34 +02:00
|
|
|
} else {
|
2020-08-16 17:43:49 +02:00
|
|
|
for (const uint32_t actCode : actSet) {
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const selp = selectActivity(flp, actCode, VAccess::READ);
|
2022-11-20 23:40:38 +01:00
|
|
|
condp = condp ? new AstOr{flp, condp, selp} : selp;
|
2020-05-09 00:42:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-11-20 23:40:38 +01:00
|
|
|
ifp = new AstIf{flp, condp};
|
2021-02-22 03:25:21 +01:00
|
|
|
if (!always) ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
2023-10-23 12:36:24 +02:00
|
|
|
subChgFuncp->addStmtsp(ifp);
|
2022-01-09 23:34:10 +01:00
|
|
|
subStmts += ifp->nodeCount();
|
2020-05-09 00:42:34 +02:00
|
|
|
prevActSet = &actSet;
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2023-10-23 12:36:24 +02:00
|
|
|
// 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);
|
|
|
|
|
|
2023-10-23 17:01:55 +02:00
|
|
|
// Set the function index of the decl
|
|
|
|
|
declp->fidx(topFuncNum);
|
|
|
|
|
|
2023-10-23 12:36:24 +02:00
|
|
|
// Track splitting due to size
|
|
|
|
|
UASSERT_OBJ(incFulp->nodeCount() == incChgp->nodeCount(), declp,
|
|
|
|
|
"Should have equal cost");
|
2024-10-23 17:51:48 +02:00
|
|
|
const VNumRange range = declp->arrayRange();
|
|
|
|
|
if (range.ranged()) {
|
|
|
|
|
// 2x because each element is a TraceInc and a VarRef
|
|
|
|
|
subStmts += range.elements() * 2;
|
|
|
|
|
} else {
|
|
|
|
|
subStmts += incChgp->nodeCount();
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-05-09 00:42:34 +02:00
|
|
|
// Track partitioning
|
|
|
|
|
nCodes += declp->codeInc();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-22 14:50:21 +02:00
|
|
|
void createCleanupFunction() {
|
2020-05-09 00:42:34 +02:00
|
|
|
FileLine* const fl = m_topScopep->fileline();
|
2022-11-20 23:40:38 +01:00
|
|
|
AstCFunc* const cleanupFuncp = new AstCFunc{fl, "trace_cleanup", m_topScopep};
|
2021-06-13 15:33:11 +02:00
|
|
|
cleanupFuncp->argTypes("void* voidSelf, " + v3Global.opt.traceClassBase()
|
|
|
|
|
+ "* /*unused*/");
|
2021-06-22 14:50:21 +02:00
|
|
|
cleanupFuncp->isTrace(true);
|
2020-05-09 00:42:34 +02:00
|
|
|
cleanupFuncp->slow(false);
|
2021-06-13 15:33:11 +02:00
|
|
|
cleanupFuncp->isStatic(true);
|
|
|
|
|
cleanupFuncp->isLoose(true);
|
2022-09-15 20:43:56 +02:00
|
|
|
m_topScopep->addBlocksp(cleanupFuncp);
|
2025-10-21 17:37:32 +02:00
|
|
|
cleanupFuncp->addStmtsp(new AstCStmt{fl, EmitCUtil::voidSelfAssign(m_topModp)});
|
|
|
|
|
cleanupFuncp->addStmtsp(new AstCStmt{fl, EmitCUtil::symClassAssign()});
|
2020-05-09 00:42:34 +02:00
|
|
|
|
|
|
|
|
// Register it
|
2025-09-26 14:25:47 +02:00
|
|
|
{
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
AstCStmt* const cstmtp = new AstCStmt{fl};
|
|
|
|
|
m_regFuncp->addStmtsp(cstmtp);
|
|
|
|
|
cstmtp->add("tracep->addCleanupCb(");
|
|
|
|
|
cstmtp->add(new AstAddrOfCFunc{fl, cleanupFuncp});
|
|
|
|
|
cstmtp->add(", vlSelf);");
|
2025-09-26 14:25:47 +02:00
|
|
|
}
|
2020-05-09 00:42:34 +02:00
|
|
|
|
|
|
|
|
// Clear global activity flag
|
2024-01-29 02:24:28 +01:00
|
|
|
cleanupFuncp->addStmtsp(
|
2025-09-26 14:25:47 +02:00
|
|
|
new AstCStmt{m_topScopep->fileline(), "vlSymsp->__Vm_activity = false;"s});
|
2020-05-09 00:42:34 +02:00
|
|
|
|
|
|
|
|
// Clear fine grained activity flags
|
2020-05-17 14:53:02 +02:00
|
|
|
for (uint32_t i = 0; i < m_activityNumber; ++i) {
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNode* const clrp = new AstAssign{fl, selectActivity(fl, i, VAccess::WRITE),
|
|
|
|
|
new AstConst{fl, AstConst::BitFalse{}}};
|
2020-05-09 00:42:34 +02:00
|
|
|
cleanupFuncp->addStmtsp(clrp);
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-09 00:42:34 +02:00
|
|
|
void createTraceFunctions() {
|
2020-05-17 14:53:02 +02:00
|
|
|
// Detect and remove duplicate values
|
|
|
|
|
detectDuplicates();
|
2025-04-17 12:23:18 +02:00
|
|
|
m_graph.removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue);
|
2020-05-09 00:42:34 +02:00
|
|
|
|
2020-05-17 14:53:02 +02:00
|
|
|
// Simplify & optimize the graph
|
2023-05-04 00:04:10 +02:00
|
|
|
if (dumpGraphLevel() >= 6) m_graph.dumpDotFilePrefixed("trace_pre");
|
2020-05-17 14:53:02 +02:00
|
|
|
graphSimplify(true);
|
2023-05-04 00:04:10 +02:00
|
|
|
if (dumpGraphLevel() >= 6) m_graph.dumpDotFilePrefixed("trace_simplified");
|
2020-05-17 14:53:02 +02:00
|
|
|
graphOptimize();
|
2023-05-04 00:04:10 +02:00
|
|
|
if (dumpGraphLevel() >= 6) m_graph.dumpDotFilePrefixed("trace_optimized");
|
2020-05-17 14:53:02 +02:00
|
|
|
|
|
|
|
|
// Create the fine grained activity flags
|
|
|
|
|
createActivityFlags();
|
|
|
|
|
|
|
|
|
|
// Form a sorted list of the traces we are interested in
|
2020-05-09 00:42:34 +02:00
|
|
|
TraceVec traces; // The sorted traces
|
2020-05-17 14:53:02 +02:00
|
|
|
// 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.
|
2023-10-23 12:36:24 +02:00
|
|
|
uint32_t nNonConstCodes = 0;
|
|
|
|
|
sortTraces(traces, nNonConstCodes);
|
2020-05-09 00:42:34 +02:00
|
|
|
|
|
|
|
|
// 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
|
2022-11-20 23:40:38 +01:00
|
|
|
m_regFuncp = new AstCFunc{m_topScopep->fileline(), "trace_register", m_topScopep};
|
2021-06-22 14:50:21 +02:00
|
|
|
m_regFuncp->argTypes(v3Global.opt.traceClassBase() + "* tracep");
|
|
|
|
|
m_regFuncp->isTrace(true);
|
|
|
|
|
m_regFuncp->slow(true);
|
|
|
|
|
m_regFuncp->isStatic(false);
|
|
|
|
|
m_regFuncp->isLoose(true);
|
2022-09-15 20:43:56 +02:00
|
|
|
m_topScopep->addBlocksp(m_regFuncp);
|
2020-05-09 00:42:34 +02:00
|
|
|
|
2023-10-23 12:36:24 +02:00
|
|
|
// Create the const dump functions. Also allocates trace codes.
|
|
|
|
|
createConstTraceFunctions(traces);
|
2020-05-09 00:42:34 +02:00
|
|
|
|
2023-10-23 12:36:24 +02:00
|
|
|
// Create the full and incremental dump functions
|
|
|
|
|
createNonConstTraceFunctions(traces, nNonConstCodes, m_parallelism);
|
2020-05-09 00:42:34 +02:00
|
|
|
|
2020-05-17 14:53:02 +02:00
|
|
|
// Remove refs to traced values from TraceDecl nodes, these have now moved under
|
|
|
|
|
// TraceInc
|
2020-10-30 23:00:40 +01:00
|
|
|
for (const auto& i : traces) {
|
|
|
|
|
AstNode* const valuep = i.second->nodep()->valuep();
|
2020-05-09 00:42:34 +02:00
|
|
|
valuep->unlinkFrBack();
|
|
|
|
|
valuep->deleteTree();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create the trace cleanup function clearing the activity flags
|
2021-06-22 14:50:21 +02:00
|
|
|
createCleanupFunction();
|
2020-05-09 00:42:34 +02:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
TraceCFuncVertex* getCFuncVertexp(AstCFunc* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
TraceCFuncVertex* vertexp
|
2023-09-01 00:00:53 +02:00
|
|
|
= nodep->user1() ? nodep->user1u().toGraphVertex()->cast<TraceCFuncVertex>() : nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!vertexp) {
|
2022-11-20 23:40:38 +01:00
|
|
|
vertexp = new TraceCFuncVertex{&m_graph, nodep};
|
2019-05-19 22:13:13 +02:00
|
|
|
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
|
2023-09-01 00:00:53 +02:00
|
|
|
= nodep->user3() ? nodep->user3u().toGraphVertex()->cast<TraceActivityVertex>()
|
|
|
|
|
: nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!vertexp) {
|
2022-11-20 23:40:38 +01:00
|
|
|
vertexp = new TraceActivityVertex{&m_graph, nodep, slow};
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->user3p(vertexp);
|
|
|
|
|
}
|
|
|
|
|
vertexp->slow(slow);
|
|
|
|
|
return vertexp;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNetlist* nodep) 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
|
|
|
|
2020-05-09 00:42:34 +02:00
|
|
|
// Add vertexes for all TraceDecl, and edges from VARs each trace looks at
|
2019-05-19 22:13:13 +02:00
|
|
|
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);
|
2020-05-09 12:38:32 +02:00
|
|
|
|
2020-05-09 00:42:34 +02:00
|
|
|
// Create the trace functions and insert them into the tree
|
|
|
|
|
createTraceFunctions();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) 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
|
|
|
}
|
2022-10-12 11:19:21 +02:00
|
|
|
void visit(AstStmtExpr* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!m_finding && !nodep->user2()) {
|
2022-10-12 11:19:21 +02:00
|
|
|
if (AstCCall* const callp = VN_CAST(nodep->exprp(), CCall)) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " CCALL " << callp);
|
2022-10-12 11:19:21 +02:00
|
|
|
// See if there are other calls in same statement list;
|
|
|
|
|
// If so, all funcs might share the same activity code
|
|
|
|
|
TraceActivityVertex* const activityVtxp
|
|
|
|
|
= getActivityVertexp(nodep, callp->funcp()->slow());
|
|
|
|
|
for (AstNode* nextp = nodep; nextp; nextp = nextp->nextp()) {
|
|
|
|
|
if (AstStmtExpr* const stmtp = VN_CAST(nextp, StmtExpr)) {
|
|
|
|
|
if (AstCCall* const ccallp = VN_CAST(stmtp->exprp(), CCall)) {
|
|
|
|
|
stmtp->user2(true); // Processed
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " SubCCALL " << ccallp);
|
2022-10-12 11:19:21 +02:00
|
|
|
V3GraphVertex* const ccallFuncVtxp = getCFuncVertexp(ccallp->funcp());
|
|
|
|
|
activityVtxp->slow(ccallp->funcp()->slow());
|
2022-11-20 23:40:38 +01:00
|
|
|
new V3GraphEdge{&m_graph, activityVtxp, ccallFuncVtxp, 1};
|
2022-10-12 11:19:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
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
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCFunc* nodep) override {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " CFUNC " << nodep);
|
2020-05-09 12:38:32 +02:00
|
|
|
V3GraphVertex* const funcVtxp = getCFuncVertexp(nodep);
|
2020-05-17 14:53:02 +02:00
|
|
|
if (!m_finding) { // If public, we need a unique activity code to allow for sets
|
|
|
|
|
// directly in this func
|
2022-08-26 12:11:44 +02:00
|
|
|
if (nodep->funcPublic() || nodep->dpiExportImpl() || nodep == v3Global.rootp()->evalp()
|
|
|
|
|
|| nodep->isCoroutine()) {
|
|
|
|
|
// Cannot treat a coroutine as slow, it may be resumed later
|
|
|
|
|
const bool slow = nodep->slow() && !nodep->isCoroutine();
|
|
|
|
|
V3GraphVertex* const activityVtxp = getActivityVertexp(nodep, slow);
|
2022-11-20 23:40:38 +01:00
|
|
|
new V3GraphEdge{&m_graph, activityVtxp, funcVtxp, 1};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-10-31 13:59:35 +01:00
|
|
|
VL_RESTORER(m_cfuncp);
|
2024-11-10 16:51:48 +01:00
|
|
|
m_cfuncp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTraceDecl* nodep) override {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " TRACE " << nodep);
|
2020-05-09 00:42:34 +02:00
|
|
|
if (!m_finding) {
|
2022-11-20 23:40:38 +01:00
|
|
|
V3GraphVertex* const vertexp = new TraceTraceVertex{&m_graph, nodep};
|
2020-05-09 00:42:34 +02:00
|
|
|
nodep->user1p(vertexp);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-10-31 13:59:35 +01:00
|
|
|
UASSERT_OBJ(m_cfuncp, nodep, "Trace not under func");
|
2025-02-28 03:18:27 +01:00
|
|
|
VL_RESTORER(m_tracep);
|
2020-05-09 00:42:34 +02:00
|
|
|
m_tracep = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarRef* nodep) 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?");
|
2020-10-31 03:28:51 +01:00
|
|
|
UASSERT_OBJ(nodep->access().isReadOnly(), nodep, "Lvalue in trace? Should be const.");
|
2019-05-19 22:13:13 +02:00
|
|
|
V3GraphVertex* varVtxp = nodep->varScopep()->user1u().toGraphVertex();
|
|
|
|
|
if (!varVtxp) {
|
2022-11-20 23:40:38 +01:00
|
|
|
varVtxp = new TraceVarVertex{&m_graph, nodep->varScopep()};
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->varScopep()->user1p(varVtxp);
|
|
|
|
|
}
|
2020-05-09 12:38:32 +02:00
|
|
|
V3GraphVertex* const traceVtxp = m_tracep->user1u().toGraphVertex();
|
2022-11-20 23:40:38 +01: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
|
2022-11-20 23:40:38 +01:00
|
|
|
new V3GraphEdge{&m_graph, m_alwaysVtxp, traceVtxp, 1};
|
2018-10-27 23:29:00 +02:00
|
|
|
}
|
2020-11-07 16:37:55 +01:00
|
|
|
} else if (m_cfuncp && m_finding && nodep->access().isWriteOrRW()) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(nodep->varScopep(), nodep, "No var scope?");
|
2020-10-31 13:59:35 +01:00
|
|
|
V3GraphVertex* const funcVtxp = getCFuncVertexp(m_cfuncp);
|
2020-05-09 12:38:32 +02:00
|
|
|
V3GraphVertex* const varVtxp = nodep->varScopep()->user1u().toGraphVertex();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (varVtxp) { // else we're not tracing this signal
|
2022-11-20 23:40:38 +01:00
|
|
|
new V3GraphEdge{&m_graph, funcVtxp, varVtxp, 1};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
//--------------------
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) 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)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_alwaysVtxp{new TraceActivityVertex{&m_graph, TraceActivityVertex::ACTIVITY_ALWAYS}} {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~TraceVisitor() override {
|
2024-08-29 02:45:51 +02:00
|
|
|
V3Stats::addStat("Tracing, Activity setters", m_statSetters);
|
|
|
|
|
V3Stats::addStat("Tracing, Activity slow blocks", m_statSettersSlow);
|
2019-05-19 22:13:13 +02:00
|
|
|
V3Stats::addStat("Tracing, Unique trace codes", m_statUniqCodes);
|
2024-08-29 02:45:51 +02:00
|
|
|
V3Stats::addStat("Tracing, Unique traced signals", m_statUniqSigs);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Trace class functions
|
|
|
|
|
|
|
|
|
|
void V3Trace::traceAll(AstNetlist* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2021-11-26 16:52:36 +01:00
|
|
|
{ TraceVisitor{nodep}; } // Destruct before checking
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("trace", 0, dumpTreeEitherLevel() >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|