Rewrite include libraries to support VL_THREADED towards future threading

This commit is contained in:
Wilson Snyder 2017-10-26 21:51:51 -04:00
parent d542921ff7
commit f91bac7b31
15 changed files with 879 additions and 260 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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