From f91bac7b31a895b7c906c33c65bb1ecc57f18913 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 26 Oct 2017 21:51:51 -0400 Subject: [PATCH] Rewrite include libraries to support VL_THREADED towards future threading --- include/verilated.cpp | 203 +++++++++++++++++++++++---- include/verilated.h | 266 ++++++++++++++++++++++++++++-------- include/verilated.mk.in | 2 + include/verilated_cov.cpp | 70 ++++++---- include/verilated_cov.h | 15 +- include/verilated_imp.h | 255 +++++++++++++++++++++++++++++----- include/verilated_save.cpp | 28 ++-- include/verilated_save.h | 52 +++---- include/verilated_sc.h | 3 +- include/verilated_vcd_c.cpp | 38 ++++-- include/verilated_vcd_c.h | 32 +++-- include/verilated_vpi.cpp | 82 +++++++---- include/verilated_vpi.h | 6 +- include/verilatedos.h | 65 +++++++-- src/V3EmitC.cpp | 22 ++- 15 files changed, 879 insertions(+), 260 deletions(-) diff --git a/include/verilated.cpp b/include/verilated.cpp index 158edb808..869520aa0 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -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 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 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()< 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 + +//=========================================================================== diff --git a/include/verilated.h b/include/verilated.h index 7ed1fa3bc..51d1c2f8e 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -37,6 +37,11 @@ #include #include #include +#ifdef VL_THREADED +# include +# include +# include +#endif // avoided to reduce compile time // avoided and instead in verilated_heavy.h to reduce compile time // 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(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(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; diff --git a/include/verilated.mk.in b/include/verilated.mk.in index b9687e8c2..49e8f3853 100644 --- a/include/verilated.mk.in +++ b/include/verilated.mk.in @@ -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 ####################################################################### diff --git a/include/verilated_cov.cpp b/include/verilated_cov.cpp index bbd12f0ab..a14d06e69 100644 --- a/include/verilated_cov.cpp +++ b/include/verilated_cov.cpp @@ -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="<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(itemp)); } -void VerilatedCov::_inserti (vluint64_t* itemp) { +void VerilatedCov::_inserti(vluint64_t* itemp) VL_MT_SAFE { VerilatedCovImp::imp().inserti(new VerilatedCoverItemSpec(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), diff --git a/include/verilated_cov.h b/include/verilated_cov.h index 96f8de7ee..18b82b87f 100644 --- a/include/verilated_cov.h +++ b/include/verilated_cov.h @@ -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 diff --git a/include/verilated_imp.h b/include/verilated_imp.h index 0d9193213..2a427c559 100644 --- a/include/verilated_imp.h +++ b/include/verilated_imp.h @@ -34,14 +34,132 @@ #include "verilated_syms.h" #include +#include #include #include #include +#ifdef VL_THREADED +# include +# include +#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 m_cb; ///< Lambda to execute when message received +public: + // CONSTRUCTORS + VerilatedMsg(const std::function& 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 VerilatedThreadQueue; + + std::atomic 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 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 + 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 // Slow - somewhat static: - ExportNameMap m_exportMap; ///< Map of - int m_exportNext; ///< Next export funcnum + VerilatedMutex m_exportMutex; ///< Protect m_nameMap + ExportNameMap m_exportMap VL_GUARDED_BY(m_exportMutex); ///< Map of + int m_exportNext VL_GUARDED_BY(m_exportMutex); ///< Next export funcnum // File I/O - std::vector m_fdps; ///< File descriptors - std::deque m_fdFree; ///< List of free descriptors (SLOW - FOPEN/CLOSE only) + VerilatedMutex m_fdMutex; ///< Protect m_fdps, m_fdFree + std::vector m_fdps VL_GUARDED_BY(m_fdMutex); ///< File descriptors + std::deque 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; isecond = 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 diff --git a/include/verilated_save.cpp b/include/verilated_save.cpp index 3c765a80d..d724ccdb4 100644 --- a/include/verilated_save.cpp +++ b/include/verilated_save.cpp @@ -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; diff --git a/include/verilated_save.h b/include/verilated_save.h index 4fa35849a..f62c53281 100644 --- a/include/verilated_save.h +++ b/include/verilated_save.h @@ -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; }; //============================================================================= diff --git a/include/verilated_sc.h b/include/verilated_sc.h index 30d387fd7..05acff837 100644 --- a/include/verilated_sc.h +++ b/include/verilated_sc.h @@ -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(&base)->sp_datatp(); } const vluint32_t* sp_datatp() const { return reinterpret_cast(m_data); } // Above reads this protected element in sc_bv_base: diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index 63cebb3ea..5460e51da 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -55,18 +55,24 @@ class VerilatedVcdSingleton { private: typedef std::vector 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(); } diff --git a/include/verilated_vcd_c.h b/include/verilated_vcd_c.h index e0384218a..f0b6bf392 100644 --- a/include/verilated_vcd_c.h +++ b/include/verilated_vcd_c.h @@ -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 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((code/94/94/94)%94+33); if (code>=(94*94)) *m_writep++ = static_cast((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 diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 4301b396a..e649f9bc0 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -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*)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 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: { diff --git a/include/verilated_vpi.h b/include/verilated_vpi.h index 95a0c1b27..a221649bb 100644 --- a/include/verilated_vpi.h +++ b/include/verilated_vpi.h @@ -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; }; diff --git a/include/verilatedos.h b/include/verilatedos.h index 401f12dc6..2b8b45651 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -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" diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index a7c1ca096..9b430f089 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -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"); }