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:
Geza Lore 2020-04-19 23:57:36 +01:00 committed by GitHub
parent 9164eb03d5
commit 39d903375b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 752 additions and 671 deletions

View File

@ -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:

View File

@ -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
View 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

View 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);
}

View File

@ -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

View File

@ -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; }