mirror of
https://github.com/verilator/verilator.git
synced 2025-01-01 04:07:34 +00:00
Factor out trace implementation common to all formats. (#2268)
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.
This commit is contained in:
parent
9164eb03d5
commit
39d903375b
@ -20,7 +20,6 @@
|
||||
// clang-format off
|
||||
|
||||
#define __STDC_LIMIT_MACROS // UINT64_MAX
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
#include "verilated_fst_c.h"
|
||||
|
||||
@ -54,65 +53,35 @@
|
||||
// clang-format on
|
||||
|
||||
//=============================================================================
|
||||
// Specialization of the generics for this trace format
|
||||
|
||||
class VerilatedFstCallInfo {
|
||||
protected:
|
||||
friend class VerilatedFst;
|
||||
VerilatedFstCallback_t m_initcb; ///< Initialization Callback function
|
||||
VerilatedFstCallback_t m_fullcb; ///< Full Dumping Callback function
|
||||
VerilatedFstCallback_t m_changecb; ///< Incremental Dumping Callback function
|
||||
void* m_userthis; ///< Fake "this" for caller
|
||||
vluint32_t m_code; ///< Starting code number
|
||||
// CONSTRUCTORS
|
||||
VerilatedFstCallInfo(VerilatedFstCallback_t icb, VerilatedFstCallback_t fcb,
|
||||
VerilatedFstCallback_t changecb, void* ut)
|
||||
: m_initcb(icb)
|
||||
, m_fullcb(fcb)
|
||||
, m_changecb(changecb)
|
||||
, m_userthis(ut)
|
||||
, m_code(1) {}
|
||||
~VerilatedFstCallInfo() {}
|
||||
};
|
||||
#define VL_DERIVED_T VerilatedFst
|
||||
#include "verilated_trace_imp.cpp"
|
||||
#undef VL_DERIVED_T
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFst
|
||||
|
||||
VerilatedFst::VerilatedFst(void* fst)
|
||||
: m_fst(fst)
|
||||
, m_fullDump(true)
|
||||
, m_minNextDumpTime(0)
|
||||
, m_nextCode(1)
|
||||
, m_scopeEscape('.')
|
||||
, m_symbolp(NULL)
|
||||
, m_sigs_oldvalp(NULL) {
|
||||
m_valueStrBuffer.reserve(64 + 1); // Need enough room for quad
|
||||
set_time_unit(Verilated::timeunitString());
|
||||
set_time_resolution(Verilated::timeprecisionString());
|
||||
}
|
||||
, m_symbolp(NULL) {}
|
||||
|
||||
VerilatedFst::~VerilatedFst() {
|
||||
if (m_fst) fstWriterClose(m_fst);
|
||||
if (m_symbolp) VL_DO_CLEAR(delete[] m_symbolp, m_symbolp = NULL);
|
||||
if (m_sigs_oldvalp) VL_DO_CLEAR(delete[] m_sigs_oldvalp, m_sigs_oldvalp = NULL);
|
||||
}
|
||||
|
||||
void VerilatedFst::open(const char* filename) VL_MT_UNSAFE {
|
||||
m_assertOne.check();
|
||||
m_fst = fstWriterCreate(filename, 1);
|
||||
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
|
||||
fstWriterSetTimescaleFromString(m_fst, m_timeRes.c_str());
|
||||
fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref
|
||||
#ifdef VL_TRACE_THREADED
|
||||
fstWriterSetParallelMode(m_fst, 1);
|
||||
#endif
|
||||
m_curScope.clear();
|
||||
m_nextCode = 1;
|
||||
|
||||
for (vluint32_t ent = 0; ent < m_callbacks.size(); ++ent) {
|
||||
VerilatedFstCallInfo* cip = m_callbacks[ent];
|
||||
cip->m_code = m_nextCode;
|
||||
// Initialize; callbacks will call decl* which update m_nextCode
|
||||
(cip->m_initcb)(this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
VerilatedTrace<VerilatedFst>::traceInit();
|
||||
|
||||
// Clear the scope stack
|
||||
std::list<std::string>::iterator it = m_curScope.begin();
|
||||
@ -123,19 +92,16 @@ void VerilatedFst::open(const char* filename) VL_MT_UNSAFE {
|
||||
|
||||
// convert m_code2symbol into an array for fast lookup
|
||||
if (!m_symbolp) {
|
||||
m_symbolp = new fstHandle[m_nextCode + 10];
|
||||
m_symbolp = new fstHandle[nextCode()];
|
||||
for (Code2SymbolType::iterator it = m_code2symbol.begin(); it != m_code2symbol.end();
|
||||
++it) {
|
||||
m_symbolp[it->first] = it->second;
|
||||
}
|
||||
}
|
||||
m_code2symbol.clear();
|
||||
|
||||
// Allocate space now we know the number of codes
|
||||
if (!m_sigs_oldvalp) m_sigs_oldvalp = new vluint32_t[m_nextCode + 10];
|
||||
}
|
||||
|
||||
void VerilatedFst::module(const std::string& name) { m_module = name; }
|
||||
void VerilatedFst::emitTimeChange(vluint64_t timeui) { fstWriterEmitTimeChange(m_fst, timeui); }
|
||||
|
||||
//=============================================================================
|
||||
// Decl
|
||||
@ -151,11 +117,7 @@ void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, vluint32_t elem
|
||||
void VerilatedFst::declSymbol(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, vluint32_t len,
|
||||
vluint32_t bits) {
|
||||
|
||||
// Make sure deduplicate tracking increments for future declarations
|
||||
int codesNeeded = 1 + int(bits / 32);
|
||||
// Not supported: if (tri) codesNeeded *= 2; // Space in change array for __en signals
|
||||
m_nextCode = std::max(m_nextCode, code + codesNeeded);
|
||||
VerilatedTrace<VerilatedFst>::declCode(code, bits, false);
|
||||
|
||||
std::pair<Code2SymbolType::iterator, bool> p
|
||||
= m_code2symbol.insert(std::make_pair(code, static_cast<fstHandle>(NULL)));
|
||||
@ -164,7 +126,7 @@ void VerilatedFst::declSymbol(vluint32_t code, const char* name, int dtypenum, f
|
||||
std::list<std::string> tokens(beg, end); // Split name
|
||||
std::string symbol_name(tokens.back());
|
||||
tokens.pop_back(); // Remove symbol name from hierarchy
|
||||
tokens.insert(tokens.begin(), m_module); // Add current module to the hierarchy
|
||||
tokens.insert(tokens.begin(), moduleName()); // Add current module to the hierarchy
|
||||
|
||||
// Find point where current and new scope diverge
|
||||
std::list<std::string>::iterator cur_it = m_curScope.begin();
|
||||
@ -205,47 +167,49 @@ void VerilatedFst::declSymbol(vluint32_t code, const char* name, int dtypenum, f
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Callbacks
|
||||
|
||||
void VerilatedFst::addCallback(VerilatedFstCallback_t initcb, VerilatedFstCallback_t fullcb,
|
||||
VerilatedFstCallback_t changecb, void* userthis) VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(isOpen())) {
|
||||
std::string msg = (std::string("Internal: ") + __FILE__ + "::" + __FUNCTION__
|
||||
+ " called with already open file");
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
}
|
||||
VerilatedFstCallInfo* cip = new VerilatedFstCallInfo(initcb, fullcb, changecb, userthis);
|
||||
m_callbacks.push_back(cip);
|
||||
void VerilatedFst::declBit(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, 1, 1);
|
||||
}
|
||||
void VerilatedFst::declBus(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, msb - lsb + 1,
|
||||
msb - lsb + 1);
|
||||
}
|
||||
void VerilatedFst::declQuad(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, msb - lsb + 1,
|
||||
msb - lsb + 1);
|
||||
}
|
||||
void VerilatedFst::declArray(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, msb - lsb + 1,
|
||||
msb - lsb + 1);
|
||||
}
|
||||
void VerilatedFst::declFloat(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, 1, 32);
|
||||
}
|
||||
void VerilatedFst::declDouble(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, 2, 64);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Dumping
|
||||
|
||||
void VerilatedFst::dump(vluint64_t timeui) {
|
||||
if (!isOpen()) return;
|
||||
if (timeui < m_minNextDumpTime) {
|
||||
VL_PRINTF_MT("%%Warning: previous dump at t=%" VL_PRI64 "u, requesting t=%" VL_PRI64 "u\n",
|
||||
m_minNextDumpTime - 1, timeui);
|
||||
return;
|
||||
}
|
||||
m_minNextDumpTime = timeui + 1;
|
||||
fstWriterEmitTimeChange(m_fst, timeui);
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
m_fullDump = false; // No more need for next dump to be full
|
||||
for (vluint32_t ent = 0; ent < m_callbacks.size(); ++ent) {
|
||||
VerilatedFstCallInfo* cip = m_callbacks[ent];
|
||||
(cip->m_fullcb)(this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (vluint32_t ent = 0; ent < m_callbacks.size(); ++ent) {
|
||||
VerilatedFstCallInfo* cip = m_callbacks[ent];
|
||||
(cip->m_changecb)(this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
void VerilatedFst::emitBit(vluint32_t code, vluint32_t newval) {
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], newval ? "1" : "0");
|
||||
}
|
||||
template <int T_Bits> void VerilatedFst::emitBus(vluint32_t code, vluint32_t newval) {
|
||||
fstWriterEmitValueChange32(m_fst, m_symbolp[code], T_Bits, newval);
|
||||
}
|
||||
void VerilatedFst::emitQuad(vluint32_t code, vluint64_t newval, int bits) {
|
||||
fstWriterEmitValueChange64(m_fst, m_symbolp[code], bits, newval);
|
||||
}
|
||||
void VerilatedFst::emitArray(vluint32_t code, const vluint32_t* newvalp, int bits) {
|
||||
fstWriterEmitValueChangeVec32(m_fst, m_symbolp[code], bits, newvalp);
|
||||
}
|
||||
void VerilatedFst::emitFloat(vluint32_t code, float newval) {
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], &newval);
|
||||
}
|
||||
void VerilatedFst::emitDouble(vluint32_t code, double newval) {
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], &newval);
|
||||
}
|
||||
|
||||
//********************************************************************
|
||||
// Local Variables:
|
||||
// End:
|
||||
|
@ -20,8 +20,8 @@
|
||||
#ifndef _VERILATED_FST_C_H_
|
||||
#define _VERILATED_FST_C_H_ 1
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
#include "verilated_trace.h"
|
||||
|
||||
#include "gtkwave/fstapi.h"
|
||||
|
||||
@ -30,176 +30,100 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class VerilatedFst;
|
||||
class VerilatedFstCallInfo;
|
||||
typedef void (*VerilatedFstCallback_t)(VerilatedFst* vcdp, void* userthis, vluint32_t code);
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFst
|
||||
/// Base class to create a Verilator FST dump
|
||||
/// This is an internally used class - see VerilatedFstC for what to call from applications
|
||||
|
||||
class VerilatedFst {
|
||||
class VerilatedFst : public VerilatedTrace<VerilatedFst> {
|
||||
private:
|
||||
// Give the superclass access to private bits (to avoid virtual functions)
|
||||
friend class VerilatedTrace<VerilatedFst>;
|
||||
|
||||
//=========================================================================
|
||||
// FST specific internals
|
||||
|
||||
typedef std::map<vluint32_t, fstHandle> Code2SymbolType;
|
||||
typedef std::map<int, fstEnumHandle> Local2FstDtype;
|
||||
typedef std::vector<VerilatedFstCallInfo*> CallbackVec;
|
||||
|
||||
private:
|
||||
void* m_fst;
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
bool m_fullDump;
|
||||
vluint64_t m_minNextDumpTime;
|
||||
vluint32_t m_nextCode; ///< Next code number to assign
|
||||
char m_scopeEscape;
|
||||
std::string m_module;
|
||||
CallbackVec m_callbacks; ///< Routines to perform dumping
|
||||
Code2SymbolType m_code2symbol;
|
||||
Local2FstDtype m_local2fstdtype;
|
||||
std::list<std::string> m_curScope;
|
||||
fstHandle* m_symbolp; ///< same as m_code2symbol, but as an array
|
||||
vluint32_t* m_sigs_oldvalp;
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedFst);
|
||||
void declSymbol(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, vluint32_t len, vluint32_t bits);
|
||||
// helpers
|
||||
std::vector<char> m_valueStrBuffer;
|
||||
|
||||
std::string m_timeRes;
|
||||
protected:
|
||||
//=========================================================================
|
||||
// Implementation of VerilatedTrace interface
|
||||
|
||||
// Implementations of protected virtual methods for VerilatedTrace
|
||||
void emitTimeChange(vluint64_t timeui) VL_OVERRIDE;
|
||||
|
||||
// Hooks called from VerilatedTrace
|
||||
bool preFullDump() VL_OVERRIDE { return isOpen(); }
|
||||
bool preChangeDump() VL_OVERRIDE { return isOpen(); }
|
||||
|
||||
// Implementations of duck-typed methods for VerilatedTrace
|
||||
void emitBit(vluint32_t code, vluint32_t newval);
|
||||
template <int T_Bits> void emitBus(vluint32_t code, vluint32_t newval);
|
||||
void emitQuad(vluint32_t code, vluint64_t newval, int bits);
|
||||
void emitArray(vluint32_t code, const vluint32_t* newvalp, int bits);
|
||||
void emitFloat(vluint32_t code, float newval);
|
||||
void emitDouble(vluint32_t code, double newval);
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
|
||||
explicit VerilatedFst(void* fst = NULL);
|
||||
~VerilatedFst();
|
||||
void changeThread() { m_assertOne.changeThread(); }
|
||||
bool isOpen() const { return m_fst != NULL; }
|
||||
|
||||
/// Open the file; call isOpen() to see if errors
|
||||
void open(const char* filename) VL_MT_UNSAFE;
|
||||
void flush() VL_MT_UNSAFE { fstWriterFlushContext(m_fst); }
|
||||
/// Close the file
|
||||
void close() VL_MT_UNSAFE {
|
||||
m_assertOne.check();
|
||||
fstWriterClose(m_fst);
|
||||
m_fst = NULL;
|
||||
}
|
||||
void set_time_unit(const char*) {}
|
||||
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
/// Flush any remaining data to this file
|
||||
void flush() VL_MT_UNSAFE { fstWriterFlushContext(m_fst); }
|
||||
/// Is file open?
|
||||
bool isOpen() const { return m_fst != NULL; }
|
||||
|
||||
void set_time_resolution(const char* unitp) { m_timeRes = unitp; }
|
||||
void set_time_resolution(const std::string& unit) { m_timeRes = unit; }
|
||||
//=========================================================================
|
||||
// Internal interface to Verilator generated code
|
||||
|
||||
// double timescaleToDouble(const char* unitp);
|
||||
// std::string doubleToTimescale(double value);
|
||||
|
||||
/// Change character that splits scopes. Note whitespace are ALWAYS escapes.
|
||||
void scopeEscape(char flag) { m_scopeEscape = flag; }
|
||||
/// Is this an escape?
|
||||
bool isScopeEscape(char c) { return isspace(c) || c == m_scopeEscape; }
|
||||
/// Inside dumping routines, called each cycle to make the dump
|
||||
void dump(vluint64_t timeui);
|
||||
/// Inside dumping routines, declare callbacks for tracings
|
||||
void addCallback(VerilatedFstCallback_t initcb, VerilatedFstCallback_t fullcb,
|
||||
VerilatedFstCallback_t changecb, void* userthis) VL_MT_UNSAFE_ONE;
|
||||
|
||||
/// Inside dumping routines, declare a module
|
||||
void module(const std::string& name);
|
||||
/// Inside dumping routines, declare a data type
|
||||
void declDTypeEnum(int dtypenum, const char* name, vluint32_t elements,
|
||||
unsigned int minValbits, const char** itemNamesp, const char** itemValuesp);
|
||||
|
||||
/// Inside dumping routines, declare a signal
|
||||
void declBit(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, 1, 1);
|
||||
}
|
||||
fstVarType vartype, bool array, int arraynum);
|
||||
void declBus(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, msb - lsb + 1,
|
||||
msb - lsb + 1);
|
||||
}
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
|
||||
void declQuad(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, msb - lsb + 1,
|
||||
msb - lsb + 1);
|
||||
}
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
|
||||
void declArray(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, msb - lsb + 1,
|
||||
msb - lsb + 1);
|
||||
}
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
|
||||
void declFloat(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, 1, 32);
|
||||
}
|
||||
fstVarType vartype, bool array, int arraynum);
|
||||
void declDouble(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, 2, 64);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Inside dumping routines used by Verilator
|
||||
|
||||
vluint32_t* oldp(vluint32_t code) { return m_sigs_oldvalp + code; }
|
||||
|
||||
//=========================================================================
|
||||
// Write back to previous value buffer value and emit
|
||||
|
||||
void fullBit(vluint32_t* oldp, vluint32_t newval) {
|
||||
*oldp = newval;
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[oldp - m_sigs_oldvalp], newval ? "1" : "0");
|
||||
}
|
||||
template <int T_Bits> void fullBus(vluint32_t* oldp, vluint32_t newval) {
|
||||
*oldp = newval;
|
||||
fstWriterEmitValueChange32(m_fst, m_symbolp[oldp - m_sigs_oldvalp], T_Bits, newval);
|
||||
}
|
||||
void fullQuad(vluint32_t* oldp, vluint64_t newval, int bits) {
|
||||
*reinterpret_cast<vluint64_t*>(oldp) = newval;
|
||||
fstWriterEmitValueChange64(m_fst, m_symbolp[oldp - m_sigs_oldvalp], bits, newval);
|
||||
}
|
||||
void fullArray(vluint32_t* oldp, const vluint32_t* newvalp, int bits) {
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) oldp[i] = newvalp[i];
|
||||
fstWriterEmitValueChangeVec32(m_fst, m_symbolp[oldp - m_sigs_oldvalp], bits, newvalp);
|
||||
}
|
||||
void fullFloat(vluint32_t* oldp, float newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
*reinterpret_cast<float*>(oldp) = newval;
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[oldp - m_sigs_oldvalp], oldp);
|
||||
}
|
||||
void fullDouble(vluint32_t* oldp, double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
*reinterpret_cast<double*>(oldp) = newval;
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[oldp - m_sigs_oldvalp], oldp);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Check previous value and emit if changed
|
||||
|
||||
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);
|
||||
}
|
||||
fstVarType vartype, bool array, int arraynum);
|
||||
};
|
||||
|
||||
// Declare specialization here as it's used in VerilatedFstC just below
|
||||
template <> void VerilatedTrace<VerilatedFst>::dump(vluint64_t timeui);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_unit(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_unit(const std::string& unit);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_resolution(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_resolution(const std::string& unit);
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFstC
|
||||
/// Create a FST dump file in C standalone (no SystemC) simulations.
|
||||
@ -239,11 +163,11 @@ public:
|
||||
/// Set time units (s/ms, defaults to ns)
|
||||
/// For Verilated models, these propage from the Verilated default --timeunit
|
||||
void set_time_unit(const char* unitp) { m_sptrace.set_time_unit(unitp); }
|
||||
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
void set_time_unit(const std::string& unit) { m_sptrace.set_time_unit(unit); }
|
||||
/// Set time resolution (s/ms, defaults to ns)
|
||||
/// For Verilated models, these propage from the Verilated default --timeunit
|
||||
void set_time_resolution(const char* unitp) { m_sptrace.set_time_resolution(unitp); }
|
||||
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
void set_time_resolution(const std::string& unit) { m_sptrace.set_time_resolution(unit); }
|
||||
|
||||
/// Internal class access
|
||||
inline VerilatedFst* spTrace() { return &m_sptrace; };
|
||||
|
184
include/verilated_trace.h
Normal file
184
include/verilated_trace.h
Normal file
@ -0,0 +1,184 @@
|
||||
// -*- 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
|
309
include/verilated_trace_imp.cpp
Normal file
309
include/verilated_trace_imp.cpp
Normal file
@ -0,0 +1,309 @@
|
||||
// -*- 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 Implementation of tracing functionality common to all trace formats
|
||||
///
|
||||
//=============================================================================
|
||||
// SPDIFF_OFF
|
||||
|
||||
// clang-format off
|
||||
|
||||
#ifndef VL_DERIVED_T
|
||||
# error "This file should be included in trace format implementations"
|
||||
#endif
|
||||
|
||||
#include "verilated_trace.h"
|
||||
|
||||
// clang-format on
|
||||
|
||||
//=============================================================================
|
||||
// Static utility functions
|
||||
|
||||
static double timescaleToDouble(const char* unitp) {
|
||||
char* endp;
|
||||
double value = strtod(unitp, &endp);
|
||||
// On error so we allow just "ns" to return 1e-9.
|
||||
if (value == 0.0 && endp == unitp) value = 1;
|
||||
unitp = endp;
|
||||
for (; *unitp && isspace(*unitp); unitp++) {}
|
||||
switch (*unitp) {
|
||||
case 's': value *= 1e1; break;
|
||||
case 'm': value *= 1e-3; break;
|
||||
case 'u': value *= 1e-6; break;
|
||||
case 'n': value *= 1e-9; break;
|
||||
case 'p': value *= 1e-12; break;
|
||||
case 'f': value *= 1e-15; break;
|
||||
case 'a': value *= 1e-18; break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static std::string doubleToTimescale(double value) {
|
||||
const char* suffixp = "s";
|
||||
// clang-format off
|
||||
if (value >= 1e0) { suffixp = "s"; value *= 1e0; }
|
||||
else if (value >= 1e-3 ) { suffixp = "ms"; value *= 1e3; }
|
||||
else if (value >= 1e-6 ) { suffixp = "us"; value *= 1e6; }
|
||||
else if (value >= 1e-9 ) { suffixp = "ns"; value *= 1e9; }
|
||||
else if (value >= 1e-12) { suffixp = "ps"; value *= 1e12; }
|
||||
else if (value >= 1e-15) { suffixp = "fs"; value *= 1e15; }
|
||||
else if (value >= 1e-18) { suffixp = "as"; value *= 1e18; }
|
||||
// clang-format on
|
||||
char valuestr[100];
|
||||
sprintf(valuestr, "%3.0f%s", value, suffixp);
|
||||
return valuestr; // Gets converted to string, so no ref to stack
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Internal callback routines for each module being traced.
|
||||
|
||||
// Each module that wishes to be traced registers a set of callbacks stored in
|
||||
// this class. When the trace file is being constructed, this class provides
|
||||
// the callback routines to be executed.
|
||||
class VerilatedTraceCallInfo {
|
||||
public: // This is in .cpp file so is not widely visible
|
||||
typedef VerilatedTrace<VL_DERIVED_T>::callback_t callback_t;
|
||||
|
||||
callback_t m_initcb; ///< Initialization Callback function
|
||||
callback_t m_fullcb; ///< Full Dumping Callback function
|
||||
callback_t m_changecb; ///< Incremental Dumping Callback function
|
||||
void* m_userthis; ///< User data pointer for callback
|
||||
vluint32_t m_code; ///< Starting code number (set later by traceInit)
|
||||
// CONSTRUCTORS
|
||||
VerilatedTraceCallInfo(callback_t icb, callback_t fcb, callback_t changecb, void* ut)
|
||||
: m_initcb(icb)
|
||||
, m_fullcb(fcb)
|
||||
, m_changecb(changecb)
|
||||
, m_userthis(ut)
|
||||
, m_code(1) {}
|
||||
~VerilatedTraceCallInfo() {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTrace
|
||||
|
||||
template <>
|
||||
VerilatedTrace<VL_DERIVED_T>::VerilatedTrace()
|
||||
: m_sigs_oldvalp(NULL)
|
||||
, m_timeLastDump(0)
|
||||
, m_fullDump(true)
|
||||
, m_nextCode(0)
|
||||
, m_scopeEscape('.')
|
||||
, m_timeRes(1e-9)
|
||||
, m_timeUnit(1e-9) {
|
||||
set_time_unit(Verilated::timeunitString());
|
||||
set_time_resolution(Verilated::timeprecisionString());
|
||||
}
|
||||
|
||||
template <> VerilatedTrace<VL_DERIVED_T>::~VerilatedTrace() {
|
||||
if (m_sigs_oldvalp) VL_DO_CLEAR(delete[] m_sigs_oldvalp, m_sigs_oldvalp = NULL);
|
||||
while (!m_callbacks.empty()) {
|
||||
delete m_callbacks.back();
|
||||
m_callbacks.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Internals available to format specific implementations
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
|
||||
m_assertOne.check();
|
||||
|
||||
// Note: It is possible to re-open a trace file (VCD in particular),
|
||||
// so we must reset the next code here, but it must have the same number
|
||||
// of codes on re-open
|
||||
const vluint32_t expectedCodes = nextCode();
|
||||
m_nextCode = 1;
|
||||
|
||||
// Call all initialize callbacks, which will call decl* for each signal.
|
||||
for (vluint32_t ent = 0; ent < m_callbacks.size(); ++ent) {
|
||||
VerilatedTraceCallInfo* cip = m_callbacks[ent];
|
||||
cip->m_code = nextCode();
|
||||
(cip->m_initcb)(self(), cip->m_userthis, cip->m_code);
|
||||
}
|
||||
|
||||
if (expectedCodes && nextCode() != expectedCodes) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"Reopening trace file with different number of signals");
|
||||
}
|
||||
|
||||
// Now that we know the number of codes, allocate space for the buffer
|
||||
// holding previous signal values.
|
||||
if (!m_sigs_oldvalp) m_sigs_oldvalp = new vluint32_t[nextCode()];
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::declCode(vluint32_t code, vluint32_t bits, bool tri) {
|
||||
if (!code) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: internal trace problem, code 0 is illegal");
|
||||
}
|
||||
// Note: The tri-state flag is not used by Verilator, but is here for
|
||||
// compatibility with some foreign code.
|
||||
int codesNeeded = (bits + 31) / 32;
|
||||
if (tri) codesNeeded *= 2;
|
||||
m_nextCode = std::max(m_nextCode, code + codesNeeded);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Internals available to format specific implementations
|
||||
|
||||
template <> std::string VerilatedTrace<VL_DERIVED_T>::timeResStr() const {
|
||||
return doubleToTimescale(m_timeRes);
|
||||
}
|
||||
|
||||
template <> std::string VerilatedTrace<VL_DERIVED_T>::timeUnitStr() const {
|
||||
return doubleToTimescale(m_timeUnit);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_unit(const char* unitp) {
|
||||
m_timeUnit = timescaleToDouble(unitp);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_unit(const std::string& unit) {
|
||||
set_time_unit(unit.c_str());
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const char* unitp) {
|
||||
m_timeRes = timescaleToDouble(unitp);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const std::string& unit) {
|
||||
set_time_resolution(unit.c_str());
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(m_timeLastDump && timeui <= m_timeLastDump)) {
|
||||
VL_PRINTF_MT("%%Warning: previous dump at t=%" VL_PRI64 "u, requesting t=%" VL_PRI64
|
||||
"u, dump call ignored\n",
|
||||
m_timeLastDump, timeui);
|
||||
return;
|
||||
}
|
||||
m_timeLastDump = timeui;
|
||||
Verilated::quiesce();
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
if (!preFullDump()) return;
|
||||
emitTimeChange(timeui);
|
||||
m_fullDump = false; // No more need for next dump to be full
|
||||
for (vluint32_t ent = 0; ent < m_callbacks.size(); ent++) {
|
||||
VerilatedTraceCallInfo* cip = m_callbacks[ent];
|
||||
(cip->m_fullcb)(self(), cip->m_userthis, cip->m_code);
|
||||
}
|
||||
} else {
|
||||
if (!preChangeDump()) return;
|
||||
emitTimeChange(timeui);
|
||||
for (vluint32_t ent = 0; ent < m_callbacks.size(); ++ent) {
|
||||
VerilatedTraceCallInfo* cip = m_callbacks[ent];
|
||||
(cip->m_changecb)(self(), cip->m_userthis, cip->m_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Non-hot path internal interface to Verilator generated code
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::addCallback(callback_t initcb, callback_t fullcb,
|
||||
callback_t changecb,
|
||||
void* userthis) VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(timeLastDump() != 0)) {
|
||||
std::string msg = (std::string("Internal: ") + __FILE__ + "::" + __FUNCTION__
|
||||
+ " called with already open file");
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
}
|
||||
VerilatedTraceCallInfo* cip = new VerilatedTraceCallInfo(initcb, fullcb, changecb, userthis);
|
||||
m_callbacks.push_back(cip);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Hot path internal interface to Verilator generated code
|
||||
|
||||
// These functions must write the new value back into the old value store,
|
||||
// and subsequently call the format specific emit* implementations. Note
|
||||
// that this file must be included in the format specific implementation, so
|
||||
// the emit* functions can be inlined for performance.
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::fullBit(vluint32_t* oldp, vluint32_t newval) {
|
||||
*oldp = newval;
|
||||
self()->emitBit(oldp - m_sigs_oldvalp, newval);
|
||||
}
|
||||
|
||||
// We want these functions specialized for sizes to avoid hard to predict
|
||||
// branches, but we don't want them inlined, so we explicitly instantiate the
|
||||
// template for each size used by Verilator.
|
||||
template <>
|
||||
template <int T_Bits>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullBus(vluint32_t* oldp, vluint32_t newval) {
|
||||
*oldp = newval;
|
||||
self()->emitBus<T_Bits>(oldp - m_sigs_oldvalp, newval);
|
||||
}
|
||||
|
||||
// Note: No specialization for width 1, covered by 'fullBit'
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<2>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<3>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<4>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<5>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<6>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<7>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<8>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<9>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<10>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<11>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<12>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<13>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<14>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<15>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<16>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<17>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<18>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<19>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<20>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<21>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<22>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<23>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<24>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<25>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<26>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<27>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<28>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<29>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<30>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<31>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedTrace<VL_DERIVED_T>::fullBus<32>(vluint32_t* oldp, vluint32_t newval);
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullQuad(vluint32_t* oldp, vluint64_t newval, int bits) {
|
||||
*reinterpret_cast<vluint64_t*>(oldp) = newval;
|
||||
self()->emitQuad(oldp - m_sigs_oldvalp, newval, bits);
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullArray(vluint32_t* oldp, const vluint32_t* newvalp,
|
||||
int bits) {
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) oldp[i] = newvalp[i];
|
||||
self()->emitArray(oldp - m_sigs_oldvalp, newvalp, bits);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::fullFloat(vluint32_t* oldp, float newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
*reinterpret_cast<float*>(oldp) = newval;
|
||||
self()->emitFloat(oldp - m_sigs_oldvalp, newval);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::fullDouble(vluint32_t* oldp, double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
*reinterpret_cast<double*>(oldp) = newval;
|
||||
self()->emitDouble(oldp - m_sigs_oldvalp, newval);
|
||||
}
|
@ -60,6 +60,13 @@
|
||||
// cache-lines.
|
||||
#define VL_TRACE_SUFFIX_ENTRY_SIZE 8 ///< Size of a suffix entry
|
||||
|
||||
//=============================================================================
|
||||
// Specialization of the generics for this trace format
|
||||
|
||||
#define VL_DERIVED_T VerilatedVcd
|
||||
#include "verilated_trace_imp.cpp"
|
||||
#undef VL_DERIVED_T
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcdImp
|
||||
/// Base class to hold some static state
|
||||
@ -101,33 +108,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcdCallInfo
|
||||
/// Internal callback routines for each module being traced.
|
||||
////
|
||||
/// Each module that wishes to be traced registers a set of
|
||||
/// callbacks stored in this class. When the trace file is being
|
||||
/// constructed, this class provides the callback routines to be executed.
|
||||
|
||||
class VerilatedVcdCallInfo {
|
||||
protected:
|
||||
friend class VerilatedVcd;
|
||||
VerilatedVcdCallback_t m_initcb; ///< Initialization Callback function
|
||||
VerilatedVcdCallback_t m_fullcb; ///< Full Dumping Callback function
|
||||
VerilatedVcdCallback_t m_changecb; ///< Incremental Dumping Callback function
|
||||
void* m_userthis; ///< Fake "this" for caller
|
||||
vluint32_t m_code; ///< Starting code number (set later by traceInit)
|
||||
// CONSTRUCTORS
|
||||
VerilatedVcdCallInfo(VerilatedVcdCallback_t icb, VerilatedVcdCallback_t fcb,
|
||||
VerilatedVcdCallback_t changecb, void* ut)
|
||||
: m_initcb(icb)
|
||||
, m_fullcb(fcb)
|
||||
, m_changecb(changecb)
|
||||
, m_userthis(ut)
|
||||
, m_code(1) {}
|
||||
~VerilatedVcdCallInfo() {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
@ -153,26 +133,18 @@ ssize_t VerilatedVcdFile::write(const char* bufp, ssize_t len) VL_MT_UNSAFE {
|
||||
VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep)
|
||||
: m_isOpen(false)
|
||||
, m_rolloverMB(0)
|
||||
, m_modDepth(0)
|
||||
, m_nextCode(1) {
|
||||
, m_modDepth(0) {
|
||||
// Not in header to avoid link issue if header is included without this .cpp file
|
||||
m_fileNewed = (filep == NULL);
|
||||
m_filep = m_fileNewed ? new VerilatedVcdFile : filep;
|
||||
m_namemapp = NULL;
|
||||
m_timeLastDump = 0;
|
||||
m_sigs_oldvalp = NULL;
|
||||
m_evcd = false;
|
||||
m_scopeEscape = '.'; // Backward compatibility
|
||||
m_fullDump = true;
|
||||
m_wrChunkSize = 8 * 1024;
|
||||
m_wrBufp = new char[m_wrChunkSize * 8];
|
||||
m_wrFlushp = m_wrBufp + m_wrChunkSize * 6;
|
||||
m_writep = m_wrBufp;
|
||||
m_wroteBytes = 0;
|
||||
m_suffixesp = NULL;
|
||||
m_timeRes = m_timeUnit = 1e-9;
|
||||
set_time_unit(Verilated::timeunitString());
|
||||
set_time_resolution(Verilated::timeprecisionString());
|
||||
}
|
||||
|
||||
void VerilatedVcd::open(const char* filename) {
|
||||
@ -193,16 +165,11 @@ void VerilatedVcd::open(const char* filename) {
|
||||
|
||||
dumpHeader();
|
||||
|
||||
// Allocate space now we know the number of codes
|
||||
if (!m_sigs_oldvalp) m_sigs_oldvalp = new vluint32_t[m_nextCode + 10];
|
||||
|
||||
// Get the direct access pointer to the code strings
|
||||
m_suffixesp = &m_suffixes[0]; // Note: C++11 m_suffixes.data();
|
||||
|
||||
if (m_rolloverMB) {
|
||||
openNext(true);
|
||||
if (!isOpen()) return;
|
||||
}
|
||||
// When using rollover, the first chunk contains the header only.
|
||||
if (m_rolloverMB) openNext(true);
|
||||
}
|
||||
|
||||
void VerilatedVcd::openNext(bool incFilename) {
|
||||
@ -245,21 +212,27 @@ void VerilatedVcd::openNext(bool incFilename) {
|
||||
}
|
||||
}
|
||||
m_isOpen = true;
|
||||
m_fullDump = true; // First dump must be full
|
||||
fullDump(true); // First dump must be full
|
||||
m_wroteBytes = 0;
|
||||
}
|
||||
|
||||
bool VerilatedVcd::preChangeDump() {
|
||||
if (VL_UNLIKELY(m_rolloverMB && m_wroteBytes > m_rolloverMB)) { openNext(true); }
|
||||
return isOpen();
|
||||
}
|
||||
|
||||
void VerilatedVcd::emitTimeChange(vluint64_t timeui) {
|
||||
printStr("#");
|
||||
printQuad(timeui);
|
||||
printStr("\n");
|
||||
}
|
||||
|
||||
void VerilatedVcd::makeNameMap() {
|
||||
// Take signal information from each module and build m_namemapp
|
||||
deleteNameMap();
|
||||
m_nextCode = 1;
|
||||
m_namemapp = new NameMap;
|
||||
for (vluint32_t ent = 0; ent < m_callbacks.size(); ent++) {
|
||||
VerilatedVcdCallInfo* cip = m_callbacks[ent];
|
||||
cip->m_code = m_nextCode;
|
||||
// Initialize; callbacks will call decl* which update m_nextCode
|
||||
(cip->m_initcb)(this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
|
||||
VerilatedTrace<VerilatedVcd>::traceInit();
|
||||
|
||||
// Though not speced, it's illegal to generate a vcd with signals
|
||||
// not under any module - it crashes at least two viewers.
|
||||
@ -292,13 +265,8 @@ void VerilatedVcd::deleteNameMap() {
|
||||
VerilatedVcd::~VerilatedVcd() {
|
||||
close();
|
||||
if (m_wrBufp) VL_DO_CLEAR(delete[] m_wrBufp, m_wrBufp = NULL);
|
||||
if (m_sigs_oldvalp) VL_DO_CLEAR(delete[] m_sigs_oldvalp, m_sigs_oldvalp = NULL);
|
||||
deleteNameMap();
|
||||
if (m_filep && m_fileNewed) VL_DO_CLEAR(delete m_filep, m_filep = NULL);
|
||||
for (CallbackVec::const_iterator it = m_callbacks.begin(); it != m_callbacks.end(); ++it) {
|
||||
delete *it;
|
||||
}
|
||||
m_callbacks.clear();
|
||||
VerilatedVcdSingleton::removeVcd(this);
|
||||
}
|
||||
|
||||
@ -328,7 +296,7 @@ void VerilatedVcd::close() {
|
||||
if (!isOpen()) return;
|
||||
if (m_evcd) {
|
||||
printStr("$vcdclose ");
|
||||
printTime(m_timeLastDump);
|
||||
printQuad(timeLastDump());
|
||||
printStr(" $end\n");
|
||||
}
|
||||
closePrev();
|
||||
@ -348,21 +316,6 @@ void VerilatedVcd::printQuad(vluint64_t n) {
|
||||
printStr(buf);
|
||||
}
|
||||
|
||||
void VerilatedVcd::printTime(vluint64_t timeui) {
|
||||
// VCD file format specification does not allow non-integers for timestamps
|
||||
// Dinotrace doesn't mind, but Cadence Vision seems to choke
|
||||
if (VL_UNLIKELY(timeui < m_timeLastDump)) {
|
||||
timeui = m_timeLastDump;
|
||||
static VL_THREAD_LOCAL bool backTime = false;
|
||||
if (!backTime) {
|
||||
backTime = true;
|
||||
VL_PRINTF_MT("%%Warning: VCD time is moving backwards, wave file may be incorrect.\n");
|
||||
}
|
||||
}
|
||||
m_timeLastDump = timeui;
|
||||
printQuad(timeui);
|
||||
}
|
||||
|
||||
void VerilatedVcd::bufferResize(vluint64_t minsize) {
|
||||
// minsize is size of largest write. We buffer at least 8 times as much data,
|
||||
// writing when we are 3/4 full (with thus 2*minsize remaining free)
|
||||
@ -408,56 +361,6 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
|
||||
m_writep = m_wrBufp;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Simple methods
|
||||
|
||||
void VerilatedVcd::set_time_unit(const char* unitp) {
|
||||
// cout<<" set_time_unit("<<unitp<<") == "<<timescaleToDouble(unitp)
|
||||
// <<" == "<<doubleToTimescale(timescaleToDouble(unitp))<<endl;
|
||||
m_timeUnit = timescaleToDouble(unitp);
|
||||
}
|
||||
|
||||
void VerilatedVcd::set_time_resolution(const char* unitp) {
|
||||
// cout<<"set_time_resolution("<<unitp<<") == "<<timescaleToDouble(unitp)
|
||||
// <<" == "<<doubleToTimescale(timescaleToDouble(unitp))<<endl;
|
||||
m_timeRes = timescaleToDouble(unitp);
|
||||
}
|
||||
|
||||
double VerilatedVcd::timescaleToDouble(const char* unitp) {
|
||||
char* endp;
|
||||
double value = strtod(unitp, &endp);
|
||||
// On error so we allow just "ns" to return 1e-9.
|
||||
if (value == 0.0 && endp == unitp) value = 1;
|
||||
unitp = endp;
|
||||
for (; *unitp && isspace(*unitp); unitp++) {}
|
||||
switch (*unitp) {
|
||||
case 's': value *= 1e1; break;
|
||||
case 'm': value *= 1e-3; break;
|
||||
case 'u': value *= 1e-6; break;
|
||||
case 'n': value *= 1e-9; break;
|
||||
case 'p': value *= 1e-12; break;
|
||||
case 'f': value *= 1e-15; break;
|
||||
case 'a': value *= 1e-18; break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string VerilatedVcd::doubleToTimescale(double value) {
|
||||
const char* suffixp = "s";
|
||||
// clang-format off
|
||||
if (value >= 1e0) { suffixp = "s"; value *= 1e0; }
|
||||
else if (value >= 1e-3 ) { suffixp = "ms"; value *= 1e3; }
|
||||
else if (value >= 1e-6 ) { suffixp = "us"; value *= 1e6; }
|
||||
else if (value >= 1e-9 ) { suffixp = "ns"; value *= 1e9; }
|
||||
else if (value >= 1e-12) { suffixp = "ps"; value *= 1e12; }
|
||||
else if (value >= 1e-15) { suffixp = "fs"; value *= 1e15; }
|
||||
else if (value >= 1e-18) { suffixp = "as"; value *= 1e18; }
|
||||
// clang-format on
|
||||
char valuestr[100];
|
||||
sprintf(valuestr, "%3.0f%s", value, suffixp);
|
||||
return valuestr; // Gets converted to string, so no ref to stack
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VCD string code
|
||||
|
||||
@ -490,8 +393,7 @@ void VerilatedVcd::dumpHeader() {
|
||||
printStr(" $end\n");
|
||||
|
||||
printStr("$timescale ");
|
||||
const std::string& timeResStr = doubleToTimescale(m_timeRes);
|
||||
printStr(timeResStr.c_str());
|
||||
printStr(timeResStr().c_str()); // lintok-begin-on-ref
|
||||
printStr(" $end\n");
|
||||
|
||||
makeNameMap();
|
||||
@ -571,37 +473,19 @@ void VerilatedVcd::dumpHeader() {
|
||||
deleteNameMap();
|
||||
}
|
||||
|
||||
void VerilatedVcd::module(const std::string& name) {
|
||||
m_assertOne.check();
|
||||
m_modName = name;
|
||||
}
|
||||
|
||||
void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep, bool array,
|
||||
int arraynum, bool tri, bool bussed, int msb, int lsb) {
|
||||
if (!code) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: internal trace problem, code 0 is illegal");
|
||||
}
|
||||
const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
|
||||
|
||||
int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
|
||||
int codesNeeded = 1 + int(bits / 32);
|
||||
if (tri) codesNeeded *= 2; // Space in change array for __en signals
|
||||
VerilatedTrace<VerilatedVcd>::declCode(code, bits, tri);
|
||||
|
||||
// Make sure array is large enough
|
||||
m_nextCode = std::max(m_nextCode, code + codesNeeded);
|
||||
if (m_sigs.capacity() <= m_nextCode) {
|
||||
m_sigs.reserve(m_nextCode * 2); // Power-of-2 allocation speeds things up
|
||||
}
|
||||
if (m_suffixes.size() <= m_nextCode * VL_TRACE_SUFFIX_ENTRY_SIZE) {
|
||||
m_suffixes.resize(m_nextCode * VL_TRACE_SUFFIX_ENTRY_SIZE * 2, 0);
|
||||
if (m_suffixes.size() <= nextCode() * VL_TRACE_SUFFIX_ENTRY_SIZE) {
|
||||
m_suffixes.resize(nextCode() * VL_TRACE_SUFFIX_ENTRY_SIZE * 2, 0);
|
||||
}
|
||||
|
||||
// Make sure write buffer is large enough (one character per bit), plus header
|
||||
bufferResize(bits + 1024);
|
||||
|
||||
// Save declaration info
|
||||
VerilatedVcdSig sig = VerilatedVcdSig(code, bits);
|
||||
m_sigs.push_back(sig);
|
||||
|
||||
// Split name into basename
|
||||
// Spaces and tabs aren't legal in VCD signal names, so:
|
||||
// Space separates each level of scope
|
||||
@ -609,8 +493,8 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
|
||||
// Tab sorts before spaces, so signals nicely will print before scopes
|
||||
// Note the hiername may be nothing, if so we'll add "\t{name}"
|
||||
std::string nameasstr = name;
|
||||
if (!m_modName.empty()) {
|
||||
nameasstr = m_modName + m_scopeEscape + nameasstr; // Optional ->module prefix
|
||||
if (!moduleName().empty()) {
|
||||
nameasstr = moduleName() + scopeEscape() + nameasstr; // Optional ->module prefix
|
||||
}
|
||||
std::string hiername;
|
||||
std::string basename;
|
||||
@ -693,7 +577,7 @@ void VerilatedVcd::declFloat(vluint32_t code, const char* name, bool array, int
|
||||
void VerilatedVcd::declDouble(vluint32_t code, const char* name, bool array, int arraynum) {
|
||||
declare(code, name, "real", array, arraynum, false, false, 63, 0);
|
||||
}
|
||||
#ifndef VL_TRACE_VCD_OLD_API
|
||||
#ifdef VL_TRACE_VCD_OLD_API
|
||||
void VerilatedVcd::declTriBit(vluint32_t code, const char* name, bool array, int arraynum) {
|
||||
declare(code, name, "wire", array, arraynum, true, false, 0, 0);
|
||||
}
|
||||
@ -714,14 +598,11 @@ void VerilatedVcd::declTriArray(vluint32_t code, const char* name, bool array, i
|
||||
//=============================================================================
|
||||
// Trace recording routines
|
||||
|
||||
#ifndef VL_TRACE_VCD_OLD_API
|
||||
|
||||
//=============================================================================
|
||||
// Pointer based variants used by Verilator
|
||||
// Emit trace entries
|
||||
|
||||
// Emit suffix, write back write pointer, check buffer
|
||||
void VerilatedVcd::finishLine(vluint32_t* oldp, char* writep) {
|
||||
const vluint32_t code = oldp - m_sigs_oldvalp;
|
||||
void VerilatedVcd::finishLine(vluint32_t code, char* writep) {
|
||||
const char* const suffixp = m_suffixesp + code * VL_TRACE_SUFFIX_ENTRY_SIZE;
|
||||
// Copy the whole suffix (this avoid having hard to predict branches which
|
||||
// helps a lot). Note suffixp could be aligned, so could load it in one go,
|
||||
@ -742,20 +623,13 @@ void VerilatedVcd::finishLine(vluint32_t* oldp, char* writep) {
|
||||
bufferCheck();
|
||||
}
|
||||
|
||||
void VerilatedVcd::fullBit(vluint32_t* oldp, vluint32_t newval) {
|
||||
*oldp = newval;
|
||||
void VerilatedVcd::emitBit(vluint32_t code, vluint32_t newval) {
|
||||
char* wp = m_writep;
|
||||
*wp++ = '0' | static_cast<char>(newval);
|
||||
finishLine(oldp, wp);
|
||||
finishLine(code, wp);
|
||||
}
|
||||
|
||||
// We do want these functions specialized for sizes to avoid hard to predict
|
||||
// branches, but we don't want them inlined, so we explicitly create one
|
||||
// specialization for each size used here here.
|
||||
|
||||
// T_Bits is the number of used bits in the value
|
||||
template <int T_Bits> void VerilatedVcd::fullBus(vluint32_t* oldp, vluint32_t newval) {
|
||||
*oldp = newval;
|
||||
template <int T_Bits> void VerilatedVcd::emitBus(vluint32_t code, vluint32_t newval) {
|
||||
char* wp = m_writep;
|
||||
*wp++ = 'b';
|
||||
newval <<= 32 - T_Bits;
|
||||
@ -764,44 +638,10 @@ template <int T_Bits> void VerilatedVcd::fullBus(vluint32_t* oldp, vluint32_t ne
|
||||
*wp++ = '0' | static_cast<char>(newval >> 31);
|
||||
newval <<= 1;
|
||||
} while (--bits);
|
||||
finishLine(oldp, wp);
|
||||
finishLine(code, wp);
|
||||
}
|
||||
// Note: No specialization for width 1, covered by 'fullBit'
|
||||
template void VerilatedVcd::fullBus<2>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<3>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<4>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<5>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<6>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<7>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<8>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<9>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<10>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<11>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<12>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<13>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<14>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<15>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<16>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<17>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<18>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<19>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<20>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<21>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<22>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<23>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<24>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<25>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<26>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<27>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<28>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<29>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<30>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<31>(vluint32_t* oldp, vluint32_t newval);
|
||||
template void VerilatedVcd::fullBus<32>(vluint32_t* oldp, vluint32_t newval);
|
||||
|
||||
// T_Bits is the number of used bits in the value
|
||||
void VerilatedVcd::fullQuad(vluint32_t* oldp, vluint64_t newval, int bits) {
|
||||
*reinterpret_cast<vluint64_t*>(oldp) = newval;
|
||||
void VerilatedVcd::emitQuad(vluint32_t code, vluint64_t newval, int bits) {
|
||||
char* wp = m_writep;
|
||||
*wp++ = 'b';
|
||||
newval <<= 64 - bits;
|
||||
@ -850,12 +690,11 @@ void VerilatedVcd::fullQuad(vluint32_t* oldp, vluint64_t newval, int bits) {
|
||||
*wp++ = '0' | static_cast<char>(newval >> 63);
|
||||
newval <<= 1;
|
||||
} while (--remaining);
|
||||
finishLine(oldp, wp);
|
||||
finishLine(code, wp);
|
||||
}
|
||||
|
||||
void VerilatedVcd::fullArray(vluint32_t* oldp, const vluint32_t* newvalp, int bits) {
|
||||
void VerilatedVcd::emitArray(vluint32_t code, const vluint32_t* newvalp, int bits) {
|
||||
int words = (bits + 31) / 32;
|
||||
for (int i = 0; i < words; ++i) oldp[i] = newvalp[i];
|
||||
char* wp = m_writep;
|
||||
*wp++ = 'b';
|
||||
// Handle the most significant word
|
||||
@ -907,41 +746,37 @@ void VerilatedVcd::fullArray(vluint32_t* oldp, const vluint32_t* newvalp, int bi
|
||||
val <<= 1;
|
||||
} while (--bits);
|
||||
}
|
||||
finishLine(oldp, wp);
|
||||
finishLine(code, wp);
|
||||
}
|
||||
|
||||
void VerilatedVcd::fullFloat(vluint32_t* oldp, float newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
*reinterpret_cast<float*>(oldp) = newval;
|
||||
void VerilatedVcd::emitFloat(vluint32_t code, float newval) {
|
||||
char* wp = m_writep;
|
||||
// Buffer can't overflow before sprintf; we sized during declaration
|
||||
sprintf(wp, "r%.16g", static_cast<double>(newval));
|
||||
wp += strlen(wp);
|
||||
finishLine(oldp, wp);
|
||||
finishLine(code, wp);
|
||||
}
|
||||
|
||||
void VerilatedVcd::fullDouble(vluint32_t* oldp, double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
*reinterpret_cast<double*>(oldp) = newval;
|
||||
void VerilatedVcd::emitDouble(vluint32_t code, double newval) {
|
||||
char* wp = m_writep;
|
||||
// Buffer can't overflow before sprintf; we sized during declaration
|
||||
sprintf(wp, "r%.16g", newval);
|
||||
wp += strlen(wp);
|
||||
finishLine(oldp, wp);
|
||||
finishLine(code, wp);
|
||||
}
|
||||
|
||||
#else // VL_TRACE_VCD_OLD_API
|
||||
#ifdef VL_TRACE_VCD_OLD_API
|
||||
|
||||
void VerilatedVcd::fullBit(vluint32_t code, const vluint32_t newval) {
|
||||
// Note the &1, so we don't require clean input -- makes more common no change case faster
|
||||
m_sigs_oldvalp[code] = newval;
|
||||
*oldp(code) = newval;
|
||||
*m_writep++ = ('0' + static_cast<char>(newval & 1));
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullBus(vluint32_t code, const vluint32_t newval, int bits) {
|
||||
m_sigs_oldvalp[code] = newval;
|
||||
*oldp(code) = newval;
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = ((newval & (1L << bit)) ? '1' : '0');
|
||||
@ -952,7 +787,7 @@ void VerilatedVcd::fullBus(vluint32_t code, const vluint32_t newval, int bits) {
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullQuad(vluint32_t code, const vluint64_t newval, int bits) {
|
||||
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) = newval;
|
||||
(*(reinterpret_cast<vluint64_t*>(oldp(code)))) = newval;
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = ((newval & (VL_ULL(1) << bit)) ? '1' : '0');
|
||||
@ -963,9 +798,7 @@ void VerilatedVcd::fullQuad(vluint32_t code, const vluint64_t newval, int bits)
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullArray(vluint32_t code, const vluint32_t* newval, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
|
||||
m_sigs_oldvalp[code + word] = newval[word];
|
||||
}
|
||||
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) { oldp(code)[word] = newval[word]; }
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = ((newval[(bit / 32)] & (1L << (bit & 0x1f))) ? '1' : '0');
|
||||
@ -976,9 +809,7 @@ void VerilatedVcd::fullArray(vluint32_t code, const vluint32_t* newval, int bits
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullArray(vluint32_t code, const vluint64_t* newval, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 64) + 1); ++word) {
|
||||
m_sigs_oldvalp[code + word] = newval[word];
|
||||
}
|
||||
for (int word = 0; word < (((bits - 1) / 64) + 1); ++word) { oldp(code)[word] = newval[word]; }
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = ((newval[(bit / 64)] & (VL_ULL(1) << (bit & 0x3f))) ? '1' : '0');
|
||||
@ -989,17 +820,17 @@ void VerilatedVcd::fullArray(vluint32_t code, const vluint64_t* newval, int bits
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
|
||||
m_sigs_oldvalp[code] = newval;
|
||||
m_sigs_oldvalp[code + 1] = newtri;
|
||||
*m_writep++ = "01zz"[m_sigs_oldvalp[code] | (m_sigs_oldvalp[code + 1] << 1)];
|
||||
oldp(code)[0] = newval;
|
||||
oldp(code)[1] = newtri;
|
||||
*m_writep++ = "01zz"[newval | (newtri << 1)];
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri,
|
||||
int bits) {
|
||||
m_sigs_oldvalp[code] = newval;
|
||||
m_sigs_oldvalp[code + 1] = newtri;
|
||||
oldp(code)[0] = newval;
|
||||
oldp(code)[1] = newtri;
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = "01zz"[((newval >> bit) & 1) | (((newtri >> bit) & 1) << 1)];
|
||||
@ -1011,8 +842,8 @@ void VerilatedVcd::fullTriBus(vluint32_t code, const vluint32_t newval, const vl
|
||||
}
|
||||
void VerilatedVcd::fullTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri,
|
||||
int bits) {
|
||||
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) = newval;
|
||||
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code + 1]))) = newtri;
|
||||
(*(reinterpret_cast<vluint64_t*>(oldp(code)))) = newval;
|
||||
(*(reinterpret_cast<vluint64_t*>(oldp(code + 1)))) = newtri;
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++
|
||||
@ -1026,8 +857,8 @@ void VerilatedVcd::fullTriQuad(vluint32_t code, const vluint64_t newval, const v
|
||||
void VerilatedVcd::fullTriArray(vluint32_t code, const vluint32_t* newvalp,
|
||||
const vluint32_t* newtrip, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
|
||||
m_sigs_oldvalp[code + word * 2] = newvalp[word];
|
||||
m_sigs_oldvalp[code + word * 2 + 1] = newtrip[word];
|
||||
oldp(code)[word * 2] = newvalp[word];
|
||||
oldp(code)[word * 2 + 1] = newtrip[word];
|
||||
}
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
@ -1042,7 +873,7 @@ void VerilatedVcd::fullTriArray(vluint32_t code, const vluint32_t* newvalp,
|
||||
}
|
||||
void VerilatedVcd::fullDouble(vluint32_t code, const double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
(*(reinterpret_cast<double*>(&m_sigs_oldvalp[code]))) = newval;
|
||||
(*(reinterpret_cast<double*>(oldp(code)))) = newval;
|
||||
// Buffer can't overflow before sprintf; we sized during declaration
|
||||
sprintf(m_writep, "r%.16g", newval);
|
||||
m_writep += strlen(m_writep);
|
||||
@ -1053,7 +884,7 @@ void VerilatedVcd::fullDouble(vluint32_t code, const double newval) {
|
||||
}
|
||||
void VerilatedVcd::fullFloat(vluint32_t code, const float newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
(*(reinterpret_cast<float*>(&m_sigs_oldvalp[code]))) = newval;
|
||||
(*(reinterpret_cast<float*>(oldp(code)))) = newval;
|
||||
// Buffer can't overflow before sprintf; we sized during declaration
|
||||
sprintf(m_writep, "r%.16g", static_cast<double>(newval));
|
||||
m_writep += strlen(m_writep);
|
||||
@ -1081,60 +912,6 @@ void VerilatedVcd::fullArrayX(vluint32_t code, int bits) { fullBusX(code, bits);
|
||||
|
||||
#endif // VL_TRACE_VCD_OLD_API
|
||||
|
||||
//=============================================================================
|
||||
// Callbacks
|
||||
|
||||
void VerilatedVcd::addCallback(VerilatedVcdCallback_t initcb, VerilatedVcdCallback_t fullcb,
|
||||
VerilatedVcdCallback_t changecb, void* userthis) VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(isOpen())) {
|
||||
std::string msg = std::string("Internal: ") + __FILE__ + "::" + __FUNCTION__
|
||||
+ " called with already open file";
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
}
|
||||
VerilatedVcdCallInfo* cip = new VerilatedVcdCallInfo(initcb, fullcb, changecb, userthis);
|
||||
m_callbacks.push_back(cip);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Dumping
|
||||
|
||||
void VerilatedVcd::dumpFull(vluint64_t timeui) {
|
||||
m_assertOne.check();
|
||||
dumpPrep(timeui);
|
||||
Verilated::quiesce();
|
||||
for (vluint32_t ent = 0; ent < m_callbacks.size(); ent++) {
|
||||
VerilatedVcdCallInfo* cip = m_callbacks[ent];
|
||||
(cip->m_fullcb)(this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::dump(vluint64_t timeui) {
|
||||
m_assertOne.check();
|
||||
if (!isOpen()) return;
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
m_fullDump = false; // No more need for next dump to be full
|
||||
dumpFull(timeui);
|
||||
return;
|
||||
}
|
||||
if (VL_UNLIKELY(m_rolloverMB && m_wroteBytes > this->m_rolloverMB)) {
|
||||
openNext(true);
|
||||
if (!isOpen()) return;
|
||||
}
|
||||
dumpPrep(timeui);
|
||||
Verilated::quiesce();
|
||||
for (vluint32_t ent = 0; ent < m_callbacks.size(); ++ent) {
|
||||
VerilatedVcdCallInfo* cip = m_callbacks[ent];
|
||||
(cip->m_changecb)(this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::dumpPrep(vluint64_t timeui) {
|
||||
printStr("#");
|
||||
printTime(timeui);
|
||||
printStr("\n");
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Static members
|
||||
|
||||
|
@ -20,15 +20,14 @@
|
||||
#ifndef _VERILATED_VCD_C_H_
|
||||
#define _VERILATED_VCD_C_H_ 1
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
#include "verilated_trace.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class VerilatedVcd;
|
||||
class VerilatedVcdCallInfo;
|
||||
|
||||
// SPDIFF_ON
|
||||
//=============================================================================
|
||||
@ -48,48 +47,26 @@ public:
|
||||
virtual ssize_t write(const char* bufp, ssize_t len) VL_MT_UNSAFE;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcdSig
|
||||
/// Internal data on one signal being traced.
|
||||
|
||||
class VerilatedVcdSig {
|
||||
protected:
|
||||
friend class VerilatedVcd;
|
||||
vluint32_t m_code; ///< VCD file code number
|
||||
int m_bits; ///< Size of value in bits
|
||||
VerilatedVcdSig(vluint32_t code, int bits)
|
||||
: m_code(code)
|
||||
, m_bits(bits) {}
|
||||
|
||||
public:
|
||||
~VerilatedVcdSig() {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
|
||||
typedef void (*VerilatedVcdCallback_t)(VerilatedVcd* vcdp, void* userthis, vluint32_t code);
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcd
|
||||
/// Base class to create a Verilator VCD dump
|
||||
/// This is an internally used class - see VerilatedVcdC for what to call from applications
|
||||
|
||||
class VerilatedVcd {
|
||||
class VerilatedVcd : public VerilatedTrace<VerilatedVcd> {
|
||||
private:
|
||||
// Give the superclass access to private bits (to avoid virtual functions)
|
||||
friend class VerilatedTrace<VerilatedVcd>;
|
||||
|
||||
//=========================================================================
|
||||
// VCD specific internals
|
||||
|
||||
VerilatedVcdFile* m_filep; ///< File we're writing to
|
||||
bool m_fileNewed; ///< m_filep needs destruction
|
||||
bool m_isOpen; ///< True indicates open file
|
||||
bool m_evcd; ///< True for evcd format
|
||||
std::string m_filename; ///< Filename we're writing to (if open)
|
||||
vluint64_t m_rolloverMB; ///< MB of file size to rollover at
|
||||
char m_scopeEscape; ///< Character to separate scope components
|
||||
int m_modDepth; ///< Depth of module hierarchy
|
||||
bool m_fullDump; ///< True indicates dump ignoring if changed
|
||||
vluint32_t m_nextCode; ///< Next code number to assign
|
||||
std::string m_modName; ///< Module name being traced now
|
||||
double m_timeRes; ///< Time resolution (ns/ms etc)
|
||||
double m_timeUnit; ///< Time units (ns/ms etc)
|
||||
vluint64_t m_timeLastDump; ///< Last time we did a dump
|
||||
|
||||
char* m_wrBufp; ///< Output buffer
|
||||
char* m_wrFlushp; ///< Output buffer flush trigger location
|
||||
@ -100,16 +77,9 @@ private:
|
||||
std::vector<char> m_suffixes; ///< VCD line end string codes + metadata
|
||||
const char* m_suffixesp; ///< Pointer to first element of above
|
||||
|
||||
vluint32_t* m_sigs_oldvalp; ///< Pointer to old signal values
|
||||
typedef std::vector<VerilatedVcdSig> SigVec;
|
||||
SigVec m_sigs; ///< Pointer to signal information
|
||||
typedef std::vector<VerilatedVcdCallInfo*> CallbackVec;
|
||||
CallbackVec m_callbacks; ///< Routines to perform dumping
|
||||
typedef std::map<std::string, std::string> NameMap;
|
||||
NameMap* m_namemapp; ///< List of names for the header
|
||||
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
|
||||
void bufferResize(vluint64_t minsize);
|
||||
void bufferFlush() VL_MT_UNSAFE_ONE;
|
||||
inline void bufferCheck() {
|
||||
@ -130,165 +100,112 @@ private:
|
||||
bool tri, bool bussed, int msb, int lsb);
|
||||
|
||||
void dumpHeader();
|
||||
void dumpPrep(vluint64_t timeui);
|
||||
void dumpFull(vluint64_t timeui);
|
||||
// cppcheck-suppress functionConst
|
||||
void dumpDone();
|
||||
|
||||
char* writeCode(char* writep, vluint32_t code);
|
||||
|
||||
void finishLine(vluint32_t* oldp, char* writep);
|
||||
void finishLine(vluint32_t code, char* writep);
|
||||
|
||||
/// Flush any remaining data from all files
|
||||
static void flush_all() VL_MT_UNSAFE_ONE;
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedVcd);
|
||||
|
||||
protected:
|
||||
//=========================================================================
|
||||
// Implementation of VerilatedTrace interface
|
||||
|
||||
// Implementations of protected virtual methods for VerilatedTrace
|
||||
void emitTimeChange(vluint64_t timeui) VL_OVERRIDE;
|
||||
|
||||
// Hooks called from VerilatedTrace
|
||||
bool preFullDump() VL_OVERRIDE { return isOpen(); }
|
||||
bool preChangeDump() VL_OVERRIDE;
|
||||
|
||||
// Implementations of duck-typed methods for VerilatedTrace
|
||||
void emitBit(vluint32_t code, vluint32_t newval);
|
||||
template <int T_Bits> void emitBus(vluint32_t code, vluint32_t newval);
|
||||
void emitQuad(vluint32_t code, vluint64_t newval, int bits);
|
||||
void emitArray(vluint32_t code, const vluint32_t* newvalp, int bits);
|
||||
void emitFloat(vluint32_t code, float newval);
|
||||
void emitDouble(vluint32_t code, double newval);
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
|
||||
explicit VerilatedVcd(VerilatedVcdFile* filep = NULL);
|
||||
~VerilatedVcd();
|
||||
/// Routines can only be called from one thread; allow next call from different thread
|
||||
void changeThread() { m_assertOne.changeThread(); }
|
||||
|
||||
// ACCESSORS
|
||||
/// Set size in megabytes after which new file should be created
|
||||
void rolloverMB(vluint64_t rolloverMB) { m_rolloverMB = rolloverMB; }
|
||||
/// Is file open?
|
||||
bool isOpen() const { return m_isOpen; }
|
||||
/// Change character that splits scopes. Note whitespace are ALWAYS escapes.
|
||||
void scopeEscape(char flag) { m_scopeEscape = flag; }
|
||||
/// Is this an escape?
|
||||
inline bool isScopeEscape(char c) { return isspace(c) || c == m_scopeEscape; }
|
||||
|
||||
// METHODS
|
||||
/// Open the file; call isOpen() to see if errors
|
||||
void open(const char* filename) VL_MT_UNSAFE_ONE;
|
||||
void openNext(bool incFilename); ///< Open next data-only file
|
||||
void close() VL_MT_UNSAFE_ONE; ///< Close the file
|
||||
/// Open next data-only file
|
||||
void openNext(bool incFilename) VL_MT_UNSAFE_ONE;
|
||||
/// Close the file
|
||||
void close() VL_MT_UNSAFE_ONE;
|
||||
/// Flush any remaining data to this file
|
||||
void flush() VL_MT_UNSAFE_ONE { bufferFlush(); }
|
||||
/// Flush any remaining data from all files
|
||||
static void flush_all() VL_MT_UNSAFE_ONE;
|
||||
/// Is file open?
|
||||
bool isOpen() const { return m_isOpen; }
|
||||
|
||||
void set_time_unit(const char* unitp); ///< Set time units (s/ms, defaults to ns)
|
||||
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
//=========================================================================
|
||||
// Internal interface to Verilator generated code
|
||||
|
||||
void set_time_resolution(const char* unitp); ///< Set time resolution (s/ms, defaults to ns)
|
||||
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
|
||||
double timescaleToDouble(const char* unitp);
|
||||
std::string doubleToTimescale(double value);
|
||||
|
||||
/// Inside dumping routines, called each cycle to make the dump
|
||||
void dump(vluint64_t timeui);
|
||||
/// Call dump with a absolute unscaled time in seconds
|
||||
void dumpSeconds(double secs) { dump(static_cast<vluint64_t>(secs * m_timeRes)); }
|
||||
|
||||
/// Inside dumping routines, declare callbacks for tracings
|
||||
void addCallback(VerilatedVcdCallback_t initcb, VerilatedVcdCallback_t fullcb,
|
||||
VerilatedVcdCallback_t changecb, void* userthis) VL_MT_UNSAFE_ONE;
|
||||
|
||||
/// Inside dumping routines, declare a module
|
||||
void module(const std::string& name);
|
||||
/// Inside dumping routines, declare a signal
|
||||
void declBit(vluint32_t code, const char* name, bool array, int arraynum);
|
||||
void declBus(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declQuad(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declArray(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declFloat(vluint32_t code, const char* name, bool array, int arraynum);
|
||||
void declDouble(vluint32_t code, const char* name, bool array, int arraynum);
|
||||
#ifndef VL_TRACE_VCD_OLD_API
|
||||
|
||||
#ifdef VL_TRACE_VCD_OLD_API
|
||||
//=========================================================================
|
||||
// Note: These are only for testing for backward compatibility with foreign
|
||||
// code and is not used by Verilator. Do not use these as there is no
|
||||
// guarantee of functionality.
|
||||
|
||||
void declTriBit(vluint32_t code, const char* name, bool array, int arraynum);
|
||||
void declTriBus(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declTriQuad(vluint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
int lsb);
|
||||
void declTriArray(vluint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
int lsb);
|
||||
#endif // VL_TRACE_VCD_OLD_API
|
||||
// ... other module_start for submodules (based on cell name)
|
||||
|
||||
//=========================================================================
|
||||
// Inside dumping routines used by Verilator
|
||||
|
||||
vluint32_t* oldp(vluint32_t code) { return m_sigs_oldvalp + code; }
|
||||
|
||||
#ifndef VL_TRACE_VCD_OLD_API
|
||||
|
||||
//=========================================================================
|
||||
// Write back to previous value buffer value and emit
|
||||
|
||||
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 value and emit if changed
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#else // VL_TRACE_VCD_OLD_API
|
||||
|
||||
// Note: These are only for testing for backward compatibility. Verilator
|
||||
// should use the more efficient versions above.
|
||||
|
||||
//=========================================================================
|
||||
// Write back to previous value buffer value and emit
|
||||
|
||||
void fullBit(vluint32_t* oldp, vluint32_t newval) { fullBit(oldp - m_sigs_oldvalp, newval); }
|
||||
void fullBit(vluint32_t* oldp, vluint32_t newval) { fullBit(oldp - this->oldp(0), newval); }
|
||||
template <int T_Bits> void fullBus(vluint32_t* oldp, vluint32_t newval) {
|
||||
fullBus(oldp - m_sigs_oldvalp, newval, T_Bits);
|
||||
fullBus(oldp - this->oldp(0), newval, T_Bits);
|
||||
}
|
||||
void fullQuad(vluint32_t* oldp, vluint64_t newval, int bits) {
|
||||
fullQuad(oldp - m_sigs_oldvalp, newval, bits);
|
||||
fullQuad(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
void fullArray(vluint32_t* oldp, const vluint32_t* newvalp, int bits) {
|
||||
fullArray(oldp - m_sigs_oldvalp, newvalp, bits);
|
||||
fullArray(oldp - this->oldp(0), newvalp, bits);
|
||||
}
|
||||
void fullFloat(vluint32_t* oldp, float newval) { fullFloat(oldp - m_sigs_oldvalp, newval); }
|
||||
void fullDouble(vluint32_t* oldp, double newval) { fullDouble(oldp - m_sigs_oldvalp, newval); }
|
||||
void fullFloat(vluint32_t* oldp, float newval) { fullFloat(oldp - this->oldp(0), newval); }
|
||||
void fullDouble(vluint32_t* oldp, double newval) { fullDouble(oldp - this->oldp(0), newval); }
|
||||
|
||||
//=========================================================================
|
||||
// Check previous value and emit if changed
|
||||
|
||||
void chgBit(vluint32_t* oldp, vluint32_t newval) { chgBit(oldp - m_sigs_oldvalp, newval); }
|
||||
void chgBit(vluint32_t* oldp, vluint32_t newval) { chgBit(oldp - this->oldp(0), newval); }
|
||||
template <int T_Bits> void chgBus(vluint32_t* oldp, vluint32_t newval) {
|
||||
chgBus(oldp - m_sigs_oldvalp, newval, T_Bits);
|
||||
chgBus(oldp - this->oldp(0), newval, T_Bits);
|
||||
}
|
||||
void chgQuad(vluint32_t* oldp, vluint64_t newval, int bits) {
|
||||
chgQuad(oldp - m_sigs_oldvalp, newval, bits);
|
||||
chgQuad(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
void chgArray(vluint32_t* oldp, const vluint32_t* newvalp, int bits) {
|
||||
chgArray(oldp - m_sigs_oldvalp, newvalp, bits);
|
||||
chgArray(oldp - this->oldp(0), newvalp, bits);
|
||||
}
|
||||
void chgFloat(vluint32_t* oldp, float newval) { chgFloat(oldp - m_sigs_oldvalp, newval); }
|
||||
void chgDouble(vluint32_t* oldp, double newval) { chgDouble(oldp - m_sigs_oldvalp, newval); }
|
||||
void chgFloat(vluint32_t* oldp, float newval) { chgFloat(oldp - this->oldp(0), newval); }
|
||||
void chgDouble(vluint32_t* oldp, double newval) { chgDouble(oldp - this->oldp(0), newval); }
|
||||
|
||||
/// Inside dumping routines, dump one signal, faster when not inlined
|
||||
/// due to code size reduction.
|
||||
@ -317,11 +234,11 @@ public:
|
||||
/// Inside dumping routines, dump one signal if it has changed.
|
||||
/// We do want to inline these to avoid calls when the value did not change.
|
||||
inline void chgBit(vluint32_t code, const vluint32_t newval) {
|
||||
vluint32_t diff = m_sigs_oldvalp[code] ^ newval;
|
||||
vluint32_t diff = oldp(code)[0] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullBit(code, newval);
|
||||
}
|
||||
inline void chgBus(vluint32_t code, const vluint32_t newval, int bits) {
|
||||
vluint32_t diff = m_sigs_oldvalp[code] ^ newval;
|
||||
vluint32_t diff = oldp(code)[0] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
|
||||
fullBus(code, newval, bits);
|
||||
@ -329,7 +246,7 @@ public:
|
||||
}
|
||||
}
|
||||
inline void chgQuad(vluint32_t code, const vluint64_t newval, int bits) {
|
||||
vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) ^ newval;
|
||||
vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 64 || (diff & ((VL_ULL(1) << bits) - 1)))) {
|
||||
fullQuad(code, newval, bits);
|
||||
@ -338,7 +255,7 @@ public:
|
||||
}
|
||||
inline void chgArray(vluint32_t code, const vluint32_t* newvalp, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
|
||||
if (VL_UNLIKELY(m_sigs_oldvalp[code + word] ^ newvalp[word])) {
|
||||
if (VL_UNLIKELY(oldp(code)[word] ^ newvalp[word])) {
|
||||
fullArray(code, newvalp, bits);
|
||||
return;
|
||||
}
|
||||
@ -346,14 +263,15 @@ public:
|
||||
}
|
||||
inline void chgArray(vluint32_t code, const vluint64_t* newvalp, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 64) + 1); ++word) {
|
||||
if (VL_UNLIKELY(m_sigs_oldvalp[code + word] ^ newvalp[word])) {
|
||||
if (VL_UNLIKELY(*(reinterpret_cast<vluint64_t*>(oldp(code + 2 * word)))
|
||||
^ newvalp[word])) {
|
||||
fullArray(code, newvalp, bits);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
|
||||
vluint32_t diff = ((m_sigs_oldvalp[code] ^ newval) | (m_sigs_oldvalp[code + 1] ^ newtri));
|
||||
vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
// Verilator 3.510 and newer provide clean input, so the below
|
||||
// is only for back compatibility
|
||||
@ -364,7 +282,7 @@ public:
|
||||
}
|
||||
inline void chgTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri,
|
||||
int bits) {
|
||||
vluint32_t diff = ((m_sigs_oldvalp[code] ^ newval) | (m_sigs_oldvalp[code + 1] ^ newtri));
|
||||
vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
|
||||
fullTriBus(code, newval, newtri, bits);
|
||||
@ -373,9 +291,8 @@ public:
|
||||
}
|
||||
inline void chgTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri,
|
||||
int bits) {
|
||||
vluint64_t diff
|
||||
= (((*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) ^ newval)
|
||||
| ((*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code + 1]))) ^ newtri));
|
||||
vluint64_t diff = (((*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval)
|
||||
| ((*(reinterpret_cast<vluint64_t*>(oldp(code + 1)))) ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 64 || (diff & ((VL_ULL(1) << bits) - 1)))) {
|
||||
fullTriQuad(code, newval, newtri, bits);
|
||||
@ -385,8 +302,8 @@ public:
|
||||
inline void chgTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip,
|
||||
int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
|
||||
if (VL_UNLIKELY((m_sigs_oldvalp[code + word * 2] ^ newvalp[word])
|
||||
| (m_sigs_oldvalp[code + word * 2 + 1] ^ newtrip[word]))) {
|
||||
if (VL_UNLIKELY((oldp(code)[word * 2] ^ newvalp[word])
|
||||
| (oldp(code)[word * 2 + 1] ^ newtrip[word]))) {
|
||||
fullTriArray(code, newvalp, newtrip, bits);
|
||||
return;
|
||||
}
|
||||
@ -394,24 +311,30 @@ public:
|
||||
}
|
||||
inline void chgDouble(vluint32_t code, const double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY((*(reinterpret_cast<double*>(&m_sigs_oldvalp[code]))) != newval)) {
|
||||
if (VL_UNLIKELY((*(reinterpret_cast<double*>(oldp(code)))) != newval)) {
|
||||
fullDouble(code, newval);
|
||||
}
|
||||
}
|
||||
inline void chgFloat(vluint32_t code, const float newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY((*(reinterpret_cast<float*>(&m_sigs_oldvalp[code]))) != newval)) {
|
||||
if (VL_UNLIKELY((*(reinterpret_cast<float*>(oldp(code)))) != newval)) {
|
||||
fullFloat(code, newval);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // VL_TRACE_VCD_OLD_API
|
||||
|
||||
protected:
|
||||
// METHODS
|
||||
void evcd(bool flag) { m_evcd = flag; }
|
||||
#endif // VL_TRACE_VCD_OLD_API
|
||||
};
|
||||
|
||||
// Declare specializations here they are used in VerilatedVcdC just below
|
||||
template <> void VerilatedTrace<VerilatedVcd>::dump(vluint64_t timeui);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_unit(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_unit(const std::string& unit);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_resolution(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_resolution(const std::string& unit);
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcdC
|
||||
/// Create a VCD dump file in C standalone (no SystemC) simulations.
|
||||
@ -460,11 +383,11 @@ public:
|
||||
/// Set time units (s/ms, defaults to ns)
|
||||
/// For Verilated models, these propage from the Verilated default --timeunit
|
||||
void set_time_unit(const char* unit) { m_sptrace.set_time_unit(unit); }
|
||||
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
void set_time_unit(const std::string& unit) { m_sptrace.set_time_unit(unit); }
|
||||
/// Set time resolution (s/ms, defaults to ns)
|
||||
/// For Verilated models, these propage from the Verilated default --timeunit
|
||||
void set_time_resolution(const char* unit) { m_sptrace.set_time_resolution(unit); }
|
||||
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
void set_time_resolution(const std::string& unit) { m_sptrace.set_time_resolution(unit); }
|
||||
|
||||
/// Internal class access
|
||||
inline VerilatedVcd* spTrace() { return &m_sptrace; }
|
||||
|
Loading…
Reference in New Issue
Block a user