forked from verilator/verilator
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathverilated_imp.h
585 lines (528 loc) · 22.9 KB
/
verilated_imp.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Code available from: https://verilator.org
//
// Copyright 2009-2024 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//=========================================================================
///
/// \file
/// \brief Verilated implementation Header, only for verilated.cpp internals.
///
/// This file is not part of the Verilated public-facing API.
/// It is only for internal use by the Verilated libraries.
///
//=========================================================================
#ifndef VERILATOR_VERILATED_IMP_H_
#define VERILATOR_VERILATED_IMP_H_
// clang-format off
#if !defined(VERILATOR_VERILATED_CPP_) && !defined(VERILATOR_VERILATED_DPI_CPP_) \
&& !defined(VERILATOR_VERILATED_VPI_CPP_) && !defined(VERILATOR_VERILATED_SAVE_CPP_)
# error "verilated_imp.h only to be included by verilated*.cpp internals"
#endif
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_syms.h"
#include <algorithm>
#include <deque>
#include <limits>
#include <map>
#include <numeric>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <functional>
#include <queue>
// clang-format on
class VerilatedScope;
//======================================================================
// Threaded message passing
// Message, enqueued on an mtask, and consumed on the main eval thread
class VerilatedMsg final {
public:
// TYPES
struct Cmp final {
bool operator()(const VerilatedMsg& a, const VerilatedMsg& b) const {
return a.mtaskId() < b.mtaskId();
}
};
private:
// MEMBERS
uint32_t m_mtaskId; // MTask that did enqueue
std::function<void()> m_cb; // Lambda to execute when message received
public:
// CONSTRUCTORS
explicit VerilatedMsg(const std::function<void()>& cb)
: m_mtaskId{Verilated::mtaskId()}
, m_cb{cb} {}
~VerilatedMsg() = default;
VerilatedMsg(const VerilatedMsg&) = default;
VerilatedMsg(VerilatedMsg&&) = default;
VerilatedMsg& operator=(const VerilatedMsg&) = default;
VerilatedMsg& operator=(VerilatedMsg&&) = default;
// METHODS
uint32_t mtaskId() const { return m_mtaskId; }
// 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 final {
using VerilatedThreadQueue = std::multiset<VerilatedMsg, VerilatedMsg::Cmp>;
std::atomic<uint64_t> m_depth; // Current depth of queue (see comments below)
mutable VerilatedMutex m_mutex; // Mutex protecting queue
VerilatedThreadQueue m_queue VL_GUARDED_BY(m_mutex); // Message queue
public:
// CONSTRUCTORS
VerilatedEvalMsgQueue()
: m_depth{0} {
assert(atomic_is_lock_free(&m_depth));
}
~VerilatedEvalMsgQueue() = default;
private:
VL_UNCOPYABLE(VerilatedEvalMsgQueue);
public:
// METHODS
// Add message to queue (called by producer)
void post(const VerilatedMsg& msg) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{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_MT_SAFE_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
const 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
{
VL_DEBUG_IF(VL_DBG_MSGF("Executing callback from mtaskId=%d\n", msg.mtaskId()););
msg.run();
}
}
}
};
// Each thread has a local queue to build up messages until the end of the eval() call
class VerilatedThreadMsgQueue final {
std::queue<VerilatedMsg> m_queue;
public:
// CONSTRUCTORS
VerilatedThreadMsgQueue() = default;
~VerilatedThreadMsgQueue() = default;
// The only call of destructor 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.
private:
VL_UNCOPYABLE(VerilatedThreadMsgQueue);
// METHODS
static VerilatedThreadMsgQueue& threadton() VL_MT_SAFE {
static 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 {
// Handle calls to threaded routines outside
// of any mtask -- if an initial block calls $finish, say.
if (Verilated::mtaskId() == 0) {
// No queueing, just do the action immediately
msg.run();
} else {
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();
}
}
};
// FILE* list constructed from a file-descriptor
class VerilatedFpList final {
FILE* m_fp[31] = {};
std::size_t m_sz = 0;
public:
using const_iterator = FILE* const*;
explicit VerilatedFpList() = default;
const_iterator begin() const { return m_fp; }
const_iterator end() const { return m_fp + m_sz; }
std::size_t size() const { return m_sz; }
static std::size_t capacity() { return 31; }
void push_back(FILE* fd) {
if (VL_LIKELY(size() < capacity())) m_fp[m_sz++] = fd;
}
};
//======================================================================
// VerilatedContextImpData
// Class for hidden implementation members inside VerilatedContext
// Avoids needing std::unordered_map inside verilated.h
class VerilatedContextImpData final {
friend class VerilatedContext;
friend class VerilatedContextImp;
protected:
// Map of <scope_name, scope pointer>
// Used by scopeInsert, scopeFind, scopeErase, scopeNameMap
mutable VerilatedMutex m_nameMutex; // Protect m_nameMap
VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex);
};
//======================================================================
// VerilatedContextImp
// Class to "add" implementation-only methods to VerilatedContext
class VerilatedContextImp final : VerilatedContext {
friend class VerilatedContext;
// MEMBERS - non-static not allowed, use only VerilatedContext
// Select initial value of otherwise uninitialized signals.
// Internal note: Globals may multi-construct, see verilated.cpp top.
// Medium speed, so uses singleton accessing
struct Statics final {
VerilatedMutex s_randMutex; // Mutex protecting s_randSeedEpoch
// Number incrementing on each reseed, 0=illegal
int s_randSeedEpoch = 1; // Reads ok, wish had a VL_WRITE_GUARDED_BY(s_randMutex)
};
static Statics& s() VL_MT_SAFE {
static Statics s_s;
return s_s;
}
public: // But only for verilated*.cpp
// CONSTRUCTORS - no data can live here, use only VerilatedContext
VerilatedContextImp() = delete;
~VerilatedContextImp() = delete;
// METHODS - extending into VerilatedContext, call via impp()->
// Random seed handling
uint64_t randSeedDefault64() const VL_MT_SAFE;
static uint32_t randSeedEpoch() VL_MT_SAFE { return s().s_randSeedEpoch; }
// METHODS - timeformat
int timeFormatUnits() const VL_MT_SAFE {
if (m_s.m_timeFormatUnits == VerilatedContext::Serialized::UNITS_NONE)
return timeprecision();
return m_s.m_timeFormatUnits;
}
void timeFormatUnits(int value) VL_MT_SAFE { m_s.m_timeFormatUnits = value; }
int timeFormatPrecision() const VL_MT_SAFE { return m_s.m_timeFormatPrecision; }
void timeFormatPrecision(int value) VL_MT_SAFE { m_s.m_timeFormatPrecision = value; }
int timeFormatWidth() const VL_MT_SAFE { return m_s.m_timeFormatWidth; }
void timeFormatWidth(int value) VL_MT_SAFE { m_s.m_timeFormatWidth = value; }
std::string timeFormatSuffix() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const VerilatedLockGuard lock{m_timeDumpMutex};
return m_timeFormatSuffix;
}
void timeFormatSuffix(const std::string& value) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const VerilatedLockGuard lock{m_timeDumpMutex};
m_timeFormatSuffix = value;
}
// METHODS - arguments
std::string argPlusMatch(const char* prefixp) VL_MT_SAFE_EXCLUDES(m_argMutex);
std::pair<int, char**> argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex);
// METHODS - scope name - INTERNAL only for verilated*.cpp
void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE;
void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE;
// METHODS - file IO - INTERNAL only for verilated*.cpp
IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock{m_fdMutex};
if (m_fdFreeMct.empty()) return 0;
const IData idx = m_fdFreeMct.back();
m_fdFreeMct.pop_back();
m_fdps[idx] = std::fopen(filenamep, "w");
if (VL_UNLIKELY(!m_fdps[idx])) return 0;
return (1 << idx);
}
IData fdNew(const char* filenamep, const char* modep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
FILE* const fp = std::fopen(filenamep, modep);
if (VL_UNLIKELY(!fp)) return 0;
// Bit 31 indicates it's a descriptor not a MCD
const VerilatedLockGuard lock{m_fdMutex};
if (m_fdFree.empty()) {
// Need to create more space in m_fdps and m_fdFree
const std::size_t start = std::max<std::size_t>(31UL + 1UL + 3UL, m_fdps.size());
const std::size_t excess = 10;
m_fdps.resize(start + excess);
std::fill(m_fdps.begin() + start, m_fdps.end(), static_cast<FILE*>(nullptr));
m_fdFree.resize(excess);
for (std::size_t i = 0, id = start; i < m_fdFree.size(); ++i, ++id) {
m_fdFree[i] = id;
}
}
const IData idx = m_fdFree.back();
m_fdFree.pop_back();
m_fdps[idx] = fp;
return (idx | (1UL << 31)); // bit 31 indicates not MCD
}
void fdFlush(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
for (const auto& i : fdlist) std::fflush(i);
}
IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
if (VL_UNLIKELY(fdlist.size() != 1)) return ~0U; // -1
return static_cast<IData>(
std::fseek(*fdlist.begin(), static_cast<long>(offset), static_cast<int>(origin)));
}
IData fdTell(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
if (VL_UNLIKELY(fdlist.size() != 1)) return ~0U; // -1
return static_cast<IData>(std::ftell(*fdlist.begin()));
}
void fdWrite(IData fdi, const std::string& output) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
for (const auto& i : fdlist) {
if (VL_UNLIKELY(!i)) continue;
(void)fwrite(output.c_str(), 1, output.size(), i);
}
}
void fdClose(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock{m_fdMutex};
if (VL_BITISSET_I(fdi, 31)) {
// Non-MCD case
const IData idx = VL_MASK_I(31) & fdi;
if (VL_UNLIKELY(idx >= m_fdps.size())) return;
if (VL_UNLIKELY(idx <= 2)) return; // stdout/stdin/stderr
if (VL_UNLIKELY(!m_fdps[idx])) return; // Already free
std::fclose(m_fdps[idx]);
m_fdps[idx] = nullptr;
m_fdFree.push_back(idx);
} else {
// MCD case
// Starts at 1 to skip stdout
fdi >>= 1;
for (int i = 1; (fdi != 0) && (i < 31); i++, fdi >>= 1) {
if (fdi & VL_MASK_I(1)) {
std::fclose(m_fdps[i]);
m_fdps[i] = nullptr;
m_fdFreeMct.push_back(i);
}
}
}
}
FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr;
return *fdlist.begin();
}
private:
VerilatedFpList fdToFpList(IData fdi) VL_REQUIRES(m_fdMutex) {
VerilatedFpList fp;
// cppverilator-suppress integerOverflow shiftTooManyBitsSigned
if ((fdi & (1 << 31)) != 0) {
// Non-MCD case
const IData idx = fdi & VL_MASK_I(31);
switch (idx) {
case 0: fp.push_back(stdin); break;
case 1: fp.push_back(stdout); break;
case 2: fp.push_back(stderr); break;
default:
if (VL_LIKELY(idx < m_fdps.size())) fp.push_back(m_fdps[idx]);
break;
}
} else {
// MCD Case
if (fdi & 1) fp.push_back(stdout);
fdi >>= 1;
for (size_t i = 1; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) {
if (fdi & VL_MASK_I(1)) fp.push_back(m_fdps[i]);
}
}
return fp;
}
protected:
// METHODS - protected
void commandArgsGuts(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex);
void commandArgsAddGutsLock(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex);
void commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(m_argMutex);
void commandArgVl(const std::string& arg);
bool commandArgVlString(const std::string& arg, const std::string& prefix,
std::string& valuer);
bool commandArgVlUint64(const std::string& arg, const std::string& prefix, uint64_t& valuer,
uint64_t min = std::numeric_limits<uint64_t>::min(),
uint64_t max = std::numeric_limits<uint64_t>::max());
void commandArgDump() const VL_MT_SAFE_EXCLUDES(m_argMutex);
};
//======================================================================
// VerilatedImp
class VerilatedImpData final {
// Whole class is internal use only - Global information shared between verilated*.cpp files.
// All only medium-speed, so use singleton function
protected:
friend class Verilated;
friend class VerilatedImp;
// TYPES
using UserMap = std::map<std::pair<const void*, void*>, void*>;
using ExportNameMap = std::map<const char*, int, VerilatedCStrCmp>;
// MEMBERS
// Nothing below here is save-restored; users expected to re-register appropriately
VerilatedMutex m_userMapMutex; // Protect m_userMap
// For userInsert, userFind. As indexed by pointer is common across contexts.
UserMap m_userMap VL_GUARDED_BY(m_userMapMutex); // Map of <(scope,userkey), userData>
VerilatedMutex m_hierMapMutex; // Protect m_hierMap
// Map that represents scope hierarchy
// Used by hierarchyAdd, hierarchyRemove, hierarchyMap
VerilatedHierarchyMap m_hierMap VL_GUARDED_BY(m_hierMapMutex);
// Slow - somewhat static:
VerilatedMutex m_exportMutex; // Protect m_nameMap
// Map of <export_func_proto, func number>
// Used by exportInsert, exportFind, exportName.
// Export numbers same across all contexts as just a string-to-number conversion
ExportNameMap m_exportMap VL_GUARDED_BY(m_exportMutex);
int m_exportNext VL_GUARDED_BY(m_exportMutex) = 0; // Next export funcnum
// CONSTRUCTORS
VerilatedImpData() = default;
};
class VerilatedImp final {
// Whole class is internal use only - Global information shared between verilated*.cpp files.
protected:
friend class Verilated;
// MEMBERS
static VerilatedImpData& s() VL_MT_SAFE { // Singleton
static VerilatedImpData s_s;
return s_s;
}
public: // But only for verilated*.cpp
// CONSTRUCTORS
VerilatedImp() = default;
~VerilatedImp() = default;
private:
VL_UNCOPYABLE(VerilatedImp);
public:
// METHODS - debug
static void versionDump() VL_MT_SAFE;
// METHODS - user scope tracking
// We implement this as a single large map instead of one map per scope.
// There's often many more scopes than userdata's and thus having a ~48byte
// per map overhead * N scopes would take much more space and cache thrashing.
// As scopep's are pointers, this implicitly handles multiple Context's
static void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
const VerilatedLockGuard lock{s().m_userMapMutex};
const auto it = s().m_userMap.find(std::make_pair(scopep, userKey));
if (it != s().m_userMap.end()) {
it->second = userData;
} else {
s().m_userMap.emplace(std::make_pair(scopep, userKey), userData);
}
}
static void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
const VerilatedLockGuard lock{s().m_userMapMutex};
const auto& it = vlstd::as_const(s().m_userMap).find(std::make_pair(scopep, userKey));
if (VL_UNLIKELY(it == s().m_userMap.end())) return nullptr;
return it->second;
}
// METHODS - But only for verilated.cpp
// Symbol table destruction cleans up the entries for each scope.
static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope on destruction, so we only iterate.
const VerilatedLockGuard lock{s().m_userMapMutex};
for (auto it = s().m_userMap.begin(); it != s().m_userMap.end();) {
if (it->first.first == scopep) {
s().m_userMap.erase(it++);
} else {
++it;
}
}
}
static void userDump() VL_MT_SAFE {
const VerilatedLockGuard lock{s().m_userMapMutex}; // Avoid it changing in middle of dump
bool first = true;
for (const auto& i : s().m_userMap) {
if (first) {
VL_PRINTF_MT(" userDump:\n");
first = false;
}
VL_PRINTF_MT(" DPI_USER_DATA scope %p key %p: %p\n", i.first.first, i.first.second,
i.second);
}
}
// METHODS - hierarchy - only for verilated*.cpp
static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE {
// Slow ok - called at construction for VPI accessible elements
const VerilatedLockGuard lock{s().m_hierMapMutex};
s().m_hierMap[fromp].push_back(top);
}
static void hierarchyRemove(const VerilatedScope* fromp,
const VerilatedScope* top) VL_MT_SAFE {
// Slow ok - called at destruction for VPI accessible elements
const VerilatedLockGuard lock{s().m_hierMapMutex};
VerilatedHierarchyMap& map = s().m_hierMap;
if (map.find(fromp) == map.end()) return;
auto& scopes = map[fromp];
const auto it = find(scopes.begin(), scopes.end(), top);
if (it != scopes.end()) scopes.erase(it);
}
static const VerilatedHierarchyMap* hierarchyMap() VL_MT_SAFE_POSTINIT {
// Thread save only assuming this is called only after model construction completed
return &s().m_hierMap;
}
// METHODS - export names - only for verilated*.cpp
// Each function prototype is converted to a function number which we
// then use to index a 2D table also indexed by scope number, because we
// can't know at Verilation time what scopes will exist in other modules
// 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) VL_MT_SAFE {
// Slow ok - called once/function at creation
const VerilatedLockGuard lock{s().m_exportMutex};
const auto it = s().m_exportMap.find(namep);
if (it == s().m_exportMap.end()) {
s().m_exportMap.emplace(namep, s().m_exportNext++);
return s().m_exportNext++;
} else {
return it->second;
}
}
static int exportFind(const char* namep) VL_MT_SAFE {
const VerilatedLockGuard lock{s().m_exportMutex};
const auto& it = s().m_exportMap.find(namep);
if (VL_LIKELY(it != s().m_exportMap.end())) return it->second;
const std::string msg = (std::string{"%Error: Testbench C called "} + namep
+ " but no such DPI export function name exists in ANY model");
VL_FATAL_MT("unknown", 0, "", msg.c_str());
return -1;
}
static const char* exportName(int funcnum) VL_MT_SAFE {
// Slowpath; find name for given export; errors only so no map to reverse-map it
const VerilatedLockGuard lock{s().m_exportMutex};
for (const auto& i : s().m_exportMap) {
if (i.second == funcnum) return i.first;
}
return "*UNKNOWN*";
}
static void exportsDump() VL_MT_SAFE {
const VerilatedLockGuard lock{s().m_exportMutex};
bool first = true;
for (const auto& i : s().m_exportMap) {
if (first) {
VL_PRINTF_MT(" exportDump:\n");
first = false;
}
VL_PRINTF_MT(" DPI_EXPORT_NAME %05d: %s\n", i.second, i.first);
}
}
// We don't free up m_exportMap until the end, because we can't be sure
// what other models are using the assigned funcnum's.
};
//======================================================================
#endif // Guard