Configure tracing at run-time, instead of compile time (#3504)

All remaining use of conditional compilation in the tracing
implementation of the run-time library are replaced with the use of
VerilatedModel::traceConfig, and is now done at run-time.
This commit is contained in:
Geza Lore 2022-07-20 11:27:10 +01:00 committed by GitHub
parent a4ed3c2086
commit 1d400dd98c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 128 additions and 137 deletions

View File

@ -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;
@ -2914,6 +2916,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

View File

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

View File

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

View File

@ -93,17 +93,7 @@ static_assert(static_cast<int>(FST_ST_VCD_PROGRAM) == static_cast<int>(VLT_TRACE
// VerilatedFst
VerilatedFst::VerilatedFst(void* fst)
:
#ifdef VL_TRACE_OFFLOAD
VerilatedTrace {
true
}
#else
VerilatedTrace {
false
}
#endif
, m_fst{fst} {}
: m_fst{fst} {}
VerilatedFst::~VerilatedFst() {
if (m_fst) fstWriterClose(m_fst);
@ -116,7 +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
if (useFstWriterThread()) fstWriterSetParallelMode(m_fst, 1);
if (m_useFstWriterThread) fstWriterSetParallelMode(m_fst, 1);
fullDump(true); // First dump must be full for fst
m_curScope.clear();
@ -278,6 +268,14 @@ void VerilatedFst::commitTraceBuffer(VerilatedFst::Buffer* bufp) {
delete bufp;
}
//=============================================================================
// Configure
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

View File

@ -55,19 +55,13 @@ 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,
fstVarType vartype, bool array, int arraynum, bool bussed, int msb, int lsb);
static constexpr bool useFstWriterThread() {
#ifdef VL_TRACE_FST_WRITER_THREAD
return true;
#else
return false;
#endif
}
protected:
//=========================================================================
// Implementation of VerilatedTrace interface
@ -83,6 +77,9 @@ protected:
virtual Buffer* getTraceBuffer() override;
virtual void commitTraceBuffer(Buffer*) override;
// Configure sub-class
virtual void configure(const VerilatedTraceConfig&) override;
public:
//=========================================================================
// External interface to client code

View File

@ -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,6 +32,7 @@
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#ifdef VL_THREADED
@ -130,6 +116,22 @@ 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
@ -180,7 +182,8 @@ private:
, m_userp{userp} {}
};
const bool m_offload; // Whether to use the offload thread (ignored if !VL_THREADED)
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 {
@ -215,7 +218,6 @@ private:
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
@ -227,8 +229,8 @@ private:
double m_timeUnit = 1e-0; // Time units (ns/ms etc)
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)
void addModel(VerilatedModel*) VL_MT_SAFE_EXCLUDES(m_mutex);
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);
@ -313,18 +315,12 @@ protected:
#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
inline bool parallel() const {
#ifdef VL_TRACE_PARALLEL
return true;
#else
return false;
#endif
}
//=========================================================================
// Virtual functions to be provided by the format specific implementation
@ -340,11 +336,14 @@ 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
explicit VerilatedTrace(bool offload);
explicit VerilatedTrace();
~VerilatedTrace();
// Set time units (s/ms, defaults to ns)
@ -366,12 +365,13 @@ public:
//=========================================================================
// Non-hot path internal interface to Verilator generated code
void addInitCb(initCb_t cb, void* userp, VerilatedModel*) VL_MT_SAFE;
void addFullCb(dumpCb_t cb, void* userp, VerilatedModel*) VL_MT_SAFE;
void addFullCb(dumpOffloadCb_t cb, void* userp, VerilatedModel*) VL_MT_SAFE;
void addChgCb(dumpCb_t cb, void* userp, VerilatedModel*) VL_MT_SAFE;
void addChgCb(dumpOffloadCb_t cb, void* userp, VerilatedModel*) VL_MT_SAFE;
void addCleanupCb(cleanupCb_t cb, void* userp, VerilatedModel*) 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; }

View File

@ -293,14 +293,7 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit(void* selfp) {
//=============================================================================
// VerilatedTrace
template <>
VerilatedTrace<VL_SUB_T, VL_BUF_T>::VerilatedTrace(bool offload)
: m_offload{offload} {
#ifndef VL_THREADED
if (m_offload) {
VL_FATAL_MT(__FILE__, __LINE__, "", "Cannot use trace offloading without VL_THREADED");
}
#endif
template <> VerilatedTrace<VL_SUB_T, VL_BUF_T>::VerilatedTrace() {
set_time_unit(Verilated::threadContextp()->timeunitString());
set_time_resolution(Verilated::threadContextp()->timeprecisionString());
}
@ -648,8 +641,17 @@ template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addModel(VerilatedModel* modelp)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
const bool firstModel = m_models.empty();
const bool newModel = m_models.insert(modelp).second;
VerilatedContext* const contextp = modelp->contextp();
if (VL_UNCOVERABLE(m_contextp && contextp != m_contextp)) { // LCOV_EXCL_START
// 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");
}
@ -657,7 +659,35 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addModel(VerilatedModel* modelp)
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 <>
@ -669,43 +699,27 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCallbackRecord(std::vector<CallbackR
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addInitCb(initCb_t cb, void* userp,
VerilatedModel* modelp) VL_MT_SAFE {
addModel(modelp);
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,
VerilatedModel* modelp) VL_MT_SAFE {
addModel(modelp);
assert(!offload());
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>::addFullCb(dumpOffloadCb_t cb, void* userp,
VerilatedModel* modelp) VL_MT_SAFE {
addModel(modelp);
assert(offload());
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,
VerilatedModel* modelp) VL_MT_SAFE {
addModel(modelp);
assert(!offload());
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>::addChgCb(dumpOffloadCb_t cb, void* userp,
VerilatedModel* modelp) VL_MT_SAFE {
addModel(modelp);
assert(offload());
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,
VerilatedModel* modelp) VL_MT_SAFE {
addModel(modelp);
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE {
addCallbackRecord(m_cleanupCbs, CallbackRecord{cb, userp});
}

View File

@ -102,8 +102,7 @@ ssize_t VerilatedVcdFile::write(const char* bufp, ssize_t len) VL_MT_UNSAFE {
//=============================================================================
// Opening/Closing
VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep)
: VerilatedTrace{false} {
VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep) {
// Not in header to avoid link issue if header is included without this .cpp file
m_fileNewed = (filep == nullptr);
m_filep = m_fileNewed ? new VerilatedVcdFile : filep;

View File

@ -112,6 +112,9 @@ protected:
virtual Buffer* getTraceBuffer() override;
virtual void commitTraceBuffer(Buffer*) override;
// Configure sub-class
virtual void configure(const VerilatedTraceConfig&) override { return; };
public:
//=========================================================================
// External interface to client code

View File

@ -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");

View File

@ -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");
@ -487,6 +490,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) {
@ -539,8 +553,8 @@ class EmitCModel final : public EmitCFunc {
puts(/**/ "}");
}
puts(/**/ "if (false && levels && options) {} // Prevent unused\n");
puts(/**/ "tfp->spTrace()->addInitCb(&" + protect("trace_init")
+ ", &(vlSymsp->TOP), this);\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");

View File

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

View File

@ -521,6 +521,7 @@ public:
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;
}

View File

@ -515,7 +515,6 @@ 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", true));
m_regFuncp->addStmtsp(new AstText(flp, ");\n", true));
} else {
// Sub functions
@ -705,7 +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);\n", true));
m_regFuncp->addStmtsp(new AstText(fl, ", vlSelf);\n", true));
// Clear global activity flag
cleanupFuncp->addStmtsp(

View File

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