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