diff --git a/include/verilated_fst_c.cpp b/include/verilated_fst_c.cpp index 7f4a763bc..a128639ca 100644 --- a/include/verilated_fst_c.cpp +++ b/include/verilated_fst_c.cpp @@ -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::traceInit(); // Clear the scope stack std::list::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::declCode(code, bits, false); std::pair p = m_code2symbol.insert(std::make_pair(code, static_cast(NULL))); @@ -164,7 +126,7 @@ void VerilatedFst::declSymbol(vluint32_t code, const char* name, int dtypenum, f std::list 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::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 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: diff --git a/include/verilated_fst_c.h b/include/verilated_fst_c.h index b59b90a5b..9a8c7eb0e 100644 --- a/include/verilated_fst_c.h +++ b/include/verilated_fst_c.h @@ -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 #include -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 { +private: + // Give the superclass access to private bits (to avoid virtual functions) + friend class VerilatedTrace; + + //========================================================================= + // FST specific internals + typedef std::map Code2SymbolType; typedef std::map Local2FstDtype; - typedef std::vector 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 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 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 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 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(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(oldp) = newval; - fstWriterEmitValueChange(m_fst, m_symbolp[oldp - m_sigs_oldvalp], oldp); - } - void fullDouble(vluint32_t* oldp, double newval) { - // cppcheck-suppress invalidPointerCast - *reinterpret_cast(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 inline void chgBus(vluint32_t* oldp, vluint32_t newval) { - const vluint32_t diff = *oldp ^ newval; - if (VL_UNLIKELY(diff)) fullBus(oldp, newval); - } - inline void chgQuad(vluint32_t* oldp, vluint64_t newval, int bits) { - const vluint64_t diff = *reinterpret_cast(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(oldp) != newval)) fullFloat(oldp, newval); - } - inline void chgDouble(vluint32_t* oldp, double newval) { - // cppcheck-suppress invalidPointerCast - if (VL_UNLIKELY(*reinterpret_cast(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::dump(vluint64_t timeui); +template <> void VerilatedTrace::set_time_unit(const char* unitp); +template <> void VerilatedTrace::set_time_unit(const std::string& unit); +template <> void VerilatedTrace::set_time_resolution(const char* unitp); +template <> void VerilatedTrace::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; }; diff --git a/include/verilated_trace.h b/include/verilated_trace.h new file mode 100644 index 000000000..8be887734 --- /dev/null +++ b/include/verilated_trace.h @@ -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 +#include + +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 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 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(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 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 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 inline void chgBus(vluint32_t* oldp, vluint32_t newval) { + const vluint32_t diff = *oldp ^ newval; + if (VL_UNLIKELY(diff)) fullBus(oldp, newval); + } + inline void chgQuad(vluint32_t* oldp, vluint64_t newval, int bits) { + const vluint64_t diff = *reinterpret_cast(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(oldp) != newval)) fullFloat(oldp, newval); + } + inline void chgDouble(vluint32_t* oldp, double newval) { + // cppcheck-suppress invalidPointerCast + if (VL_UNLIKELY(*reinterpret_cast(oldp) != newval)) fullDouble(oldp, newval); + } +}; +#endif // guard diff --git a/include/verilated_trace_imp.cpp b/include/verilated_trace_imp.cpp new file mode 100644 index 000000000..537b687dd --- /dev/null +++ b/include/verilated_trace_imp.cpp @@ -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::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::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::~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::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::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::timeResStr() const { + return doubleToTimescale(m_timeRes); +} + +template <> std::string VerilatedTrace::timeUnitStr() const { + return doubleToTimescale(m_timeUnit); +} + +//========================================================================= +// External interface to client code + +template <> void VerilatedTrace::set_time_unit(const char* unitp) { + m_timeUnit = timescaleToDouble(unitp); +} + +template <> void VerilatedTrace::set_time_unit(const std::string& unit) { + set_time_unit(unit.c_str()); +} + +template <> void VerilatedTrace::set_time_resolution(const char* unitp) { + m_timeRes = timescaleToDouble(unitp); +} + +template <> void VerilatedTrace::set_time_resolution(const std::string& unit) { + set_time_resolution(unit.c_str()); +} + +template <> void VerilatedTrace::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::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::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 +void VerilatedTrace::fullBus(vluint32_t* oldp, vluint32_t newval) { + *oldp = newval; + self()->emitBus(oldp - m_sigs_oldvalp, newval); +} + +// Note: No specialization for width 1, covered by 'fullBit' +template void VerilatedTrace::fullBus<2>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<3>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<4>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<5>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<6>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<7>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<8>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<9>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<10>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<11>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<12>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<13>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<14>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<15>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<16>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<17>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<18>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<19>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<20>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<21>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<22>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<23>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<24>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<25>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<26>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<27>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<28>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<29>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<30>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<31>(vluint32_t* oldp, vluint32_t newval); +template void VerilatedTrace::fullBus<32>(vluint32_t* oldp, vluint32_t newval); + +template <> +void VerilatedTrace::fullQuad(vluint32_t* oldp, vluint64_t newval, int bits) { + *reinterpret_cast(oldp) = newval; + self()->emitQuad(oldp - m_sigs_oldvalp, newval, bits); +} +template <> +void VerilatedTrace::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::fullFloat(vluint32_t* oldp, float newval) { + // cppcheck-suppress invalidPointerCast + *reinterpret_cast(oldp) = newval; + self()->emitFloat(oldp - m_sigs_oldvalp, newval); +} +template <> void VerilatedTrace::fullDouble(vluint32_t* oldp, double newval) { + // cppcheck-suppress invalidPointerCast + *reinterpret_cast(oldp) = newval; + self()->emitDouble(oldp - m_sigs_oldvalp, newval); +} diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index 21ce58cd5..af8bb2a15 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -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::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("<= 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::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(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 void VerilatedVcd::fullBus(vluint32_t* oldp, vluint32_t newval) { - *oldp = newval; +template void VerilatedVcd::emitBus(vluint32_t code, vluint32_t newval) { char* wp = m_writep; *wp++ = 'b'; newval <<= 32 - T_Bits; @@ -764,44 +638,10 @@ template void VerilatedVcd::fullBus(vluint32_t* oldp, vluint32_t ne *wp++ = '0' | static_cast(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(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(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(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(newval)); wp += strlen(wp); - finishLine(oldp, wp); + finishLine(code, wp); } -void VerilatedVcd::fullDouble(vluint32_t* oldp, double newval) { - // cppcheck-suppress invalidPointerCast - *reinterpret_cast(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(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(&m_sigs_oldvalp[code]))) = newval; + (*(reinterpret_cast(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(&m_sigs_oldvalp[code]))) = newval; - (*(reinterpret_cast(&m_sigs_oldvalp[code + 1]))) = newtri; + (*(reinterpret_cast(oldp(code)))) = newval; + (*(reinterpret_cast(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(&m_sigs_oldvalp[code]))) = newval; + (*(reinterpret_cast(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(&m_sigs_oldvalp[code]))) = newval; + (*(reinterpret_cast(oldp(code)))) = newval; // Buffer can't overflow before sprintf; we sized during declaration sprintf(m_writep, "r%.16g", static_cast(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 diff --git a/include/verilated_vcd_c.h b/include/verilated_vcd_c.h index 7e5dd2860..0ee8084c8 100644 --- a/include/verilated_vcd_c.h +++ b/include/verilated_vcd_c.h @@ -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 #include #include 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 { private: + // Give the superclass access to private bits (to avoid virtual functions) + friend class VerilatedTrace; + + //========================================================================= + // 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 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 SigVec; - SigVec m_sigs; ///< Pointer to signal information - typedef std::vector CallbackVec; - CallbackVec m_callbacks; ///< Routines to perform dumping typedef std::map 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 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(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 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 inline void chgBus(vluint32_t* oldp, vluint32_t newval) { - const vluint32_t diff = *oldp ^ newval; - if (VL_UNLIKELY(diff)) fullBus(oldp, newval); - } - inline void chgQuad(vluint32_t* oldp, vluint64_t newval, int bits) { - const vluint64_t diff = *reinterpret_cast(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(oldp) != newval)) fullFloat(oldp, newval); - } - inline void chgDouble(vluint32_t* oldp, double newval) { - // cppcheck-suppress invalidPointerCast - if (VL_UNLIKELY(*reinterpret_cast(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 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 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(&m_sigs_oldvalp[code]))) ^ newval; + vluint64_t diff = (*(reinterpret_cast(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(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(&m_sigs_oldvalp[code]))) ^ newval) - | ((*(reinterpret_cast(&m_sigs_oldvalp[code + 1]))) ^ newtri)); + vluint64_t diff = (((*(reinterpret_cast(oldp(code)))) ^ newval) + | ((*(reinterpret_cast(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(&m_sigs_oldvalp[code]))) != newval)) { + if (VL_UNLIKELY((*(reinterpret_cast(oldp(code)))) != newval)) { fullDouble(code, newval); } } inline void chgFloat(vluint32_t code, const float newval) { // cppcheck-suppress invalidPointerCast - if (VL_UNLIKELY((*(reinterpret_cast(&m_sigs_oldvalp[code]))) != newval)) { + if (VL_UNLIKELY((*(reinterpret_cast(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::dump(vluint64_t timeui); +template <> void VerilatedTrace::set_time_unit(const char* unitp); +template <> void VerilatedTrace::set_time_unit(const std::string& unit); +template <> void VerilatedTrace::set_time_resolution(const char* unitp); +template <> void VerilatedTrace::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; }