1 // -*- mode: C++; c-file-style: "cc-mode" -*- 2 //============================================================================= 3 // 4 // Code available from: https://verilator.org 5 // 6 // Copyright 2001-2021 by Wilson Snyder. This program is free software; you 7 // can redistribute it and/or modify it under the terms of either the GNU 8 // Lesser General Public License Version 3 or the Perl Artistic License 9 // Version 2.0. 10 // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 11 // 12 //============================================================================= 13 /// 14 /// \file 15 /// \brief Verilated internal common-tracing header 16 /// 17 /// This file is not part of the Verilated public-facing API. 18 /// It is only for internal use by Verilated tracing routines. 19 /// 20 //============================================================================= 21 22 #ifndef VERILATOR_VERILATED_TRACE_H_ 23 #define VERILATOR_VERILATED_TRACE_H_ 24 25 // clang-format off 26 27 #include "verilated.h" 28 #include "verilated_trace_defs.h" 29 30 #include <string> 31 #include <vector> 32 33 #ifdef VL_TRACE_THREADED 34 # include <condition_variable> 35 # include <deque> 36 # include <thread> 37 #endif 38 39 // clang-format on 40 41 #ifdef VL_TRACE_THREADED 42 //============================================================================= 43 // Threaded tracing 44 45 // A simple synchronized first in first out queue 46 template <class T> class VerilatedThreadQueue final { // LCOV_EXCL_LINE // lcov bug 47 private: 48 VerilatedMutex m_mutex; // Protects m_queue 49 std::condition_variable_any m_cv; 50 std::deque<T> m_queue VL_GUARDED_BY(m_mutex); 51 52 public: 53 // Put an element at the back of the queue put(T value)54 void put(T value) VL_MT_SAFE_EXCLUDES(m_mutex) { 55 const VerilatedLockGuard lock{m_mutex}; 56 m_queue.push_back(value); 57 m_cv.notify_one(); 58 } 59 60 // Put an element at the front of the queue put_front(T value)61 void put_front(T value) VL_MT_SAFE_EXCLUDES(m_mutex) { 62 const VerilatedLockGuard lock{m_mutex}; 63 m_queue.push_front(value); 64 m_cv.notify_one(); 65 } 66 67 // Get an element from the front of the queue. Blocks if none available get()68 T get() VL_MT_SAFE_EXCLUDES(m_mutex) { 69 VerilatedLockGuard lock{m_mutex}; 70 m_cv.wait(lock, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); }); 71 assert(!m_queue.empty()); 72 T value = m_queue.front(); 73 m_queue.pop_front(); 74 return value; 75 } 76 77 // Non blocking get tryGet(T & result)78 bool tryGet(T& result) VL_MT_SAFE_EXCLUDES(m_mutex) { 79 const VerilatedLockGuard lockGuard{m_mutex}; 80 if (m_queue.empty()) return false; 81 result = m_queue.front(); 82 m_queue.pop_front(); 83 return true; 84 } 85 }; 86 87 // Commands used by thread tracing. Anonymous enum in class, as we want 88 // it scoped, but we also want the automatic conversion to integer types. 89 class VerilatedTraceCommand final { 90 public: 91 // These must all fit in 4 bit at the moment, as the tracing routines 92 // pack parameters in the top bits. 93 enum : vluint8_t { 94 CHG_BIT_0 = 0x0, 95 CHG_BIT_1 = 0x1, 96 CHG_CDATA = 0x2, 97 CHG_SDATA = 0x3, 98 CHG_IDATA = 0x4, 99 CHG_QDATA = 0x5, 100 CHG_WDATA = 0x6, 101 CHG_DOUBLE = 0x8, 102 // TODO: full.. 103 TIME_CHANGE = 0xd, 104 END = 0xe, // End of buffer 105 SHUTDOWN = 0xf // Shutdown worker thread, also marks end of buffer 106 }; 107 }; 108 #endif 109 110 //============================================================================= 111 // VerilatedTrace 112 113 // VerilatedTrace uses F-bounded polymorphism to access duck-typed 114 // implementations in the format specific derived class, which must be passed 115 // as the type parameter T_Derived 116 template <class T_Derived> class VerilatedTrace VL_NOT_FINAL { 117 public: 118 //========================================================================= 119 // Generic tracing internals 120 121 using initCb_t = void (*)(void*, T_Derived*, vluint32_t); // Type of init callbacks 122 using dumpCb_t = void (*)(void*, T_Derived*); // Type of all but init callbacks 123 124 private: 125 struct CallbackRecord { 126 // Note: would make these fields const, but some old STL implementations 127 // (the one in Ubuntu 14.04 with GCC 4.8.4 in particular) use the 128 // assignment operator on inserting into collections, so they don't work 129 // with const fields... 130 union { 131 initCb_t m_initCb; // The callback function 132 dumpCb_t m_dumpCb; // The callback function 133 }; 134 void* m_userp; // The user pointer to pass to the callback (the symbol table) CallbackRecordCallbackRecord135 CallbackRecord(initCb_t cb, void* userp) 136 : m_initCb{cb} 137 , m_userp{userp} {} CallbackRecordCallbackRecord138 CallbackRecord(dumpCb_t cb, void* userp) 139 : m_dumpCb{cb} 140 , m_userp{userp} {} 141 }; 142 143 vluint32_t* m_sigs_oldvalp; // Old value store 144 vluint64_t m_timeLastDump; // Last time we did a dump 145 std::vector<CallbackRecord> m_initCbs; // Routines to initialize traciong 146 std::vector<CallbackRecord> m_fullCbs; // Routines to perform full dump 147 std::vector<CallbackRecord> m_chgCbs; // Routines to perform incremental dump 148 std::vector<CallbackRecord> m_cleanupCbs; // Routines to call at the end of dump 149 bool m_fullDump; // Whether a full dump is required on the next call to 'dump' 150 vluint32_t m_nextCode; // Next code number to assign 151 vluint32_t m_numSignals; // Number of distinct signals 152 vluint32_t m_maxBits; // Number of bits in the widest signal 153 std::string m_moduleName; // Name of module being trace initialized now 154 char m_scopeEscape; 155 double m_timeRes; // Time resolution (ns/ms etc) 156 double m_timeUnit; // Time units (ns/ms etc) 157 158 void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord& cbRec) 159 VL_MT_SAFE_EXCLUDES(m_mutex); 160 161 // Equivalent to 'this' but is of the sub-type 'T_Derived*'. Use 'self()->' 162 // to access duck-typed functions to avoid a virtual function call. self()163 T_Derived* self() { return static_cast<T_Derived*>(this); } 164 165 // Flush any remaining data for this file 166 static void onFlush(void* selfp) VL_MT_UNSAFE_ONE; 167 // Close the file on termination 168 static void onExit(void* selfp) VL_MT_UNSAFE_ONE; 169 170 #ifdef VL_TRACE_THREADED 171 // Number of total trace buffers that have been allocated 172 vluint32_t m_numTraceBuffers; 173 174 // Size of trace buffers 175 size_t m_traceBufferSize; 176 177 // Buffers handed to worker for processing 178 VerilatedThreadQueue<vluint32_t*> m_buffersToWorker; 179 // Buffers returned from worker after processing 180 VerilatedThreadQueue<vluint32_t*> m_buffersFromWorker; 181 182 // Get a new trace buffer that can be populated. May block if none available 183 vluint32_t* getTraceBuffer(); 184 185 // Write pointer into current buffer 186 vluint32_t* m_traceBufferWritep; 187 188 // End of trace buffer 189 vluint32_t* m_traceBufferEndp; 190 191 // The worker thread itself 192 std::unique_ptr<std::thread> m_workerThread; 193 194 // The function executed by the worker thread 195 void workerThreadMain(); 196 197 // Wait until given buffer is placed in m_buffersFromWorker 198 void waitForBuffer(const vluint32_t* bufferp); 199 200 // Shut down and join worker, if it's running, otherwise do nothing 201 void shutdownWorker(); 202 #endif 203 204 // CONSTRUCTORS 205 VL_UNCOPYABLE(VerilatedTrace); 206 207 protected: 208 //========================================================================= 209 // Internals available to format specific implementations 210 211 VerilatedMutex m_mutex; // Ensure dump() etc only called from single thread 212 nextCode()213 vluint32_t nextCode() const { return m_nextCode; } numSignals()214 vluint32_t numSignals() const { return m_numSignals; } maxBits()215 vluint32_t maxBits() const { return m_maxBits; } moduleName()216 const std::string& moduleName() const { return m_moduleName; } fullDump(bool value)217 void fullDump(bool value) { m_fullDump = value; } timeLastDump()218 vluint64_t timeLastDump() { return m_timeLastDump; } 219 timeRes()220 double timeRes() const { return m_timeRes; } timeUnit()221 double timeUnit() const { return m_timeUnit; } 222 std::string timeResStr() const; 223 224 void traceInit() VL_MT_UNSAFE; 225 226 void declCode(vluint32_t code, vluint32_t bits, bool tri); 227 228 // Is this an escape? isScopeEscape(char c)229 bool isScopeEscape(char c) { return std::isspace(c) || c == m_scopeEscape; } 230 // Character that splits scopes. Note whitespace are ALWAYS escapes. scopeEscape()231 char scopeEscape() { return m_scopeEscape; } 232 233 void closeBase(); 234 void flushBase(); 235 236 //========================================================================= 237 // Virtual functions to be provided by the format specific implementation 238 239 // Called when the trace moves forward to a new time point 240 virtual void emitTimeChange(vluint64_t timeui) = 0; 241 242 // These hooks are called before a full or change based dump is produced. 243 // The return value indicates whether to proceed with the dump. 244 virtual bool preFullDump() = 0; 245 virtual bool preChangeDump() = 0; 246 247 public: 248 //========================================================================= 249 // External interface to client code 250 251 explicit VerilatedTrace(); 252 ~VerilatedTrace(); 253 254 // Set time units (s/ms, defaults to ns) 255 void set_time_unit(const char* unitp) VL_MT_SAFE; 256 void set_time_unit(const std::string& unit) VL_MT_SAFE; 257 // Set time resolution (s/ms, defaults to ns) 258 void set_time_resolution(const char* unitp) VL_MT_SAFE; 259 void set_time_resolution(const std::string& unit) VL_MT_SAFE; 260 261 // Call 262 void dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex); 263 264 //========================================================================= 265 // Non-hot path internal interface to Verilator generated code 266 267 void addInitCb(initCb_t cb, void* userp) VL_MT_SAFE; 268 void addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE; 269 void addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE; 270 void addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE; 271 272 void module(const std::string& name) VL_MT_UNSAFE; scopeEscape(char flag)273 void scopeEscape(char flag) { m_scopeEscape = flag; } 274 275 //========================================================================= 276 // Hot path internal interface to Verilator generated code 277 278 // Implementation note: We rely on the following duck-typed implementations 279 // in the derived class T_Derived. These emit* functions record a format 280 // specific trace entry. Normally one would use pure virtual functions for 281 // these here, but we cannot afford dynamic dispatch for calling these as 282 // this is very hot code during tracing. 283 284 // duck-typed void emitBit(vluint32_t code, CData newval) = 0; 285 // duck-typed void emitCData(vluint32_t code, CData newval, int bits) = 0; 286 // duck-typed void emitSData(vluint32_t code, SData newval, int bits) = 0; 287 // duck-typed void emitIData(vluint32_t code, IData newval, int bits) = 0; 288 // duck-typed void emitQData(vluint32_t code, QData newval, int bits) = 0; 289 // duck-typed void emitWData(vluint32_t code, const WData* newvalp, int bits) = 0; 290 // duck-typed void emitDouble(vluint32_t code, double newval) = 0; 291 oldp(vluint32_t code)292 vluint32_t* oldp(vluint32_t code) { return m_sigs_oldvalp + code; } 293 294 // Write to previous value buffer value and emit trace entry. 295 void fullBit(vluint32_t* oldp, CData newval); 296 void fullCData(vluint32_t* oldp, CData newval, int bits); 297 void fullSData(vluint32_t* oldp, SData newval, int bits); 298 void fullIData(vluint32_t* oldp, IData newval, int bits); 299 void fullQData(vluint32_t* oldp, QData newval, int bits); 300 void fullWData(vluint32_t* oldp, const WData* newvalp, int bits); 301 void fullDouble(vluint32_t* oldp, double newval); 302 303 #ifdef VL_TRACE_THREADED 304 // Threaded tracing. Just dump everything in the trace buffer chgBit(vluint32_t code,CData newval)305 inline void chgBit(vluint32_t code, CData newval) { 306 m_traceBufferWritep[0] = VerilatedTraceCommand::CHG_BIT_0 | newval; 307 m_traceBufferWritep[1] = code; 308 m_traceBufferWritep += 2; 309 VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp);); 310 } chgCData(vluint32_t code,CData newval,int bits)311 inline void chgCData(vluint32_t code, CData newval, int bits) { 312 m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_CDATA; 313 m_traceBufferWritep[1] = code; 314 m_traceBufferWritep[2] = newval; 315 m_traceBufferWritep += 3; 316 VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp);); 317 } chgSData(vluint32_t code,SData newval,int bits)318 inline void chgSData(vluint32_t code, SData newval, int bits) { 319 m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_SDATA; 320 m_traceBufferWritep[1] = code; 321 m_traceBufferWritep[2] = newval; 322 m_traceBufferWritep += 3; 323 VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp);); 324 } chgIData(vluint32_t code,IData newval,int bits)325 inline void chgIData(vluint32_t code, IData newval, int bits) { 326 m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_IDATA; 327 m_traceBufferWritep[1] = code; 328 m_traceBufferWritep[2] = newval; 329 m_traceBufferWritep += 3; 330 VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp);); 331 } chgQData(vluint32_t code,QData newval,int bits)332 inline void chgQData(vluint32_t code, QData newval, int bits) { 333 m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_QDATA; 334 m_traceBufferWritep[1] = code; 335 *reinterpret_cast<QData*>(m_traceBufferWritep + 2) = newval; 336 m_traceBufferWritep += 4; 337 VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp);); 338 } chgWData(vluint32_t code,const WData * newvalp,int bits)339 inline void chgWData(vluint32_t code, const WData* newvalp, int bits) { 340 m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_WDATA; 341 m_traceBufferWritep[1] = code; 342 m_traceBufferWritep += 2; 343 for (int i = 0; i < (bits + 31) / 32; ++i) { *m_traceBufferWritep++ = newvalp[i]; } 344 VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp);); 345 } chgDouble(vluint32_t code,double newval)346 inline void chgDouble(vluint32_t code, double newval) { 347 m_traceBufferWritep[0] = VerilatedTraceCommand::CHG_DOUBLE; 348 m_traceBufferWritep[1] = code; 349 // cppcheck-suppress invalidPointerCast 350 *reinterpret_cast<double*>(m_traceBufferWritep + 2) = newval; 351 m_traceBufferWritep += 4; 352 VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp);); 353 } 354 355 #define CHG(name) chg##name##Impl 356 #else 357 #define CHG(name) chg##name 358 #endif 359 360 // In non-threaded mode, these are called directly by the trace callbacks, 361 // and are called chg*. In threaded mode, they are called by the worker 362 // thread and are called chg*Impl 363 364 // Check previous dumped value of signal. If changed, then emit trace entry CHG(Bit)365 inline void CHG(Bit)(vluint32_t* oldp, CData newval) { 366 const vluint32_t diff = *oldp ^ newval; 367 if (VL_UNLIKELY(diff)) fullBit(oldp, newval); 368 } CHG(CData)369 inline void CHG(CData)(vluint32_t* oldp, CData newval, int bits) { 370 const vluint32_t diff = *oldp ^ newval; 371 if (VL_UNLIKELY(diff)) fullCData(oldp, newval, bits); 372 } CHG(SData)373 inline void CHG(SData)(vluint32_t* oldp, SData newval, int bits) { 374 const vluint32_t diff = *oldp ^ newval; 375 if (VL_UNLIKELY(diff)) fullSData(oldp, newval, bits); 376 } CHG(IData)377 inline void CHG(IData)(vluint32_t* oldp, IData newval, int bits) { 378 const vluint32_t diff = *oldp ^ newval; 379 if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits); 380 } CHG(QData)381 inline void CHG(QData)(vluint32_t* oldp, QData newval, int bits) { 382 const vluint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval; 383 if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits); 384 } CHG(WData)385 inline void CHG(WData)(vluint32_t* oldp, const WData* newvalp, int bits) { 386 for (int i = 0; i < (bits + 31) / 32; ++i) { 387 if (VL_UNLIKELY(oldp[i] ^ newvalp[i])) { 388 fullWData(oldp, newvalp, bits); 389 return; 390 } 391 } 392 } CHG(Double)393 inline void CHG(Double)(vluint32_t* oldp, double newval) { 394 // cppcheck-suppress invalidPointerCast 395 if (VL_UNLIKELY(*reinterpret_cast<double*>(oldp) != newval)) fullDouble(oldp, newval); 396 } 397 398 #undef CHG 399 }; 400 #endif // guard 401