Merge branch 'master' into develop-v5
This commit is contained in:
commit
f9ecbdc70b
|
|
@ -72,6 +72,8 @@
|
|||
#endif
|
||||
// clang-format on
|
||||
|
||||
#include "verilated_trace.h"
|
||||
|
||||
// Max characters in static char string for VL_VALUE_STRING
|
||||
constexpr unsigned VL_VALUE_STRING_MAX_WIDTH = 8192;
|
||||
|
||||
|
|
@ -2162,29 +2164,6 @@ void VL_WRITEMEM_N(bool hex, // Hex format, else binary
|
|||
//===========================================================================
|
||||
// Timescale conversion
|
||||
|
||||
// Helper function for conversion of timescale strings
|
||||
// Converts (1|10|100)(s|ms|us|ns|ps|fs) to power of then
|
||||
int VL_TIME_STR_CONVERT(const char* strp) VL_PURE {
|
||||
int scale = 0;
|
||||
if (!strp) return 0;
|
||||
if (*strp++ != '1') return 0;
|
||||
while (*strp == '0') {
|
||||
++scale;
|
||||
++strp;
|
||||
}
|
||||
switch (*strp++) {
|
||||
case 's': break;
|
||||
case 'm': scale -= 3; break;
|
||||
case 'u': scale -= 6; break;
|
||||
case 'n': scale -= 9; break;
|
||||
case 'p': scale -= 12; break;
|
||||
case 'f': scale -= 15; break;
|
||||
default: return 0;
|
||||
}
|
||||
if ((scale < 0) && (*strp++ != 's')) return 0;
|
||||
if (*strp) return 0;
|
||||
return scale;
|
||||
}
|
||||
static const char* vl_time_str(int scale) VL_PURE {
|
||||
static const char* const names[]
|
||||
= {"100s", "10s", "1s", "100ms", "10ms", "1ms", "100us", "10us", "1us",
|
||||
|
|
@ -2306,8 +2285,9 @@ void VerilatedContext::checkMagic(const VerilatedContext* contextp) {
|
|||
}
|
||||
|
||||
VerilatedContext::Serialized::Serialized() {
|
||||
m_timeunit = VL_TIME_UNIT; // Initial value until overriden by _Vconfigure
|
||||
m_timeprecision = VL_TIME_PRECISION; // Initial value until overriden by _Vconfigure
|
||||
constexpr int8_t picosecond = -12;
|
||||
m_timeunit = picosecond; // Initial value until overriden by _Vconfigure
|
||||
m_timeprecision = picosecond; // Initial value until overriden by _Vconfigure
|
||||
}
|
||||
|
||||
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
|
||||
|
|
@ -2914,6 +2894,8 @@ void VerilatedImp::versionDump() VL_MT_SAFE {
|
|||
VerilatedModel::VerilatedModel(VerilatedContext& context)
|
||||
: m_context{context} {}
|
||||
|
||||
std::unique_ptr<VerilatedTraceConfig> VerilatedModel::traceConfig() const { return nullptr; }
|
||||
|
||||
//===========================================================================
|
||||
// VerilatedModule:: Methods
|
||||
|
||||
|
|
|
|||
|
|
@ -91,6 +91,8 @@ class VerilatedFstC;
|
|||
class VerilatedFstSc;
|
||||
class VerilatedScope;
|
||||
class VerilatedScopeNameMap;
|
||||
template <class, class> class VerilatedTrace;
|
||||
class VerilatedTraceConfig;
|
||||
class VerilatedVar;
|
||||
class VerilatedVarNameMap;
|
||||
class VerilatedVcd;
|
||||
|
|
@ -278,6 +280,12 @@ public:
|
|||
virtual const char* modelName() const = 0;
|
||||
/// Returns the thread level parallelism, this model was Verilated with. Always 1 or higher.
|
||||
virtual unsigned threads() const = 0;
|
||||
|
||||
private:
|
||||
// The following are for use by Verilator internals only
|
||||
template <class, class> friend class VerilatedTrace;
|
||||
// Run-time trace configuration requested by this model
|
||||
virtual std::unique_ptr<VerilatedTraceConfig> traceConfig() const;
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
|
|
|
|||
|
|
@ -142,26 +142,6 @@ ifneq ($(VM_THREADS),0)
|
|||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(VM_TRACE_THREADS),0)
|
||||
ifneq ($(VM_TRACE_THREADS),)
|
||||
ifeq ($(findstring -DVL_THREADED,$(CPPFLAGS)),)
|
||||
$(error VM_TRACE_THREADS requires VM_THREADS)
|
||||
endif
|
||||
CPPFLAGS += -DVL_TRACE_THREADED
|
||||
VK_C11=1
|
||||
VK_LIBS_THREADED=1
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
ifneq ($(VM_TRACE_FST_WRITER_THREAD),0)
|
||||
ifneq ($(VM_TRACE_FST_WRITER_THREAD),)
|
||||
CPPFLAGS += -DVL_TRACE_FST_WRITER_THREAD
|
||||
VK_C11=1
|
||||
VK_LIBS_THREADED=1
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(VK_C11),0)
|
||||
ifneq ($(VK_C11),)
|
||||
# Need C++11 at least, so always default to newest
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
#include "verilated_fst_c.h"
|
||||
|
||||
// GTKWave configuration
|
||||
#ifdef VL_TRACE_FST_WRITER_THREAD
|
||||
#ifdef VL_THREADED
|
||||
# define HAVE_LIBPTHREAD
|
||||
# define FST_WRITER_PARALLEL
|
||||
#endif
|
||||
|
|
@ -106,9 +106,7 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
|||
m_fst = fstWriterCreate(filename, 1);
|
||||
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
|
||||
fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref
|
||||
#ifdef VL_TRACE_FST_WRITER_THREAD
|
||||
fstWriterSetParallelMode(m_fst, 1);
|
||||
#endif
|
||||
if (m_useFstWriterThread) fstWriterSetParallelMode(m_fst, 1);
|
||||
fullDump(true); // First dump must be full for fst
|
||||
|
||||
m_curScope.clear();
|
||||
|
|
@ -250,23 +248,36 @@ void VerilatedFst::declDouble(uint32_t code, const char* name, int dtypenum, fst
|
|||
//=============================================================================
|
||||
// Get/commit trace buffer
|
||||
|
||||
VerilatedFstBuffer* VerilatedFst::getTraceBuffer() { return new VerilatedFstBuffer{*this}; }
|
||||
VerilatedFst::Buffer* VerilatedFst::getTraceBuffer() {
|
||||
#ifdef VL_THREADED
|
||||
if (offload()) return new OffloadBuffer{*this};
|
||||
#endif
|
||||
return new Buffer{*this};
|
||||
}
|
||||
|
||||
void VerilatedFst::commitTraceBuffer(VerilatedFstBuffer* bufp) {
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
if (bufp->m_offloadBufferWritep) {
|
||||
m_offloadBufferWritep = bufp->m_offloadBufferWritep;
|
||||
return; // Buffer will be deleted by the offload thread
|
||||
void VerilatedFst::commitTraceBuffer(VerilatedFst::Buffer* bufp) {
|
||||
#ifdef VL_THREADED
|
||||
if (offload()) {
|
||||
OffloadBuffer* const offloadBufferp = static_cast<OffloadBuffer*>(bufp);
|
||||
if (offloadBufferp->m_offloadBufferWritep) {
|
||||
m_offloadBufferWritep = offloadBufferp->m_offloadBufferWritep;
|
||||
return; // Buffer will be deleted by the offload thread
|
||||
}
|
||||
}
|
||||
#endif
|
||||
delete bufp;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFstBuffer implementation
|
||||
// Configure
|
||||
|
||||
VerilatedFstBuffer::VerilatedFstBuffer(VerilatedFst& owner)
|
||||
: VerilatedTraceBuffer<VerilatedFst, VerilatedFstBuffer>{owner} {}
|
||||
void VerilatedFst::configure(const VerilatedTraceConfig& config) {
|
||||
// If at least one model requests the FST writer thread, then use it
|
||||
m_useFstWriterThread |= config.m_useFstWriterThread;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFstBuffer implementation
|
||||
|
||||
//=============================================================================
|
||||
// Trace rendering primitives
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ public:
|
|||
using Super = VerilatedTrace<VerilatedFst, VerilatedFstBuffer>;
|
||||
|
||||
private:
|
||||
friend Buffer; // Give the buffer access to the private bits
|
||||
friend VerilatedFstBuffer; // Give the buffer access to the private bits
|
||||
|
||||
//=========================================================================
|
||||
// FST specific internals
|
||||
|
|
@ -55,6 +55,8 @@ private:
|
|||
fstHandle* m_symbolp = nullptr; // same as m_code2symbol, but as an array
|
||||
char* m_strbuf = nullptr; // String buffer long enough to hold maxBits() chars
|
||||
|
||||
bool m_useFstWriterThread = false; // Whether to use the separate FST writer thread
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedFst);
|
||||
void declare(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
|
|
@ -72,8 +74,11 @@ protected:
|
|||
virtual bool preChangeDump() override { return isOpen(); }
|
||||
|
||||
// Trace buffer management
|
||||
virtual VerilatedFstBuffer* getTraceBuffer() override;
|
||||
virtual void commitTraceBuffer(VerilatedFstBuffer*) override;
|
||||
virtual Buffer* getTraceBuffer() override;
|
||||
virtual void commitTraceBuffer(Buffer*) override;
|
||||
|
||||
// Configure sub-class
|
||||
virtual void configure(const VerilatedTraceConfig&) override;
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
|
|
@ -124,10 +129,14 @@ template <> void VerilatedFst::Super::dumpvars(int level, const std::string& hie
|
|||
//=============================================================================
|
||||
// VerilatedFstBuffer
|
||||
|
||||
class VerilatedFstBuffer final : public VerilatedTraceBuffer<VerilatedFst, VerilatedFstBuffer> {
|
||||
class VerilatedFstBuffer VL_NOT_FINAL {
|
||||
// Give the trace file access to the private bits
|
||||
friend VerilatedFst;
|
||||
friend VerilatedFst::Super;
|
||||
friend VerilatedFst::Buffer;
|
||||
friend VerilatedFst::OffloadBuffer;
|
||||
|
||||
VerilatedFst& m_owner; // Trace file owning this buffer. Required by subclasses.
|
||||
|
||||
// The FST file handle
|
||||
void* const m_fst = m_owner.m_fst;
|
||||
|
|
@ -136,10 +145,10 @@ class VerilatedFstBuffer final : public VerilatedTraceBuffer<VerilatedFst, Veril
|
|||
// String buffer long enough to hold maxBits() chars
|
||||
char* const m_strbuf = m_owner.m_strbuf;
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
explicit VerilatedFstBuffer(VerilatedFst& owner);
|
||||
~VerilatedFstBuffer() = default;
|
||||
explicit VerilatedFstBuffer(VerilatedFst& owner)
|
||||
: m_owner{owner} {}
|
||||
virtual ~VerilatedFstBuffer() = default;
|
||||
|
||||
//=========================================================================
|
||||
// Implementation of VerilatedTraceBuffer interface
|
||||
|
|
|
|||
|
|
@ -256,25 +256,7 @@ extern void _vl_debug_print_w(int lbits, WDataInP const iwp);
|
|||
//=========================================================================
|
||||
// Pli macros
|
||||
|
||||
extern int VL_TIME_STR_CONVERT(const char* strp) VL_PURE;
|
||||
|
||||
// These are deprecated and used only to establish the default precision/units.
|
||||
// Use Verilator timescale-override for better control.
|
||||
// clang-format off
|
||||
#ifndef VL_TIME_PRECISION
|
||||
# ifdef VL_TIME_PRECISION_STR
|
||||
# define VL_TIME_PRECISION VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR))
|
||||
# else
|
||||
# define VL_TIME_PRECISION (-12) ///< Timescale default units if not in Verilog - picoseconds
|
||||
# endif
|
||||
#endif
|
||||
#ifndef VL_TIME_UNIT
|
||||
# ifdef VL_TIME_UNIT_STR
|
||||
# define VL_TIME_UNIT VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR))
|
||||
# else
|
||||
# define VL_TIME_UNIT (-12) ///< Timescale default units if not in Verilog - picoseconds
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(SYSTEMC_VERSION)
|
||||
/// Return current simulation time
|
||||
|
|
|
|||
|
|
@ -24,21 +24,6 @@
|
|||
|
||||
// clang-format off
|
||||
|
||||
// In FST mode, VL_TRACE_THREADED enables offloading, but only if we also have
|
||||
// the FST writer thread. This means with --trace-threads 1, we get the FST
|
||||
// writer thread only, and with --trace-threads 2 we get offloading as well
|
||||
#if defined(VL_TRACE_FST_WRITER_THREAD) && defined(VL_TRACE_THREADED)
|
||||
# define VL_TRACE_OFFLOAD
|
||||
#endif
|
||||
// VCD tracing can happen fully in parallel
|
||||
#if defined(VM_TRACE_VCD) && VM_TRACE_VCD && defined(VL_TRACE_THREADED)
|
||||
# define VL_TRACE_PARALLEL
|
||||
#endif
|
||||
|
||||
#if defined(VL_TRACE_PARALLEL) && defined(VL_TRACE_OFFLOAD)
|
||||
# error "Cannot have VL_TRACE_PARALLEL and VL_TRACE_OFFLOAD together"
|
||||
#endif
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_trace_defs.h"
|
||||
|
||||
|
|
@ -47,9 +32,10 @@
|
|||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
#ifdef VL_THREADED
|
||||
# include <deque>
|
||||
# include <thread>
|
||||
#endif
|
||||
|
|
@ -57,9 +43,10 @@
|
|||
// clang-format on
|
||||
|
||||
class VlThreadPool;
|
||||
template <class T_Trace, class T_Buffer> class VerilatedTraceBuffer;
|
||||
template <class T_Buffer> class VerilatedTraceBuffer;
|
||||
template <class T_Buffer> class VerilatedTraceOffloadBuffer;
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
#ifdef VL_THREADED
|
||||
//=============================================================================
|
||||
// Offloaded tracing
|
||||
|
||||
|
|
@ -129,27 +116,46 @@ public:
|
|||
};
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTraceConfig
|
||||
|
||||
// Simple data representing trace configuration required by generated models.
|
||||
class VerilatedTraceConfig final {
|
||||
public:
|
||||
const bool m_useParallel; // Use parallel tracing
|
||||
const bool m_useOffloading; // Offloading trace rendering
|
||||
const bool m_useFstWriterThread; // Use the separate FST writer thread
|
||||
|
||||
VerilatedTraceConfig(bool useParallel, bool useOffloading, bool useFstWriterThread)
|
||||
: m_useParallel{useParallel}
|
||||
, m_useOffloading{useOffloading}
|
||||
, m_useFstWriterThread{useFstWriterThread} {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTrace
|
||||
|
||||
// T_Trace is the format specific subclass of VerilatedTrace.
|
||||
// T_Buffer is the format specific subclass of VerilatedTraceBuffer.
|
||||
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
|
||||
template <class T_Trace, class T_Buffer> class VerilatedTrace VL_NOT_FINAL {
|
||||
// Give the buffer (both base and derived) access to the private bits
|
||||
friend VerilatedTraceBuffer<T_Trace, T_Buffer>;
|
||||
friend T_Buffer;
|
||||
|
||||
public:
|
||||
using Buffer = T_Buffer;
|
||||
using Buffer = VerilatedTraceBuffer<T_Buffer>;
|
||||
using OffloadBuffer = VerilatedTraceOffloadBuffer<T_Buffer>;
|
||||
|
||||
//=========================================================================
|
||||
// Generic tracing internals
|
||||
|
||||
using initCb_t = void (*)(void*, T_Trace*, uint32_t); // Type of init callbacks
|
||||
using dumpCb_t = void (*)(void*, Buffer*); // Type of dump callbacks
|
||||
using dumpOffloadCb_t = void (*)(void*, OffloadBuffer*); // Type of offload dump callbacks
|
||||
using cleanupCb_t = void (*)(void*, T_Trace*); // Type of cleanup callbacks
|
||||
|
||||
private:
|
||||
// Give the buffer (both base and derived) access to the private bits
|
||||
friend T_Buffer;
|
||||
friend Buffer;
|
||||
friend OffloadBuffer;
|
||||
|
||||
struct CallbackRecord {
|
||||
// Note: would make these fields const, but some old STL implementations
|
||||
// (the one in Ubuntu 14.04 with GCC 4.8.4 in particular) use the
|
||||
|
|
@ -158,6 +164,7 @@ private:
|
|||
union { // The callback
|
||||
initCb_t m_initCb;
|
||||
dumpCb_t m_dumpCb;
|
||||
dumpOffloadCb_t m_dumpOffloadCb;
|
||||
cleanupCb_t m_cleanupCb;
|
||||
};
|
||||
void* m_userp; // The user pointer to pass to the callback (the symbol table)
|
||||
|
|
@ -167,12 +174,18 @@ private:
|
|||
CallbackRecord(dumpCb_t cb, void* userp)
|
||||
: m_dumpCb{cb}
|
||||
, m_userp{userp} {}
|
||||
CallbackRecord(dumpOffloadCb_t cb, void* userp)
|
||||
: m_dumpOffloadCb{cb}
|
||||
, m_userp{userp} {}
|
||||
CallbackRecord(cleanupCb_t cb, void* userp)
|
||||
: m_cleanupCb{cb}
|
||||
, m_userp{userp} {}
|
||||
};
|
||||
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
bool m_offload = false; // Use the offload thread (ignored if !VL_THREADED)
|
||||
bool m_parallel = false; // Use parallel tracing (ignored if !VL_THREADED)
|
||||
|
||||
#ifdef VL_THREADED
|
||||
struct ParallelWorkerData {
|
||||
const dumpCb_t m_cb; // The callback
|
||||
void* const m_userp; // The use pointer to pass to the callback
|
||||
|
|
@ -198,13 +211,13 @@ protected:
|
|||
uint32_t* m_sigs_oldvalp = nullptr; // Previous value store
|
||||
EData* m_sigs_enabledp = nullptr; // Bit vector of enabled codes (nullptr = all on)
|
||||
private:
|
||||
uint64_t m_timeLastDump = 0; // Last time we did a dump
|
||||
std::vector<bool> m_sigs_enabledVec; // Staging for m_sigs_enabledp
|
||||
std::vector<CallbackRecord> m_initCbs; // Routines to initialize tracing
|
||||
std::vector<CallbackRecord> m_fullCbs; // Routines to perform full dump
|
||||
std::vector<CallbackRecord> m_fullOffloadCbs; // Routines to perform offloaded full dump
|
||||
std::vector<CallbackRecord> m_chgCbs; // Routines to perform incremental dump
|
||||
std::vector<CallbackRecord> m_chgOffloadCbs; // Routines to perform offloaded incremental dump
|
||||
std::vector<CallbackRecord> m_cleanupCbs; // Routines to call at the end of dump
|
||||
VerilatedContext* m_contextp = nullptr; // The context used by the traced models
|
||||
bool m_fullDump = true; // Whether a full dump is required on the next call to 'dump'
|
||||
uint32_t m_nextCode = 0; // Next code number to assign
|
||||
uint32_t m_numSignals = 0; // Number of distinct signals
|
||||
|
|
@ -214,8 +227,10 @@ private:
|
|||
char m_scopeEscape = '.';
|
||||
double m_timeRes = 1e-9; // Time resolution (ns/ms etc)
|
||||
double m_timeUnit = 1e-0; // Time units (ns/ms etc)
|
||||
|
||||
void addContext(VerilatedContext*) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
uint64_t m_timeLastDump = 0; // Last time we did a dump
|
||||
bool m_didSomeDump = false; // Did at least one dump (i.e.: m_timeLastDump is valid)
|
||||
VerilatedContext* m_contextp = nullptr; // The context used by the traced models
|
||||
std::unordered_set<const VerilatedModel*> m_models; // The collection of models being traced
|
||||
|
||||
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord&& cbRec)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
|
|
@ -225,13 +240,14 @@ private:
|
|||
T_Trace* self() { return static_cast<T_Trace*>(this); }
|
||||
|
||||
void runCallbacks(const std::vector<CallbackRecord>& cbVec);
|
||||
void runOffloadedCallbacks(const std::vector<CallbackRecord>& cbVec);
|
||||
|
||||
// Flush any remaining data for this file
|
||||
static void onFlush(void* selfp) VL_MT_UNSAFE_ONE;
|
||||
// Close the file on termination
|
||||
static void onExit(void* selfp) VL_MT_UNSAFE_ONE;
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
#ifdef VL_THREADED
|
||||
// Number of total offload buffers that have been allocated
|
||||
uint32_t m_numOffloadBuffers = 0;
|
||||
// Size of offload buffers
|
||||
|
|
@ -277,7 +293,6 @@ protected:
|
|||
uint32_t numSignals() const { return m_numSignals; }
|
||||
uint32_t maxBits() const { return m_maxBits; }
|
||||
void fullDump(bool value) { m_fullDump = value; }
|
||||
uint64_t timeLastDump() { return m_timeLastDump; }
|
||||
|
||||
double timeRes() const { return m_timeRes; }
|
||||
double timeUnit() const { return m_timeUnit; }
|
||||
|
|
@ -298,6 +313,14 @@ protected:
|
|||
void closeBase();
|
||||
void flushBase();
|
||||
|
||||
#ifdef VL_THREADED
|
||||
inline bool offload() const { return m_offload; }
|
||||
inline bool parallel() const { return m_parallel; }
|
||||
#else
|
||||
static constexpr bool offload() { return false; }
|
||||
static constexpr bool parallel() { return false; }
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
// Virtual functions to be provided by the format specific implementation
|
||||
|
||||
|
|
@ -313,6 +336,9 @@ protected:
|
|||
virtual Buffer* getTraceBuffer() = 0;
|
||||
virtual void commitTraceBuffer(Buffer*) = 0;
|
||||
|
||||
// Configure sub-class
|
||||
virtual void configure(const VerilatedTraceConfig&) = 0;
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
|
|
@ -339,10 +365,13 @@ public:
|
|||
//=========================================================================
|
||||
// Non-hot path internal interface to Verilator generated code
|
||||
|
||||
void addInitCb(initCb_t cb, void* userp, VerilatedContext*) VL_MT_SAFE;
|
||||
void addFullCb(dumpCb_t cb, void* userp, VerilatedContext*) VL_MT_SAFE;
|
||||
void addChgCb(dumpCb_t cb, void* userp, VerilatedContext*) VL_MT_SAFE;
|
||||
void addCleanupCb(cleanupCb_t cb, void* userp, VerilatedContext*) VL_MT_SAFE;
|
||||
void addModel(VerilatedModel*) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
void addInitCb(initCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addFullCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addChgCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE;
|
||||
|
||||
void scopeEscape(char flag) { m_scopeEscape = flag; }
|
||||
|
||||
|
|
@ -353,32 +382,25 @@ public:
|
|||
//=============================================================================
|
||||
// VerilatedTraceBuffer
|
||||
|
||||
// T_Trace is the format specific subclass of VerilatedTrace.
|
||||
// T_Buffer is the format specific subclass of VerilatedTraceBuffer.
|
||||
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
|
||||
// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
|
||||
template <class T_Trace, class T_Buffer> class VerilatedTraceBuffer VL_NOT_FINAL {
|
||||
friend T_Trace; // Give the trace file access to the private bits
|
||||
|
||||
template <class T_Buffer> //
|
||||
class VerilatedTraceBuffer VL_NOT_FINAL : public T_Buffer {
|
||||
protected:
|
||||
T_Trace& m_owner; // The VerilatedTrace subclass that owns this buffer
|
||||
// Type of the owner trace file
|
||||
using Trace = typename std::remove_cv<
|
||||
typename std::remove_reference<decltype(T_Buffer::m_owner)>::type>::type;
|
||||
|
||||
// Previous value store
|
||||
uint32_t* const m_sigs_oldvalp = m_owner.m_sigs_oldvalp;
|
||||
// Bit vector of enabled codes (nullptr = all on)
|
||||
EData* const m_sigs_enabledp = m_owner.m_sigs_enabledp;
|
||||
static_assert(std::has_virtual_destructor<T_Buffer>::value, "");
|
||||
static_assert(std::is_base_of<VerilatedTrace<Trace, T_Buffer>, Trace>::value, "");
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
// Write pointer into current buffer
|
||||
uint32_t* m_offloadBufferWritep = m_owner.m_offloadBufferWritep;
|
||||
// End of offload buffer
|
||||
uint32_t* const m_offloadBufferEndp = m_owner.m_offloadBufferEndp;
|
||||
#endif
|
||||
friend Trace; // Give the trace file access to the private bits
|
||||
friend std::default_delete<VerilatedTraceBuffer<T_Buffer>>;
|
||||
|
||||
// Equivalent to 'this' but is of the sub-type 'T_Derived*'. Use 'self()->'
|
||||
// to access duck-typed functions to avoid a virtual function call.
|
||||
inline T_Buffer* self() { return static_cast<T_Buffer*>(this); }
|
||||
uint32_t* const m_sigs_oldvalp; // Previous value store
|
||||
EData* const m_sigs_enabledp; // Bit vector of enabled codes (nullptr = all on)
|
||||
|
||||
explicit VerilatedTraceBuffer(T_Trace& owner);
|
||||
explicit VerilatedTraceBuffer(Trace& owner);
|
||||
virtual ~VerilatedTraceBuffer() = default;
|
||||
|
||||
public:
|
||||
|
|
@ -410,7 +432,67 @@ public:
|
|||
void fullWData(uint32_t* oldp, const WData* newvalp, int bits);
|
||||
void fullDouble(uint32_t* oldp, double newval);
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
// In non-offload mode, these are called directly by the trace callbacks,
|
||||
// and are called chg*. In offload mode, they are called by the worker
|
||||
// thread and are called chg*Impl
|
||||
|
||||
// Check previous dumped value of signal. If changed, then emit trace entry
|
||||
VL_ATTR_ALWINLINE inline void chgBit(uint32_t* oldp, CData newval) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullBit(oldp, newval);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgCData(uint32_t* oldp, CData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullCData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgSData(uint32_t* oldp, SData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullSData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgIData(uint32_t* oldp, IData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgQData(uint32_t* oldp, QData newval, int bits) {
|
||||
const uint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) {
|
||||
if (VL_UNLIKELY(oldp[i] ^ newvalp[i])) {
|
||||
fullWData(oldp, newvalp, bits);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgDouble(uint32_t* oldp, double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY(*reinterpret_cast<double*>(oldp) != newval)) fullDouble(oldp, newval);
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef VL_THREADED
|
||||
//=============================================================================
|
||||
// VerilatedTraceOffloadBuffer
|
||||
|
||||
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
|
||||
// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
|
||||
template <class T_Buffer> //
|
||||
class VerilatedTraceOffloadBuffer final : public VerilatedTraceBuffer<T_Buffer> {
|
||||
using typename VerilatedTraceBuffer<T_Buffer>::Trace;
|
||||
|
||||
friend Trace; // Give the trace file access to the private bits
|
||||
|
||||
uint32_t* m_offloadBufferWritep; // Write pointer into current buffer
|
||||
uint32_t* const m_offloadBufferEndp; // End of offload buffer
|
||||
|
||||
explicit VerilatedTraceOffloadBuffer(Trace& owner);
|
||||
virtual ~VerilatedTraceOffloadBuffer() = default;
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// Hot path internal interface to Verilator generated code
|
||||
|
||||
// Offloaded tracing. Just dump everything in the offload buffer
|
||||
inline void chgBit(uint32_t code, CData newval) {
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_BIT_0 | newval;
|
||||
|
|
@ -461,63 +543,7 @@ public:
|
|||
m_offloadBufferWritep += 4;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
|
||||
#define chgBit chgBitImpl
|
||||
#define chgCData chgCDataImpl
|
||||
#define chgSData chgSDataImpl
|
||||
#define chgIData chgIDataImpl
|
||||
#define chgQData chgQDataImpl
|
||||
#define chgWData chgWDataImpl
|
||||
#define chgDouble chgDoubleImpl
|
||||
#endif
|
||||
|
||||
// In non-offload mode, these are called directly by the trace callbacks,
|
||||
// and are called chg*. In offload mode, they are called by the worker
|
||||
// thread and are called chg*Impl
|
||||
|
||||
// Check previous dumped value of signal. If changed, then emit trace entry
|
||||
VL_ATTR_ALWINLINE inline void chgBit(uint32_t* oldp, CData newval) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullBit(oldp, newval);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgCData(uint32_t* oldp, CData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullCData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgSData(uint32_t* oldp, SData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullSData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgIData(uint32_t* oldp, IData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgQData(uint32_t* oldp, QData newval, int bits) {
|
||||
const uint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) {
|
||||
if (VL_UNLIKELY(oldp[i] ^ newvalp[i])) {
|
||||
fullWData(oldp, newvalp, bits);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgDouble(uint32_t* oldp, double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY(*reinterpret_cast<double*>(oldp) != newval)) fullDouble(oldp, newval);
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
#undef chgBit
|
||||
#undef chgCData
|
||||
#undef chgSData
|
||||
#undef chgIData
|
||||
#undef chgQData
|
||||
#undef chgWData
|
||||
#undef chgDouble
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // guard
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
#include "verilated_intrinsics.h"
|
||||
#include "verilated_trace.h"
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
#ifdef VL_THREADED
|
||||
# include "verilated_threads.h"
|
||||
# include <list>
|
||||
#endif
|
||||
|
|
@ -78,7 +78,7 @@ static std::string doubleToTimescale(double value) {
|
|||
return valuestr; // Gets converted to string, so no ref to stack
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
#ifdef VL_THREADED
|
||||
//=========================================================================
|
||||
// Buffer management
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
|
|||
|
||||
const uint32_t* readp = bufferp;
|
||||
|
||||
std::unique_ptr<VL_BUF_T> traceBufp; // We own the passed tracebuffer
|
||||
std::unique_ptr<Buffer> traceBufp; // We own the passed tracebuffer
|
||||
|
||||
while (true) {
|
||||
const uint32_t cmd = readp[0];
|
||||
|
|
@ -143,44 +143,44 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
|
|||
// CHG_* commands
|
||||
case VerilatedTraceOffloadCommand::CHG_BIT_0:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_0 " << top);
|
||||
traceBufp->chgBitImpl(oldp, 0);
|
||||
traceBufp->chgBit(oldp, 0);
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_BIT_1:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_1 " << top);
|
||||
traceBufp->chgBitImpl(oldp, 1);
|
||||
traceBufp->chgBit(oldp, 1);
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_CDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_CDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
traceBufp->chgCDataImpl(oldp, *readp, top);
|
||||
traceBufp->chgCData(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_SDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_SDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
traceBufp->chgSDataImpl(oldp, *readp, top);
|
||||
traceBufp->chgSData(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_IDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_IDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
traceBufp->chgIDataImpl(oldp, *readp, top);
|
||||
traceBufp->chgIData(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_QDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_QDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
traceBufp->chgQDataImpl(oldp, *reinterpret_cast<const QData*>(readp), top);
|
||||
traceBufp->chgQData(oldp, *reinterpret_cast<const QData*>(readp), top);
|
||||
readp += 2;
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_WDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_WDATA " << top);
|
||||
traceBufp->chgWDataImpl(oldp, readp, top);
|
||||
traceBufp->chgWData(oldp, readp, top);
|
||||
readp += VL_WORDS_I(top);
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_DOUBLE:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_DOUBLE " << top);
|
||||
traceBufp->chgDoubleImpl(oldp, *reinterpret_cast<const double*>(readp));
|
||||
traceBufp->chgDouble(oldp, *reinterpret_cast<const double*>(readp));
|
||||
readp += 2;
|
||||
continue;
|
||||
|
||||
|
|
@ -196,7 +196,7 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
|
|||
case VerilatedTraceOffloadCommand::TRACE_BUFFER:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command TRACE_BUFFER " << top);
|
||||
readp -= 1; // No code in this command, undo increment
|
||||
traceBufp.reset(*reinterpret_cast<VL_BUF_T* const*>(readp));
|
||||
traceBufp.reset(*reinterpret_cast<Buffer* const*>(readp));
|
||||
readp += 2;
|
||||
continue;
|
||||
|
||||
|
|
@ -252,24 +252,28 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::shutdownOffloadWorker() {
|
|||
// Life cycle
|
||||
|
||||
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::closeBase() {
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
shutdownOffloadWorker();
|
||||
while (m_numOffloadBuffers) {
|
||||
delete[] m_offloadBuffersFromWorker.get();
|
||||
--m_numOffloadBuffers;
|
||||
#ifdef VL_THREADED
|
||||
if (offload()) {
|
||||
shutdownOffloadWorker();
|
||||
while (m_numOffloadBuffers) {
|
||||
delete[] m_offloadBuffersFromWorker.get();
|
||||
--m_numOffloadBuffers;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::flushBase() {
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
// Hand an empty buffer to the worker thread
|
||||
uint32_t* const bufferp = getOffloadBuffer();
|
||||
*bufferp = VerilatedTraceOffloadCommand::END;
|
||||
m_offloadBuffersToWorker.put(bufferp);
|
||||
// Wait for it to be returned. As the processing is in-order,
|
||||
// this ensures all previous buffers have been processed.
|
||||
waitForOffloadBuffer(bufferp);
|
||||
#ifdef VL_THREADED
|
||||
if (offload()) {
|
||||
// Hand an empty buffer to the worker thread
|
||||
uint32_t* const bufferp = getOffloadBuffer();
|
||||
*bufferp = VerilatedTraceOffloadCommand::END;
|
||||
m_offloadBuffersToWorker.put(bufferp);
|
||||
// Wait for it to be returned. As the processing is in-order,
|
||||
// this ensures all previous buffers have been processed.
|
||||
waitForOffloadBuffer(bufferp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -299,9 +303,7 @@ template <> VerilatedTrace<VL_SUB_T, VL_BUF_T>::~VerilatedTrace() {
|
|||
if (m_sigs_enabledp) VL_DO_CLEAR(delete[] m_sigs_enabledp, m_sigs_enabledp = nullptr);
|
||||
Verilated::removeFlushCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush, this);
|
||||
Verilated::removeExitCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit, this);
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
closeBase();
|
||||
#endif
|
||||
if (offload()) closeBase();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
|
|
@ -355,17 +357,19 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::traceInit() VL_MT_UNSAFE {
|
|||
Verilated::addFlushCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush, this);
|
||||
Verilated::addExitCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit, this);
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
// Compute offload buffer size. we need to be able to store a new value for
|
||||
// each signal, which is 'nextCode()' entries after the init callbacks
|
||||
// above have been run, plus up to 2 more words of metadata per signal,
|
||||
// plus fixed overhead of 1 for a termination flag and 3 for a time stamp
|
||||
// update.
|
||||
m_offloadBufferSize = nextCode() + numSignals() * 2 + 4;
|
||||
#ifdef VL_THREADED
|
||||
if (offload()) {
|
||||
// Compute offload buffer size. we need to be able to store a new value for
|
||||
// each signal, which is 'nextCode()' entries after the init callbacks
|
||||
// above have been run, plus up to 2 more words of metadata per signal,
|
||||
// plus fixed overhead of 1 for a termination flag and 3 for a time stamp
|
||||
// update.
|
||||
m_offloadBufferSize = nextCode() + numSignals() * 2 + 4;
|
||||
|
||||
// Start the worker thread
|
||||
m_workerThread.reset(
|
||||
new std::thread{&VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain, this});
|
||||
// Start the worker thread
|
||||
m_workerThread.reset(
|
||||
new std::thread{&VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain, this});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -451,7 +455,7 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dumpvars(int level, const std::string&
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
#ifdef VL_THREADED
|
||||
template <> //
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::parallelWorkerTask(void* datap, bool) {
|
||||
ParallelWorkerData* const wdp = reinterpret_cast<ParallelWorkerData*>(datap);
|
||||
|
|
@ -479,45 +483,47 @@ template <> VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWo
|
|||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runCallbacks(const std::vector<CallbackRecord>& cbVec) {
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
// If tracing in parallel, dispatch to the thread pool
|
||||
VlThreadPool* threadPoolp = static_cast<VlThreadPool*>(m_contextp->threadPoolp());
|
||||
// List of work items for thread (std::list, as ParallelWorkerData is not movable)
|
||||
std::list<ParallelWorkerData> workerData;
|
||||
// We use the whole pool + the main thread
|
||||
const unsigned threads = threadPoolp->numThreads() + 1;
|
||||
// Main thread executes all jobs with index % threads == 0
|
||||
std::vector<ParallelWorkerData*> mainThreadWorkerData;
|
||||
// Enuque all the jobs
|
||||
for (unsigned i = 0; i < cbVec.size(); ++i) {
|
||||
const CallbackRecord& cbr = cbVec[i];
|
||||
// Always get the trace buffer on the main thread
|
||||
Buffer* const bufp = getTraceBuffer();
|
||||
// Create new work item
|
||||
workerData.emplace_back(cbr.m_dumpCb, cbr.m_userp, bufp);
|
||||
// Grab the new work item
|
||||
ParallelWorkerData* const itemp = &workerData.back();
|
||||
// Enqueue task to thread pool, or main thread
|
||||
if (unsigned rem = i % threads) {
|
||||
threadPoolp->workerp(rem - 1)->addTask(parallelWorkerTask, itemp);
|
||||
} else {
|
||||
mainThreadWorkerData.push_back(itemp);
|
||||
#ifdef VL_THREADED
|
||||
if (parallel()) {
|
||||
// If tracing in parallel, dispatch to the thread pool
|
||||
VlThreadPool* threadPoolp = static_cast<VlThreadPool*>(m_contextp->threadPoolp());
|
||||
// List of work items for thread (std::list, as ParallelWorkerData is not movable)
|
||||
std::list<ParallelWorkerData> workerData;
|
||||
// We use the whole pool + the main thread
|
||||
const unsigned threads = threadPoolp->numThreads() + 1;
|
||||
// Main thread executes all jobs with index % threads == 0
|
||||
std::vector<ParallelWorkerData*> mainThreadWorkerData;
|
||||
// Enuque all the jobs
|
||||
for (unsigned i = 0; i < cbVec.size(); ++i) {
|
||||
const CallbackRecord& cbr = cbVec[i];
|
||||
// Always get the trace buffer on the main thread
|
||||
Buffer* const bufp = getTraceBuffer();
|
||||
// Create new work item
|
||||
workerData.emplace_back(cbr.m_dumpCb, cbr.m_userp, bufp);
|
||||
// Grab the new work item
|
||||
ParallelWorkerData* const itemp = &workerData.back();
|
||||
// Enqueue task to thread pool, or main thread
|
||||
if (unsigned rem = i % threads) {
|
||||
threadPoolp->workerp(rem - 1)->addTask(parallelWorkerTask, itemp);
|
||||
} else {
|
||||
mainThreadWorkerData.push_back(itemp);
|
||||
}
|
||||
}
|
||||
// Execute main thead jobs
|
||||
for (ParallelWorkerData* const itemp : mainThreadWorkerData) {
|
||||
parallelWorkerTask(itemp, false);
|
||||
}
|
||||
// Commit all trace buffers in order
|
||||
for (ParallelWorkerData& item : workerData) {
|
||||
// Wait until ready
|
||||
item.wait();
|
||||
// Commit the buffer
|
||||
commitTraceBuffer(item.m_bufp);
|
||||
}
|
||||
}
|
||||
// Execute main thead jobs
|
||||
for (ParallelWorkerData* const itemp : mainThreadWorkerData) {
|
||||
parallelWorkerTask(itemp, false);
|
||||
}
|
||||
// Commit all trace buffers in order
|
||||
for (ParallelWorkerData& item : workerData) {
|
||||
// Wait until ready
|
||||
item.wait();
|
||||
// Commit the buffer
|
||||
commitTraceBuffer(item.m_bufp);
|
||||
}
|
||||
|
||||
// Done
|
||||
return;
|
||||
// Done
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Fall back on sequential execution
|
||||
for (const CallbackRecord& cbr : cbVec) {
|
||||
|
|
@ -527,19 +533,35 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runCallbacks(const std::vector<Callback
|
|||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runOffloadedCallbacks(
|
||||
const std::vector<CallbackRecord>& cbVec) {
|
||||
// Fall back on sequential execution
|
||||
for (const CallbackRecord& cbr : cbVec) {
|
||||
#ifdef VL_THREADED
|
||||
Buffer* traceBufferp = getTraceBuffer();
|
||||
cbr.m_dumpOffloadCb(cbr.m_userp, static_cast<OffloadBuffer*>(traceBufferp));
|
||||
commitTraceBuffer(traceBufferp);
|
||||
#else
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// Not really VL_MT_SAFE but more VL_MT_UNSAFE_ONE.
|
||||
// This does get the mutex, but if multiple threads are trying to dump
|
||||
// chances are the data being dumped will have other problems
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (VL_UNCOVERABLE(m_timeLastDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
|
||||
if (VL_UNCOVERABLE(m_didSomeDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
|
||||
VL_PRINTF_MT("%%Warning: previous dump at t=%" PRIu64 ", requesting t=%" PRIu64
|
||||
", dump call ignored\n",
|
||||
m_timeLastDump, timeui);
|
||||
return;
|
||||
} // LCOV_EXCL_STOP
|
||||
m_timeLastDump = timeui;
|
||||
m_didSomeDump = true;
|
||||
|
||||
Verilated::quiesce();
|
||||
|
||||
|
|
@ -550,35 +572,47 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
|
|||
if (!preChangeDump()) return;
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
// Currently only incremental dumps run on the worker thread
|
||||
uint32_t* bufferp = nullptr;
|
||||
if (VL_LIKELY(!m_fullDump)) {
|
||||
// Get the offload buffer we are about to fill
|
||||
bufferp = getOffloadBuffer();
|
||||
m_offloadBufferWritep = bufferp;
|
||||
m_offloadBufferEndp = bufferp + m_offloadBufferSize;
|
||||
if (offload()) {
|
||||
#ifdef VL_THREADED
|
||||
// Currently only incremental dumps run on the worker thread
|
||||
if (VL_LIKELY(!m_fullDump)) {
|
||||
// Get the offload buffer we are about to fill
|
||||
bufferp = getOffloadBuffer();
|
||||
m_offloadBufferWritep = bufferp;
|
||||
m_offloadBufferEndp = bufferp + m_offloadBufferSize;
|
||||
|
||||
// Tell worker to update time point
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
|
||||
*reinterpret_cast<uint64_t*>(m_offloadBufferWritep + 1) = timeui;
|
||||
m_offloadBufferWritep += 3;
|
||||
// Tell worker to update time point
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
|
||||
*reinterpret_cast<uint64_t*>(m_offloadBufferWritep + 1) = timeui;
|
||||
m_offloadBufferWritep += 3;
|
||||
} else {
|
||||
// Update time point
|
||||
flushBase();
|
||||
emitTimeChange(timeui);
|
||||
}
|
||||
#else
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
|
||||
#endif
|
||||
} else {
|
||||
// Update time point
|
||||
flushBase();
|
||||
emitTimeChange(timeui);
|
||||
}
|
||||
#else
|
||||
// Update time point
|
||||
emitTimeChange(timeui);
|
||||
#endif
|
||||
|
||||
// Run the callbacks
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
m_fullDump = false; // No more need for next dump to be full
|
||||
runCallbacks(m_fullCbs);
|
||||
if (offload()) {
|
||||
runOffloadedCallbacks(m_fullOffloadCbs);
|
||||
} else {
|
||||
runCallbacks(m_fullCbs);
|
||||
}
|
||||
} else {
|
||||
runCallbacks(m_chgCbs);
|
||||
if (offload()) {
|
||||
runOffloadedCallbacks(m_chgOffloadCbs);
|
||||
} else {
|
||||
runCallbacks(m_chgCbs);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < m_cleanupCbs.size(); ++i) {
|
||||
|
|
@ -586,8 +620,8 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
|
|||
cbr.m_cleanupCb(cbr.m_userp, self());
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
if (VL_LIKELY(bufferp)) {
|
||||
#ifdef VL_THREADED
|
||||
if (offload() && VL_LIKELY(bufferp)) {
|
||||
// Mark end of the offload buffer we just filled
|
||||
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::END;
|
||||
|
||||
|
|
@ -604,15 +638,56 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
|
|||
// Non-hot path internal interface to Verilator generated code
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addContext(VerilatedContext* contextp)
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addModel(VerilatedModel* modelp)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (m_contextp && contextp != m_contextp) {
|
||||
VL_FATAL_MT(
|
||||
__FILE__, __LINE__, "",
|
||||
"A trace file instance can only handle models from the same simulation context");
|
||||
|
||||
const bool firstModel = m_models.empty();
|
||||
const bool newModel = m_models.insert(modelp).second;
|
||||
VerilatedContext* const contextp = modelp->contextp();
|
||||
|
||||
// Validate
|
||||
if (!newModel) { // LCOV_EXCL_START
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"The same model has already been added to this trace file");
|
||||
}
|
||||
if (VL_UNCOVERABLE(m_contextp && contextp != m_contextp)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"A trace file instance can only handle models from the same context");
|
||||
}
|
||||
if (VL_UNCOVERABLE(m_didSomeDump)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"Cannot add models to a trace file if 'dump' has already been called");
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
// Keep hold of the context
|
||||
m_contextp = contextp;
|
||||
|
||||
// Get the desired trace config from the model
|
||||
const std::unique_ptr<VerilatedTraceConfig> configp = modelp->traceConfig();
|
||||
#ifndef VL_THREADED
|
||||
if (configp->m_useOffloading) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Cannot use trace offloading without VL_THREADED");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Configure trace base class
|
||||
if (!firstModel) {
|
||||
if (m_offload != configp->m_useOffloading) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"Either all or no models using the same trace file must use offloading");
|
||||
}
|
||||
}
|
||||
m_offload = configp->m_useOffloading;
|
||||
// If at least one model requests parallel tracing, then use it
|
||||
m_parallel |= configp->m_useParallel;
|
||||
|
||||
if (VL_UNCOVERABLE(m_parallel && m_offload)) { // LCOV_EXCL_START
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Cannot use parallel tracing with offloading");
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
// Configure format specific sub class
|
||||
configure(*(configp.get()));
|
||||
}
|
||||
|
||||
template <>
|
||||
|
|
@ -620,36 +695,31 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCallbackRecord(std::vector<CallbackR
|
|||
CallbackRecord&& cbRec)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START
|
||||
const std::string msg = (std::string{"Internal: "} + __FILE__ + "::" + __FUNCTION__
|
||||
+ " called with already open file");
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
} // LCOV_EXCL_STOP
|
||||
cbVec.push_back(cbRec);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addInitCb(initCb_t cb, void* userp,
|
||||
VerilatedContext* contextp) VL_MT_SAFE {
|
||||
addContext(contextp);
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_initCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpCb_t cb, void* userp,
|
||||
VerilatedContext* contextp) VL_MT_SAFE {
|
||||
addContext(contextp);
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_fullCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpCb_t cb, void* userp,
|
||||
VerilatedContext* contextp) VL_MT_SAFE {
|
||||
addContext(contextp);
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_fullOffloadCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_chgCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCleanupCb(cleanupCb_t cb, void* userp,
|
||||
VerilatedContext* contextp) VL_MT_SAFE {
|
||||
addContext(contextp);
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_chgOffloadCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_cleanupCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
|
||||
|
|
@ -756,20 +826,10 @@ static inline void cvtQDataToStr(char* dstp, QData value) {
|
|||
// VerilatedTraceBuffer
|
||||
|
||||
template <> //
|
||||
VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::VerilatedTraceBuffer(VL_SUB_T& owner)
|
||||
: m_owner{owner} {
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
if (m_offloadBufferWritep) {
|
||||
using This = VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>*;
|
||||
// Tack on the buffer address
|
||||
static_assert(2 * sizeof(uint32_t) >= sizeof(This),
|
||||
"This should be enough on all plafrorms");
|
||||
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::TRACE_BUFFER;
|
||||
*reinterpret_cast<This*>(m_offloadBufferWritep) = this;
|
||||
m_offloadBufferWritep += 2;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
VerilatedTraceBuffer<VL_BUF_T>::VerilatedTraceBuffer(Trace& owner)
|
||||
: VL_BUF_T{owner}
|
||||
, m_sigs_oldvalp{owner.m_sigs_oldvalp}
|
||||
, m_sigs_enabledp{owner.m_sigs_enabledp} {}
|
||||
|
||||
// These functions must write the new value back into the old value store,
|
||||
// and subsequently call the format specific emit* implementations. Note
|
||||
|
|
@ -777,61 +837,81 @@ VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::VerilatedTraceBuffer(VL_SUB_T& owner)
|
|||
// the emit* functions can be inlined for performance.
|
||||
|
||||
template <> //
|
||||
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullBit(uint32_t* oldp, CData newval) {
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullBit(uint32_t* oldp, CData newval) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitBit(code, newval);
|
||||
emitBit(code, newval);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullCData(uint32_t* oldp, CData newval, int bits) {
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullCData(uint32_t* oldp, CData newval, int bits) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitCData(code, newval, bits);
|
||||
emitCData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullSData(uint32_t* oldp, SData newval, int bits) {
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullSData(uint32_t* oldp, SData newval, int bits) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitSData(code, newval, bits);
|
||||
emitSData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullIData(uint32_t* oldp, IData newval, int bits) {
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullIData(uint32_t* oldp, IData newval, int bits) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitIData(code, newval, bits);
|
||||
emitIData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullQData(uint32_t* oldp, QData newval, int bits) {
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullQData(uint32_t* oldp, QData newval, int bits) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*reinterpret_cast<QData*>(oldp) = newval;
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitQData(code, newval, bits);
|
||||
emitQData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullWData(uint32_t* oldp, const WData* newvalp,
|
||||
int bits) {
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullWData(uint32_t* oldp, const WData* newvalp, int bits) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
for (int i = 0; i < VL_WORDS_I(bits); ++i) oldp[i] = newvalp[i];
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitWData(code, newvalp, bits);
|
||||
emitWData(code, newvalp, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullDouble(uint32_t* oldp, double newval) {
|
||||
template <> //
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullDouble(uint32_t* oldp, double newval) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*reinterpret_cast<double*>(oldp) = newval;
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
self()->emitDouble(code, newval);
|
||||
emitDouble(code, newval);
|
||||
}
|
||||
|
||||
#ifdef VL_THREADED
|
||||
//=========================================================================
|
||||
// VerilatedTraceOffloadBuffer
|
||||
|
||||
template <> //
|
||||
VerilatedTraceOffloadBuffer<VL_BUF_T>::VerilatedTraceOffloadBuffer(VL_SUB_T& owner)
|
||||
: VerilatedTraceBuffer<VL_BUF_T>{owner}
|
||||
, m_offloadBufferWritep{owner.m_offloadBufferWritep}
|
||||
, m_offloadBufferEndp{owner.m_offloadBufferEndp} {
|
||||
if (m_offloadBufferWritep) {
|
||||
using This = VerilatedTraceBuffer<VL_BUF_T>*;
|
||||
// Tack on the buffer address
|
||||
static_assert(2 * sizeof(uint32_t) >= sizeof(This),
|
||||
"This should be enough on all plafrorms");
|
||||
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::TRACE_BUFFER;
|
||||
*reinterpret_cast<This*>(m_offloadBufferWritep) = static_cast<This>(this);
|
||||
m_offloadBufferWritep += 2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // VL_CPPCHECK
|
||||
|
|
|
|||
|
|
@ -229,9 +229,11 @@ VerilatedVcd::~VerilatedVcd() {
|
|||
if (m_wrBufp) VL_DO_CLEAR(delete[] m_wrBufp, m_wrBufp = nullptr);
|
||||
deleteNameMap();
|
||||
if (m_filep && m_fileNewed) VL_DO_CLEAR(delete m_filep, m_filep = nullptr);
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
assert(m_numBuffers == m_freeBuffers.size());
|
||||
for (auto& pair : m_freeBuffers) VL_DO_CLEAR(delete[] pair.first, pair.first = nullptr);
|
||||
#ifdef VL_THREADED
|
||||
if (parallel()) {
|
||||
assert(m_numBuffers == m_freeBuffers.size());
|
||||
for (auto& pair : m_freeBuffers) VL_DO_CLEAR(delete[] pair.first, pair.first = nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -260,11 +262,6 @@ void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
|||
// This function is on the flush() call path
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (!isOpen()) return;
|
||||
if (m_evcd) {
|
||||
printStr("$vcdclose ");
|
||||
printQuad(timeLastDump());
|
||||
printStr(" $end\n");
|
||||
}
|
||||
closePrev();
|
||||
// closePrev() called Super::flush(), so we just
|
||||
// need to shut down the tracing thread here.
|
||||
|
|
@ -514,38 +511,29 @@ void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, b
|
|||
|
||||
// Print reference
|
||||
std::string decl = "$var ";
|
||||
if (m_evcd) {
|
||||
decl += "port";
|
||||
} else {
|
||||
decl += wirep; // usually "wire"
|
||||
}
|
||||
decl += wirep; // usually "wire"
|
||||
|
||||
constexpr size_t bufsize = 1000;
|
||||
char buf[bufsize];
|
||||
VL_SNPRINTF(buf, bufsize, " %2d ", bits);
|
||||
decl += buf;
|
||||
if (m_evcd) {
|
||||
VL_SNPRINTF(buf, bufsize, "<%u", code);
|
||||
decl += buf;
|
||||
} else {
|
||||
// Add string code to decl
|
||||
char* const endp = writeCode(buf, code);
|
||||
*endp = '\0';
|
||||
decl += buf;
|
||||
// Build suffix array entry
|
||||
char* const entryp = &m_suffixes[code * VL_TRACE_SUFFIX_ENTRY_SIZE];
|
||||
const size_t length = endp - buf;
|
||||
assert(length <= VL_TRACE_MAX_VCD_CODE_SIZE);
|
||||
// 1 bit values don't have a ' ' separator between value and string code
|
||||
const bool isBit = bits == 1;
|
||||
entryp[0] = ' '; // Separator
|
||||
// Use memcpy as we checked size above, and strcpy is flagged unsafe
|
||||
std::memcpy(entryp + !isBit, buf,
|
||||
std::strlen(buf)); // Code (overwrite separator if isBit)
|
||||
entryp[length + !isBit] = '\n'; // Replace '\0' with line termination '\n'
|
||||
// Set length of suffix (used to increment write pointer)
|
||||
entryp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = !isBit + length + 1;
|
||||
}
|
||||
// Add string code to decl
|
||||
char* const endp = writeCode(buf, code);
|
||||
*endp = '\0';
|
||||
decl += buf;
|
||||
// Build suffix array entry
|
||||
char* const entryp = &m_suffixes[code * VL_TRACE_SUFFIX_ENTRY_SIZE];
|
||||
const size_t length = endp - buf;
|
||||
assert(length <= VL_TRACE_MAX_VCD_CODE_SIZE);
|
||||
// 1 bit values don't have a ' ' separator between value and string code
|
||||
const bool isBit = bits == 1;
|
||||
entryp[0] = ' '; // Separator
|
||||
// Use memcpy as we checked size above, and strcpy is flagged unsafe
|
||||
std::memcpy(entryp + !isBit, buf,
|
||||
std::strlen(buf)); // Code (overwrite separator if isBit)
|
||||
entryp[length + !isBit] = '\n'; // Replace '\0' with line termination '\n'
|
||||
// Set length of suffix (used to increment write pointer)
|
||||
entryp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = !isBit + length + 1;
|
||||
decl += " ";
|
||||
decl += basename;
|
||||
if (array) {
|
||||
|
|
@ -583,67 +571,63 @@ void VerilatedVcd::declDouble(uint32_t code, const char* name, bool array, int a
|
|||
//=============================================================================
|
||||
// Get/commit trace buffer
|
||||
|
||||
VerilatedVcdBuffer* VerilatedVcd::getTraceBuffer() {
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
// Note: This is called from VeriltedVcd::dump, which already holds the lock
|
||||
// If no buffer available, allocate a new one
|
||||
if (m_freeBuffers.empty()) {
|
||||
constexpr size_t pageSize = 4096;
|
||||
// 4 * m_maxSignalBytes, so we can reserve 2 * m_maxSignalBytes at the end for safety
|
||||
size_t startingSize = roundUpToMultipleOf<pageSize>(4 * m_maxSignalBytes);
|
||||
m_freeBuffers.emplace_back(new char[startingSize], startingSize);
|
||||
++m_numBuffers;
|
||||
VerilatedVcd::Buffer* VerilatedVcd::getTraceBuffer() {
|
||||
VerilatedVcd::Buffer* const bufp = new Buffer{*this};
|
||||
#ifdef VL_THREADED
|
||||
if (parallel()) {
|
||||
// Note: This is called from VeriltedVcd::dump, which already holds the lock
|
||||
// If no buffer available, allocate a new one
|
||||
if (m_freeBuffers.empty()) {
|
||||
constexpr size_t pageSize = 4096;
|
||||
// 4 * m_maxSignalBytes, so we can reserve 2 * m_maxSignalBytes at the end for safety
|
||||
size_t startingSize = roundUpToMultipleOf<pageSize>(4 * m_maxSignalBytes);
|
||||
m_freeBuffers.emplace_back(new char[startingSize], startingSize);
|
||||
++m_numBuffers;
|
||||
}
|
||||
// Grab a buffer
|
||||
const auto pair = m_freeBuffers.back();
|
||||
m_freeBuffers.pop_back();
|
||||
// Initialize
|
||||
bufp->m_writep = bufp->m_bufp = pair.first;
|
||||
bufp->m_size = pair.second;
|
||||
bufp->adjustGrowp();
|
||||
}
|
||||
// Grab a buffer
|
||||
const auto pair = m_freeBuffers.back();
|
||||
m_freeBuffers.pop_back();
|
||||
// Return the buffer
|
||||
return new VerilatedVcdBuffer{*this, pair.first, pair.second};
|
||||
#else
|
||||
return new VerilatedVcdBuffer{*this};
|
||||
#endif
|
||||
// Return the buffer
|
||||
return bufp;
|
||||
}
|
||||
|
||||
void VerilatedVcd::commitTraceBuffer(VerilatedVcdBuffer* bufp) {
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
// Note: This is called from VeriltedVcd::dump, which already holds the lock
|
||||
// Resize output buffer. Note, we use the full size of the trace buffer, as
|
||||
// this is a lot more stable than the actual occupancy of the trace buffer.
|
||||
// This helps us to avoid re-allocations due to small size changes.
|
||||
bufferResize(bufp->m_size);
|
||||
// Compute occupancy of buffer
|
||||
const size_t usedSize = bufp->m_writep - bufp->m_bufp;
|
||||
// Copy to output buffer
|
||||
std::memcpy(m_writep, bufp->m_bufp, usedSize);
|
||||
// Adjust write pointer
|
||||
m_writep += usedSize;
|
||||
// Flush if necessary
|
||||
bufferCheck();
|
||||
// Put buffer back on free list
|
||||
m_freeBuffers.emplace_back(bufp->m_bufp, bufp->m_size);
|
||||
void VerilatedVcd::commitTraceBuffer(VerilatedVcd::Buffer* bufp) {
|
||||
if (parallel()) {
|
||||
#if VL_THREADED
|
||||
// Note: This is called from VeriltedVcd::dump, which already holds the lock
|
||||
// Resize output buffer. Note, we use the full size of the trace buffer, as
|
||||
// this is a lot more stable than the actual occupancy of the trace buffer.
|
||||
// This helps us to avoid re-allocations due to small size changes.
|
||||
bufferResize(bufp->m_size);
|
||||
// Compute occupancy of buffer
|
||||
const size_t usedSize = bufp->m_writep - bufp->m_bufp;
|
||||
// Copy to output buffer
|
||||
std::memcpy(m_writep, bufp->m_bufp, usedSize);
|
||||
// Adjust write pointer
|
||||
m_writep += usedSize;
|
||||
// Flush if necessary
|
||||
bufferCheck();
|
||||
// Put buffer back on free list
|
||||
m_freeBuffers.emplace_back(bufp->m_bufp, bufp->m_size);
|
||||
#else
|
||||
// Needs adjusting for emitTimeChange
|
||||
m_writep = bufp->m_writep;
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
|
||||
#endif
|
||||
} else {
|
||||
// Needs adjusting for emitTimeChange
|
||||
m_writep = bufp->m_writep;
|
||||
}
|
||||
delete bufp;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcdBuffer implementation
|
||||
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
VerilatedVcdBuffer::VerilatedVcdBuffer(VerilatedVcd& owner, char* bufp, size_t size)
|
||||
: VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer>{owner}
|
||||
, m_writep{bufp}
|
||||
, m_bufp{bufp}
|
||||
, m_size{size} {
|
||||
adjustGrowp();
|
||||
}
|
||||
#else
|
||||
VerilatedVcdBuffer::VerilatedVcdBuffer(VerilatedVcd& owner)
|
||||
: VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer>{owner} {}
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// Trace rendering primitives
|
||||
|
||||
|
|
@ -679,35 +663,39 @@ void VerilatedVcdBuffer::finishLine(uint32_t code, char* writep) {
|
|||
// suffix, which was stored in the last byte of the suffix buffer entry.
|
||||
m_writep = writep + suffixp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1];
|
||||
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
// Double the size of the buffer if necessary
|
||||
if (VL_UNLIKELY(m_writep >= m_growp)) {
|
||||
// Compute occupied size of current buffer
|
||||
const size_t usedSize = m_writep - m_bufp;
|
||||
// We are always doubling the size
|
||||
m_size *= 2;
|
||||
// Allocate the new buffer
|
||||
char* const newBufp = new char[m_size];
|
||||
// Copy from current buffer to new buffer
|
||||
std::memcpy(newBufp, m_bufp, usedSize);
|
||||
// Delete current buffer
|
||||
delete[] m_bufp;
|
||||
// Make new buffer the current buffer
|
||||
m_bufp = newBufp;
|
||||
// Adjust write pointer
|
||||
m_writep = m_bufp + usedSize;
|
||||
// Adjust resize limit
|
||||
adjustGrowp();
|
||||
}
|
||||
if (m_owner.parallel()) {
|
||||
#ifdef VL_THREADED
|
||||
// Double the size of the buffer if necessary
|
||||
if (VL_UNLIKELY(m_writep >= m_growp)) {
|
||||
// Compute occupied size of current buffer
|
||||
const size_t usedSize = m_writep - m_bufp;
|
||||
// We are always doubling the size
|
||||
m_size *= 2;
|
||||
// Allocate the new buffer
|
||||
char* const newBufp = new char[m_size];
|
||||
// Copy from current buffer to new buffer
|
||||
std::memcpy(newBufp, m_bufp, usedSize);
|
||||
// Delete current buffer
|
||||
delete[] m_bufp;
|
||||
// Make new buffer the current buffer
|
||||
m_bufp = newBufp;
|
||||
// Adjust write pointer
|
||||
m_writep = m_bufp + usedSize;
|
||||
// Adjust resize limit
|
||||
adjustGrowp();
|
||||
}
|
||||
#else
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY(m_writep > m_wrFlushp)) {
|
||||
m_owner.m_writep = m_writep;
|
||||
m_owner.bufferFlush();
|
||||
m_writep = m_owner.m_writep;
|
||||
}
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
|
||||
#endif
|
||||
} else {
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY(m_writep > m_wrFlushp)) {
|
||||
m_owner.m_writep = m_writep;
|
||||
m_owner.bufferFlush();
|
||||
m_writep = m_owner.m_writep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public:
|
|||
using Super = VerilatedTrace<VerilatedVcd, VerilatedVcdBuffer>;
|
||||
|
||||
private:
|
||||
friend Buffer; // Give the buffer access to the private bits
|
||||
friend VerilatedVcdBuffer; // Give the buffer access to the private bits
|
||||
|
||||
//=========================================================================
|
||||
// VCD specific internals
|
||||
|
|
@ -49,7 +49,6 @@ private:
|
|||
VerilatedVcdFile* m_filep; // File we're writing to
|
||||
bool m_fileNewed; // m_filep needs destruction
|
||||
bool m_isOpen = false; // True indicates open file
|
||||
bool m_evcd = false; // True for evcd format
|
||||
std::string m_filename; // Filename we're writing to (if open)
|
||||
uint64_t m_rolloverMB = 0; // MB of file size to rollover at
|
||||
int m_modDepth = 0; // Depth of module hierarchy
|
||||
|
|
@ -66,7 +65,7 @@ private:
|
|||
using NameMap = std::map<const std::string, const std::string>;
|
||||
NameMap* m_namemapp = nullptr; // List of names for the header
|
||||
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
#ifdef VL_THREADED
|
||||
// Vector of free trace buffers as (pointer, size) pairs.
|
||||
std::vector<std::pair<char*, size_t>> m_freeBuffers;
|
||||
size_t m_numBuffers = 0; // Number of trace buffers allocated
|
||||
|
|
@ -110,8 +109,11 @@ protected:
|
|||
virtual bool preChangeDump() override;
|
||||
|
||||
// Trace buffer management
|
||||
virtual VerilatedVcdBuffer* getTraceBuffer() override;
|
||||
virtual void commitTraceBuffer(VerilatedVcdBuffer*) override;
|
||||
virtual Buffer* getTraceBuffer() override;
|
||||
virtual void commitTraceBuffer(Buffer*) override;
|
||||
|
||||
// Configure sub-class
|
||||
virtual void configure(const VerilatedTraceConfig&) override { return; };
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
|
|
@ -160,47 +162,46 @@ template <> void VerilatedVcd::Super::dumpvars(int level, const std::string& hie
|
|||
//=============================================================================
|
||||
// VerilatedVcdBuffer
|
||||
|
||||
class VerilatedVcdBuffer final : public VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer> {
|
||||
// Give the trace file access to the private bits
|
||||
class VerilatedVcdBuffer VL_NOT_FINAL {
|
||||
// Give the trace file ans sub-classes access to the private bits
|
||||
friend VerilatedVcd;
|
||||
friend VerilatedVcd::Super;
|
||||
friend VerilatedVcd::Buffer;
|
||||
friend VerilatedVcd::OffloadBuffer;
|
||||
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
char* m_writep; // Write pointer into m_bufp
|
||||
char* m_bufp; // The beginning of the trace buffer
|
||||
size_t m_size; // The size of the buffer at m_bufp
|
||||
char* m_growp; // Resize limit pointer
|
||||
#else
|
||||
char* m_writep = m_owner.m_writep; // Write pointer into output buffer
|
||||
char* const m_wrFlushp = m_owner.m_wrFlushp; // Output buffer flush trigger location
|
||||
#endif
|
||||
VerilatedVcd& m_owner; // Trace file owning this buffer. Required by subclasses.
|
||||
|
||||
// Write pointer into output buffer (in parallel mode, this is set up in 'getTraceBuffer')
|
||||
char* m_writep = m_owner.parallel() ? nullptr : m_owner.m_writep;
|
||||
// Output buffer flush trigger location (only used when not parallel)
|
||||
char* const m_wrFlushp = m_owner.parallel() ? nullptr : m_owner.m_wrFlushp;
|
||||
|
||||
// VCD line end string codes + metadata
|
||||
const char* const m_suffixes = m_owner.m_suffixes.data();
|
||||
// The maximum number of bytes a single signal can emit
|
||||
const size_t m_maxSignalBytes = m_owner.m_maxSignalBytes;
|
||||
|
||||
void finishLine(uint32_t code, char* writep);
|
||||
#ifdef VL_THREADED
|
||||
// Additional data for parallel tracing only
|
||||
char* m_bufp = nullptr; // The beginning of the trace buffer
|
||||
size_t m_size = 0; // The size of the buffer at m_bufp
|
||||
char* m_growp = nullptr; // Resize limit pointer
|
||||
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
void adjustGrowp() {
|
||||
m_growp = (m_bufp + m_size) - (2 * m_maxSignalBytes);
|
||||
assert(m_growp >= m_bufp + m_maxSignalBytes);
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
void finishLine(uint32_t code, char* writep);
|
||||
|
||||
// CONSTRUCTOR
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
explicit VerilatedVcdBuffer(VerilatedVcd& owner, char* bufp, size_t size);
|
||||
#else
|
||||
explicit VerilatedVcdBuffer(VerilatedVcd& owner);
|
||||
#endif
|
||||
~VerilatedVcdBuffer() = default;
|
||||
explicit VerilatedVcdBuffer(VerilatedVcd& owner)
|
||||
: m_owner{owner} {}
|
||||
virtual ~VerilatedVcdBuffer() = default;
|
||||
|
||||
//=========================================================================
|
||||
// Implementation of VerilatedTraceBuffer interface
|
||||
|
||||
// Implementations of duck-typed methods for VerilatedTraceBuffer. These are
|
||||
// called from only one place (the full* methods), so always inline them.
|
||||
VL_ATTR_ALWINLINE inline void emitBit(uint32_t code, CData newval);
|
||||
|
|
|
|||
|
|
@ -674,10 +674,10 @@ void AstNode::addHereThisAsNext(AstNode* newp) {
|
|||
tailp->m_headtailp = newp;
|
||||
}
|
||||
// Iterator fixup
|
||||
if (newLastp->m_iterpp) {
|
||||
*(newLastp->m_iterpp) = this;
|
||||
} else if (this->m_iterpp) {
|
||||
if (newLastp->m_iterpp) *(newLastp->m_iterpp) = this;
|
||||
if (this->m_iterpp) {
|
||||
*(this->m_iterpp) = newp;
|
||||
this->m_iterpp = nullptr;
|
||||
}
|
||||
//
|
||||
debugTreeChange(this, "-addHereThisAsNext: ", __LINE__, true);
|
||||
|
|
|
|||
14
src/V3Ast.h
14
src/V3Ast.h
|
|
@ -1728,13 +1728,13 @@ public:
|
|||
void dtypeSetVoid() { dtypep(findVoidDType()); }
|
||||
|
||||
// Data type locators
|
||||
AstNodeDType* findBitDType() { return findBasicDType(VBasicDTypeKwd::LOGIC); }
|
||||
AstNodeDType* findDoubleDType() { return findBasicDType(VBasicDTypeKwd::DOUBLE); }
|
||||
AstNodeDType* findStringDType() { return findBasicDType(VBasicDTypeKwd::STRING); }
|
||||
AstNodeDType* findSigned32DType() { return findBasicDType(VBasicDTypeKwd::INTEGER); }
|
||||
AstNodeDType* findUInt32DType() { return findBasicDType(VBasicDTypeKwd::UINT32); }
|
||||
AstNodeDType* findUInt64DType() { return findBasicDType(VBasicDTypeKwd::UINT64); }
|
||||
AstNodeDType* findCHandleDType() { return findBasicDType(VBasicDTypeKwd::CHANDLE); }
|
||||
AstNodeDType* findBitDType() const { return findBasicDType(VBasicDTypeKwd::LOGIC); }
|
||||
AstNodeDType* findDoubleDType() const { return findBasicDType(VBasicDTypeKwd::DOUBLE); }
|
||||
AstNodeDType* findStringDType() const { return findBasicDType(VBasicDTypeKwd::STRING); }
|
||||
AstNodeDType* findSigned32DType() const { return findBasicDType(VBasicDTypeKwd::INTEGER); }
|
||||
AstNodeDType* findUInt32DType() const { return findBasicDType(VBasicDTypeKwd::UINT32); }
|
||||
AstNodeDType* findUInt64DType() const { return findBasicDType(VBasicDTypeKwd::UINT64); }
|
||||
AstNodeDType* findCHandleDType() const { return findBasicDType(VBasicDTypeKwd::CHANDLE); }
|
||||
AstNodeDType* findEmptyQueueDType() const;
|
||||
AstNodeDType* findVoidDType() const;
|
||||
AstNodeDType* findQueueIndexDType() const;
|
||||
|
|
|
|||
|
|
@ -673,6 +673,9 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
|||
const CTypeRecursed key = adtypep->keyDTypep()->cTypeRecurse(true);
|
||||
const CTypeRecursed val = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
info.m_type = "VlAssocArray<" + key.m_type + ", " + val.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
info.m_type = "VlAssocArray<std::string, " + sub.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
info.m_type = "VlQueue<" + sub.m_type + ">";
|
||||
|
|
@ -1724,6 +1727,10 @@ string AstQueueDType::prettyDTypeName() const {
|
|||
if (boundConst()) str += ":" + cvtToStr(boundConst());
|
||||
return str + "]";
|
||||
}
|
||||
void AstWildcardArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[*]";
|
||||
}
|
||||
void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[]";
|
||||
|
|
|
|||
146
src/V3AstNodes.h
146
src/V3AstNodes.h
|
|
@ -278,6 +278,17 @@ public:
|
|||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
};
|
||||
|
||||
class AstWildcardRange final : public AstNodeRange {
|
||||
// Wildcard range specification, for wildcard index type associative arrays
|
||||
public:
|
||||
explicit AstWildcardRange(FileLine* fl)
|
||||
: ASTGEN_SUPER_WildcardRange(fl) {}
|
||||
ASTNODE_NODE_FUNCS(WildcardRange)
|
||||
virtual string emitC() { V3ERROR_NA_RETURN(""); }
|
||||
virtual string emitVerilog() { return "[*]"; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
};
|
||||
|
||||
class AstGatePin final : public AstNodeMath {
|
||||
// Possibly expand a gate primitive input pin value to match the range of the gate primitive
|
||||
public:
|
||||
|
|
@ -819,6 +830,62 @@ public:
|
|||
virtual bool isCompound() const override { return true; }
|
||||
};
|
||||
|
||||
class AstWildcardArrayDType final : public AstNodeDType {
|
||||
// Wildcard index type associative array data type, ie "some_dtype var_name [*]"
|
||||
// Children: DTYPE (moved to refDTypep() in V3Width)
|
||||
private:
|
||||
AstNodeDType* m_refDTypep; // Elements of this type (after widthing)
|
||||
public:
|
||||
AstWildcardArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_WildcardArrayDType(fl) {
|
||||
childDTypep(dtp); // Only for parser
|
||||
refDTypep(nullptr);
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(WildcardArrayDType)
|
||||
virtual const char* broken() const override {
|
||||
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|
||||
|| (!m_refDTypep && childDTypep())));
|
||||
return nullptr;
|
||||
}
|
||||
virtual void cloneRelink() override {
|
||||
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
|
||||
}
|
||||
virtual bool same(const AstNode* samep) const override {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
if (!asamep->subDTypep()) return false;
|
||||
return (subDTypep() == asamep->subDTypep());
|
||||
}
|
||||
virtual bool similarDType(AstNodeDType* samep) const override {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
return type() == samep->type() && asamep->subDTypep()
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
virtual void dumpSmall(std::ostream& str) const override;
|
||||
virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
// op1 = Range of variable
|
||||
AstNodeDType* childDTypep() const { return VN_AS(op1p(), NodeDType); }
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
virtual AstNodeDType* subDTypep() const override {
|
||||
return m_refDTypep ? m_refDTypep : childDTypep();
|
||||
}
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
virtual AstNodeDType* virtRefDTypep() const override { return m_refDTypep; }
|
||||
virtual void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
|
||||
// METHODS
|
||||
virtual AstBasicDType* basicp() const override { return subDTypep()->basicp(); }
|
||||
virtual AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; }
|
||||
virtual AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
virtual int widthAlignBytes() const override {
|
||||
return sizeof(std::map<std::string, std::string>);
|
||||
}
|
||||
virtual int widthTotalBytes() const override {
|
||||
return sizeof(std::map<std::string, std::string>);
|
||||
}
|
||||
virtual bool isCompound() const override { return true; }
|
||||
};
|
||||
|
||||
class AstBasicDType final : public AstNodeDType {
|
||||
// Builtin atomic/vectored data type
|
||||
// Children: RANGE (converted to constant in V3Width)
|
||||
|
|
@ -1687,6 +1754,44 @@ public:
|
|||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
};
|
||||
|
||||
class AstWildcardSel final : public AstNodeSel {
|
||||
// Parents: math|stmt
|
||||
// Children: varref|arraysel, math
|
||||
private:
|
||||
void init(AstNode* fromp) {
|
||||
if (fromp && VN_IS(fromp->dtypep()->skipRefp(), WildcardArrayDType)) {
|
||||
// Strip off array to find what array references
|
||||
dtypeFrom(VN_AS(fromp->dtypep()->skipRefp(), WildcardArrayDType)->subDTypep());
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
AstWildcardSel(FileLine* fl, AstNode* fromp, AstNode* bitp)
|
||||
: ASTGEN_SUPER_WildcardSel(fl, fromp, bitp) {
|
||||
init(fromp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(WildcardSel)
|
||||
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override {
|
||||
return new AstWildcardSel{this->fileline(), lhsp, rhsp};
|
||||
}
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
V3ERROR_NA;
|
||||
}
|
||||
virtual string emitVerilog() override { return "%k(%l%f[%r])"; }
|
||||
virtual string emitC() override { return "%li%k[%ri]"; }
|
||||
virtual bool cleanOut() const override { return true; }
|
||||
virtual bool cleanLhs() const override { return false; }
|
||||
virtual bool cleanRhs() const override { return true; }
|
||||
virtual bool sizeMattersLhs() const override { return false; }
|
||||
virtual bool sizeMattersRhs() const override { return false; }
|
||||
virtual bool isGateOptimizable() const override {
|
||||
return true;
|
||||
} // esp for V3Const::ifSameAssign
|
||||
virtual bool isPredictOptimizable() const override { return false; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
};
|
||||
|
||||
class AstWordSel final : public AstNodeSel {
|
||||
// Select a single word from a multi-word wide value
|
||||
public:
|
||||
|
|
@ -4879,6 +4984,47 @@ public:
|
|||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
};
|
||||
|
||||
class AstConsWildcard final : public AstNodeMath {
|
||||
// Construct a wildcard assoc array and return object, '{}
|
||||
// Parents: math
|
||||
// Children: expression (elements or other queues)
|
||||
public:
|
||||
AstConsWildcard(FileLine* fl, AstNode* defaultp)
|
||||
: ASTGEN_SUPER_ConsWildcard(fl) {
|
||||
setNOp1p(defaultp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(ConsWildcard)
|
||||
virtual string emitVerilog() override { return "'{}"; }
|
||||
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
virtual string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
virtual bool cleanOut() const override { return true; }
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
AstNode* defaultp() const { return op1p(); }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
};
|
||||
class AstSetWildcard final : public AstNodeMath {
|
||||
// Set a wildcard assoc array element and return object, '{}
|
||||
// Parents: math
|
||||
// Children: expression (elements or other queues)
|
||||
public:
|
||||
AstSetWildcard(FileLine* fl, AstNode* lhsp, AstNode* keyp, AstNode* valuep)
|
||||
: ASTGEN_SUPER_SetWildcard(fl) {
|
||||
setOp1p(lhsp);
|
||||
setNOp2p(keyp);
|
||||
setOp3p(valuep);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(SetWildcard)
|
||||
virtual string emitVerilog() override { return "'{}"; }
|
||||
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
virtual string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
virtual bool cleanOut() const override { return true; }
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
AstNode* keyp() const { return op2p(); }
|
||||
AstNode* valuep() const { return op3p(); }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
};
|
||||
|
||||
class AstConsDynArray final : public AstNodeMath {
|
||||
// Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs}
|
||||
// Parents: math
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ private:
|
|||
if (VN_IS(nodep, Var)
|
||||
|| VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType) // Or arrays
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), WildcardArrayDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), ClassRefDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), QueueDType)
|
||||
|
|
|
|||
|
|
@ -611,6 +611,17 @@ void EmitCFunc::emitVarReset(AstVar* varp) {
|
|||
emitSetVarConstant(varNameProtected + ".at(" + cvtToStr(itr.first) + ")",
|
||||
VN_AS(valuep, Const));
|
||||
}
|
||||
} else if (AstWildcardArrayDType* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
|
||||
if (initarp->defaultp()) {
|
||||
emitSetVarConstant(varNameProtected + ".atDefault()",
|
||||
VN_AS(initarp->defaultp(), Const));
|
||||
}
|
||||
const auto& mapr = initarp->map();
|
||||
for (const auto& itr : mapr) {
|
||||
AstNode* const valuep = itr.second->valuep();
|
||||
emitSetVarConstant(varNameProtected + ".at(" + cvtToStr(itr.first) + ")",
|
||||
VN_AS(valuep, Const));
|
||||
}
|
||||
} else if (AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
if (initarp->defaultp()) {
|
||||
puts("for (int __Vi=0; __Vi<" + cvtToStr(adtypep->elementsConst()));
|
||||
|
|
@ -642,6 +653,11 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
|
|||
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
||||
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
|
||||
suffix + ".atDefault()" + cvtarray);
|
||||
} else if (AstWildcardArrayDType* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
|
||||
// Access std::array as C array
|
||||
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
||||
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
|
||||
suffix + ".atDefault()" + cvtarray);
|
||||
} else if (VN_IS(dtypep, ClassRefDType)) {
|
||||
return ""; // Constructor does it
|
||||
} else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
||||
|
|
|
|||
|
|
@ -371,6 +371,14 @@ public:
|
|||
}
|
||||
puts(")");
|
||||
}
|
||||
virtual void visit(AstWildcardSel* nodep) override {
|
||||
iterateAndNextNull(nodep->fromp());
|
||||
putbs(".at(");
|
||||
AstWildcardArrayDType* const adtypep = VN_AS(nodep->fromp()->dtypep(), WildcardArrayDType);
|
||||
UASSERT_OBJ(adtypep, nodep, "Wildcard select on non-wildcard-associative type");
|
||||
iterateAndNextNull(nodep->bitp());
|
||||
puts(")");
|
||||
}
|
||||
virtual void visit(AstCCall* nodep) override {
|
||||
const AstCFunc* const funcp = nodep->funcp();
|
||||
const AstNodeModule* const funcModp = EmitCParentModule::get(funcp);
|
||||
|
|
@ -1181,6 +1189,24 @@ public:
|
|||
iterateAndNextNull(nodep->valuep());
|
||||
puts(")");
|
||||
}
|
||||
virtual void visit(AstConsWildcard* nodep) override {
|
||||
putbs(nodep->dtypep()->cType("", false, false));
|
||||
puts("()");
|
||||
if (nodep->defaultp()) {
|
||||
putbs(".setDefault(");
|
||||
iterateAndNextNull(nodep->defaultp());
|
||||
puts(")");
|
||||
}
|
||||
}
|
||||
virtual void visit(AstSetWildcard* nodep) override {
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
putbs(".set(");
|
||||
iterateAndNextNull(nodep->keyp());
|
||||
puts(", ");
|
||||
putbs("");
|
||||
iterateAndNextNull(nodep->valuep());
|
||||
puts(")");
|
||||
}
|
||||
virtual void visit(AstConsDynArray* nodep) override {
|
||||
putbs(nodep->dtypep()->cType("", false, false));
|
||||
if (!nodep->lhsp()) {
|
||||
|
|
|
|||
|
|
@ -113,12 +113,6 @@ class CMakeEmitter final {
|
|||
cmake_set_raw(*of, name + "_COVERAGE", v3Global.opt.coverage() ? "1" : "0");
|
||||
*of << "# Threaded output mode? 0/1/N threads (from --threads)\n";
|
||||
cmake_set_raw(*of, name + "_THREADS", cvtToStr(v3Global.opt.threads()));
|
||||
*of << "# Threaded tracing output mode? 0/1/N threads (from --threads/--trace-threads)\n";
|
||||
cmake_set_raw(*of, name + "_TRACE_THREADS", cvtToStr(v3Global.opt.vmTraceThreads()));
|
||||
cmake_set_raw(*of, name + "_TRACE_FST_WRITER_THREAD",
|
||||
v3Global.opt.traceThreads() && v3Global.opt.traceFormat().fst() ? "1" : "0");
|
||||
*of << "# Struct output mode? 0/1 (from --trace-structs)\n";
|
||||
cmake_set_raw(*of, name + "_TRACE_STRUCTS", cvtToStr(v3Global.opt.traceStructs()));
|
||||
*of << "# VCD Tracing output mode? 0/1 (from --trace)\n";
|
||||
cmake_set_raw(*of, name + "_TRACE_VCD",
|
||||
(v3Global.opt.trace() && v3Global.opt.traceFormat().vcd()) ? "1" : "0");
|
||||
|
|
|
|||
|
|
@ -223,6 +223,9 @@ class EmitCModel final : public EmitCFunc {
|
|||
puts("const char* hierName() const override final;\n");
|
||||
puts("const char* modelName() const override final;\n");
|
||||
puts("unsigned threads() const override final;\n");
|
||||
if (v3Global.opt.trace()) {
|
||||
puts("std::unique_ptr<VerilatedTraceConfig> traceConfig() const override final;\n");
|
||||
}
|
||||
|
||||
puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n");
|
||||
|
||||
|
|
@ -429,6 +432,17 @@ class EmitCModel final : public EmitCFunc {
|
|||
+ "\"; }\n");
|
||||
puts("unsigned " + topClassName() + "::threads() const { return "
|
||||
+ cvtToStr(std::max(1, v3Global.opt.threads())) + "; }\n");
|
||||
|
||||
if (v3Global.opt.trace()) {
|
||||
puts("std::unique_ptr<VerilatedTraceConfig> " + topClassName()
|
||||
+ "::traceConfig() const {\n");
|
||||
puts("return std::unique_ptr<VerilatedTraceConfig>{new VerilatedTraceConfig{");
|
||||
puts(v3Global.opt.useTraceParallel() ? "true" : "false");
|
||||
puts(v3Global.opt.useTraceOffload() ? ", true" : ", false");
|
||||
puts(v3Global.opt.useFstWriterThread() ? ", true" : ", false");
|
||||
puts("}};\n");
|
||||
puts("};\n");
|
||||
}
|
||||
}
|
||||
|
||||
void emitTraceMethods(AstNodeModule* modp) {
|
||||
|
|
@ -481,8 +495,8 @@ class EmitCModel final : public EmitCFunc {
|
|||
puts(/**/ "}");
|
||||
}
|
||||
puts(/**/ "if (false && levels && options) {} // Prevent unused\n");
|
||||
puts(/**/ "tfp->spTrace()->addInitCb(&" + protect("trace_init")
|
||||
+ ", &(vlSymsp->TOP), contextp());\n");
|
||||
puts(/**/ "tfp->spTrace()->addModel(this);\n");
|
||||
puts(/**/ "tfp->spTrace()->addInitCb(&" + protect("trace_init") + ", &(vlSymsp->TOP));\n");
|
||||
puts(/**/ topModNameProtected + "__" + protect("trace_register")
|
||||
+ "(&(vlSymsp->TOP), tfp->spTrace());\n");
|
||||
|
||||
|
|
|
|||
|
|
@ -73,15 +73,6 @@ public:
|
|||
of.puts("VM_TRACE_FST = ");
|
||||
of.puts(v3Global.opt.trace() && v3Global.opt.traceFormat().fst() ? "1" : "0");
|
||||
of.puts("\n");
|
||||
of.puts(
|
||||
"# Tracing threaded output mode? 0/1/N threads (from --threads/--trace-thread)\n");
|
||||
of.puts("VM_TRACE_THREADS = ");
|
||||
of.puts(cvtToStr(v3Global.opt.vmTraceThreads()));
|
||||
of.puts("\n");
|
||||
of.puts("# Separate FST writer thread? 0/1 (from --trace-fst with --trace-thread > 0)\n");
|
||||
of.puts("VM_TRACE_FST_WRITER_THREAD = ");
|
||||
of.puts(v3Global.opt.traceThreads() && v3Global.opt.traceFormat().fst() ? "1" : "0");
|
||||
of.puts("\n");
|
||||
|
||||
of.puts("\n### Object file lists...\n");
|
||||
for (int support = 0; support < 3; ++support) {
|
||||
|
|
@ -369,13 +360,13 @@ class EmitMkHierVerilation final {
|
|||
|
||||
// Rules to process hierarchical blocks
|
||||
of.puts("\n# Verilate hierarchical blocks\n");
|
||||
for (V3HierBlockPlan::const_iterator it = m_planp->begin(); it != m_planp->end(); ++it) {
|
||||
const string prefix = it->second->hierPrefix();
|
||||
const string argsFile = it->second->commandArgsFileName(false);
|
||||
of.puts(it->second->hierGenerated(true));
|
||||
for (const V3HierBlock* const blockp : m_planp->hierBlocksSorted()) {
|
||||
const string prefix = blockp->hierPrefix();
|
||||
const string argsFile = blockp->commandArgsFileName(false);
|
||||
of.puts(blockp->hierGenerated(true));
|
||||
of.puts(": $(VM_HIER_INPUT_FILES) $(VM_HIER_VERILOG_LIBS) ");
|
||||
of.puts(V3Os::filenameNonDir(argsFile) + " ");
|
||||
const V3HierBlock::HierBlockSet& children = it->second->children();
|
||||
const V3HierBlock::HierBlockSet& children = blockp->children();
|
||||
for (V3HierBlock::HierBlockSet::const_iterator child = children.begin();
|
||||
child != children.end(); ++child) {
|
||||
of.puts((*child)->hierWrapper(true) + " ");
|
||||
|
|
@ -384,16 +375,16 @@ class EmitMkHierVerilation final {
|
|||
emitLaunchVerilator(of, argsFile);
|
||||
|
||||
// Rule to build lib*.a
|
||||
of.puts(it->second->hierLib(true));
|
||||
of.puts(blockp->hierLib(true));
|
||||
of.puts(": ");
|
||||
of.puts(it->second->hierMk(true));
|
||||
of.puts(blockp->hierMk(true));
|
||||
of.puts(" ");
|
||||
for (V3HierBlock::HierBlockSet::const_iterator child = children.begin();
|
||||
child != children.end(); ++child) {
|
||||
of.puts((*child)->hierLib(true));
|
||||
of.puts(" ");
|
||||
}
|
||||
of.puts("\n\t$(MAKE) -f " + it->second->hierMk(false) + " -C " + prefix);
|
||||
of.puts("\n\t$(MAKE) -f " + blockp->hierMk(false) + " -C " + prefix);
|
||||
of.puts(" VM_PREFIX=" + prefix);
|
||||
of.puts("\n\n");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,6 +133,11 @@ private:
|
|||
iterateNull(nodep->virtRefDTypep());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstWildcardArrayDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
||||
iterateNull(nodep->virtRefDTypep());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstBasicDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
|
||||
m_hash += nodep->keyword();
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ V3StringList V3HierBlock::commandArgs(bool forCMake) const {
|
|||
opts.push_back(" --lib-create " + modp()->name()); // possibly mangled name
|
||||
if (v3Global.opt.protectKeyProvided())
|
||||
opts.push_back(" --protect-key " + v3Global.opt.protectKeyDefaulted());
|
||||
opts.push_back(" --hierarchical-child");
|
||||
opts.push_back(" --hierarchical-child " + cvtToStr(std::max(1, v3Global.opt.threads())));
|
||||
|
||||
const StrGParams gparamsStr = stringifyParams(gparams(), true);
|
||||
for (StrGParams::const_iterator paramIt = gparamsStr.begin(); paramIt != gparamsStr.end();
|
||||
|
|
@ -306,10 +306,6 @@ public:
|
|||
|
||||
//######################################################################
|
||||
|
||||
bool V3HierBlockPlan::isHierBlock(const AstNodeModule* modp) const {
|
||||
return m_blocks.find(modp) != m_blocks.end();
|
||||
}
|
||||
|
||||
void V3HierBlockPlan::add(const AstNodeModule* modp, const std::vector<AstVar*>& gparams) {
|
||||
const iterator it = m_blocks.find(modp);
|
||||
if (it == m_blocks.end()) {
|
||||
|
|
|
|||
|
|
@ -109,7 +109,6 @@ public:
|
|||
using HierVector = std::vector<const V3HierBlock*>;
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
bool isHierBlock(const AstNodeModule* modp) const;
|
||||
void add(const AstNodeModule* modp, const std::vector<AstVar*>& gparams);
|
||||
void registerUsage(const AstNodeModule* parentp, const AstNodeModule* childp);
|
||||
|
||||
|
|
|
|||
|
|
@ -480,7 +480,7 @@ private:
|
|||
// mangled_name, BlockOptions
|
||||
const V3HierBlockOptSet& hierBlocks = v3Global.opt.hierBlocks();
|
||||
const auto hierIt = vlstd::as_const(hierBlocks).find(v3Global.opt.topModule());
|
||||
UASSERT((hierIt != hierBlocks.end()) == v3Global.opt.hierChild(),
|
||||
UASSERT((hierIt != hierBlocks.end()) == !!v3Global.opt.hierChild(),
|
||||
"information of the top module must exist if --hierarchical-child is set");
|
||||
// Look at all modules, and store pointers to all module names
|
||||
for (AstNodeModule *nextp, *nodep = v3Global.rootp()->modulesp(); nodep; nodep = nextp) {
|
||||
|
|
|
|||
|
|
@ -1124,7 +1124,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
const V3HierarchicalBlockOption opt(valp);
|
||||
m_hierBlocks.emplace(opt.mangledName(), opt);
|
||||
});
|
||||
DECL_OPTION("-hierarchical-child", OnOff, &m_hierChild);
|
||||
DECL_OPTION("-hierarchical-child", Set, &m_hierChild);
|
||||
|
||||
DECL_OPTION("-I", CbPartialMatch,
|
||||
[this, &optdir](const char* optp) { addIncDirUser(parseFileArg(optdir, optp)); });
|
||||
|
|
|
|||
|
|
@ -241,7 +241,6 @@ private:
|
|||
bool m_exe = false; // main switch: --exe
|
||||
bool m_flatten = false; // main switch: --flatten
|
||||
bool m_hierarchical = false; // main switch: --hierarchical
|
||||
bool m_hierChild = false; // main switch: --hierarchical-child
|
||||
bool m_ignc = false; // main switch: --ignc
|
||||
bool m_lintOnly = false; // main switch: --lint-only
|
||||
bool m_gmake = false; // main switch: --make gmake
|
||||
|
|
@ -287,6 +286,7 @@ private:
|
|||
int m_dumpTree = 0; // main switch: --dump-tree
|
||||
int m_expandLimit = 64; // main switch: --expand-limit
|
||||
int m_gateStmts = 100; // main switch: --gate-stmts
|
||||
int m_hierChild = 0; // main switch: --hierarchical-child
|
||||
int m_ifDepth = 0; // main switch: --if-depth
|
||||
int m_inlineMult = 2000; // main switch: --inline-mult
|
||||
int m_instrCountDpi = 200; // main switch: --instr-count-dpi
|
||||
|
|
@ -516,7 +516,10 @@ public:
|
|||
int traceMaxWidth() const { return m_traceMaxWidth; }
|
||||
int traceThreads() const { return m_traceThreads; }
|
||||
bool useTraceOffload() const { return trace() && traceFormat().fst() && traceThreads() > 1; }
|
||||
bool useTraceParallel() const { return trace() && traceFormat().vcd() && threads() > 1; }
|
||||
bool useTraceParallel() const {
|
||||
return trace() && traceFormat().vcd() && threads() && (threads() > 1 || hierChild() > 1);
|
||||
}
|
||||
bool useFstWriterThread() const { return traceThreads() && traceFormat().fst(); }
|
||||
unsigned vmTraceThreads() const {
|
||||
return useTraceParallel() ? threads() : useTraceOffload() ? 1 : 0;
|
||||
}
|
||||
|
|
@ -603,7 +606,7 @@ public:
|
|||
}
|
||||
|
||||
bool hierarchical() const { return m_hierarchical; }
|
||||
bool hierChild() const { return m_hierChild; }
|
||||
int hierChild() const { return m_hierChild; }
|
||||
bool hierTop() const { return !m_hierChild && !m_hierBlocks.empty(); }
|
||||
const V3HierBlockOptSet& hierBlocks() const { return m_hierBlocks; }
|
||||
// Directory to save .tree, .dot, .dat, .vpp for hierarchical block top
|
||||
|
|
|
|||
|
|
@ -138,6 +138,8 @@ AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep, AstNodeRange* nra
|
|||
AstNode* const keyp = arangep->elementsp()->unlinkFrBack();
|
||||
arrayp = new AstBracketArrayDType(nrangep->fileline(), VFlagChildDType(), arrayp,
|
||||
keyp);
|
||||
} else if (VN_IS(nrangep, WildcardRange)) {
|
||||
arrayp = new AstWildcardArrayDType{nrangep->fileline(), VFlagChildDType{}, arrayp};
|
||||
} else {
|
||||
UASSERT_OBJ(0, nrangep, "Expected range or unsized range");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -498,7 +498,9 @@ private:
|
|||
};
|
||||
if (isTopFunc) {
|
||||
// Top functions
|
||||
funcp->argTypes("void* voidSelf, " + v3Global.opt.traceClassBase() + "::Buffer* bufp");
|
||||
funcp->argTypes("void* voidSelf, " + v3Global.opt.traceClassBase()
|
||||
+ "::" + (v3Global.opt.useTraceOffload() ? "OffloadBuffer" : "Buffer")
|
||||
+ "* bufp");
|
||||
addInitStr(voidSelfAssign(m_topModp));
|
||||
addInitStr(symClassAssign());
|
||||
// Add global activity check to change dump functions
|
||||
|
|
@ -513,12 +515,12 @@ private:
|
|||
}
|
||||
m_regFuncp->addStmtsp(new AstAddrOfCFunc(flp, funcp));
|
||||
m_regFuncp->addStmtsp(new AstText(flp, ", vlSelf", true));
|
||||
m_regFuncp->addStmtsp(
|
||||
new AstText(flp, ", vlSelf->vlSymsp->__Vm_modelp->contextp()", true));
|
||||
m_regFuncp->addStmtsp(new AstText(flp, ");\n", true));
|
||||
} else {
|
||||
// Sub functions
|
||||
funcp->argTypes(v3Global.opt.traceClassBase() + "::Buffer* bufp");
|
||||
funcp->argTypes(v3Global.opt.traceClassBase()
|
||||
+ "::" + +(v3Global.opt.useTraceOffload() ? "OffloadBuffer" : "Buffer")
|
||||
+ "* bufp");
|
||||
// Setup base references. Note in rare occasions we can end up with an empty trace
|
||||
// sub function, hence the VL_ATTR_UNUSED attributes.
|
||||
if (full) {
|
||||
|
|
@ -702,8 +704,7 @@ private:
|
|||
// Register it
|
||||
m_regFuncp->addStmtsp(new AstText(fl, "tracep->addCleanupCb(", true));
|
||||
m_regFuncp->addStmtsp(new AstAddrOfCFunc(fl, cleanupFuncp));
|
||||
m_regFuncp->addStmtsp(
|
||||
new AstText(fl, ", vlSelf, vlSelf->vlSymsp->__Vm_modelp->contextp());\n", true));
|
||||
m_regFuncp->addStmtsp(new AstText(fl, ", vlSelf);\n", true));
|
||||
|
||||
// Clear global activity flag
|
||||
cleanupFuncp->addStmtsp(
|
||||
|
|
|
|||
170
src/V3Width.cpp
170
src/V3Width.cpp
|
|
@ -981,6 +981,27 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstWildcardSel* nodep) override {
|
||||
// Signed/Real: Output type based on array-declared type; binary operator
|
||||
if (m_vup->prelim()) {
|
||||
const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp();
|
||||
const AstWildcardArrayDType* const adtypep = VN_CAST(fromDtp, WildcardArrayDType);
|
||||
if (!adtypep) {
|
||||
UINFO(1, " Related dtype: " << fromDtp << endl);
|
||||
nodep->v3fatalSrc("Wildcard array reference is not to wildcard array");
|
||||
}
|
||||
const AstBasicDType* const basicp = nodep->bitp()->dtypep()->skipRefp()->basicp();
|
||||
if (!basicp
|
||||
|| (basicp->keyword() != VBasicDTypeKwd::STRING
|
||||
&& !basicp->keyword().isIntNumeric())) {
|
||||
nodep->v3error("Wildcard index must be integral (IEEE 1800-2017 7.8.1)");
|
||||
}
|
||||
iterateCheckTyped(nodep, "Wildcard associative select", nodep->bitp(),
|
||||
adtypep->findStringDType(), BOTH);
|
||||
nodep->dtypeFrom(adtypep->subDTypep());
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstSliceSel* nodep) override {
|
||||
// Always creates as output an unpacked array
|
||||
if (m_vup->prelim()) {
|
||||
|
|
@ -1582,6 +1603,14 @@ private:
|
|||
nodep->dtypep(nodep); // The array itself, not subDtype
|
||||
UINFO(4, "dtWidthed " << nodep << endl);
|
||||
}
|
||||
virtual void visit(AstWildcardArrayDType* nodep) override {
|
||||
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
|
||||
// Iterate into subDTypep() to resolve that type and update pointer.
|
||||
nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
|
||||
// Cleanup array size
|
||||
nodep->dtypep(nodep); // The array itself, not subDtype
|
||||
UINFO(4, "dtWidthed " << nodep << endl);
|
||||
}
|
||||
virtual void visit(AstBasicDType* nodep) override {
|
||||
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
|
||||
if (nodep->generic()) return; // Already perfect
|
||||
|
|
@ -2178,6 +2207,31 @@ private:
|
|||
EXTEND_EXP);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstConsWildcard* nodep) override {
|
||||
// Type computed when constructed here
|
||||
auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), WildcardArrayDType);
|
||||
UASSERT_OBJ(vdtypep, nodep, "ConsWildcard requires wildcard upper parent data type");
|
||||
if (m_vup->prelim()) {
|
||||
nodep->dtypeFrom(vdtypep);
|
||||
if (nodep->defaultp()) {
|
||||
iterateCheck(nodep, "default", nodep->defaultp(), CONTEXT, FINAL,
|
||||
vdtypep->subDTypep(), EXTEND_EXP);
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstSetWildcard* nodep) override {
|
||||
// Type computed when constructed here
|
||||
auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), WildcardArrayDType);
|
||||
UASSERT_OBJ(vdtypep, nodep, "SetWildcard requires wildcard upper parent data type");
|
||||
if (m_vup->prelim()) {
|
||||
nodep->dtypeFrom(vdtypep);
|
||||
userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, BOTH}.p());
|
||||
iterateCheck(nodep, "key", nodep->keyp(), CONTEXT, FINAL, vdtypep->findStringDType(),
|
||||
EXTEND_EXP);
|
||||
iterateCheck(nodep, "value", nodep->valuep(), CONTEXT, FINAL, vdtypep->subDTypep(),
|
||||
EXTEND_EXP);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstConsDynArray* nodep) override {
|
||||
// Type computed when constructed here
|
||||
AstDynArrayDType* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), DynArrayDType);
|
||||
|
|
@ -2426,6 +2480,7 @@ private:
|
|||
}
|
||||
} else if (VN_IS(fromDtp, EnumDType) //
|
||||
|| VN_IS(fromDtp, AssocArrayDType) //
|
||||
|| VN_IS(fromDtp, WildcardArrayDType) //
|
||||
|| VN_IS(fromDtp, UnpackArrayDType) //
|
||||
|| VN_IS(fromDtp, DynArrayDType) //
|
||||
|| VN_IS(fromDtp, QueueDType) //
|
||||
|
|
@ -2523,6 +2578,8 @@ private:
|
|||
methodCallEnum(nodep, adtypep);
|
||||
} else if (AstAssocArrayDType* const adtypep = VN_CAST(fromDtp, AssocArrayDType)) {
|
||||
methodCallAssoc(nodep, adtypep);
|
||||
} else if (AstWildcardArrayDType* const adtypep = VN_CAST(fromDtp, WildcardArrayDType)) {
|
||||
methodCallWildcard(nodep, adtypep);
|
||||
} else if (AstDynArrayDType* const adtypep = VN_CAST(fromDtp, DynArrayDType)) {
|
||||
methodCallDyn(nodep, adtypep);
|
||||
} else if (AstQueueDType* const adtypep = VN_CAST(fromDtp, QueueDType)) {
|
||||
|
|
@ -2684,6 +2741,89 @@ private:
|
|||
nodep->v3error("Unknown built-in enum method " << nodep->prettyNameQ());
|
||||
}
|
||||
}
|
||||
void methodCallWildcard(AstMethodCall* nodep, AstWildcardArrayDType* adtypep) {
|
||||
AstCMethodHard* newp = nullptr;
|
||||
if (nodep->name() == "num" // function int num()
|
||||
|| nodep->name() == "size") {
|
||||
methodOkArguments(nodep, 0, 0);
|
||||
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
|
||||
"size"}; // So don't need num()
|
||||
newp->dtypeSetSigned32();
|
||||
} else if (nodep->name() == "first" // function int first(ref index)
|
||||
|| nodep->name() == "last" //
|
||||
|| nodep->name() == "next" //
|
||||
|| nodep->name() == "prev" //
|
||||
|| nodep->name() == "unique_index" //
|
||||
|| nodep->name() == "find_index" || nodep->name() == "find_first_index"
|
||||
|| nodep->name() == "find_last_index") {
|
||||
nodep->v3error("Array method " << nodep->prettyNameQ()
|
||||
<< " not legal on wildcard associative arrays");
|
||||
} else if (nodep->name() == "exists") { // function int exists(input index)
|
||||
// IEEE really should have made this a "bit" return
|
||||
methodOkArguments(nodep, 1, 1);
|
||||
AstNode* const index_exprp = methodCallWildcardIndexExpr(nodep, adtypep);
|
||||
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "exists",
|
||||
index_exprp->unlinkFrBack()};
|
||||
newp->dtypeSetSigned32();
|
||||
newp->pure(true);
|
||||
} else if (nodep->name() == "delete") { // function void delete([input integer index])
|
||||
methodOkArguments(nodep, 0, 1);
|
||||
methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
|
||||
if (!nodep->pinsp()) {
|
||||
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
|
||||
"clear"};
|
||||
newp->makeStatement();
|
||||
} else {
|
||||
AstNode* const index_exprp = methodCallWildcardIndexExpr(nodep, adtypep);
|
||||
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
|
||||
"erase", index_exprp->unlinkFrBack()};
|
||||
newp->makeStatement();
|
||||
}
|
||||
} else if (nodep->name() == "sort" || nodep->name() == "rsort"
|
||||
|| nodep->name() == "reverse" || nodep->name() == "shuffle") {
|
||||
nodep->v3error("Array method " << nodep->prettyNameQ()
|
||||
<< " not legal on associative arrays");
|
||||
} else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor"
|
||||
|| nodep->name() == "sum" || nodep->name() == "product") {
|
||||
// All value return
|
||||
AstWith* const withp
|
||||
= methodWithArgument(nodep, false, false, adtypep->subDTypep(),
|
||||
adtypep->findStringDType(), adtypep->subDTypep());
|
||||
methodOkArguments(nodep, 0, 0);
|
||||
methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
|
||||
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
|
||||
"r_" + nodep->name(), withp};
|
||||
newp->dtypeFrom(withp ? withp->dtypep() : adtypep->subDTypep());
|
||||
if (!nodep->firstAbovep()) newp->makeStatement();
|
||||
} else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique") {
|
||||
methodOkArguments(nodep, 0, 0);
|
||||
methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
|
||||
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
|
||||
nodep->name()};
|
||||
newp->dtypeFrom(adtypep);
|
||||
if (!nodep->firstAbovep()) newp->makeStatement();
|
||||
} else if (nodep->name() == "find" || nodep->name() == "find_first"
|
||||
|| nodep->name() == "find_last") {
|
||||
AstWith* const withp
|
||||
= methodWithArgument(nodep, true, false, nodep->findBitDType(),
|
||||
adtypep->findStringDType(), adtypep->subDTypep());
|
||||
methodOkArguments(nodep, 0, 0);
|
||||
methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
|
||||
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
|
||||
nodep->name(), withp};
|
||||
newp->dtypeFrom(adtypep);
|
||||
if (!nodep->firstAbovep()) newp->makeStatement();
|
||||
} else {
|
||||
nodep->v3error("Unknown wildcard associative array method " << nodep->prettyNameQ());
|
||||
nodep->dtypeFrom(adtypep->subDTypep()); // Best guess
|
||||
}
|
||||
if (newp) {
|
||||
newp->protect(false);
|
||||
newp->didWidth(true);
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
}
|
||||
void methodCallAssoc(AstMethodCall* nodep, AstAssocArrayDType* adtypep) {
|
||||
AstCMethodHard* newp = nullptr;
|
||||
if (nodep->name() == "num" // function int num()
|
||||
|
|
@ -2789,6 +2929,13 @@ private:
|
|||
VL_DANGLING(index_exprp); // May have been edited
|
||||
return VN_AS(nodep->pinsp(), Arg)->exprp();
|
||||
}
|
||||
AstNode* methodCallWildcardIndexExpr(AstMethodCall* nodep, AstWildcardArrayDType* adtypep) {
|
||||
AstNode* const index_exprp = VN_CAST(nodep->pinsp(), Arg)->exprp();
|
||||
iterateCheck(nodep, "index", index_exprp, CONTEXT, FINAL, adtypep->findStringDType(),
|
||||
EXTEND_EXP);
|
||||
VL_DANGLING(index_exprp); // May have been edited
|
||||
return VN_AS(nodep->pinsp(), Arg)->exprp();
|
||||
}
|
||||
void methodCallLValueRecurse(AstMethodCall* nodep, AstNode* childp, const VAccess& access) {
|
||||
if (AstNodeVarRef* const varrefp = VN_CAST(childp, NodeVarRef)) {
|
||||
varrefp->access(access);
|
||||
|
|
@ -3395,6 +3542,8 @@ private:
|
|||
VL_DO_DANGLING(patternArray(nodep, vdtypep, defaultp), nodep);
|
||||
} else if (auto* const vdtypep = VN_CAST(dtypep, AssocArrayDType)) {
|
||||
VL_DO_DANGLING(patternAssoc(nodep, vdtypep, defaultp), nodep);
|
||||
} else if (auto* const vdtypep = VN_CAST(dtypep, WildcardArrayDType)) {
|
||||
VL_DO_DANGLING(patternWildcard(nodep, vdtypep, defaultp), nodep);
|
||||
} else if (auto* const vdtypep = VN_CAST(dtypep, DynArrayDType)) {
|
||||
VL_DO_DANGLING(patternDynArray(nodep, vdtypep, defaultp), nodep);
|
||||
} else if (auto* const vdtypep = VN_CAST(dtypep, QueueDType)) {
|
||||
|
|
@ -3578,6 +3727,26 @@ private:
|
|||
// if (debug() >= 9) newp->dumpTree("-apat-out: ");
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
|
||||
}
|
||||
void patternWildcard(AstPattern* nodep, AstWildcardArrayDType* arrayDtp,
|
||||
AstPatMember* defaultp) {
|
||||
AstNode* defaultValuep = nullptr;
|
||||
if (defaultp) defaultValuep = defaultp->lhssp()->unlinkFrBack();
|
||||
AstNode* newp = new AstConsWildcard{nodep->fileline(), defaultValuep};
|
||||
newp->dtypeFrom(arrayDtp);
|
||||
for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
|
||||
patp = VN_AS(patp->nextp(), PatMember)) {
|
||||
patp->dtypep(arrayDtp->subDTypep());
|
||||
AstNode* const valuep = patternMemberValueIterate(patp);
|
||||
AstNode* const keyp = patp->keyp();
|
||||
auto* const newap
|
||||
= new AstSetWildcard{nodep->fileline(), newp, keyp->unlinkFrBack(), valuep};
|
||||
newap->dtypeFrom(arrayDtp);
|
||||
newp = newap;
|
||||
}
|
||||
nodep->replaceWith(newp);
|
||||
// if (debug() >= 9) newp->dumpTree("-apat-out: ");
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
|
||||
}
|
||||
void patternDynArray(AstPattern* nodep, AstDynArrayDType* arrayp, AstPatMember*) {
|
||||
AstNode* newp = new AstConsDynArray(nodep->fileline());
|
||||
newp->dtypeFrom(arrayp);
|
||||
|
|
@ -4094,6 +4263,7 @@ private:
|
|||
added = true;
|
||||
newFormat += "%g";
|
||||
} else if (VN_IS(dtypep, AssocArrayDType) //
|
||||
|| VN_IS(dtypep, WildcardArrayDType) //
|
||||
|| VN_IS(dtypep, ClassRefDType) //
|
||||
|| VN_IS(dtypep, DynArrayDType) //
|
||||
|| VN_IS(dtypep, QueueDType)) {
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ private:
|
|||
if (const AstNodeArrayDType* const adtypep = VN_CAST(ddtypep, NodeArrayDType)) {
|
||||
fromRange = adtypep->declRange();
|
||||
} else if (VN_IS(ddtypep, AssocArrayDType)) {
|
||||
} else if (VN_IS(ddtypep, WildcardArrayDType)) {
|
||||
} else if (VN_IS(ddtypep, DynArrayDType)) {
|
||||
} else if (VN_IS(ddtypep, QueueDType)) {
|
||||
} else if (const AstNodeUOrStructDType* const adtypep
|
||||
|
|
@ -257,6 +258,15 @@ private:
|
|||
if (debug() >= 9) newp->dumpTree(cout, "--SELBTn: ");
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
} else if (const AstWildcardArrayDType* const adtypep
|
||||
= VN_CAST(ddtypep, WildcardArrayDType)) {
|
||||
// SELBIT(array, index) -> WILDCARDSEL(array, index)
|
||||
AstNode* const subp = rhsp;
|
||||
AstWildcardSel* const newp = new AstWildcardSel{nodep->fileline(), fromp, subp};
|
||||
newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference
|
||||
if (debug() >= 9) newp->dumpTree(cout, "--SELBTn: ");
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
} else if (const AstDynArrayDType* const adtypep = VN_CAST(ddtypep, DynArrayDType)) {
|
||||
// SELBIT(array, index) -> CMETHODCALL(queue, "at", index)
|
||||
AstNode* const subp = rhsp;
|
||||
|
|
|
|||
|
|
@ -2077,10 +2077,8 @@ variable_dimension<nodeRangep>: // ==IEEE: variable_dimension
|
|||
// // IEEE: associative_dimension (if data_type)
|
||||
// // Can't tell which until see if expr is data type or not
|
||||
| '[' exprOrDataType ']' { $$ = new AstBracketRange($1, $2); }
|
||||
| yP_BRASTAR ']'
|
||||
{ $$ = nullptr; BBUNSUP($1, "Unsupported: [*] wildcard associative arrays"); }
|
||||
| '[' '*' ']'
|
||||
{ $$ = nullptr; BBUNSUP($2, "Unsupported: [*] wildcard associative arrays"); }
|
||||
| yP_BRASTAR ']' { $$ = new AstWildcardRange{$1}; }
|
||||
| '[' '*' ']' { $$ = new AstWildcardRange{$1}; }
|
||||
// // IEEE: queue_dimension
|
||||
// // '[' '$' ']' -- $ is part of expr, see '[' constExpr ']'
|
||||
// // '[' '$' ':' expr ']' -- anyrange:expr:$
|
||||
|
|
|
|||
|
|
@ -2,18 +2,15 @@
|
|||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003-2019 by Wilson Snyder. This program is free software; you
|
||||
# Copyright 2019 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
|
||||
|
||||
scenarios(vlt_all => 1);
|
||||
scenarios(simulator => 1);
|
||||
|
||||
compile(
|
||||
make_top_shell => 0,
|
||||
make_main => 0,
|
||||
v_flags2 => ["--exe $Self->{t_dir}/t_timescale.cpp"],
|
||||
);
|
||||
|
||||
execute(
|
||||
|
|
@ -22,26 +22,25 @@ module t (/*AUTOARG*/
|
|||
cyc <= cyc + 1;
|
||||
begin
|
||||
// Wildcard
|
||||
string a [*];
|
||||
string a [*] = '{default: "nope", "BBBBB": "fooing", 23'h434343: "baring"};
|
||||
int k;
|
||||
string v;
|
||||
|
||||
v = a["CCC"]; `checks(v, "baring");
|
||||
v = a["BBBBB"]; `checks(v, "fooing");
|
||||
|
||||
a[32'd1234] = "fooed";
|
||||
a[4'd3] = "bared";
|
||||
i = a.num(); `checkh(i, 2);
|
||||
i = a.size(); `checkh(i, 2);
|
||||
v = a[32'd1234]; `checks(v, "fooed");
|
||||
a[79'h4141] = "bazed";
|
||||
i = a.num(); `checkh(i, 5);
|
||||
i = a.size(); `checkh(i, 5);
|
||||
v = a[39'd1234]; `checks(v, "fooed");
|
||||
v = a["AA"]; `checks(v, "bazed");
|
||||
v = a[4'd3]; `checks(v, "bared");
|
||||
i = a.exists("baz"); `checkh(i, 0);
|
||||
i = a.exists(4'd3); `checkh(i, 1);
|
||||
i = a.first(k); `checkh(i, 1); `checks(k, 4'd3);
|
||||
i = a.next(k); `checkh(i, 1); `checks(k, 32'd1234);
|
||||
i = a.next(k); `checkh(i, 0);
|
||||
i = a.last(k); `checkh(i, 1); `checks(k, 32'd1234);
|
||||
i = a.prev(k); `checkh(i, 1); `checks(k, 4'd3);
|
||||
i = a.prev(k); `checkh(i, 0);
|
||||
a.delete(4'd3);
|
||||
i = a.size(); `checkh(i, 1);
|
||||
i = a.size(); `checkh(i, 4);
|
||||
end
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
%Error: t/t_assoc_wildcard_bad.v:23:13: The 1 arguments passed to .num method does not match its requiring 0 arguments
|
||||
: ... In instance t
|
||||
23 | v = a.num("badarg");
|
||||
| ^~~
|
||||
%Error: t/t_assoc_wildcard_bad.v:24:13: The 1 arguments passed to .size method does not match its requiring 0 arguments
|
||||
: ... In instance t
|
||||
24 | v = a.size("badarg");
|
||||
| ^~~~
|
||||
%Error: t/t_assoc_wildcard_bad.v:25:13: The 0 arguments passed to .exists method does not match its requiring 1 arguments
|
||||
: ... In instance t
|
||||
25 | v = a.exists();
|
||||
| ^~~~~~
|
||||
%Error: t/t_assoc_wildcard_bad.v:26:13: The 2 arguments passed to .exists method does not match its requiring 1 arguments
|
||||
: ... In instance t
|
||||
26 | v = a.exists(k, "bad2");
|
||||
| ^~~~~~
|
||||
%Error: t/t_assoc_wildcard_bad.v:27:9: The 2 arguments passed to .delete method does not match its requiring 0 to 1 arguments
|
||||
: ... In instance t
|
||||
27 | a.delete(k, "bad2");
|
||||
| ^~~~~~
|
||||
%Error: t/t_assoc_wildcard_bad.v:29:9: Array method 'sort' not legal on associative arrays
|
||||
: ... In instance t
|
||||
29 | a.sort;
|
||||
| ^~~~
|
||||
%Error: t/t_assoc_wildcard_bad.v:30:9: Array method 'rsort' not legal on associative arrays
|
||||
: ... In instance t
|
||||
30 | a.rsort;
|
||||
| ^~~~~
|
||||
%Error: t/t_assoc_wildcard_bad.v:31:9: Array method 'reverse' not legal on associative arrays
|
||||
: ... In instance t
|
||||
31 | a.reverse;
|
||||
| ^~~~~~~
|
||||
%Error: t/t_assoc_wildcard_bad.v:32:9: Array method 'shuffle' not legal on associative arrays
|
||||
: ... In instance t
|
||||
32 | a.shuffle;
|
||||
| ^~~~~~~
|
||||
%Error: t/t_assoc_wildcard_bad.v:34:9: Array method 'first' not legal on wildcard associative arrays
|
||||
: ... In instance t
|
||||
34 | a.first;
|
||||
| ^~~~~
|
||||
%Error: t/t_assoc_wildcard_bad.v:35:9: Array method 'last' not legal on wildcard associative arrays
|
||||
: ... In instance t
|
||||
35 | a.last;
|
||||
| ^~~~
|
||||
%Error: t/t_assoc_wildcard_bad.v:36:9: Array method 'next' not legal on wildcard associative arrays
|
||||
: ... In instance t
|
||||
36 | a.next;
|
||||
| ^~~~
|
||||
%Error: t/t_assoc_wildcard_bad.v:37:9: Array method 'prev' not legal on wildcard associative arrays
|
||||
: ... In instance t
|
||||
37 | a.prev;
|
||||
| ^~~~
|
||||
%Error: t/t_assoc_wildcard_bad.v:38:9: Array method 'unique_index' not legal on wildcard associative arrays
|
||||
: ... In instance t
|
||||
38 | a.unique_index;
|
||||
| ^~~~~~~~~~~~
|
||||
%Error: t/t_assoc_wildcard_bad.v:39:9: Array method 'find_index' not legal on wildcard associative arrays
|
||||
: ... In instance t
|
||||
39 | a.find_index;
|
||||
| ^~~~~~~~~~
|
||||
%Error: t/t_assoc_wildcard_bad.v:40:9: Array method 'find_first_index' not legal on wildcard associative arrays
|
||||
: ... In instance t
|
||||
40 | a.find_first_index;
|
||||
| ^~~~~~~~~~~~~~~~
|
||||
%Error: t/t_assoc_wildcard_bad.v:41:9: Array method 'find_last_index' not legal on wildcard associative arrays
|
||||
: ... In instance t
|
||||
41 | a.find_last_index;
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Error: t/t_assoc_wildcard_bad.v:43:8: Wildcard index must be integral (IEEE 1800-2017 7.8.1)
|
||||
: ... In instance t
|
||||
43 | a[x] = "bad";
|
||||
| ^
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2019 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
typedef class Cls;
|
||||
|
||||
class Cls;
|
||||
integer imembera;
|
||||
integer imemberb;
|
||||
endclass : Cls
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
|
||||
initial begin
|
||||
string a [*];
|
||||
string k;
|
||||
string v;
|
||||
|
||||
Cls x;
|
||||
|
||||
v = a.num("badarg");
|
||||
v = a.size("badarg");
|
||||
v = a.exists(); // Bad
|
||||
v = a.exists(k, "bad2");
|
||||
a.delete(k, "bad2");
|
||||
|
||||
a.sort; // Not legal on assoc
|
||||
a.rsort; // Not legal on assoc
|
||||
a.reverse; // Not legal on assoc
|
||||
a.shuffle; // Not legal on assoc
|
||||
|
||||
a.first; // Not legal on wildcard
|
||||
a.last; // Not legal on wildcard
|
||||
a.next; // Not legal on wildcard
|
||||
a.prev; // Not legal on wildcard
|
||||
a.unique_index; // Not legal on wildcard
|
||||
a.find_index; // Not legal on wildcard
|
||||
a.find_first_index; // Not legal on wildcard
|
||||
a.find_last_index; // Not legal on wildcard
|
||||
|
||||
a[x] = "bad";
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 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
|
||||
|
||||
scenarios(simulator => 1);
|
||||
|
||||
compile(
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2019 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
`define stop $stop
|
||||
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
`define checks(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
initial begin
|
||||
int q[*];
|
||||
int qe[*]; // Empty
|
||||
int qv[$]; // Value returns
|
||||
int qi[$]; // Index returns
|
||||
int i;
|
||||
string v;
|
||||
|
||||
q = '{"a":1, "b":2, "c":2, "d":4, "e":3};
|
||||
v = $sformatf("%p", q); `checks(v, "'{\"a\":'h1, \"b\":'h2, \"c\":'h2, \"d\":'h4, \"e\":'h3} ");
|
||||
|
||||
// NOT tested: with ... selectors
|
||||
|
||||
//q.sort; // Not legal on assoc - see t_assoc_meth_bad
|
||||
//q.rsort; // Not legal on assoc - see t_assoc_meth_bad
|
||||
//q.reverse; // Not legal on assoc - see t_assoc_meth_bad
|
||||
//q.shuffle; // Not legal on assoc - see t_assoc_meth_bad
|
||||
|
||||
v = $sformatf("%p", qe); `checks(v, "'{}");
|
||||
qv = q.unique;
|
||||
v = $sformatf("%p", qv); `checks(v, "'{'h1, 'h2, 'h4, 'h3} ");
|
||||
qv = qe.unique;
|
||||
v = $sformatf("%p", qv); `checks(v, "'{}");
|
||||
|
||||
//q.unique_index; // Not legal on wildcard assoc - see t_assoc_wildcard_bad
|
||||
|
||||
// These require an with clause or are illegal
|
||||
qv = q.find with (item == 2);
|
||||
v = $sformatf("%p", qv); `checks(v, "'{'h2, 'h2} ");
|
||||
qv = q.find_first with (item == 2);
|
||||
v = $sformatf("%p", qv); `checks(v, "'{'h2} ");
|
||||
qv = q.find_last with (item == 2);
|
||||
v = $sformatf("%p", qv); `checks(v, "'{'h2} ");
|
||||
|
||||
qv = q.find with (item == 20);
|
||||
v = $sformatf("%p", qv); `checks(v, "'{}");
|
||||
qv = q.find_first with (item == 20);
|
||||
v = $sformatf("%p", qv); `checks(v, "'{}");
|
||||
qv = q.find_last with (item == 20);
|
||||
v = $sformatf("%p", qv); `checks(v, "'{}");
|
||||
|
||||
//q.find_index; // Not legal on wildcard assoc - see t_assoc_wildcard_bad
|
||||
//q.find_first_index; // Not legal on wildcard assoc - see t_assoc_wildcard_bad
|
||||
//q.find_last_index; // Not legal on wildcard assoc - see t_assoc_wildcard_bad
|
||||
|
||||
qv = q.min;
|
||||
v = $sformatf("%p", qv); `checks(v, "'{'h1} ");
|
||||
qv = q.max;
|
||||
v = $sformatf("%p", qv); `checks(v, "'{'h4} ");
|
||||
|
||||
qv = qe.min;
|
||||
v = $sformatf("%p", qv); `checks(v, "'{}");
|
||||
qv = qe.max;
|
||||
v = $sformatf("%p", qv); `checks(v, "'{}");
|
||||
|
||||
// Reduction methods
|
||||
|
||||
i = q.sum;
|
||||
`checkh(i, 32'hc);
|
||||
i = q.sum with (item + 1);
|
||||
`checkh(i, 32'h11);
|
||||
i = q.product;
|
||||
`checkh(i, 32'h30);
|
||||
i = q.product with (item + 1);
|
||||
`checkh(i, 32'h168);
|
||||
|
||||
i = qe.sum;
|
||||
`checkh(i, 32'h0);
|
||||
i = qe.product;
|
||||
`checkh(i, 32'h0);
|
||||
|
||||
q = '{10:32'b1100, 11:32'b1010};
|
||||
i = q.and;
|
||||
`checkh(i, 32'b1000);
|
||||
i = q.and with (item + 1);
|
||||
`checkh(i, 32'b1001);
|
||||
i = q.or;
|
||||
`checkh(i, 32'b1110);
|
||||
i = q.or with (item + 1);
|
||||
`checkh(i, 32'b1111);
|
||||
i = q.xor;
|
||||
`checkh(i, 32'b0110);
|
||||
i = q.xor with (item + 1);
|
||||
`checkh(i, 32'b0110);
|
||||
|
||||
i = qe.and;
|
||||
`checkh(i, 32'b0);
|
||||
i = qe.or;
|
||||
`checkh(i, 32'b0);
|
||||
i = qe.xor;
|
||||
`checkh(i, 32'b0);
|
||||
|
||||
i = q.and();
|
||||
`checkh(i, 32'b1000);
|
||||
i = q.and() with (item + 1);
|
||||
`checkh(i, 32'b1001);
|
||||
i = q.or();
|
||||
`checkh(i, 32'b1110);
|
||||
i = q.or() with (item + 1);
|
||||
`checkh(i, 32'b1111);
|
||||
i = q.xor();
|
||||
`checkh(i, 32'b0110);
|
||||
i = q.xor() with (item + 1);
|
||||
`checkh(i, 32'b0110);
|
||||
|
||||
i = qe.and();
|
||||
`checkh(i, 32'b0);
|
||||
i = qe.or();
|
||||
`checkh(i, 32'b0);
|
||||
i = qe.xor();
|
||||
`checkh(i, 32'b0);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
%Error-UNSUPPORTED: t/t_assoc_wildcard_unsup.v:25:19: Unsupported: [*] wildcard associative arrays
|
||||
25 | string a [*];
|
||||
| ^~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: Exiting due to
|
||||
|
|
@ -14,7 +14,7 @@ top_filename("t/t_hier_block.v");
|
|||
lint(
|
||||
fails => 1,
|
||||
verilator_flags2 => ['--hierarchical',
|
||||
'--hierarchical-child',
|
||||
'--hierarchical-child 1',
|
||||
'modName',
|
||||
],
|
||||
expect_filename => $Self->{golden_filename},
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2020 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
#include <verilated.h>
|
||||
|
||||
#include "TestCheck.h"
|
||||
|
||||
#include VM_PREFIX_INCLUDE
|
||||
|
||||
unsigned long long main_time = 0;
|
||||
double sc_time_stamp() { return (double)main_time; }
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define FILENM "t_timescale.cpp"
|
||||
|
||||
int errors = 0;
|
||||
|
||||
int main(int argc, char** argv, char** env) {
|
||||
VM_PREFIX* top = new VM_PREFIX("top");
|
||||
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("100s"), 2);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("10s"), 1);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1s"), 0);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("100ms"), -1);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("10ms"), -2);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1ms"), -3);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("100us"), -4);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("10us"), -5);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1us"), -6);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("100ns"), -7);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("10ns"), -8);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1ns"), -9);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("100ps"), -10);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("10ps"), -11);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1ps"), -12);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("100fs"), -13);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("10fs"), -14);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1fs"), -15);
|
||||
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1.5s"), 0);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1s "), 0);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1ss"), 0);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT("s"), 0);
|
||||
TEST_CHECK_EQ(VL_TIME_STR_CONVERT(0), 0);
|
||||
|
||||
top->final();
|
||||
VL_DO_DANGLING(delete top, top);
|
||||
printf("*-* All Finished *-*\n");
|
||||
|
||||
return errors ? 10 : 0;
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
module t;
|
||||
|
||||
endmodule
|
||||
|
|
@ -261,15 +261,6 @@ function(verilate TARGET)
|
|||
set_property(TARGET ${TARGET} PROPERTY VERILATOR_THREADED ON)
|
||||
endif()
|
||||
|
||||
if (${VERILATE_PREFIX}_TRACE_THREADS)
|
||||
# If any verilate() call specifies TRACE_THREADS, define VL_TRACE_THREADED in the final build
|
||||
set_property(TARGET ${TARGET} PROPERTY VERILATOR_TRACE_THREADED ON)
|
||||
endif()
|
||||
|
||||
if (${VERILATE_PREFIX}_TRACE_FST_WRITER_THREAD)
|
||||
set_property(TARGET ${TARGET} PROPERTY VERILATOR_TRACE_FST_WRITER_TRHEAD ON)
|
||||
endif()
|
||||
|
||||
if (${VERILATE_PREFIX}_COVERAGE)
|
||||
# If any verilate() call specifies COVERAGE, define VM_COVERAGE in the final build
|
||||
set_property(TARGET ${TARGET} PROPERTY VERILATOR_COVERAGE ON)
|
||||
|
|
@ -330,8 +321,6 @@ function(verilate TARGET)
|
|||
VM_COVERAGE=$<BOOL:$<TARGET_PROPERTY:VERILATOR_COVERAGE>>
|
||||
VM_SC=$<BOOL:$<TARGET_PROPERTY:VERILATOR_SYSTEMC>>
|
||||
$<$<BOOL:$<TARGET_PROPERTY:VERILATOR_THREADED>>:VL_THREADED>
|
||||
$<$<BOOL:$<TARGET_PROPERTY:VERILATOR_TRACE_THREADED>>:VL_TRACE_THREADED>
|
||||
$<$<BOOL:$<TARGET_PROPERTY:VERILATOR_TRACE_FST_WRITER_TRHEAD>>:VL_TRACE_FST_WRITER_THREAD>
|
||||
VM_TRACE=$<BOOL:$<TARGET_PROPERTY:VERILATOR_TRACE>>
|
||||
VM_TRACE_VCD=$<BOOL:$<TARGET_PROPERTY:VERILATOR_TRACE_VCD>>
|
||||
VM_TRACE_FST=$<BOOL:$<TARGET_PROPERTY:VERILATOR_TRACE_FST>>
|
||||
|
|
|
|||
Loading…
Reference in New Issue