mirror of
https://github.com/verilator/verilator.git
synced 2025-01-21 22:04:03 +00:00
39d903375b
This patch de-duplicates common functionality between the VCD and FST trace implementation. It also enables adding new trace formats more easily and consistently. No functional nor performance change intended.
185 lines
7.5 KiB
C++
185 lines
7.5 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//=============================================================================
|
|
//
|
|
// THIS MODULE IS PUBLICLY LICENSED
|
|
//
|
|
// Copyright 2001-2020 by Wilson Snyder. This program is free software; you
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
// Version 2.0.
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
//
|
|
//=============================================================================
|
|
///
|
|
/// \file
|
|
/// \brief Tracing functionality common to all formats
|
|
///
|
|
//=============================================================================
|
|
// SPDIFF_OFF
|
|
|
|
#ifndef _VERILATED_TRACE_H_
|
|
#define _VERILATED_TRACE_H_ 1
|
|
|
|
#include "verilated.h"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
class VerilatedTraceCallInfo;
|
|
|
|
//=============================================================================
|
|
// VerilatedTrace
|
|
|
|
// VerilatedTrace uses F-bounded polymorphism to access duck-typed
|
|
// implementations in the format specific derived class, which must be passed
|
|
// as the type parameter T_Derived
|
|
template <class T_Derived> class VerilatedTrace {
|
|
private:
|
|
//=========================================================================
|
|
// Generic tracing internals
|
|
|
|
vluint32_t* m_sigs_oldvalp; ///< Old value store
|
|
vluint64_t m_timeLastDump; ///< Last time we did a dump
|
|
std::vector<VerilatedTraceCallInfo*> m_callbacks; ///< Routines to perform dumping
|
|
bool m_fullDump; ///< Whether a full dump is required on the next call to 'dump'
|
|
vluint32_t m_nextCode; ///< Next code number to assign
|
|
std::string m_moduleName; ///< Name of module being trace initialized now
|
|
char m_scopeEscape;
|
|
double m_timeRes; ///< Time resolution (ns/ms etc)
|
|
double m_timeUnit; ///< Time units (ns/ms etc)
|
|
|
|
// Equivalent to 'this' but is of the sub-type 'T_Derived*'. Use 'self()->'
|
|
// to access duck-typed functions to avoid a virtual function call.
|
|
T_Derived* self() { return static_cast<T_Derived*>(this); }
|
|
|
|
// CONSTRUCTORS
|
|
VL_UNCOPYABLE(VerilatedTrace);
|
|
|
|
protected:
|
|
//=========================================================================
|
|
// Internals available to format specific implementations
|
|
|
|
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
|
|
|
vluint32_t nextCode() const { return m_nextCode; }
|
|
const std::string& moduleName() const { return m_moduleName; }
|
|
void fullDump(bool value) { m_fullDump = value; }
|
|
vluint64_t timeLastDump() { return m_timeLastDump; }
|
|
|
|
double timeRes() const { return m_timeRes; }
|
|
double timeUnit() const { return m_timeUnit; }
|
|
std::string timeResStr() const;
|
|
std::string timeUnitStr() const;
|
|
|
|
void traceInit() VL_MT_UNSAFE;
|
|
|
|
void declCode(vluint32_t code, vluint32_t bits, bool tri);
|
|
|
|
/// Is this an escape?
|
|
bool isScopeEscape(char c) { return isspace(c) || c == m_scopeEscape; }
|
|
/// Character that splits scopes. Note whitespace are ALWAYS escapes.
|
|
char scopeEscape() { return m_scopeEscape; }
|
|
|
|
//=========================================================================
|
|
// Virtual functions to be provided by the format specific implementation
|
|
|
|
// Called when the trace moves forward to a new time point
|
|
virtual void emitTimeChange(vluint64_t timeui) = 0;
|
|
|
|
// These hooks are called before a full or change based dump is produced.
|
|
// The return value indicates whether to proceed with the dump.
|
|
virtual bool preFullDump() { return true; }
|
|
virtual bool preChangeDump() { return true; }
|
|
|
|
public:
|
|
//=========================================================================
|
|
// External interface to client code
|
|
|
|
explicit VerilatedTrace();
|
|
~VerilatedTrace();
|
|
|
|
// Set time units (s/ms, defaults to ns)
|
|
void set_time_unit(const char* unitp);
|
|
void set_time_unit(const std::string& unit);
|
|
// Set time resolution (s/ms, defaults to ns)
|
|
void set_time_resolution(const char* unitp);
|
|
void set_time_resolution(const std::string& unit);
|
|
|
|
// Call
|
|
void dump(vluint64_t timeui);
|
|
|
|
//=========================================================================
|
|
// Non-hot path internal interface to Verilator generated code
|
|
|
|
typedef void (*callback_t)(T_Derived* tracep, void* userthis, vluint32_t code);
|
|
|
|
void changeThread() { m_assertOne.changeThread(); }
|
|
|
|
void addCallback(callback_t initcb, callback_t fullcb, callback_t changecb,
|
|
void* userthis) VL_MT_UNSAFE_ONE;
|
|
|
|
void module(const std::string& name) VL_MT_UNSAFE_ONE {
|
|
m_assertOne.check();
|
|
m_moduleName = name;
|
|
}
|
|
|
|
void scopeEscape(char flag) { m_scopeEscape = flag; }
|
|
|
|
//=========================================================================
|
|
// Hot path internal interface to Verilator generated code
|
|
|
|
// Implementation note: We rely on the following duck-typed implementations
|
|
// in the derived class T_Derived. These emit* functions record a format
|
|
// specific trace entry. Normally one would use pure virtual functions for
|
|
// these here, but we cannot afford dynamic dispatch for calling these as
|
|
// this is very hot code during tracing.
|
|
|
|
// duck-typed void emitBit(vluint32_t code, vluint32_t newval) = 0;
|
|
// duck-typed template <int T_Bits> void emitBus(vluint32_t code, vluint32_t newval) = 0;
|
|
// duck-typed void emitQuad(vluint32_t code, vluint64_t newval, int bits) = 0;
|
|
// duck-typed void emitArray(vluint32_t code, const vluint32_t* newvalp, int bits) = 0;
|
|
// duck-typed void emitFloat(vluint32_t code, float newval) = 0;
|
|
// duck-typed void emitDouble(vluint32_t code, double newval) = 0;
|
|
|
|
vluint32_t* oldp(vluint32_t code) { return m_sigs_oldvalp + code; }
|
|
|
|
// Write to previous value buffer value and emit trace entry.
|
|
void fullBit(vluint32_t* oldp, vluint32_t newval);
|
|
template <int T_Bits> void fullBus(vluint32_t* oldp, vluint32_t newval);
|
|
void fullQuad(vluint32_t* oldp, vluint64_t newval, int bits);
|
|
void fullArray(vluint32_t* oldp, const vluint32_t* newvalp, int bits);
|
|
void fullFloat(vluint32_t* oldp, float newval);
|
|
void fullDouble(vluint32_t* oldp, double newval);
|
|
|
|
// Check previous dumped value of signal. If changed, then emit trace entry
|
|
inline void chgBit(vluint32_t* oldp, vluint32_t newval) {
|
|
const vluint32_t diff = *oldp ^ newval;
|
|
if (VL_UNLIKELY(diff)) fullBit(oldp, newval);
|
|
}
|
|
template <int T_Bits> inline void chgBus(vluint32_t* oldp, vluint32_t newval) {
|
|
const vluint32_t diff = *oldp ^ newval;
|
|
if (VL_UNLIKELY(diff)) fullBus<T_Bits>(oldp, newval);
|
|
}
|
|
inline void chgQuad(vluint32_t* oldp, vluint64_t newval, int bits) {
|
|
const vluint64_t diff = *reinterpret_cast<vluint64_t*>(oldp) ^ newval;
|
|
if (VL_UNLIKELY(diff)) fullQuad(oldp, newval, bits);
|
|
}
|
|
inline void chgArray(vluint32_t* oldp, const vluint32_t* newvalp, int bits) {
|
|
for (int i = 0; i < (bits + 31) / 32; ++i) {
|
|
if (VL_UNLIKELY(oldp[i] ^ newvalp[i])) {
|
|
fullArray(oldp, newvalp, bits);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
inline void chgFloat(vluint32_t* oldp, float newval) {
|
|
// cppcheck-suppress invalidPointerCast
|
|
if (VL_UNLIKELY(*reinterpret_cast<float*>(oldp) != newval)) fullFloat(oldp, newval);
|
|
}
|
|
inline void chgDouble(vluint32_t* oldp, double newval) {
|
|
// cppcheck-suppress invalidPointerCast
|
|
if (VL_UNLIKELY(*reinterpret_cast<double*>(oldp) != newval)) fullDouble(oldp, newval);
|
|
}
|
|
};
|
|
#endif // guard
|