forked from github/verilator
Rewrite include libraries to support VL_THREADED towards future threading
This commit is contained in:
parent
d542921ff7
commit
f91bac7b31
@ -33,13 +33,13 @@
|
||||
// Global variables
|
||||
|
||||
// Slow path variables
|
||||
VerilatedMutex Verilated::m_mutex;
|
||||
VerilatedVoidCb Verilated::s_flushCb = NULL;
|
||||
|
||||
// Keep below together in one cache line
|
||||
Verilated::Serialized Verilated::s_s;
|
||||
VL_THREAD_LOCAL const VerilatedScope* Verilated::t_dpiScopep = NULL;
|
||||
VL_THREAD_LOCAL const char* Verilated::t_dpiFilename = "";
|
||||
VL_THREAD_LOCAL int Verilated::t_dpiLineno = 0;
|
||||
VL_THREAD_LOCAL Verilated::ThreadLocal Verilated::t_s;
|
||||
|
||||
struct Verilated::CommandArgValues Verilated::s_args = {0, NULL};
|
||||
|
||||
VerilatedImp VerilatedImp::s_s;
|
||||
@ -84,16 +84,34 @@ void vl_fatal (const char* filename, int linenum, const char* hier, const char*
|
||||
//===========================================================================
|
||||
// Wrapper to call certain functions via messages when multithreaded
|
||||
|
||||
void VL_FINISH_MT (const char* filename, int linenum, const char* hier) {
|
||||
void VL_FINISH_MT (const char* filename, int linenum, const char* hier) VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg([=](){
|
||||
vl_finish(filename, linenum, hier);
|
||||
}));
|
||||
#else
|
||||
vl_finish(filename, linenum, hier);
|
||||
#endif
|
||||
}
|
||||
|
||||
void VL_STOP_MT (const char* filename, int linenum, const char* hier) {
|
||||
void VL_STOP_MT (const char* filename, int linenum, const char* hier) VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg([=](){
|
||||
vl_stop(filename, linenum, hier);
|
||||
}));
|
||||
#else
|
||||
vl_stop(filename, linenum, hier);
|
||||
#endif
|
||||
}
|
||||
|
||||
void VL_FATAL_MT (const char* filename, int linenum, const char* hier, const char* msg) {
|
||||
void VL_FATAL_MT (const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg([=](){
|
||||
vl_fatal(filename, linenum, hier, msg);
|
||||
}));
|
||||
#else
|
||||
vl_fatal(filename, linenum, hier, msg);
|
||||
#endif
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
@ -113,13 +131,24 @@ std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE {
|
||||
return out;
|
||||
}
|
||||
|
||||
vluint64_t _vl_dbg_sequence_number() {
|
||||
vluint64_t _vl_dbg_sequence_number() VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
static std::atomic<vluint64_t> sequence;
|
||||
#else
|
||||
static vluint64_t sequence = 0;
|
||||
#endif
|
||||
return ++sequence;
|
||||
}
|
||||
|
||||
vluint32_t VL_THREAD_ID() {
|
||||
vluint32_t VL_THREAD_ID() VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
// Alternative is to use std::this_thread::get_id, but that returns a hard-to-read number and is very slow
|
||||
static std::atomic<vluint32_t> s_nextId (0);
|
||||
static VL_THREAD_LOCAL vluint32_t t_myId = ++s_nextId;
|
||||
return t_myId;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void VL_DBG_MSGF(const char* formatp, ...) VL_MT_SAFE {
|
||||
@ -133,6 +162,18 @@ void VL_DBG_MSGF(const char* formatp, ...) VL_MT_SAFE {
|
||||
VL_PRINTF_MT("-V{t%d,%" VL_PRI64 "d}%s", VL_THREAD_ID(), _vl_dbg_sequence_number(), out.c_str());
|
||||
}
|
||||
|
||||
#ifdef VL_THREADED
|
||||
void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE {
|
||||
va_list ap;
|
||||
va_start(ap, formatp);
|
||||
std::string out = _vl_string_vprintf(formatp, ap);
|
||||
va_end(ap);
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg([=](){
|
||||
VL_PRINTF("%s", out.c_str());
|
||||
}));
|
||||
}
|
||||
#endif
|
||||
|
||||
//===========================================================================
|
||||
// Overall class init
|
||||
|
||||
@ -148,8 +189,25 @@ Verilated::Serialized::Serialized() {
|
||||
//===========================================================================
|
||||
// Random reset -- Only called at init time, so don't inline.
|
||||
|
||||
IData VL_RAND32() {
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
IData VL_RAND32() VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
static VL_THREAD_LOCAL bool t_seeded = false;
|
||||
static VL_THREAD_LOCAL drand48_data t_buffer;
|
||||
static VerilatedMutex s_mutex;
|
||||
if (VL_UNLIKELY(!t_seeded)) {
|
||||
t_seeded = true;
|
||||
long seedval;
|
||||
{
|
||||
VerilatedLockGuard guard(s_mutex);
|
||||
seedval = lrand48()<<16 ^ lrand48();
|
||||
if (!seedval) seedval++;
|
||||
}
|
||||
srand48_r(seedval, &t_buffer);
|
||||
}
|
||||
long v0; lrand48_r(&t_buffer, &v0);
|
||||
long v1; lrand48_r(&t_buffer, &v1);
|
||||
return (v0<<16) ^ v1;
|
||||
#elif defined(_WIN32) && !defined(__CYGWIN__)
|
||||
// Windows doesn't have lrand48(), although Cygwin does.
|
||||
return (rand()<<16) ^ rand();
|
||||
#else
|
||||
@ -157,16 +215,16 @@ IData VL_RAND32() {
|
||||
#endif
|
||||
}
|
||||
|
||||
IData VL_RANDOM_I(int obits) {
|
||||
IData VL_RANDOM_I(int obits) VL_MT_SAFE {
|
||||
return VL_RAND32() & VL_MASK_I(obits);
|
||||
}
|
||||
|
||||
QData VL_RANDOM_Q(int obits) {
|
||||
QData VL_RANDOM_Q(int obits) VL_MT_SAFE {
|
||||
QData data = ((QData)VL_RAND32()<<VL_ULL(32)) | (QData)VL_RAND32();
|
||||
return data & VL_MASK_Q(obits);
|
||||
}
|
||||
|
||||
WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) {
|
||||
WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
for (int i=0; i<VL_WORDS_I(obits); ++i) {
|
||||
if (i<(VL_WORDS_I(obits)-1)) {
|
||||
outwp[i] = VL_RAND32();
|
||||
@ -177,7 +235,7 @@ WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) {
|
||||
return outwp;
|
||||
}
|
||||
|
||||
IData VL_RAND_RESET_I(int obits) {
|
||||
IData VL_RAND_RESET_I(int obits) VL_MT_SAFE {
|
||||
if (Verilated::randReset()==0) return 0;
|
||||
IData data = ~0;
|
||||
if (Verilated::randReset()!=1) { // if 2, randomize
|
||||
@ -187,7 +245,7 @@ IData VL_RAND_RESET_I(int obits) {
|
||||
return data;
|
||||
}
|
||||
|
||||
QData VL_RAND_RESET_Q(int obits) {
|
||||
QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE {
|
||||
if (Verilated::randReset()==0) return 0;
|
||||
QData data = VL_ULL(~0);
|
||||
if (Verilated::randReset()!=1) { // if 2, randomize
|
||||
@ -197,7 +255,7 @@ QData VL_RAND_RESET_Q(int obits) {
|
||||
return data;
|
||||
}
|
||||
|
||||
WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) {
|
||||
WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
for (int i=0; i<VL_WORDS_I(obits); ++i) {
|
||||
if (i<(VL_WORDS_I(obits)-1)) {
|
||||
outwp[i] = VL_RAND_RESET_I(32);
|
||||
@ -1340,7 +1398,18 @@ std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) VL_MT_SAFE {
|
||||
//===========================================================================
|
||||
// Verilated:: Methods
|
||||
|
||||
void Verilated::debug(int val) {
|
||||
Verilated::ThreadLocal::ThreadLocal()
|
||||
:
|
||||
#ifdef VL_THREADED
|
||||
t_trainId(0),
|
||||
#endif
|
||||
t_dpiScopep(NULL), t_dpiFilename(0), t_dpiLineno(0) {
|
||||
}
|
||||
Verilated::ThreadLocal::~ThreadLocal() {
|
||||
}
|
||||
|
||||
void Verilated::debug(int val) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
s_s.s_debug = val;
|
||||
if (val) {
|
||||
#ifdef VL_DEBUG
|
||||
@ -1350,12 +1419,32 @@ void Verilated::debug(int val) {
|
||||
#endif
|
||||
}
|
||||
}
|
||||
void Verilated::randReset(int val) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
s_s.s_randReset = val;
|
||||
}
|
||||
void Verilated::calcUnusedSigs(bool flag) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
s_s.s_calcUnusedSigs = flag;
|
||||
}
|
||||
void Verilated::gotFinish(bool flag) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
s_s.s_gotFinish = flag;
|
||||
}
|
||||
void Verilated::assertOn(bool flag) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
s_s.s_assertOn = flag;
|
||||
}
|
||||
void Verilated::fatalOnVpiError(bool flag) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
s_s.s_fatalOnVpiError = flag;
|
||||
}
|
||||
|
||||
const char* Verilated::catName(const char* n1, const char* n2) VL_MT_SAFE {
|
||||
// Returns new'ed data
|
||||
// Used by symbol table creation to make module names
|
||||
static char* strp = NULL;
|
||||
static size_t len = 0;
|
||||
static VL_THREAD_LOCAL char* strp = NULL;
|
||||
static VL_THREAD_LOCAL size_t len = 0;
|
||||
size_t newlen = strlen(n1)+strlen(n2)+2;
|
||||
if (!strp || newlen > len) {
|
||||
if (strp) delete [] strp;
|
||||
@ -1368,7 +1457,8 @@ const char* Verilated::catName(const char* n1, const char* n2) VL_MT_SAFE {
|
||||
return strp;
|
||||
}
|
||||
|
||||
void Verilated::flushCb(VerilatedVoidCb cb) {
|
||||
void Verilated::flushCb(VerilatedVoidCb cb) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
if (s_flushCb == cb) {} // Ok - don't duplicate
|
||||
else if (!s_flushCb) { s_flushCb=cb; }
|
||||
else {
|
||||
@ -1377,13 +1467,19 @@ void Verilated::flushCb(VerilatedVoidCb cb) {
|
||||
}
|
||||
}
|
||||
|
||||
void Verilated::commandArgs(int argc, const char** argv) {
|
||||
void Verilated::flushCall() VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
if (s_flushCb) (*s_flushCb)();
|
||||
}
|
||||
|
||||
void Verilated::commandArgs(int argc, const char** argv) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
s_args.argc = argc;
|
||||
s_args.argv = argv;
|
||||
VerilatedImp::commandArgs(argc,argv);
|
||||
}
|
||||
|
||||
const char* Verilated::commandArgsPlusMatch(const char* prefixp) {
|
||||
const char* Verilated::commandArgsPlusMatch(const char* prefixp) VL_MT_SAFE {
|
||||
const std::string& match = VerilatedImp::argPlusMatch(prefixp);
|
||||
static VL_THREAD_LOCAL char outstr[VL_VALUE_STRING_MAX_WIDTH];
|
||||
if (match == "") return "";
|
||||
@ -1392,26 +1488,69 @@ const char* Verilated::commandArgsPlusMatch(const char* prefixp) {
|
||||
return outstr;
|
||||
}
|
||||
|
||||
void Verilated::internalsDump() {
|
||||
void Verilated::quiesce() VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
// Wait until all threads under this evaluation are quiet
|
||||
// THREADED-TODO
|
||||
#endif
|
||||
}
|
||||
|
||||
void Verilated::internalsDump() VL_MT_SAFE {
|
||||
VerilatedImp::internalsDump();
|
||||
}
|
||||
|
||||
void Verilated::scopesDump() {
|
||||
void Verilated::scopesDump() VL_MT_SAFE {
|
||||
VerilatedImp::scopesDump();
|
||||
}
|
||||
|
||||
const VerilatedScope* Verilated::scopeFind(const char* namep) {
|
||||
void Verilated::numThreads(unsigned threads) VL_MT_SAFE {
|
||||
VerilatedImp::numThreads(threads);
|
||||
}
|
||||
unsigned Verilated::numThreads() VL_MT_SAFE {
|
||||
return VerilatedImp::numThreads();
|
||||
}
|
||||
void Verilated::spawnThreads() VL_MT_SAFE {
|
||||
VerilatedImp::spawnThreads();
|
||||
}
|
||||
|
||||
const VerilatedScope* Verilated::scopeFind(const char* namep) VL_MT_SAFE {
|
||||
return VerilatedImp::scopeFind(namep);
|
||||
}
|
||||
|
||||
int Verilated::exportFuncNum(const char* namep) {
|
||||
int Verilated::exportFuncNum(const char* namep) VL_MT_SAFE {
|
||||
return VerilatedImp::exportFind(namep);
|
||||
}
|
||||
|
||||
const VerilatedScopeNameMap* Verilated::scopeNameMap() {
|
||||
const VerilatedScopeNameMap* Verilated::scopeNameMap() VL_MT_SAFE {
|
||||
return VerilatedImp::scopeNameMap();
|
||||
}
|
||||
|
||||
#ifdef VL_THREADED
|
||||
void Verilated::endOfThreadTrainGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("End of thread train\n"););
|
||||
VerilatedThreadMsgQueue::flush(evalMsgQp);
|
||||
}
|
||||
|
||||
void Verilated::endOfEvalGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("End-of-eval cleanup\n"););
|
||||
evalMsgQp->process();
|
||||
}
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// VerilatedSyms:: Methods
|
||||
|
||||
VerilatedSyms::VerilatedSyms() {
|
||||
#ifdef VL_THREADED
|
||||
__Vm_evalMsgQp = new VerilatedEvalMsgQueue;
|
||||
#endif
|
||||
}
|
||||
VerilatedSyms::~VerilatedSyms() {
|
||||
#ifdef VL_THREADED
|
||||
delete __Vm_evalMsgQp;
|
||||
#endif
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// VerilatedModule:: Methods
|
||||
|
||||
@ -1572,3 +1711,13 @@ void VerilatedScope::scopeDump() const {
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// VerilatedOneThreaded:: Methods
|
||||
|
||||
#ifdef VL_THREADED
|
||||
void VerilatedAssertOneThread::fatal_different() VL_MT_SAFE {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Routine called that is single threaded, but called from"
|
||||
" a different thread then the expected constructing thread");
|
||||
}
|
||||
#endif
|
||||
|
||||
//===========================================================================
|
||||
|
@ -37,6 +37,11 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#ifdef VL_THREADED
|
||||
# include <atomic>
|
||||
# include <mutex>
|
||||
# include <thread>
|
||||
#endif
|
||||
// <iostream> avoided to reduce compile time
|
||||
// <map> avoided and instead in verilated_heavy.h to reduce compile time
|
||||
// <string> avoided and instead in verilated_heavy.h to reduce compile time
|
||||
@ -68,6 +73,7 @@ typedef void (*VerilatedVoidCb)(void);
|
||||
|
||||
class SpTraceVcd;
|
||||
class SpTraceVcdCFile;
|
||||
class VerilatedEvalMsgQueue;
|
||||
class VerilatedScopeNameMap;
|
||||
class VerilatedVar;
|
||||
class VerilatedVarNameMap;
|
||||
@ -96,6 +102,82 @@ enum VerilatedVarFlags {
|
||||
VLVF_PUB_RW=(1<<9) // Public writable
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
/// Mutex and threading support
|
||||
|
||||
/// Return current thread ID (or 0), not super fast, cache if needed
|
||||
extern vluint32_t VL_THREAD_ID() VL_MT_SAFE;
|
||||
|
||||
#if VL_THREADED
|
||||
|
||||
/// Mutex, wrapped to allow -fthread_safety checks
|
||||
class VL_CAPABILITY("mutex") VerilatedMutex {
|
||||
private:
|
||||
std::mutex m_mutex; // Mutex
|
||||
public:
|
||||
VerilatedMutex() {}
|
||||
~VerilatedMutex() {}
|
||||
/// Acquire/lock mutex
|
||||
void lock() VL_ACQUIRE() { m_mutex.lock(); }
|
||||
/// Release/unlock mutex
|
||||
void unlock() VL_RELEASE() { m_mutex.unlock(); }
|
||||
/// Try to acquire mutex. Returns true on success, and false on failure.
|
||||
bool try_lock() VL_TRY_ACQUIRE(true) { return m_mutex.try_lock(); }
|
||||
};
|
||||
|
||||
/// Lock guard for mutex (ala std::lock_guard), wrapped to allow -fthread_safety checks
|
||||
class VL_SCOPED_CAPABILITY VerilatedLockGuard {
|
||||
VerilatedLockGuard(); ///< N/A, always use named constructor below
|
||||
VerilatedLockGuard(const VerilatedLockGuard&); ///< N/A, no copy constructor
|
||||
private:
|
||||
VerilatedMutex& m_mutexr;
|
||||
public:
|
||||
VerilatedLockGuard(VerilatedMutex& mutexr) VL_ACQUIRE(mutexr) : m_mutexr(mutexr) {
|
||||
m_mutexr.lock();
|
||||
}
|
||||
~VerilatedLockGuard() VL_RELEASE() {
|
||||
m_mutexr.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
#else // !VL_THREADED
|
||||
|
||||
// Empty classes to avoid #ifdefs everywhere
|
||||
class VerilatedMutex {};
|
||||
class VerilatedLockGuard {
|
||||
public:
|
||||
VerilatedLockGuard(VerilatedMutex&) {}
|
||||
~VerilatedLockGuard() {}
|
||||
};
|
||||
#endif // VL_THREADED
|
||||
|
||||
/// Remember the calling thread at construction time, and make sure later calls use same thread
|
||||
class VerilatedAssertOneThread {
|
||||
// MEMBERS
|
||||
#if defined(VL_THREADED) && defined(VL_DEBUG)
|
||||
vluint32_t m_threadid; /// Thread that is legal
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
/// The constructor establishes the thread id for all later calls.
|
||||
/// If necessary, a different class could be made that inits it otherwise.
|
||||
VerilatedAssertOneThread() : m_threadid(VL_THREAD_ID()) { }
|
||||
~VerilatedAssertOneThread() { check(); }
|
||||
// METHODS
|
||||
/// Check that the current thread ID is the same as the construction thread ID
|
||||
void check() VL_MT_UNSAFE_ONE {
|
||||
// Memoize results in local thread, to prevent slow get_id() call
|
||||
VL_THREAD_LOCAL bool t_okThread = (m_threadid == VL_THREAD_ID());
|
||||
if (!VL_LIKELY(t_okThread)) {
|
||||
fatal_different();
|
||||
}
|
||||
}
|
||||
static void fatal_different() VL_MT_SAFE;
|
||||
#else // !VL_THREADED || !VL_DEBUG
|
||||
public:
|
||||
void check() {}
|
||||
#endif
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
/// Base class for all Verilated module classes
|
||||
|
||||
@ -159,15 +241,32 @@ public:
|
||||
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
// Functions overridable by user defines
|
||||
// (Internals however must use VL_PRINTF_MT, which calls these.)
|
||||
|
||||
#ifndef VL_PRINTF
|
||||
# define VL_PRINTF printf ///< Print ala printf, called from main thread; may redefine if desired
|
||||
#endif
|
||||
#ifndef VL_VPRINTF
|
||||
# define VL_VPRINTF vprintf ///< Print ala vprintf, called from main thread; may redefine if desired
|
||||
#endif
|
||||
|
||||
//===========================================================================
|
||||
/// Verilator symbol table base class
|
||||
|
||||
class VerilatedSyms {
|
||||
// VerilatedSyms base class exists just so symbol tables have a common pointer type
|
||||
public: // But for internal use only
|
||||
#ifdef VL_THREADED
|
||||
VerilatedEvalMsgQueue* __Vm_evalMsgQp;
|
||||
#endif
|
||||
VerilatedSyms();
|
||||
~VerilatedSyms();
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
/// Verilator global static information class
|
||||
/// Verilator global class information class
|
||||
/// This class is initialized by main thread only. Reading post-init is thread safe.
|
||||
|
||||
class VerilatedScope {
|
||||
// Fastpath:
|
||||
@ -181,19 +280,19 @@ class VerilatedScope {
|
||||
public: // But internals only - called from VerilatedModule's
|
||||
VerilatedScope();
|
||||
~VerilatedScope();
|
||||
void configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp);
|
||||
void exportInsert(int finalize, const char* namep, void* cb);
|
||||
void configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp) VL_MT_UNSAFE;
|
||||
void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE;
|
||||
void varInsert(int finalize, const char* namep, void* datap,
|
||||
VerilatedVarType vltype, int vlflags, int dims, ...);
|
||||
VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE;
|
||||
// ACCESSORS
|
||||
const char* name() const { return m_namep; }
|
||||
inline VerilatedSyms* symsp() const { return m_symsp; }
|
||||
VerilatedVar* varFind(const char* namep) const;
|
||||
VerilatedVarNameMap* varsp() const { return m_varsp; }
|
||||
VerilatedVar* varFind(const char* namep) const VL_MT_SAFE_POSTINIT;
|
||||
VerilatedVarNameMap* varsp() const VL_MT_SAFE_POSTINIT { return m_varsp; }
|
||||
void scopeDump() const;
|
||||
void* exportFindError(int funcnum) const;
|
||||
static void* exportFindNullError(int funcnum);
|
||||
static inline void* exportFind(const VerilatedScope* scopep, int funcnum) {
|
||||
static void* exportFindNullError(int funcnum) VL_MT_SAFE;
|
||||
static inline void* exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(!scopep)) return exportFindNullError(funcnum);
|
||||
if (VL_LIKELY(funcnum < scopep->m_funcnumMax)) {
|
||||
// m_callbacksp must be declared, as Max'es are > 0
|
||||
@ -210,6 +309,8 @@ public: // But internals only - called from VerilatedModule's
|
||||
class Verilated {
|
||||
// MEMBERS
|
||||
// Slow path variables
|
||||
static VerilatedMutex m_mutex; ///< Mutex for all static members, when VL_THREADED
|
||||
|
||||
static VerilatedVoidCb s_flushCb; ///< Flush callback function
|
||||
|
||||
static struct Serialized { // All these members serialized/deserialized
|
||||
@ -224,10 +325,6 @@ class Verilated {
|
||||
Serialized();
|
||||
} s_s;
|
||||
|
||||
static VL_THREAD_LOCAL const VerilatedScope* t_dpiScopep; ///< DPI context scope
|
||||
static VL_THREAD_LOCAL const char* t_dpiFilename; ///< DPI context filename
|
||||
static VL_THREAD_LOCAL int t_dpiLineno; ///< DPI context line number
|
||||
|
||||
// no need to be save-restored (serialized) the
|
||||
// assumption is that the restore is allowed to pass different arguments
|
||||
static struct CommandArgValues {
|
||||
@ -235,6 +332,20 @@ class Verilated {
|
||||
const char** argv;
|
||||
} s_args;
|
||||
|
||||
// Not covered by mutex, as per-thread
|
||||
static VL_THREAD_LOCAL struct ThreadLocal {
|
||||
#ifdef VL_THREADED
|
||||
vluint32_t t_trainId; ///< Current train# executing on this thread
|
||||
vluint32_t t_endOfEvalReqd; ///< Messages may be pending, thread needs endOf-eval calls
|
||||
#endif
|
||||
const VerilatedScope* t_dpiScopep; ///< DPI context scope
|
||||
const char* t_dpiFilename; ///< DPI context filename
|
||||
int t_dpiLineno; ///< DPI context line number
|
||||
|
||||
ThreadLocal();
|
||||
~ThreadLocal();
|
||||
} t_s;
|
||||
|
||||
public:
|
||||
|
||||
// METHODS - User called
|
||||
@ -244,88 +355,118 @@ public:
|
||||
/// 0 = Set to zeros
|
||||
/// 1 = Set all bits to one
|
||||
/// 2 = Randomize all bits
|
||||
static void randReset(int val) { s_s.s_randReset=val; }
|
||||
static int randReset() { return s_s.s_randReset; } ///< Return randReset value
|
||||
static void randReset(int val) VL_MT_SAFE;
|
||||
static int randReset() VL_MT_SAFE { return s_s.s_randReset; } ///< Return randReset value
|
||||
|
||||
/// Enable debug of internal verilated code
|
||||
static void debug(int level);
|
||||
static void debug(int level) VL_MT_SAFE;
|
||||
#ifdef VL_DEBUG
|
||||
static inline int debug() { return s_s.s_debug; } ///< Return debug value
|
||||
/// Return debug level
|
||||
/// When multithreaded this may not immediately react to another thread changing the level (no mutex)
|
||||
static inline int debug() VL_MT_SAFE { return s_s.s_debug; }
|
||||
#else
|
||||
static inline int debug() { return 0; } ///< Constant 0 debug, so C++'s optimizer rips up
|
||||
static inline int debug() VL_PURE { return 0; } ///< Return constant 0 debug level, so C++'s optimizer rips up
|
||||
#endif
|
||||
/// Enable calculation of unused signals
|
||||
static void calcUnusedSigs(bool flag) { s_s.s_calcUnusedSigs=flag; }
|
||||
static bool calcUnusedSigs() { return s_s.s_calcUnusedSigs; } ///< Return calcUnusedSigs value
|
||||
static void calcUnusedSigs(bool flag) VL_MT_SAFE;
|
||||
static bool calcUnusedSigs() VL_MT_SAFE { return s_s.s_calcUnusedSigs; } ///< Return calcUnusedSigs value
|
||||
/// Did the simulation $finish?
|
||||
static void gotFinish(bool flag) { s_s.s_gotFinish=flag; }
|
||||
static bool gotFinish() { return s_s.s_gotFinish; } ///< Return if got a $finish
|
||||
static void gotFinish(bool flag) VL_MT_SAFE;
|
||||
static bool gotFinish() VL_MT_SAFE { return s_s.s_gotFinish; } ///< Return if got a $finish
|
||||
/// Allow traces to at some point be enabled (disables some optimizations)
|
||||
static void traceEverOn(bool flag) {
|
||||
static void traceEverOn(bool flag) VL_MT_SAFE {
|
||||
if (flag) { calcUnusedSigs(flag); }
|
||||
}
|
||||
/// Enable/disable assertions
|
||||
static void assertOn(bool flag) { s_s.s_assertOn=flag; }
|
||||
static bool assertOn() { return s_s.s_assertOn; }
|
||||
static void assertOn(bool flag) VL_MT_SAFE;
|
||||
static bool assertOn() VL_MT_SAFE { return s_s.s_assertOn; }
|
||||
/// Enable/disable vpi fatal
|
||||
static void fatalOnVpiError(bool flag) { s_s.s_fatalOnVpiError=flag; }
|
||||
static bool fatalOnVpiError() { return s_s.s_fatalOnVpiError; }
|
||||
static void fatalOnVpiError(bool flag) VL_MT_SAFE;
|
||||
static bool fatalOnVpiError() VL_MT_SAFE { return s_s.s_fatalOnVpiError; }
|
||||
/// Flush callback for VCD waves
|
||||
static void flushCb(VerilatedVoidCb cb);
|
||||
static void flushCall() { if (s_flushCb) (*s_flushCb)(); }
|
||||
static void flushCb(VerilatedVoidCb cb) VL_MT_SAFE;
|
||||
static void flushCall() VL_MT_SAFE;
|
||||
|
||||
/// Record command line arguments, for retrieval by $test$plusargs/$value$plusargs
|
||||
static void commandArgs(int argc, const char** argv);
|
||||
static void commandArgs(int argc, char** argv) { commandArgs(argc, const_cast<const char**>(argv)); }
|
||||
static void commandArgs(int argc, const char** argv) VL_MT_SAFE;
|
||||
static void commandArgs(int argc, char** argv) VL_MT_SAFE { commandArgs(argc, const_cast<const char**>(argv)); }
|
||||
static void commandArgsAdd(int argc, const char** argv);
|
||||
static CommandArgValues* getCommandArgs() {return &s_args;}
|
||||
static CommandArgValues* getCommandArgs() VL_MT_SAFE { return &s_args; }
|
||||
/// Match plusargs with a given prefix. Returns static char* valid only for a single call
|
||||
static const char* commandArgsPlusMatch(const char* prefixp);
|
||||
static const char* commandArgsPlusMatch(const char* prefixp) VL_MT_SAFE;
|
||||
|
||||
/// Produce name & version for (at least) VPI
|
||||
static const char* productName() VL_PURE { return VERILATOR_PRODUCT; }
|
||||
static const char* productVersion() VL_PURE { return VERILATOR_VERSION; }
|
||||
|
||||
/// When multithreaded, quiesce the model to prepare for trace/saves/coverage
|
||||
/// This may only be called when no locks are held.
|
||||
static void quiesce() VL_MT_SAFE;
|
||||
|
||||
/// For debugging, print much of the Verilator internal state.
|
||||
/// The output of this function may change in future
|
||||
/// releases - contact the authors before production use.
|
||||
static void internalsDump();
|
||||
static void internalsDump() VL_MT_SAFE;
|
||||
|
||||
/// For debugging, print text list of all scope names with
|
||||
/// dpiImport/Export context. This function may change in future
|
||||
/// releases - contact the authors before production use.
|
||||
static void scopesDump();
|
||||
static void scopesDump() VL_MT_SAFE;
|
||||
|
||||
// METHODS - INTERNAL USE ONLY
|
||||
/// Set the number of threads to execute on.
|
||||
/// 0x0 = use all available CPU threads, or 1 if no support compiled in
|
||||
/// Ignored after spawnThreads() has been called
|
||||
static void numThreads(unsigned threads) VL_MT_SAFE;
|
||||
static unsigned numThreads() VL_MT_SAFE;
|
||||
/// Spawn child threads, using numThreads() as # of threads
|
||||
/// Verilator calls this automatically on the first eval() call
|
||||
/// User code may call it earlier if desired
|
||||
/// Once called the first time, later calls are ignored
|
||||
static void spawnThreads() VL_MT_SAFE;
|
||||
|
||||
public:
|
||||
// METHODS - INTERNAL USE ONLY (but public due to what uses it)
|
||||
// Internal: Create a new module name by concatenating two strings
|
||||
static const char* catName(const char* n1, const char* n2); // Returns new'ed data
|
||||
static const char* catName(const char* n1, const char* n2); // Returns static data
|
||||
// Internal: Find scope
|
||||
static const VerilatedScope* scopeFind(const char* namep);
|
||||
static const VerilatedScopeNameMap* scopeNameMap();
|
||||
static const VerilatedScope* scopeFind(const char* namep) VL_MT_SAFE;
|
||||
static const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE;
|
||||
// Internal: Get and set DPI context
|
||||
static const VerilatedScope* dpiScope() { return t_dpiScopep; }
|
||||
static void dpiScope(const VerilatedScope* scopep) { t_dpiScopep=scopep; }
|
||||
static void dpiContext(const VerilatedScope* scopep, const char* filenamep, int lineno) {
|
||||
t_dpiScopep=scopep; t_dpiFilename=filenamep; t_dpiLineno=lineno; }
|
||||
static void dpiClearContext() { t_dpiScopep = NULL; }
|
||||
static bool dpiInContext() { return t_dpiScopep != NULL; }
|
||||
static const char* dpiFilenamep() { return t_dpiFilename; }
|
||||
static int dpiLineno() { return t_dpiLineno; }
|
||||
static int exportFuncNum(const char* namep);
|
||||
static const VerilatedScope* dpiScope() VL_MT_SAFE { return t_s.t_dpiScopep; }
|
||||
static void dpiScope(const VerilatedScope* scopep) VL_MT_SAFE { t_s.t_dpiScopep=scopep; }
|
||||
static void dpiContext(const VerilatedScope* scopep, const char* filenamep, int lineno) VL_MT_SAFE {
|
||||
t_s.t_dpiScopep = scopep; t_s.t_dpiFilename = filenamep; t_s.t_dpiLineno = lineno; }
|
||||
static void dpiClearContext() VL_MT_SAFE { t_s.t_dpiScopep = NULL; }
|
||||
static bool dpiInContext() VL_MT_SAFE { return t_s.t_dpiScopep != NULL; }
|
||||
static const char* dpiFilenamep() VL_MT_SAFE { return t_s.t_dpiFilename; }
|
||||
static int dpiLineno() VL_MT_SAFE { return t_s.t_dpiLineno; }
|
||||
static int exportFuncNum(const char* namep) VL_MT_SAFE;
|
||||
static size_t serializedSize() VL_PURE { return sizeof(s_s); }
|
||||
static void* serializedPtr() VL_MT_UNSAFE { return &s_s; }
|
||||
static void* serializedPtr() VL_MT_UNSAFE { return &s_s; } // Unsafe, for Serialize only
|
||||
#ifdef VL_THREADED
|
||||
/// Set the trainId, called when a train starts
|
||||
static void trainId(vluint32_t id) VL_MT_SAFE { t_s.t_trainId = id; }
|
||||
static vluint32_t trainId() VL_MT_SAFE { return t_s.t_trainId; }
|
||||
static void endOfEvalReqdInc() VL_MT_SAFE { ++t_s.t_endOfEvalReqd; }
|
||||
static void endOfEvalReqdDec() VL_MT_SAFE { --t_s.t_endOfEvalReqd; }
|
||||
/// Called at end of each thread train, before finishing eval
|
||||
static void endOfThreadTrain(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(t_s.t_endOfEvalReqd)) { endOfThreadTrainGuts(evalMsgQp); } }
|
||||
/// Called at end of eval loop
|
||||
static void endOfEval(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(t_s.t_endOfEvalReqd)) { endOfEvalGuts(evalMsgQp); } }
|
||||
#endif
|
||||
|
||||
private:
|
||||
#ifdef VL_THREADED
|
||||
static void endOfThreadTrainGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE;
|
||||
static void endOfEvalGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE;
|
||||
#endif
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
// Extern functions -- User may override -- See verilated.cpp
|
||||
|
||||
#ifndef VL_PRINTF
|
||||
# define VL_PRINTF printf ///< Print ala printf; may redefine if desired
|
||||
#endif
|
||||
#ifndef VL_VPRINTF
|
||||
# define VL_VPRINTF vprintf ///< Print ala vprintf; may redefine if desired
|
||||
#endif
|
||||
|
||||
/// Routine to call for $finish
|
||||
/// User code may wish to replace this function, to do so, define VL_USER_FINISH.
|
||||
/// This code does not have to be thread safe.
|
||||
@ -349,14 +490,19 @@ extern void vl_fatal (const char* filename, int linenum, const char* hier,
|
||||
// Extern functions -- Slow path
|
||||
|
||||
/// Multithread safe wrapper for calls to $finish
|
||||
extern void VL_FINISH_MT (const char* filename, int linenum, const char* hier);
|
||||
extern void VL_FINISH_MT (const char* filename, int linenum, const char* hier) VL_MT_SAFE;
|
||||
/// Multithread safe wrapper for calls to $stop
|
||||
extern void VL_STOP_MT (const char* filename, int linenum, const char* hier);
|
||||
extern void VL_STOP_MT (const char* filename, int linenum, const char* hier) VL_MT_SAFE;
|
||||
/// Multithread safe wrapper to call for a couple of fatal messages
|
||||
extern void VL_FATAL_MT (const char* filename, int linenum, const char* hier,
|
||||
const char* msg);
|
||||
const char* msg) VL_MT_SAFE;
|
||||
|
||||
/// Print a string, multithread safe. Eventually VL_PRINTF will get called.
|
||||
#define VL_PRINTF_MT VL_PRINTF
|
||||
#ifdef VL_THREADED
|
||||
extern void VL_PRINTF_MT(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE;
|
||||
#else
|
||||
# define VL_PRINTF_MT VL_PRINTF // The following parens will take care of themselves
|
||||
#endif
|
||||
/// Print a debug message from internals with standard prefix, with printf style format
|
||||
extern void VL_DBG_MSGF(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE;
|
||||
|
||||
|
@ -113,8 +113,10 @@ endif
|
||||
##### Threaded builds
|
||||
|
||||
ifneq ($(VM_THREADS),0)
|
||||
ifneq ($(VM_THREADS),)
|
||||
# Need C++11 at least, so always default to newest
|
||||
CPPFLAGS += -DVL_THREADED $(CFG_CXXFLAGS_STD_NEWEST)
|
||||
endif
|
||||
endif
|
||||
|
||||
#######################################################################
|
||||
|
@ -96,13 +96,14 @@ private:
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
ValueIndexMap m_valueIndexes; ///< For each key/value a unique arbitrary index value
|
||||
IndexValueMap m_indexValues; ///< For each key/value a unique arbitrary index value
|
||||
ItemList m_items; ///< List of all items
|
||||
VerilatedMutex m_mutex; ///< Protects all members, when VL_THREADED. Wrapper deals with setting it.
|
||||
ValueIndexMap m_valueIndexes VL_GUARDED_BY(m_mutex); ///< For each key/value a unique arbitrary index value
|
||||
IndexValueMap m_indexValues VL_GUARDED_BY(m_mutex); ///< For each key/value a unique arbitrary index value
|
||||
ItemList m_items VL_GUARDED_BY(m_mutex); ///< List of all items
|
||||
|
||||
VerilatedCovImpItem* m_insertp; ///< Item about to insert
|
||||
const char* m_insertFilenamep; ///< Filename about to insert
|
||||
int m_insertLineno; ///< Line number about to insert
|
||||
VerilatedCovImpItem* m_insertp VL_GUARDED_BY(m_mutex); ///< Item about to insert
|
||||
const char* m_insertFilenamep VL_GUARDED_BY(m_mutex); ///< Filename about to insert
|
||||
int m_insertLineno VL_GUARDED_BY(m_mutex); ///< Line number about to insert
|
||||
|
||||
// CONSTRUCTORS
|
||||
VerilatedCovImp() {
|
||||
@ -112,14 +113,14 @@ private:
|
||||
}
|
||||
public:
|
||||
~VerilatedCovImp() { clearGuts(); }
|
||||
static VerilatedCovImp& imp() {
|
||||
static VerilatedCovImp& imp() VL_MT_SAFE {
|
||||
static VerilatedCovImp s_singleton;
|
||||
return s_singleton;
|
||||
}
|
||||
|
||||
private:
|
||||
// PRIVATE METHODS
|
||||
int valueIndex(const std::string& value) {
|
||||
int valueIndex(const std::string& value) VL_REQUIRES(m_mutex) {
|
||||
static int nextIndex = KEY_UNDEF+1;
|
||||
ValueIndexMap::iterator iter = m_valueIndexes.find(value);
|
||||
if (iter != m_valueIndexes.end()) return iter->second;
|
||||
@ -194,7 +195,7 @@ private:
|
||||
//cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add<<"\nch o="<<out<<endl;
|
||||
return out;
|
||||
}
|
||||
bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match) {
|
||||
bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match) VL_REQUIRES(m_mutex) {
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
if (itemp->m_keys[i] != KEY_UNDEF) {
|
||||
// We don't compare keys, only values
|
||||
@ -217,7 +218,7 @@ private:
|
||||
if (combineHier ("q.za","q.zb") !="q.z*") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("1.2.3.a","9.8.7.a") !="*.a") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
}
|
||||
void clearGuts() {
|
||||
void clearGuts() VL_REQUIRES(m_mutex) {
|
||||
for (ItemList::const_iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
VerilatedCovImpItem* itemp = *(it);
|
||||
delete itemp;
|
||||
@ -229,10 +230,14 @@ private:
|
||||
|
||||
public:
|
||||
// PUBLIC METHODS
|
||||
void clear() {
|
||||
clearGuts();
|
||||
void clear() VL_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
clearGuts();
|
||||
}
|
||||
void clearNonMatch (const char* matchp) {
|
||||
void clearNonMatch(const char* matchp) VL_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
if (matchp && matchp[0]) {
|
||||
ItemList newlist;
|
||||
for (ItemList::iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
@ -246,23 +251,28 @@ public:
|
||||
m_items = newlist;
|
||||
}
|
||||
}
|
||||
void zero() {
|
||||
void zero() VL_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
for (ItemList::const_iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
(*it)->zero();
|
||||
}
|
||||
}
|
||||
|
||||
// We assume there's always call to i/f/p in that order
|
||||
void inserti (VerilatedCovImpItem* itemp) {
|
||||
void inserti (VerilatedCovImpItem* itemp) VL_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
assert(!m_insertp);
|
||||
m_insertp = itemp;
|
||||
}
|
||||
void insertf (const char* filenamep, int lineno) {
|
||||
void insertf (const char* filenamep, int lineno) VL_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
m_insertFilenamep = filenamep;
|
||||
m_insertLineno = lineno;
|
||||
}
|
||||
void insertp (const char* ckeyps[MAX_KEYS],
|
||||
const char* valps[MAX_KEYS]) {
|
||||
const char* valps[MAX_KEYS]) VL_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
assert(m_insertp);
|
||||
// First two key/vals are filename
|
||||
ckeyps[0]="filename"; valps[0]=m_insertFilenamep;
|
||||
@ -315,7 +325,9 @@ public:
|
||||
m_insertp = NULL;
|
||||
}
|
||||
|
||||
void write (const char* filename) {
|
||||
void write(const char* filename) VL_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
#ifndef VM_COVERAGE
|
||||
VL_FATAL_MT("",0,"","%Error: Called VerilatedCov::write when VM_COVERAGE disabled\n");
|
||||
#endif
|
||||
@ -388,25 +400,25 @@ public:
|
||||
//=============================================================================
|
||||
// VerilatedCov
|
||||
|
||||
void VerilatedCov::clear() {
|
||||
void VerilatedCov::clear() VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().clear();
|
||||
}
|
||||
void VerilatedCov::clearNonMatch (const char* matchp) {
|
||||
void VerilatedCov::clearNonMatch(const char* matchp) VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().clearNonMatch(matchp);
|
||||
}
|
||||
void VerilatedCov::zero() {
|
||||
void VerilatedCov::zero() VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().zero();
|
||||
}
|
||||
void VerilatedCov::write (const char* filenamep) {
|
||||
void VerilatedCov::write(const char* filenamep) VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().write(filenamep);
|
||||
}
|
||||
void VerilatedCov::_inserti (vluint32_t* itemp) {
|
||||
void VerilatedCov::_inserti(vluint32_t* itemp) VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().inserti(new VerilatedCoverItemSpec<vluint32_t>(itemp));
|
||||
}
|
||||
void VerilatedCov::_inserti (vluint64_t* itemp) {
|
||||
void VerilatedCov::_inserti(vluint64_t* itemp) VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().inserti(new VerilatedCoverItemSpec<vluint64_t>(itemp));
|
||||
}
|
||||
void VerilatedCov::_insertf (const char* filename, int lineno) {
|
||||
void VerilatedCov::_insertf(const char* filename, int lineno) VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().insertf(filename,lineno);
|
||||
}
|
||||
|
||||
@ -416,7 +428,7 @@ void VerilatedCov::_insertf (const char* filename, int lineno) {
|
||||
#define N(n) "","" // Null argument list
|
||||
void VerilatedCov::_insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9),
|
||||
A(10),A(11),A(12),A(13),A(14),A(15),A(16),A(17),A(18),A(19),
|
||||
A(20),A(21),A(22),A(23),A(24),A(25),A(26),A(27),A(28),A(29)) {
|
||||
A(20),A(21),A(22),A(23),A(24),A(25),A(26),A(27),A(28),A(29)) VL_MT_SAFE {
|
||||
const char* keyps[VerilatedCovImpBase::MAX_KEYS]
|
||||
= {NULL,NULL,NULL, // filename,lineno,page
|
||||
key0,key1,key2,key3,key4,key5,key6,key7,key8,key9,
|
||||
@ -431,20 +443,20 @@ void VerilatedCov::_insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9),
|
||||
}
|
||||
|
||||
// And versions with fewer arguments (oh for a language with named parameters!)
|
||||
void VerilatedCov::_insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9)) {
|
||||
void VerilatedCov::_insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9)) VL_MT_SAFE {
|
||||
_insertp(C(0),C(1),C(2),C(3),C(4),C(5),C(6),C(7),C(8),C(9),
|
||||
N(10),N(11),N(12),N(13),N(14),N(15),N(16),N(17),N(18),N(19),
|
||||
N(20),N(21),N(22),N(23),N(24),N(25),N(26),N(27),N(28),N(29));
|
||||
}
|
||||
void VerilatedCov::_insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9),
|
||||
A(10),A(11),A(12),A(13),A(14),A(15),A(16),A(17),A(18),A(19)) {
|
||||
A(10),A(11),A(12),A(13),A(14),A(15),A(16),A(17),A(18),A(19)) VL_MT_SAFE {
|
||||
_insertp(C(0),C(1),C(2),C(3),C(4),C(5),C(6),C(7),C(8),C(9),
|
||||
C(10),C(11),C(12),C(13),C(14),C(15),C(16),C(17),C(18),C(19),
|
||||
N(20),N(21),N(22),N(23),N(24),N(25),N(26),N(27),N(28),N(29));
|
||||
}
|
||||
// Backward compatibility for Verilator
|
||||
void VerilatedCov::_insertp (A(0), A(1), K(2),int val2, K(3),int val3,
|
||||
K(4),const std::string& val4, A(5),A(6)) {
|
||||
K(4),const std::string& val4, A(5),A(6)) VL_MT_SAFE {
|
||||
std::string val2str = vlCovCvtToStr(val2);
|
||||
std::string val3str = vlCovCvtToStr(val3);
|
||||
_insertp(C(0),C(1),
|
||||
|
@ -82,6 +82,7 @@ template< class T> std::string vlCovCvtToStr (const T& t) VL_PURE {
|
||||
/// Verilator coverage global class
|
||||
////
|
||||
/// Global class with methods affecting all coverage data.
|
||||
/// All public methods in this class are thread safe.
|
||||
|
||||
class VerilatedCov {
|
||||
public:
|
||||
@ -89,16 +90,16 @@ public:
|
||||
/// Return default filename
|
||||
static const char* defaultFilename() VL_PURE { return "coverage.dat"; }
|
||||
/// Write all coverage data to a file
|
||||
static void write (const char* filenamep = defaultFilename());
|
||||
static void write (const char* filenamep = defaultFilename()) VL_MT_SAFE;
|
||||
/// Insert a coverage item
|
||||
/// We accept from 1-30 key/value pairs, all as strings.
|
||||
/// Call _insert1, followed by _insert2 and _insert3
|
||||
/// Do not call directly; use VL_COVER_INSERT or higher level macros instead
|
||||
// _insert1: Remember item pointer with count. (Not const, as may add zeroing function)
|
||||
static void _inserti (vluint32_t* itemp);
|
||||
static void _inserti (vluint64_t* itemp);
|
||||
static void _inserti (vluint32_t* itemp) VL_MT_SAFE;
|
||||
static void _inserti (vluint64_t* itemp) VL_MT_SAFE;
|
||||
// _insert2: Set default filename and line number
|
||||
static void _insertf (const char* filename, int lineno);
|
||||
static void _insertf (const char* filename, int lineno) VL_MT_SAFE;
|
||||
// _insert3: Set parameters
|
||||
// We could have just the maximum argument version, but this compiles
|
||||
// much slower (nearly 2x) than having smaller versions also. However
|
||||
@ -120,11 +121,11 @@ public:
|
||||
#undef A
|
||||
#undef D
|
||||
/// Clear coverage points (and call delete on all items)
|
||||
static void clear();
|
||||
static void clear() VL_MT_SAFE;
|
||||
/// Clear items not matching the provided string
|
||||
static void clearNonMatch (const char* matchp);
|
||||
static void clearNonMatch (const char* matchp) VL_MT_SAFE;
|
||||
/// Zero coverage points
|
||||
static void zero();
|
||||
static void zero() VL_MT_SAFE;
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
|
@ -34,14 +34,132 @@
|
||||
#include "verilated_syms.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#ifdef VL_THREADED
|
||||
# include <functional>
|
||||
# include <queue>
|
||||
#endif
|
||||
|
||||
class VerilatedScope;
|
||||
|
||||
//======================================================================
|
||||
// Types
|
||||
// Threaded message passing
|
||||
|
||||
#ifdef VL_THREADED
|
||||
/// Message, enqueued on a train, and consumed on the main eval thread
|
||||
class VerilatedMsg {
|
||||
public:
|
||||
// TYPES
|
||||
struct Cmp {
|
||||
bool operator() (const VerilatedMsg& a, const VerilatedMsg& b) {
|
||||
return a.trainId() < b.trainId(); }
|
||||
};
|
||||
private:
|
||||
// MEMBERS
|
||||
vluint32_t m_trainId; ///< Train that did enqueue
|
||||
std::function<void()> m_cb; ///< Lambda to execute when message received
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedMsg(const std::function<void()>& cb)
|
||||
: m_trainId(Verilated::trainId()), m_cb(cb) {}
|
||||
~VerilatedMsg() {}
|
||||
// METHODS
|
||||
vluint32_t trainId() const { return m_trainId; }
|
||||
/// Execute the lambda function
|
||||
void run() const { m_cb(); }
|
||||
};
|
||||
|
||||
/// Each thread has a queue it pushes to
|
||||
/// This assumes no thread starts pushing the next tick until the previous has drained.
|
||||
/// If more aggressiveness is needed, a double-buffered scheme might work well.
|
||||
class VerilatedEvalMsgQueue {
|
||||
typedef std::multiset<VerilatedMsg, VerilatedMsg::Cmp> VerilatedThreadQueue;
|
||||
|
||||
std::atomic<vluint64_t> m_depth; ///< Current depth of queue (see comments below)
|
||||
|
||||
VerilatedMutex m_mutex; ///< Mutex protecting queue
|
||||
VerilatedThreadQueue m_queue VL_GUARDED_BY(m_mutex); ///< Message queue
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedEvalMsgQueue() : m_depth(0) { }
|
||||
~VerilatedEvalMsgQueue() { }
|
||||
VerilatedEvalMsgQueue(const VerilatedEvalMsgQueue&) = delete;
|
||||
VerilatedEvalMsgQueue& operator=(const VerilatedEvalMsgQueue&) = delete;
|
||||
public:
|
||||
// METHODS
|
||||
//// Add message to queue (called by producer)
|
||||
void post(const VerilatedMsg& msg) VL_EXCLUDES(m_mutex) {
|
||||
Verilated::endOfEvalReqdInc(); // No mutex, threadsafe
|
||||
VerilatedLockGuard guard(m_mutex);
|
||||
m_queue.insert(msg); // Pass by value to copy the message into queue
|
||||
++m_depth;
|
||||
}
|
||||
/// Service queue until completion (called by consumer)
|
||||
void process() VL_EXCLUDES(m_mutex) {
|
||||
// Tracking m_depth is redundant to e.g. getting the mutex and looking at queue size,
|
||||
// but on the reader side it's 4x faster to test an atomic then getting a mutex
|
||||
while (m_depth) {
|
||||
// Wait for a message to be added to the queue
|
||||
// We don't use unique_lock as want to unlock with the message copy still in scope
|
||||
m_mutex.lock();
|
||||
assert(!m_queue.empty()); // Otherwise m_depth is wrong
|
||||
// Unfortunately to release the lock we need to copy the message
|
||||
// (Or have the message be a pointer, but then new/delete cost on each message)
|
||||
// We assume messages are small, so copy
|
||||
auto it = m_queue.begin();
|
||||
const VerilatedMsg msg = *(it);
|
||||
m_queue.erase(it);
|
||||
m_mutex.unlock();
|
||||
m_depth--; // Ok if outside critical section as only this code checks the value
|
||||
Verilated::endOfEvalReqdDec(); // No mutex, threadsafe
|
||||
{
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("Executing callback from trainId=%d\n", msg.trainId()););
|
||||
msg.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Each thread has a local queue to build up messages until the end of the eval() call
|
||||
class VerilatedThreadMsgQueue {
|
||||
std::queue<VerilatedMsg> m_queue;
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedThreadMsgQueue() { }
|
||||
~VerilatedThreadMsgQueue() {
|
||||
// The only call of this with a non-empty queue is a fatal error.
|
||||
// So this does not flush the queue, as the destination queue is not known to this class.
|
||||
}
|
||||
VerilatedThreadMsgQueue(const VerilatedThreadMsgQueue&) = delete;
|
||||
VerilatedThreadMsgQueue& operator=(const VerilatedThreadMsgQueue&) = delete;
|
||||
private:
|
||||
// METHODS
|
||||
static VerilatedThreadMsgQueue& threadton() {
|
||||
static VL_THREAD_LOCAL VerilatedThreadMsgQueue t_s;
|
||||
return t_s;
|
||||
}
|
||||
public:
|
||||
/// Add message to queue, called by producer
|
||||
static void post(const VerilatedMsg& msg) VL_MT_SAFE {
|
||||
Verilated::endOfEvalReqdInc();
|
||||
threadton().m_queue.push(msg); // Pass by value to copy the message into queue
|
||||
}
|
||||
/// Push all messages to the eval's queue
|
||||
static void flush(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
|
||||
while (!threadton().m_queue.empty()) {
|
||||
evalMsgQp->post(threadton().m_queue.front());
|
||||
threadton().m_queue.pop();
|
||||
Verilated::endOfEvalReqdDec();
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif // VL_THREADED
|
||||
|
||||
//======================================================================
|
||||
// VerilatedImp
|
||||
|
||||
class VerilatedImp {
|
||||
// Whole class is internal use only - Global information shared between verilated*.cpp files.
|
||||
@ -56,17 +174,29 @@ class VerilatedImp {
|
||||
|
||||
// Nothing here is save-restored; users expected to re-register appropriately
|
||||
|
||||
ArgVec m_argVec; ///< Argument list (NOT save-restored, may want different results)
|
||||
bool m_argVecLoaded; ///< Ever loaded argument list
|
||||
UserMap m_userMap; ///< Map of <(scope,userkey), userData>
|
||||
VerilatedScopeNameMap m_nameMap; ///< Map of <scope_name, scope pointer>
|
||||
VerilatedMutex m_argMutex; ///< Protect m_argVec, m_argVecLoaded
|
||||
ArgVec m_argVec VL_GUARDED_BY(m_argMutex); ///< Argument list (NOT save-restored, may want different results)
|
||||
bool m_argVecLoaded VL_GUARDED_BY(m_argMutex); ///< Ever loaded argument list
|
||||
|
||||
VerilatedMutex m_userMapMutex; ///< Protect m_userMap
|
||||
UserMap m_userMap VL_GUARDED_BY(m_userMapMutex); ///< Map of <(scope,userkey), userData>
|
||||
|
||||
VerilatedMutex m_nameMutex; ///< Protect m_nameMap
|
||||
VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex); ///< Map of <scope_name, scope pointer>
|
||||
// Slow - somewhat static:
|
||||
ExportNameMap m_exportMap; ///< Map of <export_func_proto, func number>
|
||||
int m_exportNext; ///< Next export funcnum
|
||||
VerilatedMutex m_exportMutex; ///< Protect m_nameMap
|
||||
ExportNameMap m_exportMap VL_GUARDED_BY(m_exportMutex); ///< Map of <export_func_proto, func number>
|
||||
int m_exportNext VL_GUARDED_BY(m_exportMutex); ///< Next export funcnum
|
||||
|
||||
// File I/O
|
||||
std::vector<FILE*> m_fdps; ///< File descriptors
|
||||
std::deque<IData> m_fdFree; ///< List of free descriptors (SLOW - FOPEN/CLOSE only)
|
||||
VerilatedMutex m_fdMutex; ///< Protect m_fdps, m_fdFree
|
||||
std::vector<FILE*> m_fdps VL_GUARDED_BY(m_fdMutex); ///< File descriptors
|
||||
std::deque<IData> m_fdFree VL_GUARDED_BY(m_fdMutex); ///< List of free descriptors (SLOW - FOPEN/CLOSE only)
|
||||
|
||||
// Threads
|
||||
VerilatedMutex m_threadMutex; ///< Protect m_numThreads, etc
|
||||
bool m_spawned VL_GUARDED_BY(m_threadMutex); ///< Already called spawnThreads()
|
||||
unsigned m_numThreads VL_GUARDED_BY(m_threadMutex); ///< Number of threads user requested, 0x0=all cpus
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// CONSTRUCTORS
|
||||
@ -77,7 +207,8 @@ public: // But only for verilated*.cpp
|
||||
m_fdps[2] = stderr;
|
||||
}
|
||||
~VerilatedImp() {}
|
||||
static void internalsDump() {
|
||||
static void internalsDump() VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_argMutex);
|
||||
VL_PRINTF_MT("internalsDump:\n");
|
||||
VL_PRINTF_MT(" Argv:");
|
||||
for (ArgVec::const_iterator it=s_s.m_argVec.begin(); it!=s_s.m_argVec.end(); ++it) {
|
||||
@ -91,16 +222,18 @@ public: // But only for verilated*.cpp
|
||||
}
|
||||
|
||||
// METHODS - arguments
|
||||
static void commandArgs(int argc, const char** argv) {
|
||||
public:
|
||||
static void commandArgs(int argc, const char** argv) VL_EXCLUDES(s_s.m_argMutex) {
|
||||
VerilatedLockGuard guard(s_s.m_argMutex);
|
||||
s_s.m_argVec.clear(); // Always clear
|
||||
commandArgsAdd(argc, argv);
|
||||
commandArgsAddGuts(argc, argv);
|
||||
}
|
||||
static void commandArgsAdd(int argc, const char** argv) {
|
||||
if (!s_s.m_argVecLoaded) s_s.m_argVec.clear();
|
||||
for (int i=0; i<argc; ++i) s_s.m_argVec.push_back(argv[i]);
|
||||
s_s.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok
|
||||
static void commandArgsAdd(int argc, const char** argv) VL_EXCLUDES(s_s.m_argMutex) {
|
||||
VerilatedLockGuard guard(s_s.m_argMutex);
|
||||
commandArgsAddGuts(argc, argv);
|
||||
}
|
||||
static std::string argPlusMatch(const char* prefixp) {
|
||||
static std::string argPlusMatch(const char* prefixp) VL_EXCLUDES(s_s.m_argMutex) {
|
||||
VerilatedLockGuard guard(s_s.m_argMutex);
|
||||
// Note prefixp does not include the leading "+"
|
||||
size_t len = strlen(prefixp);
|
||||
if (VL_UNLIKELY(!s_s.m_argVecLoaded)) {
|
||||
@ -116,26 +249,36 @@ public: // But only for verilated*.cpp
|
||||
}
|
||||
return "";
|
||||
}
|
||||
private:
|
||||
static void commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(s_s.m_argMutex) {
|
||||
if (!s_s.m_argVecLoaded) s_s.m_argVec.clear();
|
||||
for (int i=0; i<argc; ++i) s_s.m_argVec.push_back(argv[i]);
|
||||
s_s.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok
|
||||
}
|
||||
|
||||
public:
|
||||
// METHODS - user scope tracking
|
||||
// We implement this as a single large map instead of one map per scope
|
||||
// There's often many more scopes than userdata's and thus having a ~48byte
|
||||
// per map overhead * N scopes would take much more space and cache thrashing.
|
||||
static inline void userInsert(const void* scopep, void* userKey, void* userData) {
|
||||
static inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_userMapMutex);
|
||||
UserMap::iterator it=s_s.m_userMap.find(std::make_pair(scopep,userKey));
|
||||
if (it != s_s.m_userMap.end()) it->second = userData;
|
||||
// When we support VL_THREADs, we need a lock around this insert, as it's runtime
|
||||
else s_s.m_userMap.insert(it, std::make_pair(std::make_pair(scopep,userKey),userData));
|
||||
}
|
||||
static inline void* userFind(const void* scopep, void* userKey) {
|
||||
static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_userMapMutex);
|
||||
UserMap::const_iterator it=s_s.m_userMap.find(std::make_pair(scopep,userKey));
|
||||
if (VL_LIKELY(it != s_s.m_userMap.end())) return it->second;
|
||||
else return NULL;
|
||||
}
|
||||
private:
|
||||
/// Symbol table destruction cleans up the entries for each scope.
|
||||
static void userEraseScope(const VerilatedScope* scopep) {
|
||||
static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
|
||||
// Slow ok - called once/scope on destruction, so we simply iterate.
|
||||
VerilatedLockGuard guard(s_s.m_userMapMutex);
|
||||
for (UserMap::iterator it=s_s.m_userMap.begin(); it!=s_s.m_userMap.end(); ) {
|
||||
if (it->first.first == scopep) {
|
||||
s_s.m_userMap.erase(it++);
|
||||
@ -144,7 +287,8 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
static void userDump() {
|
||||
static void userDump() VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_userMapMutex); // Avoid it changing in middle of dump
|
||||
bool first = true;
|
||||
for (UserMap::const_iterator it=s_s.m_userMap.begin(); it!=s_s.m_userMap.end(); ++it) {
|
||||
if (first) { VL_PRINTF_MT(" userDump:\n"); first=false; }
|
||||
@ -155,25 +299,29 @@ private:
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - scope name
|
||||
static void scopeInsert(const VerilatedScope* scopep) {
|
||||
static void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE {
|
||||
// Slow ok - called once/scope at construction
|
||||
VerilatedLockGuard guard(s_s.m_nameMutex);
|
||||
VerilatedScopeNameMap::iterator it=s_s.m_nameMap.find(scopep->name());
|
||||
if (it == s_s.m_nameMap.end()) {
|
||||
s_s.m_nameMap.insert(it, std::make_pair(scopep->name(),scopep));
|
||||
}
|
||||
}
|
||||
static inline const VerilatedScope* scopeFind(const char* namep) {
|
||||
static inline const VerilatedScope* scopeFind(const char* namep) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_nameMutex); // If too slow, can assume this is only VL_MT_SAFE_POSINIT
|
||||
VerilatedScopeNameMap::const_iterator it=s_s.m_nameMap.find(namep);
|
||||
if (VL_LIKELY(it != s_s.m_nameMap.end())) return it->second;
|
||||
else return NULL;
|
||||
}
|
||||
static void scopeErase(const VerilatedScope* scopep) {
|
||||
static void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE {
|
||||
// Slow ok - called once/scope at destruction
|
||||
VerilatedLockGuard guard(s_s.m_nameMutex);
|
||||
userEraseScope(scopep);
|
||||
VerilatedScopeNameMap::iterator it=s_s.m_nameMap.find(scopep->name());
|
||||
if (it != s_s.m_nameMap.end()) s_s.m_nameMap.erase(it);
|
||||
}
|
||||
static void scopesDump() {
|
||||
static void scopesDump() VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_nameMutex);
|
||||
VL_PRINTF_MT(" scopesDump:\n");
|
||||
for (VerilatedScopeNameMap::const_iterator it=s_s.m_nameMap.begin(); it!=s_s.m_nameMap.end(); ++it) {
|
||||
const VerilatedScope* scopep = it->second;
|
||||
@ -181,7 +329,8 @@ public: // But only for verilated*.cpp
|
||||
}
|
||||
VL_PRINTF_MT("\n");
|
||||
}
|
||||
static const VerilatedScopeNameMap* scopeNameMap() {
|
||||
static const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE_POSTINIT {
|
||||
// Thread save only assuming this is called only after model construction completed
|
||||
return &s_s.m_nameMap;
|
||||
}
|
||||
|
||||
@ -194,8 +343,9 @@ public: // But only for verilated*.cpp
|
||||
// in the design that also happen to have our same callback function.
|
||||
// Rather than a 2D map, the integer scheme saves 500ish ns on a likely
|
||||
// miss at the cost of a multiply, and all lookups move to slowpath.
|
||||
static int exportInsert(const char* namep) {
|
||||
static int exportInsert(const char* namep) VL_MT_SAFE {
|
||||
// Slow ok - called once/function at creation
|
||||
VerilatedLockGuard guard(s_s.m_exportMutex);
|
||||
ExportNameMap::iterator it=s_s.m_exportMap.find(namep);
|
||||
if (it == s_s.m_exportMap.end()) {
|
||||
s_s.m_exportMap.insert(it, std::make_pair(namep, s_s.m_exportNext++));
|
||||
@ -204,7 +354,8 @@ public: // But only for verilated*.cpp
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
static int exportFind(const char* namep) {
|
||||
static int exportFind(const char* namep) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_exportMutex);
|
||||
ExportNameMap::const_iterator it=s_s.m_exportMap.find(namep);
|
||||
if (VL_LIKELY(it != s_s.m_exportMap.end())) return it->second;
|
||||
std::string msg = (std::string("%Error: Testbench C called ")+namep
|
||||
@ -212,14 +363,16 @@ public: // But only for verilated*.cpp
|
||||
VL_FATAL_MT("unknown",0,"", msg.c_str());
|
||||
return -1;
|
||||
}
|
||||
static const char* exportName(int funcnum) {
|
||||
static const char* exportName(int funcnum) VL_MT_SAFE {
|
||||
// Slowpath; find name for given export; errors only so no map to reverse-map it
|
||||
VerilatedLockGuard guard(s_s.m_exportMutex);
|
||||
for (ExportNameMap::const_iterator it=s_s.m_exportMap.begin(); it!=s_s.m_exportMap.end(); ++it) {
|
||||
if (it->second == funcnum) return it->first;
|
||||
}
|
||||
return "*UNKNOWN*";
|
||||
}
|
||||
static void exportsDump() {
|
||||
static void exportsDump() VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_exportMutex);
|
||||
bool first = true;
|
||||
for (ExportNameMap::const_iterator it=s_s.m_exportMap.begin(); it!=s_s.m_exportMap.end(); ++it) {
|
||||
if (first) { VL_PRINTF_MT(" exportDump:\n"); first=false; }
|
||||
@ -231,9 +384,10 @@ public: // But only for verilated*.cpp
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - file IO
|
||||
static IData fdNew(FILE* fp) {
|
||||
static IData fdNew(FILE* fp) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(!fp)) return 0;
|
||||
// Bit 31 indicates it's a descriptor not a MCD
|
||||
VerilatedLockGuard guard(s_s.m_fdMutex);
|
||||
if (s_s.m_fdFree.empty()) {
|
||||
// Need to create more space in m_fdps and m_fdFree
|
||||
size_t start = s_s.m_fdps.size();
|
||||
@ -244,18 +398,53 @@ public: // But only for verilated*.cpp
|
||||
s_s.m_fdps[idx] = fp;
|
||||
return (idx | (1UL<<31)); // bit 31 indicates not MCD
|
||||
}
|
||||
static void fdDelete(IData fdi) {
|
||||
static void fdDelete(IData fdi) VL_MT_SAFE {
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
VerilatedLockGuard guard(s_s.m_fdMutex);
|
||||
if (VL_UNLIKELY(!(fdi & (1ULL<<31)) || idx >= s_s.m_fdps.size())) return;
|
||||
if (VL_UNLIKELY(!s_s.m_fdps[idx])) return; // Already free
|
||||
s_s.m_fdps[idx] = NULL;
|
||||
s_s.m_fdFree.push_back(idx);
|
||||
}
|
||||
static inline FILE* fdToFp(IData fdi) {
|
||||
static inline FILE* fdToFp(IData fdi) VL_MT_SAFE {
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
VerilatedLockGuard guard(s_s.m_fdMutex); // This might get slow, if it does we can cache it
|
||||
if (VL_UNLIKELY(!(fdi & (1ULL<<31)) || idx >= s_s.m_fdps.size())) return NULL;
|
||||
return s_s.m_fdps[idx];
|
||||
}
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - Threading
|
||||
static void numThreads(unsigned threads) VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_threadMutex);
|
||||
if (!s_s.m_spawned) s_s.m_numThreads = threads;
|
||||
}
|
||||
static unsigned numThreads() VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_threadMutex);
|
||||
#ifdef VL_THREADED
|
||||
unsigned threads = s_s.m_numThreads;
|
||||
if (threads == 0x0) {
|
||||
threads = std::thread::hardware_concurrency(); // Or 0=unknown, C++11
|
||||
}
|
||||
if (threads<1) threads = 1;
|
||||
return threads;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
static void spawnThreads() VL_MT_SAFE {
|
||||
VerilatedLockGuard guard(s_s.m_threadMutex);
|
||||
if (!s_s.m_spawned) {
|
||||
// Convert numThreads from 0 to the spawned number
|
||||
numThreads(numThreads());
|
||||
s_s.m_spawned = true;
|
||||
#ifdef VL_THREADED
|
||||
// THREADED-TODO startup threads
|
||||
#endif
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
|
||||
#endif // Guard
|
||||
|
@ -48,7 +48,7 @@ static const char* const VLTSAVE_TRAILER_STR = "vltsaved"; ///< Value of last by
|
||||
//=============================================================================
|
||||
// Searalization
|
||||
|
||||
bool VerilatedDeserialize::readDiffers (const void* __restrict datap, size_t size) {
|
||||
bool VerilatedDeserialize::readDiffers (const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
bufferCheck();
|
||||
const vluint8_t* __restrict dp = (const vluint8_t* __restrict)datap;
|
||||
vluint8_t miss = 0;
|
||||
@ -58,7 +58,7 @@ bool VerilatedDeserialize::readDiffers (const void* __restrict datap, size_t siz
|
||||
return (miss!=0);
|
||||
}
|
||||
|
||||
VerilatedDeserialize& VerilatedDeserialize::readAssert (const void* __restrict datap, size_t size) {
|
||||
VerilatedDeserialize& VerilatedDeserialize::readAssert (const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNLIKELY(readDiffers(datap,size))) {
|
||||
std::string fn = filename();
|
||||
std::string msg = std::string("Can't deserialize save-restore file as was made from different model");
|
||||
@ -68,7 +68,7 @@ VerilatedDeserialize& VerilatedDeserialize::readAssert (const void* __restrict d
|
||||
return *this; // For function chaining
|
||||
}
|
||||
|
||||
void VerilatedSerialize::header() {
|
||||
void VerilatedSerialize::header() VL_MT_UNSAFE_ONE {
|
||||
VerilatedSerialize& os = *this; // So can cut and paste standard << code below
|
||||
assert((strlen(VLTSAVE_HEADER_STR) & 7) == 0); // Keep aligned
|
||||
os.write(VLTSAVE_HEADER_STR, strlen(VLTSAVE_HEADER_STR));
|
||||
@ -78,7 +78,7 @@ void VerilatedSerialize::header() {
|
||||
os.write(Verilated::serializedPtr(), Verilated::serializedSize());
|
||||
}
|
||||
|
||||
void VerilatedDeserialize::header() {
|
||||
void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
|
||||
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
|
||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, strlen(VLTSAVE_HEADER_STR)))) {
|
||||
std::string fn = filename();
|
||||
@ -89,13 +89,13 @@ void VerilatedDeserialize::header() {
|
||||
os.read(Verilated::serializedPtr(), Verilated::serializedSize());
|
||||
}
|
||||
|
||||
void VerilatedSerialize::trailer() {
|
||||
void VerilatedSerialize::trailer() VL_MT_UNSAFE_ONE {
|
||||
VerilatedSerialize& os = *this; // So can cut and paste standard << code below
|
||||
assert((strlen(VLTSAVE_TRAILER_STR) & 7) == 0); // Keep aligned
|
||||
os.write(VLTSAVE_TRAILER_STR, strlen(VLTSAVE_TRAILER_STR));
|
||||
}
|
||||
|
||||
void VerilatedDeserialize::trailer() {
|
||||
void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE {
|
||||
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
|
||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, strlen(VLTSAVE_TRAILER_STR)))) {
|
||||
std::string fn = filename();
|
||||
@ -110,7 +110,8 @@ void VerilatedDeserialize::trailer() {
|
||||
//=============================================================================
|
||||
// Opening/Closing
|
||||
|
||||
void VerilatedSave::open (const char* filenamep) {
|
||||
void VerilatedSave::open(const char* filenamep) VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (isOpen()) return;
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("- save: opening save file %s\n",filenamep););
|
||||
|
||||
@ -132,7 +133,8 @@ void VerilatedSave::open (const char* filenamep) {
|
||||
header();
|
||||
}
|
||||
|
||||
void VerilatedRestore::open (const char* filenamep) {
|
||||
void VerilatedRestore::open(const char* filenamep) VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (isOpen()) return;
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("- restore: opening restore file %s\n",filenamep););
|
||||
|
||||
@ -155,7 +157,7 @@ void VerilatedRestore::open (const char* filenamep) {
|
||||
header();
|
||||
}
|
||||
|
||||
void VerilatedSave::close () {
|
||||
void VerilatedSave::close() VL_MT_UNSAFE_ONE {
|
||||
if (!isOpen()) return;
|
||||
trailer();
|
||||
flush();
|
||||
@ -163,7 +165,7 @@ void VerilatedSave::close () {
|
||||
::close(m_fd); // May get error, just ignore it
|
||||
}
|
||||
|
||||
void VerilatedRestore::close () {
|
||||
void VerilatedRestore::close() VL_MT_UNSAFE_ONE {
|
||||
if (!isOpen()) return;
|
||||
trailer();
|
||||
flush();
|
||||
@ -174,7 +176,8 @@ void VerilatedRestore::close () {
|
||||
//=============================================================================
|
||||
// Buffer management
|
||||
|
||||
void VerilatedSave::flush() {
|
||||
void VerilatedSave::flush() VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
vluint8_t* wp = m_bufp;
|
||||
while (1) {
|
||||
@ -197,7 +200,8 @@ void VerilatedSave::flush() {
|
||||
m_cp = m_bufp; // Reset buffer
|
||||
}
|
||||
|
||||
void VerilatedRestore::fill() {
|
||||
void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
// Move remaining characters down to start of buffer. (No memcpy, overlaps allowed)
|
||||
vluint8_t* rp = m_bufp;
|
||||
|
@ -38,12 +38,13 @@ protected:
|
||||
vluint8_t* m_bufp; ///< Output buffer
|
||||
bool m_isOpen; ///< True indicates open file/stream
|
||||
std::string m_filename; ///< Filename, for error messages
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
|
||||
inline static size_t bufferSize() { return 256*1024; } // See below for slack calculation
|
||||
inline static size_t bufferInsertSize() { return 16*1024; }
|
||||
|
||||
void header();
|
||||
void trailer();
|
||||
void header() VL_MT_UNSAFE_ONE;
|
||||
void trailer() VL_MT_UNSAFE_ONE;
|
||||
public:
|
||||
// CREATORS
|
||||
VerilatedSerialize() {
|
||||
@ -58,9 +59,9 @@ public:
|
||||
// METHODS
|
||||
bool isOpen() const { return m_isOpen; }
|
||||
std::string filename() const { return m_filename; }
|
||||
virtual void close() { flush(); }
|
||||
virtual void flush() {}
|
||||
inline VerilatedSerialize& write (const void* __restrict datap, size_t size) {
|
||||
virtual void close() VL_MT_UNSAFE_ONE { flush(); }
|
||||
virtual void flush() VL_MT_UNSAFE_ONE {}
|
||||
inline VerilatedSerialize& write(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
const vluint8_t* __restrict dp = (const vluint8_t* __restrict)datap;
|
||||
while (size) {
|
||||
bufferCheck();
|
||||
@ -73,7 +74,7 @@ public:
|
||||
}
|
||||
private:
|
||||
VerilatedSerialize(const VerilatedSerialize&) VL_EQ_DELETE; ///< N/A, no copy constructor
|
||||
VerilatedSerialize& bufferCheck() {
|
||||
VerilatedSerialize& bufferCheck() VL_MT_UNSAFE_ONE {
|
||||
// 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_cp > (m_bufp+(bufferSize()-bufferInsertSize())))) {
|
||||
@ -96,13 +97,14 @@ protected:
|
||||
vluint8_t* m_endp; ///< Last valid byte in m_bufp buffer
|
||||
bool m_isOpen; ///< True indicates open file/stream
|
||||
std::string m_filename; ///< Filename, for error messages
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
|
||||
inline static size_t bufferSize() { return 256*1024; } // See below for slack calculation
|
||||
inline static size_t bufferInsertSize() { return 16*1024; }
|
||||
|
||||
virtual void fill() = 0;
|
||||
void header();
|
||||
void trailer();
|
||||
void header() VL_MT_UNSAFE_ONE;
|
||||
void trailer() VL_MT_UNSAFE_ONE;
|
||||
public:
|
||||
// CREATORS
|
||||
VerilatedDeserialize() {
|
||||
@ -118,9 +120,9 @@ public:
|
||||
// METHODS
|
||||
bool isOpen() const { return m_isOpen; }
|
||||
std::string filename() const { return m_filename; }
|
||||
virtual void close() { flush(); }
|
||||
virtual void flush() {}
|
||||
inline VerilatedDeserialize& read (void* __restrict datap, size_t size) {
|
||||
virtual void close() VL_MT_UNSAFE_ONE { flush(); }
|
||||
virtual void flush() VL_MT_UNSAFE_ONE {}
|
||||
inline VerilatedDeserialize& read(void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
vluint8_t* __restrict dp = (vluint8_t* __restrict)datap;
|
||||
while (size) {
|
||||
bufferCheck();
|
||||
@ -132,12 +134,12 @@ public:
|
||||
return *this; // For function chaining
|
||||
}
|
||||
// Read a datum and compare with expected value
|
||||
bool readDiffers (const void* __restrict datap, size_t size);
|
||||
VerilatedDeserialize& readAssert (const void* __restrict datap, size_t size);
|
||||
VerilatedDeserialize& readAssert (vluint64_t data) { return readAssert(&data, sizeof(data)); }
|
||||
VerilatedDeserialize& readAssert(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE;
|
||||
VerilatedDeserialize& readAssert(vluint64_t data) VL_MT_UNSAFE_ONE { return readAssert(&data, sizeof(data)); }
|
||||
private:
|
||||
VerilatedDeserialize(const VerilatedDeserialize&) VL_EQ_DELETE; ///< N/A, no copy constructor
|
||||
VerilatedDeserialize& bufferCheck() {
|
||||
bool readDiffers(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE;
|
||||
VerilatedDeserialize& bufferCheck() VL_MT_UNSAFE_ONE {
|
||||
// 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_cp+bufferInsertSize()) > m_endp)) {
|
||||
@ -149,6 +151,7 @@ private:
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedSave - serialize to a file
|
||||
// This class is not thread safe, it must be called by a single thread
|
||||
|
||||
class VerilatedSave : public VerilatedSerialize {
|
||||
private:
|
||||
@ -159,14 +162,15 @@ public:
|
||||
VerilatedSave() { m_fd=-1; }
|
||||
virtual ~VerilatedSave() { close(); }
|
||||
// METHODS
|
||||
void open(const char* filenamep); ///< Open the file; call isOpen() to see if errors
|
||||
void open(const std::string& filename) { open(filename.c_str()); }
|
||||
virtual void close();
|
||||
virtual void flush();
|
||||
void open(const char* filenamep) VL_MT_UNSAFE_ONE; ///< Open the file; call isOpen() to see if errors
|
||||
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
|
||||
virtual void close() VL_MT_UNSAFE_ONE;
|
||||
virtual void flush() VL_MT_UNSAFE_ONE;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedRestore - deserialize from a file
|
||||
// This class is not thread safe, it must be called by a single thread
|
||||
|
||||
class VerilatedRestore : public VerilatedDeserialize {
|
||||
private:
|
||||
@ -178,11 +182,11 @@ public:
|
||||
virtual ~VerilatedRestore() { close(); }
|
||||
|
||||
// METHODS
|
||||
void open(const char* filenamep); ///< Open the file; call isOpen() to see if errors
|
||||
void open(const std::string& filename) { open(filename.c_str()); }
|
||||
virtual void close();
|
||||
virtual void flush() {}
|
||||
virtual void fill();
|
||||
void open(const char* filenamep) VL_MT_UNSAFE_ONE; ///< Open the file; call isOpen() to see if errors
|
||||
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
|
||||
virtual void close() VL_MT_UNSAFE_ONE;
|
||||
virtual void flush() VL_MT_UNSAFE_ONE {}
|
||||
virtual void fill() VL_MT_UNSAFE_ONE;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
|
@ -35,11 +35,12 @@
|
||||
// We want to get a pointer to m_data in the sc_bv_base class,
|
||||
// but it is protected. So make an exposing class, then use
|
||||
// cast magic to get at it. Saves patching get_datap in SystemC.
|
||||
// This class is thread safe (though most of SystemC is not).
|
||||
|
||||
#define VL_SC_BV_DATAP(bv) (VlScBvExposer::sp_datap(bv))
|
||||
class VlScBvExposer : public sc_bv_base {
|
||||
public:
|
||||
static const vluint32_t* sp_datap(const sc_bv_base& base) {
|
||||
static const vluint32_t* sp_datap(const sc_bv_base& base) VL_MT_SAFE {
|
||||
return static_cast<const VlScBvExposer*>(&base)->sp_datatp(); }
|
||||
const vluint32_t* sp_datatp() const { return reinterpret_cast<vluint32_t*>(m_data); }
|
||||
// Above reads this protected element in sc_bv_base:
|
||||
|
@ -55,18 +55,24 @@ class VerilatedVcdSingleton {
|
||||
private:
|
||||
typedef std::vector<VerilatedVcd*> VcdVec;
|
||||
struct Singleton {
|
||||
VcdVec s_vcdVecp; ///< List of all created traces
|
||||
VerilatedMutex s_vcdMutex; ///< Protect the singleton
|
||||
VcdVec s_vcdVecp VL_GUARDED_BY(s_vcdMutex); ///< List of all created traces
|
||||
};
|
||||
static Singleton& singleton() { static Singleton s; return s; }
|
||||
public:
|
||||
static void pushVcd(VerilatedVcd* vcdp) {
|
||||
static void pushVcd(VerilatedVcd* vcdp) VL_EXCLUDES(singleton().s_vcdMutex) {
|
||||
VerilatedLockGuard guard(singleton().s_vcdMutex);
|
||||
singleton().s_vcdVecp.push_back(vcdp);
|
||||
}
|
||||
static void removeVcd(const VerilatedVcd* vcdp) {
|
||||
static void removeVcd(const VerilatedVcd* vcdp) VL_EXCLUDES(singleton().s_vcdMutex) {
|
||||
VerilatedLockGuard guard(singleton().s_vcdMutex);
|
||||
VcdVec::iterator pos = find(singleton().s_vcdVecp.begin(), singleton().s_vcdVecp.end(), vcdp);
|
||||
if (pos != singleton().s_vcdVecp.end()) { singleton().s_vcdVecp.erase(pos); }
|
||||
}
|
||||
static void flush_all() {
|
||||
static void flush_all() VL_EXCLUDES(singleton().s_vcdMutex) VL_MT_UNSAFE_ONE {
|
||||
// Thread safety: Although this function is protected by a mutex so perhaps
|
||||
// in the future we can allow tracing in separate threads, vcdp->flush() assumes call from single thread
|
||||
VerilatedLockGuard guard(singleton().s_vcdMutex);
|
||||
for (VcdVec::const_iterator it=singleton().s_vcdVecp.begin(); it!=singleton().s_vcdVecp.end(); ++it) {
|
||||
VerilatedVcd* vcdp = *it;
|
||||
vcdp->flush();
|
||||
@ -103,16 +109,16 @@ protected:
|
||||
//=============================================================================
|
||||
// VerilatedVcdFile
|
||||
|
||||
bool VerilatedVcdFile::open(const std::string& name) {
|
||||
bool VerilatedVcdFile::open(const std::string& name) VL_MT_UNSAFE {
|
||||
m_fd = ::open(name.c_str(), O_CREAT|O_WRONLY|O_TRUNC|O_LARGEFILE|O_NONBLOCK, 0666);
|
||||
return (m_fd>=0);
|
||||
}
|
||||
|
||||
void VerilatedVcdFile::close() {
|
||||
void VerilatedVcdFile::close() VL_MT_UNSAFE {
|
||||
::close(m_fd);
|
||||
}
|
||||
|
||||
ssize_t VerilatedVcdFile::write(const char* bufp, ssize_t len) {
|
||||
ssize_t VerilatedVcdFile::write(const char* bufp, ssize_t len) VL_MT_UNSAFE {
|
||||
return ::write(m_fd, bufp, len);
|
||||
}
|
||||
|
||||
@ -141,6 +147,7 @@ VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep)
|
||||
}
|
||||
|
||||
void VerilatedVcd::open (const char* filename) {
|
||||
m_assertOne.check();
|
||||
if (isOpen()) return;
|
||||
|
||||
// Set member variables
|
||||
@ -171,6 +178,7 @@ void VerilatedVcd::open (const char* filename) {
|
||||
void VerilatedVcd::openNext (bool incFilename) {
|
||||
// Open next filename in concat sequence, mangle filename if
|
||||
// incFilename is true.
|
||||
m_assertOne.check();
|
||||
closePrev(); // Close existing
|
||||
if (incFilename) {
|
||||
// Find _0000.{ext} in filename
|
||||
@ -274,9 +282,9 @@ void VerilatedVcd::closePrev () {
|
||||
}
|
||||
|
||||
void VerilatedVcd::closeErr () {
|
||||
// This function is on the flush() call path
|
||||
// Close due to an error. We might abort before even getting here,
|
||||
// depending on the definition of vl_fatal.
|
||||
// This function is on the flush() call path
|
||||
if (!isOpen()) return;
|
||||
|
||||
// No buffer flush, just fclose
|
||||
@ -286,6 +294,7 @@ void VerilatedVcd::closeErr () {
|
||||
|
||||
void VerilatedVcd::close() {
|
||||
// This function is on the flush() call path
|
||||
m_assertOne.check();
|
||||
if (!isOpen()) return;
|
||||
if (m_evcd) {
|
||||
printStr("$vcdclose ");
|
||||
@ -338,11 +347,12 @@ void VerilatedVcd::bufferResize(vluint64_t minsize) {
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::bufferFlush () {
|
||||
void VerilatedVcd::bufferFlush () VL_MT_UNSAFE_ONE {
|
||||
// This function is on the flush() call path
|
||||
// We add output data to m_writep.
|
||||
// When it gets nearly full we dump it using this routine which calls write()
|
||||
// This is much faster than using buffered I/O
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
char* wp = m_wrBufp;
|
||||
while (1) {
|
||||
@ -503,6 +513,7 @@ void VerilatedVcd::dumpHeader () {
|
||||
}
|
||||
|
||||
void VerilatedVcd::module (const std::string& name) {
|
||||
m_assertOne.check();
|
||||
m_modName = name;
|
||||
}
|
||||
|
||||
@ -621,10 +632,11 @@ void VerilatedVcd::fullFloat (vluint32_t code, const float newval) {
|
||||
//=============================================================================
|
||||
// Callbacks
|
||||
|
||||
void VerilatedVcd::addCallback (
|
||||
void VerilatedVcd::addCallback VL_MT_UNSAFE_ONE (
|
||||
VerilatedVcdCallback_t initcb, VerilatedVcdCallback_t fullcb, VerilatedVcdCallback_t changecb,
|
||||
void* userthis)
|
||||
{
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(isOpen())) {
|
||||
std::string msg = std::string("Internal: ")+__FILE__+"::"+__FUNCTION__+" called with already open file";
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"",msg.c_str());
|
||||
@ -637,7 +649,9 @@ void VerilatedVcd::addCallback (
|
||||
// Dumping
|
||||
|
||||
void VerilatedVcd::dumpFull (vluint64_t timeui) {
|
||||
m_assertOne.check();
|
||||
dumpPrep (timeui);
|
||||
Verilated::quiesce();
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) {
|
||||
VerilatedVcdCallInfo *cip = m_callbacks[ent];
|
||||
(cip->m_fullcb) (this, cip->m_userthis, cip->m_code);
|
||||
@ -645,6 +659,7 @@ void VerilatedVcd::dumpFull (vluint64_t timeui) {
|
||||
}
|
||||
|
||||
void VerilatedVcd::dump (vluint64_t timeui) {
|
||||
m_assertOne.check();
|
||||
if (!isOpen()) return;
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
m_fullDump = false; // No need for more full dumps
|
||||
@ -656,6 +671,7 @@ void VerilatedVcd::dump (vluint64_t timeui) {
|
||||
if (!isOpen()) return;
|
||||
}
|
||||
dumpPrep (timeui);
|
||||
Verilated::quiesce();
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) {
|
||||
VerilatedVcdCallInfo *cip = m_callbacks[ent];
|
||||
(cip->m_changecb) (this, cip->m_userthis, cip->m_code);
|
||||
@ -671,7 +687,7 @@ void VerilatedVcd::dumpPrep (vluint64_t timeui) {
|
||||
//======================================================================
|
||||
// Static members
|
||||
|
||||
void VerilatedVcd::flush_all() {
|
||||
void VerilatedVcd::flush_all() VL_MT_UNSAFE_ONE {
|
||||
VerilatedVcdSingleton::flush_all();
|
||||
}
|
||||
|
||||
|
@ -45,9 +45,9 @@ public:
|
||||
// METHODS
|
||||
VerilatedVcdFile() : m_fd(0) {}
|
||||
virtual ~VerilatedVcdFile() {}
|
||||
virtual bool open(const std::string& name);
|
||||
virtual void close();
|
||||
virtual ssize_t write(const char* bufp, ssize_t len);
|
||||
virtual bool open(const std::string& name) VL_MT_UNSAFE;
|
||||
virtual void close() VL_MT_UNSAFE;
|
||||
virtual ssize_t write(const char* bufp, ssize_t len) VL_MT_UNSAFE;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@ -105,8 +105,10 @@ private:
|
||||
typedef std::map<std::string,std::string> NameMap;
|
||||
NameMap* m_namemapp; ///< List of names for the header
|
||||
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
|
||||
void bufferResize(vluint64_t minsize);
|
||||
void bufferFlush();
|
||||
void bufferFlush() VL_MT_UNSAFE_ONE;
|
||||
inline void bufferCheck() {
|
||||
// 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
|
||||
@ -129,6 +131,8 @@ private:
|
||||
void dumpHeader();
|
||||
void dumpPrep (vluint64_t timeui);
|
||||
void dumpFull (vluint64_t timeui);
|
||||
// cppcheck-suppress functionConst
|
||||
void dumpDone ();
|
||||
inline void printCode (vluint32_t code) {
|
||||
if (code>=(94*94*94)) *m_writep++ = static_cast<char>((code/94/94/94)%94+33);
|
||||
if (code>=(94*94)) *m_writep++ = static_cast<char>((code/94/94)%94+33);
|
||||
@ -165,13 +169,13 @@ public:
|
||||
inline bool isScopeEscape(char c) { return isspace(c) || c==m_scopeEscape; }
|
||||
|
||||
// METHODS
|
||||
void open (const char* filename); ///< Open the file; call isOpen() to see if errors
|
||||
void open(const char* filename) VL_MT_UNSAFE_ONE; ///< Open the file; call isOpen() to see if errors
|
||||
void openNext (bool incFilename); ///< Open next data-only file
|
||||
void close (); ///< Close the file
|
||||
void close() VL_MT_UNSAFE_ONE; ///< Close the file
|
||||
/// Flush any remaining data to this file
|
||||
void flush() { bufferFlush(); }
|
||||
void flush() VL_MT_UNSAFE_ONE { bufferFlush(); }
|
||||
/// Flush any remaining data from all files
|
||||
static void flush_all();
|
||||
static void flush_all() VL_MT_UNSAFE_ONE;
|
||||
|
||||
void set_time_unit (const char* unit); ///< Set time units (s/ms, defaults to ns)
|
||||
void set_time_unit (const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
@ -190,7 +194,7 @@ public:
|
||||
/// Inside dumping routines, declare callbacks for tracings
|
||||
void addCallback (VerilatedVcdCallback_t init, VerilatedVcdCallback_t full,
|
||||
VerilatedVcdCallback_t change,
|
||||
void* userthis);
|
||||
void* userthis) VL_MT_UNSAFE_ONE;
|
||||
|
||||
/// Inside dumping routines, declare a module
|
||||
void module (const std::string& name);
|
||||
@ -397,6 +401,8 @@ public:
|
||||
//=============================================================================
|
||||
// VerilatedVcdC
|
||||
/// Create a VCD dump file in C standalone (no SystemC) simulations.
|
||||
/// Also derived for use in SystemC simulations.
|
||||
/// Thread safety: Unless otherwise indicated, every function is VL_MT_UNSAFE_ONE
|
||||
|
||||
class VerilatedVcdC {
|
||||
VerilatedVcd m_sptrace; ///< Trace file being created
|
||||
@ -411,17 +417,17 @@ public:
|
||||
/// Open a new VCD file
|
||||
/// This includes a complete header dump each time it is called,
|
||||
/// just as if this object was deleted and reconstructed.
|
||||
void open (const char* filename) { m_sptrace.open(filename); }
|
||||
void open(const char* filename) VL_MT_UNSAFE_ONE { m_sptrace.open(filename); }
|
||||
/// Continue a VCD dump by rotating to a new file name
|
||||
/// The header is only in the first file created, this allows
|
||||
/// "cat" to be used to combine the header plus any number of data files.
|
||||
void openNext (bool incFilename=true) { m_sptrace.openNext(incFilename); }
|
||||
void openNext(bool incFilename=true) VL_MT_UNSAFE_ONE { m_sptrace.openNext(incFilename); }
|
||||
/// Set size in megabytes after which new file should be created
|
||||
void rolloverMB(size_t rolloverMB) { m_sptrace.rolloverMB(rolloverMB); };
|
||||
/// Close dump
|
||||
void close() { m_sptrace.close(); }
|
||||
void close() VL_MT_UNSAFE_ONE { m_sptrace.close(); }
|
||||
/// Flush dump
|
||||
void flush() { m_sptrace.flush(); }
|
||||
void flush() VL_MT_UNSAFE_ONE { m_sptrace.flush(); }
|
||||
/// Write one cycle of dump data
|
||||
void dump (vluint64_t timeui) { m_sptrace.dump(timeui); }
|
||||
/// Write one cycle of dump data - backward compatible and to reduce
|
||||
|
@ -62,22 +62,22 @@
|
||||
// Base VPI handled object
|
||||
class VerilatedVpio {
|
||||
// MEM MANGLEMENT
|
||||
static vluint8_t* s_freeHead;
|
||||
static VL_THREAD_LOCAL vluint8_t* t_freeHead;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedVpio() {}
|
||||
virtual ~VerilatedVpio() {}
|
||||
inline static void* operator new(size_t size) {
|
||||
inline static void* operator new(size_t size) VL_MT_SAFE {
|
||||
// We new and delete tons of vpi structures, so keep them around
|
||||
// To simplify our free list, we use a size large enough for all derived types
|
||||
// We reserve word zero for the next pointer, as that's safer in case a
|
||||
// dangling reference to the original remains around.
|
||||
static size_t chunk = 96;
|
||||
static const size_t chunk = 96;
|
||||
if (VL_UNLIKELY(size>chunk)) VL_FATAL_MT(__FILE__,__LINE__,"", "increase chunk");
|
||||
if (VL_LIKELY(s_freeHead)) {
|
||||
vluint8_t* newp = s_freeHead;
|
||||
s_freeHead = *((vluint8_t**)newp);
|
||||
if (VL_LIKELY(t_freeHead)) {
|
||||
vluint8_t* newp = t_freeHead;
|
||||
t_freeHead = *((vluint8_t**)newp);
|
||||
return newp+8;
|
||||
} else {
|
||||
// +8: 8 bytes for next
|
||||
@ -85,10 +85,10 @@ public:
|
||||
return newp+8;
|
||||
}
|
||||
}
|
||||
inline static void operator delete(void* obj, size_t size) {
|
||||
inline static void operator delete(void* obj, size_t size) VL_MT_SAFE {
|
||||
vluint8_t* oldp = ((vluint8_t*)obj)-8;
|
||||
*((void**)oldp) = s_freeHead;
|
||||
s_freeHead = oldp;
|
||||
*((void**)oldp) = t_freeHead;
|
||||
t_freeHead = oldp;
|
||||
}
|
||||
// MEMBERS
|
||||
static inline VerilatedVpio* castp(vpiHandle h) { return dynamic_cast<VerilatedVpio*>((VerilatedVpio*)h); }
|
||||
@ -323,12 +323,14 @@ class VerilatedVpiImp {
|
||||
VpioCbList m_cbObjLists[CB_ENUM_MAX_VALUE]; // Callbacks for each supported reason
|
||||
VpioTimedCbs m_timedCbs; // Time based callbacks
|
||||
VerilatedVpiError* m_errorInfop; // Container for vpi error info
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
|
||||
static VerilatedVpiImp s_s; // Singleton
|
||||
|
||||
public:
|
||||
VerilatedVpiImp() { m_errorInfop=NULL; }
|
||||
~VerilatedVpiImp() {}
|
||||
static void assertOneCheck() { s_s.m_assertOne.check(); }
|
||||
static void cbReasonAdd(VerilatedVpioCb* vop) {
|
||||
if (vop->reason() == cbValueChange) {
|
||||
if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(vop->cb_datap()->obj)) {
|
||||
@ -355,7 +357,8 @@ public:
|
||||
s_s.m_timedCbs.erase(it);
|
||||
}
|
||||
}
|
||||
static void callTimedCbs() {
|
||||
static void callTimedCbs() VL_MT_UNSAFE_ONE {
|
||||
assertOneCheck();
|
||||
QData time = VL_TIME_Q();
|
||||
for (VpioTimedCbs::iterator it=s_s.m_timedCbs.begin(); it!=s_s.m_timedCbs.end(); ) {
|
||||
if (VL_UNLIKELY(it->first <= time)) {
|
||||
@ -387,7 +390,8 @@ public:
|
||||
(vop->cb_rtnp()) (vop->cb_datap());
|
||||
}
|
||||
}
|
||||
static void callValueCbs() {
|
||||
static void callValueCbs() VL_MT_UNSAFE_ONE {
|
||||
assertOneCheck();
|
||||
VpioCbList& cbObjList = s_s.m_cbObjLists[cbValueChange];
|
||||
typedef std::set<VerilatedVpioVar*> VpioVarSet;
|
||||
VpioVarSet update; // set of objects to update after callbacks
|
||||
@ -417,7 +421,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
static VerilatedVpiError* error_info(); // getter for vpi error info
|
||||
static VerilatedVpiError* error_info() VL_MT_UNSAFE_ONE; // getter for vpi error info
|
||||
};
|
||||
|
||||
class VerilatedVpiError {
|
||||
@ -449,7 +453,7 @@ public:
|
||||
m_errorInfo.product = (PLI_BYTE8*)Verilated::productName();
|
||||
}
|
||||
~VerilatedVpiError() {}
|
||||
static void selfTest();
|
||||
static void selfTest() VL_MT_UNSAFE_ONE;
|
||||
VerilatedVpiError* setMessage(PLI_INT32 level) {
|
||||
m_flag=true;
|
||||
m_errorInfo.level = level;
|
||||
@ -481,33 +485,34 @@ public:
|
||||
}
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "vpi_unsupported called without error info set");
|
||||
}
|
||||
static const char* strFromVpiVal(PLI_INT32 vpiVal);
|
||||
static const char* strFromVpiObjType(PLI_INT32 vpiVal);
|
||||
static const char* strFromVpiMethod(PLI_INT32 vpiVal);
|
||||
static const char* strFromVpiCallbackReason(PLI_INT32 vpiVal);
|
||||
static const char* strFromVpiProp(PLI_INT32 vpiVal);
|
||||
static const char* strFromVpiVal(PLI_INT32 vpiVal) VL_MT_SAFE;
|
||||
static const char* strFromVpiObjType(PLI_INT32 vpiVal) VL_MT_SAFE;
|
||||
static const char* strFromVpiMethod(PLI_INT32 vpiVal) VL_MT_SAFE;
|
||||
static const char* strFromVpiCallbackReason(PLI_INT32 vpiVal) VL_MT_SAFE;
|
||||
static const char* strFromVpiProp(PLI_INT32 vpiVal) VL_MT_SAFE;
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
|
||||
VerilatedVpiImp VerilatedVpiImp::s_s; // Singleton
|
||||
vluint8_t* VerilatedVpio::s_freeHead = NULL;
|
||||
VL_THREAD_LOCAL vluint8_t* VerilatedVpio::t_freeHead = NULL;
|
||||
|
||||
//======================================================================
|
||||
// VerilatedVpi implementation
|
||||
|
||||
void VerilatedVpi::callTimedCbs() {
|
||||
void VerilatedVpi::callTimedCbs() VL_MT_UNSAFE_ONE {
|
||||
VerilatedVpiImp::callTimedCbs();
|
||||
}
|
||||
|
||||
void VerilatedVpi::callValueCbs() {
|
||||
void VerilatedVpi::callValueCbs() VL_MT_UNSAFE_ONE {
|
||||
VerilatedVpiImp::callValueCbs();
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VerilatedVpiImp implementation
|
||||
|
||||
VerilatedVpiError* VerilatedVpiImp::error_info() {
|
||||
VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
if (VL_UNLIKELY(!s_s.m_errorInfop)) {
|
||||
s_s.m_errorInfop = new VerilatedVpiError();
|
||||
}
|
||||
@ -864,10 +869,12 @@ const char* VerilatedVpiError::strFromVpiProp(PLI_INT32 vpiVal) VL_MT_SAFE {
|
||||
CHECK_RESULT_CSTR(strVal, #enum); \
|
||||
} while (0)
|
||||
|
||||
void VerilatedVpi::selfTest() {
|
||||
void VerilatedVpi::selfTest() VL_MT_UNSAFE_ONE {
|
||||
VerilatedVpiError::selfTest();
|
||||
}
|
||||
void VerilatedVpiError::selfTest() {
|
||||
void VerilatedVpiError::selfTest() VL_MT_UNSAFE_ONE {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
|
||||
CHECK_ENUM_STR(strFromVpiVal, vpiBinStrVal);
|
||||
CHECK_ENUM_STR(strFromVpiVal, vpiRawFourStateVal);
|
||||
|
||||
@ -902,6 +909,7 @@ void VerilatedVpiError::selfTest() {
|
||||
// callback related
|
||||
|
||||
vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
// cppcheck-suppress nullPointer
|
||||
if (VL_UNLIKELY(!cb_data_p)) {
|
||||
@ -941,6 +949,7 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
||||
|
||||
PLI_INT32 vpi_remove_cb(vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_remove_cb %p\n",object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VerilatedVpioCb* vop = VerilatedVpioCb::castp(object);
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
@ -965,6 +974,7 @@ void vpi_get_systf_info(vpiHandle object, p_vpi_systf_data systf_data_p) {
|
||||
// for obtaining handles
|
||||
|
||||
vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
if (VL_UNLIKELY(!namep)) return NULL;
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_name %s %p\n",namep,scope););
|
||||
@ -1000,6 +1010,7 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||
vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
|
||||
// Used to get array entries
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_index %p %d\n",object, indx););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VerilatedVpioVar* varop = VerilatedVpioVar::castp(object);
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
if (VL_LIKELY(varop)) {
|
||||
@ -1025,6 +1036,7 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
|
||||
|
||||
vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle %d %p\n",type,object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
switch (type) {
|
||||
case vpiLeftRange: {
|
||||
@ -1067,6 +1079,7 @@ vpiHandle vpi_handle_multi(PLI_INT32 type, vpiHandle refHandle1, vpiHandle refHa
|
||||
|
||||
vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_iterate %d %p\n",type,object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
switch (type) {
|
||||
case vpiMemoryWord: {
|
||||
@ -1104,6 +1117,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
||||
}
|
||||
vpiHandle vpi_scan(vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_scan %p\n",object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
VerilatedVpio* vop = VerilatedVpio::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return NULL;
|
||||
@ -1115,6 +1129,7 @@ vpiHandle vpi_scan(vpiHandle object) {
|
||||
PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
|
||||
// Leave this in the header file - in many cases the compiler can constant propagate "object"
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get %d %p\n",property,object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
switch (property) {
|
||||
case vpiTimePrecision: {
|
||||
@ -1156,6 +1171,7 @@ PLI_INT64 vpi_get64(PLI_INT32 property, vpiHandle object) {
|
||||
|
||||
PLI_BYTE8 *vpi_get_str(PLI_INT32 property, vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_str %d %p\n",property,object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VerilatedVpio* vop = VerilatedVpio::castp(object);
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
if (VL_UNLIKELY(!vop)) return NULL;
|
||||
@ -1194,6 +1210,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
||||
// cppcheck-suppress variableScope
|
||||
static VL_THREAD_LOCAL int outStrSz = sizeof(outStr)-1;
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_value %p\n",object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
if (VL_UNLIKELY(!value_p)) return;
|
||||
if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) {
|
||||
@ -1449,6 +1466,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
||||
vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
|
||||
p_vpi_time time_p, PLI_INT32 flags) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value %p %p\n",object, value_p););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
if (VL_UNLIKELY(!value_p)) {
|
||||
_VL_VPI_WARNING(__FILE__, __LINE__, "Ignoring vpi_put_value with NULL value pointer");
|
||||
@ -1721,6 +1739,7 @@ void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p,
|
||||
|
||||
void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
|
||||
// cppcheck-suppress nullPointer
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
if (VL_UNLIKELY(!time_p)) {
|
||||
_VL_VPI_WARNING(__FILE__, __LINE__, "Ignoring vpi_get_time with NULL value pointer");
|
||||
return;
|
||||
@ -1741,11 +1760,13 @@ void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
|
||||
// I/O routines
|
||||
|
||||
PLI_UINT32 vpi_mcd_open(PLI_BYTE8 *filenamep) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
return VL_FOPEN_S(filenamep,"wb");
|
||||
}
|
||||
|
||||
PLI_UINT32 vpi_mcd_close(PLI_UINT32 mcd) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
VL_FCLOSE_I(mcd); return 0;
|
||||
}
|
||||
@ -1755,6 +1776,7 @@ PLI_BYTE8 *vpi_mcd_name(PLI_UINT32 mcd) {
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_mcd_printf(PLI_UINT32 mcd, PLI_BYTE8 *formatp, ...) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
va_list ap;
|
||||
va_start(ap,formatp);
|
||||
@ -1764,6 +1786,7 @@ PLI_INT32 vpi_mcd_printf(PLI_UINT32 mcd, PLI_BYTE8 *formatp, ...) {
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_printf(PLI_BYTE8 *formatp, ...) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
va_list ap;
|
||||
va_start(ap,formatp);
|
||||
@ -1773,11 +1796,13 @@ PLI_INT32 vpi_printf(PLI_BYTE8 *formatp, ...) {
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_vprintf(PLI_BYTE8* formatp, va_list ap) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
return VL_VPRINTF(formatp, ap);
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_mcd_vprintf(PLI_UINT32 mcd, PLI_BYTE8 *format, va_list ap) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
FILE* fp = VL_CVT_I_FP(mcd);
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
// cppcheck-suppress nullPointer
|
||||
@ -1787,12 +1812,14 @@ PLI_INT32 vpi_mcd_vprintf(PLI_UINT32 mcd, PLI_BYTE8 *format, va_list ap) {
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_flush(void) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
Verilated::flushCall();
|
||||
return 0;
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
FILE* fp = VL_CVT_I_FP(mcd);
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
if (VL_UNLIKELY(!fp)) return 1;
|
||||
@ -1808,6 +1835,7 @@ PLI_INT32 vpi_compare_objects(vpiHandle object1, vpiHandle object2) {
|
||||
PLI_INT32 vpi_chk_error(p_vpi_error_info error_info_p) {
|
||||
// executing vpi_chk_error does not reset error
|
||||
// error_info_p can be NULL, so only return level in that case
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
p_vpi_error_info _error_info_p = VerilatedVpiImp::error_info()->getError();
|
||||
if (error_info_p && _error_info_p) {
|
||||
*error_info_p = *_error_info_p;
|
||||
@ -1817,12 +1845,14 @@ PLI_INT32 vpi_chk_error(p_vpi_error_info error_info_p) {
|
||||
};
|
||||
|
||||
PLI_INT32 vpi_free_object(vpiHandle object) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
return vpi_release_handle(object); // Deprecated
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_release_handle (vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_release_handle %p\n",object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VerilatedVpio* vop = VerilatedVpio::castp(object);
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
@ -1831,7 +1861,8 @@ PLI_INT32 vpi_release_handle (vpiHandle object) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_get_vlog_info(p_vpi_vlog_info vlog_info_p) {
|
||||
PLI_INT32 vpi_get_vlog_info(p_vpi_vlog_info vlog_info_p) VL_MT_SAFE {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
vlog_info_p->argc = Verilated::getCommandArgs()->argc;
|
||||
vlog_info_p->argv = (PLI_BYTE8**)Verilated::getCommandArgs()->argv;
|
||||
@ -1857,6 +1888,7 @@ PLI_INT32 vpi_put_userdata(vpiHandle obj, void *userdata) {
|
||||
|
||||
PLI_INT32 vpi_control(PLI_INT32 operation, ...) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_control %d\n",operation););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
switch (operation) {
|
||||
case vpiFinish: {
|
||||
|
@ -40,12 +40,12 @@ class VerilatedVpi {
|
||||
public:
|
||||
/// Call timed callbacks
|
||||
/// Users should call this from their main loops
|
||||
static void callTimedCbs();
|
||||
static void callTimedCbs() VL_MT_UNSAFE_ONE;
|
||||
/// Call value based callbacks
|
||||
/// Users should call this from their main loops
|
||||
static void callValueCbs();
|
||||
static void callValueCbs() VL_MT_UNSAFE_ONE;
|
||||
/// Self test, for internal use only
|
||||
static void selfTest();
|
||||
static void selfTest() VL_MT_UNSAFE_ONE;
|
||||
};
|
||||
|
||||
|
||||
|
@ -42,43 +42,84 @@
|
||||
# else
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__ ((format (printf, fmtArgNum, fmtArgNum+1)))
|
||||
# endif
|
||||
# define VL_ATTR_PURE __attribute__ ((pure))
|
||||
# define VL_ATTR_UNUSED __attribute__ ((unused))
|
||||
# define VL_FUNC __func__
|
||||
# if defined(__clang__) && defined(VL_THREADED)
|
||||
# define VL_ACQUIRE(...) __attribute__ ((acquire_capability(__VA_ARGS__)))
|
||||
# define VL_ACQUIRE_SHARED(...) __attribute__ ((acquire_shared_capability(__VA_ARGS__)))
|
||||
# define VL_RELEASE(...) __attribute__ ((release_capability(__VA_ARGS__)))
|
||||
# define VL_RELEASE_SHARED(...) __attribute__ ((release_shared_capability(__VA_ARGS__)))
|
||||
# define VL_TRY_ACQUIRE(...) __attribute__ ((try_acquire_capability(__VA_ARGS__)))
|
||||
# define VL_TRY_ACQUIRE_SHARED(...) __attribute__ ((try_acquire_shared_capability(__VA_ARGS__)))
|
||||
# define VL_CAPABILITY(x) __attribute__ ((capability(x)))
|
||||
# define VL_REQUIRES(x) __attribute__ ((requires_capability(x)))
|
||||
# define VL_GUARDED_BY(x) __attribute__ ((guarded_by(x)))
|
||||
# define VL_EXCLUDES(x) __attribute__ ((locks_excluded(x)))
|
||||
# define VL_SCOPED_CAPABILITY __attribute__ ((scoped_lockable))
|
||||
# endif
|
||||
# define VL_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
# define VL_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
# define VL_UNREACHABLE __builtin_unreachable();
|
||||
# define VL_PREFETCH_RD(p) __builtin_prefetch((p),0)
|
||||
# define VL_PREFETCH_RW(p) __builtin_prefetch((p),1)
|
||||
#elif defined(_MSC_VER)
|
||||
# define VL_ATTR_ALIGNED(alignment)
|
||||
# define VL_ATTR_ALWINLINE
|
||||
# define VL_ATTR_NORETURN
|
||||
# define VL_ATTR_PRINTF(fmtArgNum)
|
||||
# define VL_ATTR_UNUSED
|
||||
# define VL_FUNC __FUNCTION__
|
||||
# define VL_LIKELY(x) (!!(x))
|
||||
# define VL_UNLIKELY(x) (!!(x))
|
||||
# define VL_UNREACHABLE
|
||||
# define VL_PREFETCH_RD(p)
|
||||
# define VL_PREFETCH_RW(p)
|
||||
#else
|
||||
#endif
|
||||
|
||||
// Defaults for unsupported compiler features
|
||||
#ifndef VL_ATTR_ALIGNED
|
||||
# define VL_ATTR_ALIGNED(alignment) ///< Align structure to specified byte alignment
|
||||
#endif
|
||||
#ifndef VL_ATTR_ALWINLINE
|
||||
# define VL_ATTR_ALWINLINE ///< Inline, even when not optimizing
|
||||
#endif
|
||||
#ifndef VL_ATTR_NORETURN
|
||||
# define VL_ATTR_NORETURN ///< Function does not ever return
|
||||
#endif
|
||||
#ifndef VL_ATTR_PRINTF
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) ///< Function with printf format checking
|
||||
#endif
|
||||
#ifndef VL_ATTR_PURE
|
||||
# define VL_ATTR_PURE ///< Function is pure (and thus also VL_MT_SAFE)
|
||||
#endif
|
||||
#ifndef VL_ATTR_UNUSED
|
||||
# define VL_ATTR_UNUSED ///< Function that may be never used
|
||||
#endif
|
||||
#ifndef VL_FUNC
|
||||
# define VL_FUNC "__func__" ///< Name of current function for error macros
|
||||
#endif
|
||||
#ifndef VL_CAPABILITY
|
||||
# define VL_ACQUIRE(...) ///< Function requires a capability/lock (-fthread-safety)
|
||||
# define VL_ACQUIRE_SHARED(...) ///< Function aquires a shared capability/lock (-fthread-safety)
|
||||
# define VL_RELEASE(...) ///< Function releases a capability/lock (-fthread-safety)
|
||||
# define VL_RELEASE_SHARED(...) ///< Function releases a shared capability/lock (-fthread-safety)
|
||||
# define VL_TRY_ACQUIRE(...) ///< Function returns bool if aquired a capability (-fthread-safety)
|
||||
# define VL_TRY_ACQUIRE_SHARED(...) ///< Function returns bool if aquired a shared capability (-fthread-safety)
|
||||
# define VL_REQUIRES(x) ///< Function requires a capability inbound (-fthread-safety)
|
||||
# define VL_EXCLUDES(x) ///< Function requires not having a capability inbound (-fthread-safety)
|
||||
# define VL_CAPABILITY(x) ///< Name of capability/lock (-fthread-safety)
|
||||
# define VL_GUARDED_BY(x) ///< Name of mutex protecting this variable (-fthread-safety)
|
||||
# define VL_SCOPED_CAPABILITY ///< Scoped threaded capability/lock (-fthread-safety)
|
||||
#endif
|
||||
#ifndef VL_LIKELY
|
||||
# define VL_LIKELY(x) (!!(x)) ///< Boolean expression more often true than false
|
||||
# define VL_UNLIKELY(x) (!!(x)) ///< Boolean expression more often false than true
|
||||
#endif
|
||||
#ifndef VL_UNREACHABLE
|
||||
# define VL_UNREACHABLE ///< Point that may never be reached
|
||||
#endif
|
||||
#ifndef VL_PREFETCH_RD
|
||||
# define VL_PREFETCH_RD(p) ///< Prefetch data with read intent
|
||||
#endif
|
||||
#ifndef VL_PREFETCH_RW
|
||||
# define VL_PREFETCH_RW(p) ///< Prefetch data with read/write intent
|
||||
#endif
|
||||
|
||||
#ifdef VL_THREADED
|
||||
# ifdef __GNUC__
|
||||
# if (__cplusplus < 201103L) && !defined(VL_THREADED_NO_C11_WARNING)
|
||||
# error "VL_THREADED support plans to move to C++-11 and later only; use newer --std to be ready"
|
||||
# error "VL_THREADED/--threads support requires C++-11 or newer only; use newer compiler"
|
||||
# endif
|
||||
# else
|
||||
# error "Unsupported compiler for VL_THREADED: No thread-local declarator"
|
||||
|
@ -1738,8 +1738,16 @@ void EmitCImp::emitWrapEval(AstNodeModule* modp) {
|
||||
if (v3Global.opt.inhibitSim()) {
|
||||
puts("if (VL_UNLIKELY(__Vm_inhibitSim)) return;\n");
|
||||
}
|
||||
putsDecoration("// Evaluate till stable\n");
|
||||
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+++++TOP Evaluate "+modClassName(modp)+"::eval\\n\"); );\n");
|
||||
|
||||
if (v3Global.opt.threads()) { // THREADED-TODO move to per-train
|
||||
uint32_t trainId = 0;
|
||||
putsDecoration("// Train "+cvtToStr(trainId)+" start\n");
|
||||
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"Train starting, trainId="+cvtToStr(trainId)+"\\n\"););\n");
|
||||
puts("Verilated::trainId("+cvtToStr(trainId)+");\n");
|
||||
}
|
||||
|
||||
putsDecoration("// Evaluate till stable\n");
|
||||
puts("int __VclockLoop = 0;\n");
|
||||
puts("QData __Vchange = 1;\n");
|
||||
puts("while (VL_LIKELY(__Vchange)) {\n");
|
||||
@ -1752,6 +1760,12 @@ void EmitCImp::emitWrapEval(AstNodeModule* modp) {
|
||||
puts( "if (VL_UNLIKELY(++__VclockLoop > "+cvtToStr(v3Global.opt.convergeLimit())
|
||||
+")) VL_FATAL_MT(__FILE__,__LINE__,__FILE__,\"Verilated model didn't converge\");\n");
|
||||
puts("}\n");
|
||||
if (v3Global.opt.threads()) { // THREADED-TODO move to end of all trains on thread
|
||||
puts("Verilated::endOfThreadTrain(vlSymsp->__Vm_evalMsgQp);\n");
|
||||
}
|
||||
if (v3Global.opt.threads()) {
|
||||
puts("Verilated::endOfEval(vlSymsp->__Vm_evalMsgQp);\n");
|
||||
}
|
||||
puts("}\n");
|
||||
splitSizeInc(10);
|
||||
|
||||
@ -2053,8 +2067,10 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
|
||||
|
||||
// Save/restore
|
||||
if (v3Global.opt.savable() && modp->isTop()) {
|
||||
puts("inline VerilatedSerialize& operator<<(VerilatedSerialize& os, "+modClassName(modp)+"& rhs) { rhs.__Vserialize(os); return os; }\n");
|
||||
puts("inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, "+modClassName(modp)+"& rhs) { rhs.__Vdeserialize(os); return os; }\n");
|
||||
puts("inline VerilatedSerialize& operator<<(VerilatedSerialize& os, "+modClassName(modp)+"& rhs) {\n"
|
||||
"Verilated::quiesce(); rhs.__Vserialize(os); return os; }\n");
|
||||
puts("inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, "+modClassName(modp)+"& rhs) {\n"
|
||||
"Verilated::quiesce(); rhs.__Vdeserialize(os); return os; }\n");
|
||||
puts("\n");
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user