Add simulation context (VerilatedContext) (#2660). (#2813)

**   Add simulation context (VerilatedContext) to allow multiple fully independent
      models to be in the same process.  Please see the updated examples.
**   Add context->time() and context->timeInc() API calls, to set simulation time.
      These now are recommended in place of the legacy sc_time_stamp().
This commit is contained in:
Wilson Snyder 2021-03-07 11:01:54 -05:00 committed by GitHub
parent caa9c99837
commit 2cad22a22a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 2216 additions and 1128 deletions

View File

@ -3,6 +3,13 @@ Revision history for Verilator
The contributors that suggested a given feature are shown in []. Thanks! The contributors that suggested a given feature are shown in []. Thanks!
* Verilator 4.111 devel * Verilator 4.111 devel
(Next release version number will be 4.200)
** Add simulation context (VerilatedContext) to allow multiple fully independent
models to be in the same process. Please see the updated examples.
** Add context->time() and context->timeInc() API calls, to set simulation time.
These now are recommended in place of the legacy sc_time_stamp().
**** --inhibit-sim is planned for deprecation, file a bug if this is still being used. **** --inhibit-sim is planned for deprecation, file a bug if this is still being used.

View File

@ -1953,7 +1953,8 @@ Displays program version and exits.
=head1 EXAMPLE C++ EXECUTION =head1 EXAMPLE C++ EXECUTION
We'll compile this example into C++. We'll compile this example into C++. For an extended and commented version
of what this C++ code is doing, see examples/make_tracing_c/sim_main.cpp.
mkdir test_our mkdir test_our
cd test_our cd test_our
@ -1968,21 +1969,24 @@ We'll compile this example into C++.
#include "Vour.h" #include "Vour.h"
#include "verilated.h" #include "verilated.h"
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
Verilated::commandArgs(argc, argv); VerilatedContext* contextp = new VerilatedContext;
Vour* top = new Vour; contextp->commandArgs(argc, argv);
while (!Verilated::gotFinish()) { top->eval(); } Vour* top = new Vour{contextp};
while (!contextp->gotFinish()) { top->eval(); }
delete top; delete top;
delete contextp;
return 0; return 0;
} }
EOF EOF
See the README in the source kit for various ways to install or point to Next we need Verilator installed. See the README in the source kit for
Verilator binaries. In brief, if you installed Verilator using the package various ways to install or point to Verilator binaries. In brief, if you
manager of your operating system, or did a "make install" to place installed Verilator using the package manager of your operating system, or
Verilator into your default path, you do not need anything special in your did a "make install" to place Verilator into your default path, you do not
environment, and should not have VERILATOR_ROOT set. However, if you need anything special in your environment, and should not have
installed Verilator from sources and want to run Verilator out of where you VERILATOR_ROOT set. However, if you installed Verilator from sources and
compiled Verilator, you need to point to the kit: want to run Verilator out of where you compiled Verilator, you need to
point to the kit:
# See above; don't do this if using an OS-distributed Verilator # See above; don't do this if using an OS-distributed Verilator
export VERILATOR_ROOT=/path/to/where/verilator/was/installed export VERILATOR_ROOT=/path/to/where/verilator/was/installed
@ -2100,15 +2104,15 @@ When using SystemC, evaluation of the Verilated model is managed by the SystemC
kernel, and for the most part can be ignored. When using C++, the user kernel, and for the most part can be ignored. When using C++, the user
must call eval(), or eval_step() and eval_end_step(). must call eval(), or eval_step() and eval_end_step().
1. When there is a single design instantiated at the C++ level that needs to 1. When there is a single design instantiated at the C++ level that needs
evaluate, just call designp->eval(). to evaluate within a given context, just call designp->eval().
2. When there are multiple designs instantiated at the C++ level that 2. When there are multiple designs instantiated at the C++ level that need
need to evaluate, call first_designp->eval_step() then ->eval_step() on all to evaluate within a context, call first_designp->eval_step() then
other designs. Then call ->eval_end_step() on the first design then all ->eval_step() on all other designs. Then call ->eval_end_step() on the
other designs. If there is only a single design, you would call first design then all other designs. If there is only a single design, you
eval_step() then eval_end_step(); in fact eval() described above is just a would call eval_step() then eval_end_step(); in fact eval() described above
wrapper which calls these two functions. is just a wrapper which calls these two functions.
When eval() is called Verilator looks for changes in clock signals and When eval() is called Verilator looks for changes in clock signals and
evaluates related sequential always blocks, such as computing always_ff @ evaluates related sequential always blocks, such as computing always_ff @
@ -5417,8 +5421,14 @@ controlled by --assert are disabled.
=item Why do I get "undefined reference to `sc_time_stamp()'"? =item Why do I get "undefined reference to `sc_time_stamp()'"?
In C++ (non SystemC) code you need to define this function so that the In 4.112 and later, using the timeInc function is recommended instead. See
simulator knows the current time. See the "CONNECTING TO C++" examples. the "CONNECTING TO C++" examples. Some linkers (MSVC++) still require
sc_time_stamp() to be defined, either define this with ("double
sc_time_stamp() { return 0; }") or compile the Verilated code with
-DVL_TIME_CONTEXT.
Prior to Verilator 4.112, the sc_time_stamp function needs to be defined in
C++ (non SystemC) to return the current simulation time.
=item Why do I get "undefined reference to `VL_RAND_RESET_I' or `Verilated::...'"? =item Why do I get "undefined reference to `VL_RAND_RESET_I' or `Verilated::...'"?

View File

@ -16,25 +16,24 @@
#include <verilated_vcd_c.h> #include <verilated_vcd_c.h>
#endif #endif
vluint64_t main_time = 0;
double sc_time_stamp() { return main_time; }
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
if (false && argc && argv && env) {} if (false && argc && argv && env) {}
Verilated::debug(0); // Construct context to hold simulation time, etc
Verilated::randReset(2); VerilatedContext* contextp = new VerilatedContext;
Verilated::commandArgs(argc, argv); contextp->debug(0);
contextp->randReset(2);
contextp->commandArgs(argc, argv);
// Construct the Verilated model, including the secret module // Construct the Verilated model, including the secret module
Vtop* top = new Vtop; Vtop* top = new Vtop{contextp};
#if VM_TRACE #if VM_TRACE
// When tracing, the contents of the secret module will not be seen // When tracing, the contents of the secret module will not be seen
VerilatedVcdC* tfp = nullptr; VerilatedVcdC* tfp = nullptr;
const char* flag = Verilated::commandArgsPlusMatch("trace"); const char* flag = contextp->commandArgsPlusMatch("trace");
if (flag && 0 == strcmp(flag, "+trace")) { if (flag && 0 == strcmp(flag, "+trace")) {
Verilated::traceEverOn(true); contextp->traceEverOn(true);
VL_PRINTF("Enabling waves into logs/vlt_dump.vcd...\n"); VL_PRINTF("Enabling waves into logs/vlt_dump.vcd...\n");
tfp = new VerilatedVcdC; tfp = new VerilatedVcdC;
top->trace(tfp, 99); top->trace(tfp, 99);
@ -46,12 +45,12 @@ int main(int argc, char** argv, char** env) {
top->clk = 0; top->clk = 0;
// Simulate until $finish // Simulate until $finish
while (!Verilated::gotFinish()) { while (!contextp->gotFinish()) {
main_time++; contextp->timeInc(1);
top->clk = ~top->clk & 0x1; top->clk = ~top->clk & 0x1;
top->eval(); top->eval();
#if VM_TRACE #if VM_TRACE
if (tfp) tfp->dump(main_time); if (tfp) tfp->dump(contextp->time());
#endif #endif
} }
@ -69,6 +68,8 @@ int main(int argc, char** argv, char** env) {
// Destroy model // Destroy model
delete top; delete top;
top = nullptr; top = nullptr;
delete contextp;
contextp = nullptr;
// Return good completion status // Return good completion status
// Don't use exit() or destructor won't get called // Don't use exit() or destructor won't get called

View File

@ -14,13 +14,6 @@
// Include model header, generated from Verilating "top.v" // Include model header, generated from Verilating "top.v"
#include "Vtop.h" #include "Vtop.h"
// Current simulation time (64-bit unsigned)
vluint64_t main_time = 0;
// Called by $time in Verilog
double sc_time_stamp() {
return main_time; // Note does conversion to real, to match SystemC
}
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
// This is a more complicated example, please also see the simpler examples/make_hello_c. // This is a more complicated example, please also see the simpler examples/make_hello_c.
@ -30,25 +23,34 @@ int main(int argc, char** argv, char** env) {
// Create logs/ directory in case we have traces to put under it // Create logs/ directory in case we have traces to put under it
Verilated::mkdir("logs"); Verilated::mkdir("logs");
// Construct a VerilatedContext to hold simulation time, etc.
// Multiple modules (made later below with Vtop) may share the same
// context to share time, or modules may have different contexts if
// they should be independent from each other.
// Using unique_ptr is similar to
// "VerilatedContext* contextp = new VerilatedContext" then deleting at end.
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
// Set debug level, 0 is off, 9 is highest presently used // Set debug level, 0 is off, 9 is highest presently used
// May be overridden by commandArgs argument parsing // May be overridden by commandArgs argument parsing
Verilated::debug(0); contextp->debug(0);
// Randomization reset policy // Randomization reset policy
// May be overridden by commandArgs argument parsing // May be overridden by commandArgs argument parsing
Verilated::randReset(2); contextp->randReset(2);
// Verilator must compute traced signals // Verilator must compute traced signals
Verilated::traceEverOn(true); contextp->traceEverOn(true);
// Pass arguments so Verilated code can see them, e.g. $value$plusargs // Pass arguments so Verilated code can see them, e.g. $value$plusargs
// This needs to be called before you create any model // This needs to be called before you create any model
Verilated::commandArgs(argc, argv); contextp->commandArgs(argc, argv);
// Construct the Verilated model, from Vtop.h generated from Verilating "top.v". // Construct the Verilated model, from Vtop.h generated from Verilating "top.v".
// Using unique_ptr is similar to "Vtop* top = new Vtop" then deleting at end. // Using unique_ptr is similar to "Vtop* top = new Vtop" then deleting at end.
// "TOP" will be the hierarchical name of the module. // "TOP" will be the hierarchical name of the module.
const std::unique_ptr<Vtop> top{new Vtop}; const std::unique_ptr<Vtop> top{new Vtop{contextp.get(), "TOP"}};
// Set Vtop's input signals // Set Vtop's input signals
top->reset_l = !0; top->reset_l = !0;
@ -60,8 +62,19 @@ int main(int argc, char** argv, char** env) {
top->in_wide[2] = 0x3; top->in_wide[2] = 0x3;
// Simulate until $finish // Simulate until $finish
while (!Verilated::gotFinish()) { while (!contextp->gotFinish()) {
main_time++; // Time passes... // Historical note, older versions of Verilator used
// Verilated::gotFinish() below in place of contextp->gotFinish().
// Most of the contextp-> calls can use Verilated:: calls instead;
// the Verilated:: versions simply assume there's a single context
// being used (per thread). It's faster and clearer to use the
// newer contextp-> versions.
contextp->timeInc(1); // 1 timeprecision period passes...
// Historical note, older versions of Verilator required a
// sc_time_stamp() function instead of using timeInc. Once
// timeInc() is called (with non-zero), the Verilated libraries
// assume the new API, and sc_time_stamp() will no longer work.
// Toggle a fast (time/2 period) clock // Toggle a fast (time/2 period) clock
top->clk = !top->clk; top->clk = !top->clk;
@ -71,7 +84,7 @@ int main(int argc, char** argv, char** env) {
// this only on a negedge of clk, because we know // this only on a negedge of clk, because we know
// reset is not sampled there. // reset is not sampled there.
if (!top->clk) { if (!top->clk) {
if (main_time > 1 && main_time < 10) { if (contextp->time() > 1 && contextp->time() < 10) {
top->reset_l = !1; // Assert reset top->reset_l = !1; // Assert reset
} else { } else {
top->reset_l = !0; // Deassert reset top->reset_l = !0; // Deassert reset
@ -89,8 +102,8 @@ int main(int argc, char** argv, char** env) {
// Read outputs // Read outputs
VL_PRINTF("[%" VL_PRI64 "d] clk=%x rstl=%x iquad=%" VL_PRI64 "x" VL_PRINTF("[%" VL_PRI64 "d] clk=%x rstl=%x iquad=%" VL_PRI64 "x"
" -> oquad=%" VL_PRI64 "x owide=%x_%08x_%08x\n", " -> oquad=%" VL_PRI64 "x owide=%x_%08x_%08x\n",
main_time, top->clk, top->reset_l, top->in_quad, top->out_quad, top->out_wide[2], contextp->time(), top->clk, top->reset_l, top->in_quad, top->out_quad,
top->out_wide[1], top->out_wide[0]); top->out_wide[2], top->out_wide[1], top->out_wide[0]);
} }
// Final model cleanup // Final model cleanup
@ -99,7 +112,7 @@ int main(int argc, char** argv, char** env) {
// Coverage analysis (calling write only after the test is known to pass) // Coverage analysis (calling write only after the test is known to pass)
#if VM_COVERAGE #if VM_COVERAGE
Verilated::mkdir("logs"); Verilated::mkdir("logs");
VerilatedCov::write("logs/coverage.dat"); contextp->coveragep()->write("logs/coverage.dat");
#endif #endif
// Return good completion status // Return good completion status

View File

@ -78,41 +78,16 @@ static_assert(sizeof(vluint64_t) == 8, "vluint8_t is missized");
//=========================================================================== //===========================================================================
// Global variables // Global variables
// Internal note: Globals may multi-construct, see verilated.cpp top.
// Slow path variables // Fast path, keep together
VerilatedMutex Verilated::s_mutex; int Verilated::s_debug = 0;
VerilatedContext* Verilated::s_lastContextp = nullptr;
// Keep below together in one cache line // Keep below together in one cache line
Verilated::Serialized Verilated::s_s; // Internal note: Globals may multi-construct, see verilated.cpp top.
Verilated::NonSerialized Verilated::s_ns;
VL_THREAD_LOCAL Verilated::ThreadLocal Verilated::t_s; VL_THREAD_LOCAL Verilated::ThreadLocal Verilated::t_s;
Verilated::CommandArgValues Verilated::s_args;
VerilatedImp::VerilatedImpU VerilatedImp::s_s;
// Guarantees to call setup() and teardown() just once.
struct VerilatedInitializer {
VerilatedInitializer() { setup(); }
~VerilatedInitializer() { teardown(); }
void setup() {
static bool done = false;
if (!done) {
VerilatedImp::setup();
Verilated::s_ns.setup();
done = true;
}
}
void teardown() {
static bool done = false;
if (!done) {
VerilatedImp::teardown();
Verilated::s_ns.teardown();
done = true;
}
}
} s_VerilatedInitializer;
//=========================================================================== //===========================================================================
// User definable functions // User definable functions
// Note a TODO is a future version of the API will pass a structure so that // Note a TODO is a future version of the API will pass a structure so that
@ -123,29 +98,41 @@ void vl_finish(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE
if (false && hier) {} if (false && hier) {}
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
"- %s:%d: Verilog $finish\n", filename, linenum); "- %s:%d: Verilog $finish\n", filename, linenum);
if (Verilated::gotFinish()) { if (Verilated::threadContextp()->gotFinish()) {
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
"- %s:%d: Second verilog $finish, exiting\n", filename, linenum); "- %s:%d: Second verilog $finish, exiting\n", filename, linenum);
Verilated::runFlushCallbacks(); Verilated::runFlushCallbacks();
Verilated::runExitCallbacks(); Verilated::runExitCallbacks();
exit(0); exit(0);
} }
Verilated::gotFinish(true); Verilated::threadContextp()->gotFinish(true);
} }
#endif #endif
#ifndef VL_USER_STOP ///< Define this to override this function #ifndef VL_USER_STOP ///< Define this to override this function
void vl_stop(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE { void vl_stop(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE {
Verilated::gotFinish(true); const char* const msg = "Verilog $stop";
Verilated::runFlushCallbacks(); Verilated::threadContextp()->gotError(true);
vl_fatal(filename, linenum, hier, "Verilog $stop"); Verilated::threadContextp()->gotFinish(true);
if (Verilated::threadContextp()->fatalOnError()) {
vl_fatal(filename, linenum, hier, msg);
} else {
if (filename && filename[0]) {
// Not VL_PRINTF_MT, already on main thread
VL_PRINTF("%%Error: %s:%d: %s\n", filename, linenum, msg);
} else {
VL_PRINTF("%%Error: %s\n", msg);
}
Verilated::runFlushCallbacks();
}
} }
#endif #endif
#ifndef VL_USER_FATAL ///< Define this to override this function #ifndef VL_USER_FATAL ///< Define this to override this function
void vl_fatal(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_UNSAFE { void vl_fatal(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_UNSAFE {
if (false && hier) {} if (false && hier) {}
Verilated::gotFinish(true); Verilated::threadContextp()->gotError(true);
Verilated::threadContextp()->gotFinish(true);
if (filename && filename[0]) { if (filename && filename[0]) {
// Not VL_PRINTF_MT, already on main thread // Not VL_PRINTF_MT, already on main thread
VL_PRINTF("%%Error: %s:%d: %s\n", filename, linenum, msg); VL_PRINTF("%%Error: %s:%d: %s\n", filename, linenum, msg);
@ -167,8 +154,9 @@ void vl_fatal(const char* filename, int linenum, const char* hier, const char* m
#ifndef VL_USER_STOP_MAYBE ///< Define this to override this function #ifndef VL_USER_STOP_MAYBE ///< Define this to override this function
void vl_stop_maybe(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_UNSAFE { void vl_stop_maybe(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_UNSAFE {
Verilated::errorCountInc(); Verilated::threadContextp()->errorCountInc();
if (maybe && Verilated::errorCount() < Verilated::errorLimit()) { if (maybe
&& Verilated::threadContextp()->errorCount() < Verilated::threadContextp()->errorLimit()) {
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
"-Info: %s:%d: %s\n", filename, linenum, "-Info: %s:%d: %s\n", filename, linenum,
"Verilog $stop, ignored due to +verilator+error+limit"); "Verilog $stop, ignored due to +verilator+error+limit");
@ -279,34 +267,6 @@ void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE {
} }
#endif #endif
//===========================================================================
// Overall class init
Verilated::Serialized::Serialized() {
s_calcUnusedSigs = false;
s_gotFinish = false;
s_assertOn = true;
s_fatalOnVpiError = true; // retains old default behaviour
s_errorCount = 0;
s_errorLimit = 1;
s_randReset = 0;
s_randSeed = 0;
s_randSeedEpoch = 1;
s_timeunit = VL_TIME_UNIT; // Initial value until overriden by _Vconfigure
s_timeprecision = VL_TIME_PRECISION; // Initial value until overriden by _Vconfigure
}
void Verilated::NonSerialized::setup() { s_profThreadsFilenamep = strdup("profile_threads.dat"); }
void Verilated::NonSerialized::teardown() {
if (s_profThreadsFilenamep) {
VL_DO_CLEAR(free(const_cast<char*>(s_profThreadsFilenamep)),
s_profThreadsFilenamep = nullptr);
}
}
size_t Verilated::serialized2Size() VL_PURE { return sizeof(VerilatedImp::s_s.v.m_ser); }
void* Verilated::serialized2Ptr() VL_MT_UNSAFE { return &VerilatedImp::s_s.v.m_ser; }
//=========================================================================== //===========================================================================
// Random -- Mostly called at init time, so not inline. // Random -- Mostly called at init time, so not inline.
@ -324,13 +284,14 @@ static vluint32_t vl_sys_rand32() VL_MT_UNSAFE {
} }
vluint64_t vl_rand64() VL_MT_SAFE { vluint64_t vl_rand64() VL_MT_SAFE {
static VL_THREAD_LOCAL vluint32_t t_seedEpoch = 0;
static VL_THREAD_LOCAL vluint64_t t_state[2]; static VL_THREAD_LOCAL vluint64_t t_state[2];
static VL_THREAD_LOCAL vluint32_t t_seedEpoch = 0;
// For speed, we use a thread-local epoch number to know when to reseed // For speed, we use a thread-local epoch number to know when to reseed
if (VL_UNLIKELY(t_seedEpoch != Verilated::randSeedEpoch())) { // A thread always belongs to a single context, so this works out ok
// Set epoch before state, in case races with new seeding if (VL_UNLIKELY(t_seedEpoch != VerilatedContextImp::randSeedEpoch())) {
t_seedEpoch = Verilated::randSeedEpoch(); // Set epoch before state, to avoid race case with new seeding
t_state[0] = Verilated::randSeedDefault64(); t_seedEpoch = VerilatedContextImp::randSeedEpoch();
t_state[0] = Verilated::threadContextp()->impp()->randSeedDefault64();
t_state[1] = t_state[0]; t_state[1] = t_state[0];
// Fix state as algorithm is slow to randomize if many zeros // Fix state as algorithm is slow to randomize if many zeros
// This causes a loss of ~ 1 bit of seed entropy, no big deal // This causes a loss of ~ 1 bit of seed entropy, no big deal
@ -357,23 +318,23 @@ WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE {
#endif #endif
IData VL_RANDOM_SEEDED_II(int obits, IData seed) VL_MT_SAFE { IData VL_RANDOM_SEEDED_II(int obits, IData seed) VL_MT_SAFE {
Verilated::randSeed(static_cast<int>(seed)); Verilated::threadContextp()->randSeed(static_cast<int>(seed));
return VL_RANDOM_I(obits); return VL_RANDOM_I(obits);
} }
IData VL_RAND_RESET_I(int obits) VL_MT_SAFE { IData VL_RAND_RESET_I(int obits) VL_MT_SAFE {
if (Verilated::randReset() == 0) return 0; if (Verilated::threadContextp()->randReset() == 0) return 0;
IData data = ~0; IData data = ~0;
if (Verilated::randReset() != 1) { // if 2, randomize if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
data = VL_RANDOM_I(obits); data = VL_RANDOM_I(obits);
} }
data &= VL_MASK_I(obits); data &= VL_MASK_I(obits);
return data; return data;
} }
QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE { QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE {
if (Verilated::randReset() == 0) return 0; if (Verilated::threadContextp()->randReset() == 0) return 0;
QData data = ~0ULL; QData data = ~0ULL;
if (Verilated::randReset() != 1) { // if 2, randomize if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
data = VL_RANDOM_Q(obits); data = VL_RANDOM_Q(obits);
} }
data &= VL_MASK_Q(obits); data &= VL_MASK_Q(obits);
@ -676,10 +637,10 @@ std::string VL_DECIMAL_NW(int width, WDataInP lwp) VL_MT_SAFE {
std::string _vl_vsformat_time(char* tmp, double ld, bool left, size_t width) { std::string _vl_vsformat_time(char* tmp, double ld, bool left, size_t width) {
// Double may lose precision, but sc_time_stamp has similar limit // Double may lose precision, but sc_time_stamp has similar limit
std::string suffix = VerilatedImp::timeFormatSuffix(); std::string suffix = Verilated::threadContextp()->impp()->timeFormatSuffix();
int userUnits = VerilatedImp::timeFormatUnits(); // 0..-15 int userUnits = Verilated::threadContextp()->impp()->timeFormatUnits(); // 0..-15
int fracDigits = VerilatedImp::timeFormatPrecision(); // 0..N int fracDigits = Verilated::threadContextp()->impp()->timeFormatPrecision(); // 0..N
int prec = Verilated::timeprecision(); // 0..-15 int prec = Verilated::threadContextp()->timeprecision(); // 0..-15
int shift = prec - userUnits + fracDigits; // 0..-15 int shift = prec - userUnits + fracDigits; // 0..-15
double shiftd = vl_time_multiplier(shift); double shiftd = vl_time_multiplier(shift);
double scaled = ld * shiftd; double scaled = ld * shiftd;
@ -788,7 +749,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
double d = va_arg(ap, double); double d = va_arg(ap, double);
if (lbits) {} // UNUSED - always 64 if (lbits) {} // UNUSED - always 64
if (fmt == '^') { // Realtime if (fmt == '^') { // Realtime
if (!widthSet) width = VerilatedImp::timeFormatWidth(); if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth();
output += _vl_vsformat_time(t_tmp, d, left, width); output += _vl_vsformat_time(t_tmp, d, left, width);
} else { } else {
std::string fmts(pctp, pos - pctp + 1); std::string fmts(pctp, pos - pctp + 1);
@ -887,7 +848,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
break; break;
} }
case 't': { // Time case 't': { // Time
if (!widthSet) width = VerilatedImp::timeFormatWidth(); if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth();
output += _vl_vsformat_time(t_tmp, static_cast<double>(ld), left, width); output += _vl_vsformat_time(t_tmp, static_cast<double>(ld), left, width);
break; break;
} }
@ -1261,7 +1222,7 @@ done:
FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE { FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE {
// Expected non-MCD case; returns null on MCD descriptors. // Expected non-MCD case; returns null on MCD descriptors.
return VerilatedImp::fdToFp(lhs); return Verilated::threadContextp()->impp()->fdToFp(lhs);
} }
void _vl_vint_to_string(int obits, char* destoutp, WDataInP sourcep) VL_MT_SAFE { void _vl_vint_to_string(int obits, char* destoutp, WDataInP sourcep) VL_MT_SAFE {
@ -1339,20 +1300,20 @@ IData VL_FERROR_IN(IData, std::string& outputr) VL_MT_SAFE {
} }
IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) { IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) {
return VerilatedImp::fdNew(filename.c_str(), mode.c_str()); return Verilated::threadContextp()->impp()->fdNew(filename.c_str(), mode.c_str());
} }
IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE { IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE {
return VerilatedImp::fdNewMcd(filename.c_str()); return Verilated::threadContextp()->impp()->fdNewMcd(filename.c_str());
} }
void VL_FFLUSH_I(IData fdi) VL_MT_SAFE { VerilatedImp::fdFlush(fdi); } void VL_FFLUSH_I(IData fdi) VL_MT_SAFE { Verilated::threadContextp()->impp()->fdFlush(fdi); }
IData VL_FSEEK_I(IData fdi, IData offset, IData origin) VL_MT_SAFE { IData VL_FSEEK_I(IData fdi, IData offset, IData origin) VL_MT_SAFE {
return VerilatedImp::fdSeek(fdi, offset, origin); return Verilated::threadContextp()->impp()->fdSeek(fdi, offset, origin);
} }
IData VL_FTELL_I(IData fdi) VL_MT_SAFE { return VerilatedImp::fdTell(fdi); } IData VL_FTELL_I(IData fdi) VL_MT_SAFE { return Verilated::threadContextp()->impp()->fdTell(fdi); }
void VL_FCLOSE_I(IData fdi) VL_MT_SAFE { void VL_FCLOSE_I(IData fdi) VL_MT_SAFE {
// While threadsafe, each thread can only access different file handles // While threadsafe, each thread can only access different file handles
VerilatedImp::fdClose(fdi); Verilated::threadContextp()->impp()->fdClose(fdi);
} }
void VL_SFORMAT_X(int obits, CData& destr, const char* formatp, ...) VL_MT_SAFE { void VL_SFORMAT_X(int obits, CData& destr, const char* formatp, ...) VL_MT_SAFE {
@ -1451,7 +1412,7 @@ void VL_FWRITEF(IData fpi, const char* formatp, ...) VL_MT_SAFE {
_vl_vsformat(t_output, formatp, ap); _vl_vsformat(t_output, formatp, ap);
va_end(ap); va_end(ap);
VerilatedImp::fdWrite(fpi, t_output); Verilated::threadContextp()->impp()->fdWrite(fpi, t_output);
} }
IData VL_FSCANF_IX(IData fpi, const char* formatp, ...) VL_MT_SAFE { IData VL_FSCANF_IX(IData fpi, const char* formatp, ...) VL_MT_SAFE {
@ -1566,7 +1527,7 @@ IData VL_SYSTEM_IW(int lhswords, WDataInP lhsp) VL_MT_SAFE {
} }
IData VL_TESTPLUSARGS_I(const char* formatp) VL_MT_SAFE { IData VL_TESTPLUSARGS_I(const char* formatp) VL_MT_SAFE {
const std::string& match = VerilatedImp::argPlusMatch(formatp); const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(formatp);
return match.empty() ? 0 : 1; return match.empty() ? 0 : 1;
} }
@ -1594,7 +1555,7 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M
} }
} }
const std::string& match = VerilatedImp::argPlusMatch(prefix.c_str()); const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str());
const char* dp = match.c_str() + 1 /*leading + */ + prefix.length(); const char* dp = match.c_str() + 1 /*leading + */ + prefix.length();
if (match.empty()) return 0; if (match.empty()) return 0;
@ -1663,7 +1624,7 @@ IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_S
} }
} }
} }
const std::string& match = VerilatedImp::argPlusMatch(prefix.c_str()); const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str());
const char* dp = match.c_str() + 1 /*leading + */ + prefix.length(); const char* dp = match.c_str() + 1 /*leading + */ + prefix.length();
if (match.empty()) return 0; if (match.empty()) return 0;
rdr = std::string(dp); rdr = std::string(dp);
@ -1671,7 +1632,7 @@ IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_S
} }
const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE { const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE {
const std::string& match = VerilatedImp::argPlusMatch(prefixp); const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefixp);
static VL_THREAD_LOCAL char t_outstr[VL_VALUE_STRING_MAX_WIDTH]; static VL_THREAD_LOCAL char t_outstr[VL_VALUE_STRING_MAX_WIDTH];
if (match.empty()) return nullptr; if (match.empty()) return nullptr;
char* dp = t_outstr; char* dp = t_outstr;
@ -1760,25 +1721,6 @@ IData VL_ATOI_N(const std::string& str, int base) VL_PURE {
return static_cast<IData>(v); return static_cast<IData>(v);
} }
//===========================================================================
// Dumping
const char* vl_dumpctl_filenamep(bool setit, const std::string& filename) VL_MT_SAFE {
// This function performs both accessing and setting so it's easy to make an in-function static
static VL_THREAD_LOCAL std::string t_filename;
if (setit) {
t_filename = filename;
} else {
static VL_THREAD_LOCAL bool t_warned = false;
if (VL_UNLIKELY(t_filename.empty() && !t_warned)) {
t_warned = true;
VL_PRINTF_MT("%%Warning: $dumpvar ignored as not proceeded by $dumpfile\n");
return "";
}
}
return t_filename.c_str();
}
//=========================================================================== //===========================================================================
// Readmem/writemem // Readmem/writemem
@ -2210,99 +2152,119 @@ double vl_time_multiplier(int scale) VL_PURE {
return pow10[scale]; return pow10[scale];
} }
} }
const char* Verilated::timeunitString() VL_MT_SAFE { return vl_time_str(timeunit()); }
const char* Verilated::timeprecisionString() VL_MT_SAFE { return vl_time_str(timeprecision()); }
void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp) VL_MT_SAFE { void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
const VerilatedContext* contextp) VL_MT_SAFE {
VL_PRINTF_MT("Time scale of %s is %s / %s\n", namep, timeunitp, VL_PRINTF_MT("Time scale of %s is %s / %s\n", namep, timeunitp,
Verilated::timeprecisionString()); contextp->timeprecisionString());
} }
void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, int width,
int width) VL_MT_SAFE { VerilatedContext* contextp) VL_MT_SAFE {
VerilatedImp::timeFormatUnits(units); contextp->impp()->timeFormatUnits(units);
VerilatedImp::timeFormatPrecision(precision); contextp->impp()->timeFormatPrecision(precision);
VerilatedImp::timeFormatSuffix(suffix); contextp->impp()->timeFormatSuffix(suffix);
VerilatedImp::timeFormatWidth(width); contextp->impp()->timeFormatWidth(width);
} }
//=========================================================================== //======================================================================
// Verilated:: Methods // VerilatedContext:: Methods
void Verilated::debug(int level) VL_MT_SAFE { VerilatedContext::VerilatedContext()
const VerilatedLockGuard lock(s_mutex); : m_impdatap{new VerilatedContextImpData} {
s_ns.s_debug = level; Verilated::lastContextp(this);
if (level) { Verilated::threadContextp(this);
#ifdef VL_DEBUG m_ns.m_profThreadsFilename = "profile_threads.dat";
VL_DEBUG_IF(VL_DBG_MSGF("- Verilated::debug is on." m_fdps.resize(31);
" Message prefix indicates {<thread>,<sequence_number>}.\n");); std::fill(m_fdps.begin(), m_fdps.end(), (FILE*)0);
#else m_fdFreeMct.resize(30);
VL_PRINTF_MT("- Verilated::debug attempted," for (std::size_t i = 0, id = 1; i < m_fdFreeMct.size(); ++i, ++id) m_fdFreeMct[i] = id;
" but compiled without VL_DEBUG, so messages suppressed.\n" }
"- Suggest remake using 'make ... CPPFLAGS=-DVL_DEBUG'\n");
#endif // Must declare here not in interface, as otherwise forward declarations not known
VerilatedContext::~VerilatedContext() {}
VerilatedContext::Serialized::Serialized() {
m_timeunit = VL_TIME_UNIT; // Initial value until overriden by _Vconfigure
m_timeprecision = VL_TIME_PRECISION; // Initial value until overriden by _Vconfigure
}
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
m_s.m_assertOn = flag;
}
void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
m_s.m_calcUnusedSigs = flag;
}
void VerilatedContext::dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const VerilatedLockGuard lock(m_timeDumpMutex);
m_dumpfile = flag;
}
std::string VerilatedContext::dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const VerilatedLockGuard lock(m_timeDumpMutex);
if (VL_UNLIKELY(m_dumpfile.empty())) {
VL_PRINTF_MT("%%Warning: $dumpvar ignored as not proceeded by $dumpfile\n");
return "";
} }
return m_dumpfile;
} }
void Verilated::randReset(int val) VL_MT_SAFE { void VerilatedContext::errorCount(int val) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex); const VerilatedLockGuard lock(m_mutex);
s_s.s_randReset = val; m_s.m_errorCount = val;
} }
void Verilated::randSeed(int val) VL_MT_SAFE { void VerilatedContext::errorCountInc() VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex); const VerilatedLockGuard lock(m_mutex);
s_s.s_randSeed = val; ++m_s.m_errorCount;
vluint64_t newEpoch = s_s.s_randSeedEpoch + 1;
if (VL_UNLIKELY(newEpoch == 0)) newEpoch = 1;
// Obververs must see new epoch AFTER seed updated
#ifdef VL_THREADED
std::atomic_signal_fence(std::memory_order_release);
#endif
s_s.s_randSeedEpoch = newEpoch;
} }
vluint64_t Verilated::randSeedDefault64() VL_MT_SAFE { void VerilatedContext::errorLimit(int val) VL_MT_SAFE {
if (Verilated::randSeed() != 0) { const VerilatedLockGuard lock(m_mutex);
return ((static_cast<vluint64_t>(Verilated::randSeed()) << 32) m_s.m_errorLimit = val;
^ (static_cast<vluint64_t>(Verilated::randSeed())));
} else {
return ((static_cast<vluint64_t>(vl_sys_rand32()) << 32)
^ (static_cast<vluint64_t>(vl_sys_rand32())));
}
} }
void Verilated::calcUnusedSigs(bool flag) VL_MT_SAFE { void VerilatedContext::fatalOnError(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex); const VerilatedLockGuard lock(m_mutex);
s_s.s_calcUnusedSigs = flag; m_s.m_fatalOnError = flag;
} }
void Verilated::errorCount(int val) VL_MT_SAFE { void VerilatedContext::fatalOnVpiError(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex); const VerilatedLockGuard lock(m_mutex);
s_s.s_errorCount = val; m_s.m_fatalOnVpiError = flag;
} }
void Verilated::errorCountInc() VL_MT_SAFE { void VerilatedContext::gotError(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex); const VerilatedLockGuard lock(m_mutex);
++s_s.s_errorCount; m_s.m_gotError = flag;
} }
void Verilated::errorLimit(int val) VL_MT_SAFE { void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex); const VerilatedLockGuard lock(m_mutex);
s_s.s_errorLimit = val; m_s.m_gotFinish = flag;
} }
void Verilated::gotFinish(bool flag) VL_MT_SAFE { void VerilatedContext::profThreadsStart(vluint64_t flag) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex); const VerilatedLockGuard lock(m_mutex);
s_s.s_gotFinish = flag; m_ns.m_profThreadsStart = flag;
} }
void Verilated::assertOn(bool flag) VL_MT_SAFE { void VerilatedContext::profThreadsWindow(vluint64_t flag) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex); const VerilatedLockGuard lock(m_mutex);
s_s.s_assertOn = flag; m_ns.m_profThreadsWindow = flag;
} }
void Verilated::fatalOnVpiError(bool flag) VL_MT_SAFE { void VerilatedContext::profThreadsFilename(const std::string& flag) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex); const VerilatedLockGuard lock(m_mutex);
s_s.s_fatalOnVpiError = flag; m_ns.m_profThreadsFilename = flag;
} }
void Verilated::timeunit(int value) VL_MT_SAFE { std::string VerilatedContext::profThreadsFilename() const VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
return m_ns.m_profThreadsFilename;
}
void VerilatedContext::randReset(int val) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
m_s.m_randReset = val;
}
void VerilatedContext::timeunit(int value) VL_MT_SAFE {
if (value < 0) value = -value; // Stored as 0..15 if (value < 0) value = -value; // Stored as 0..15
const VerilatedLockGuard lock(s_mutex); const VerilatedLockGuard lock(m_mutex);
s_s.s_timeunit = value; m_s.m_timeunit = value;
} }
void Verilated::timeprecision(int value) VL_MT_SAFE { void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
if (value < 0) value = -value; // Stored as 0..15 if (value < 0) value = -value; // Stored as 0..15
const VerilatedLockGuard lock(s_mutex); const VerilatedLockGuard lock(m_mutex);
s_s.s_timeprecision = value; m_s.m_timeprecision = value;
#ifdef SYSTEMC_VERSION #ifdef SYSTEMC_VERSION
sc_time sc_res = sc_get_time_resolution(); sc_time sc_res = sc_get_time_resolution();
int sc_prec = 99; int sc_prec = 99;
@ -2331,18 +2293,243 @@ void Verilated::timeprecision(int value) VL_MT_SAFE {
} }
#endif #endif
} }
void Verilated::profThreadsStart(vluint64_t flag) VL_MT_SAFE { const char* VerilatedContext::timeunitString() const VL_MT_SAFE { return vl_time_str(timeunit()); }
const VerilatedLockGuard lock(s_mutex); const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE {
s_ns.s_profThreadsStart = flag; return vl_time_str(timeprecision());
} }
void Verilated::profThreadsWindow(vluint64_t flag) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex); void VerilatedContext::commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) {
s_ns.s_profThreadsWindow = flag; const VerilatedLockGuard lock(m_argMutex);
m_args.m_argVec.clear(); // Empty first, then add
impp()->commandArgsAddGuts(argc, argv);
} }
void Verilated::profThreadsFilenamep(const char* flagp) VL_MT_SAFE { void VerilatedContext::commandArgsAdd(int argc, const char** argv)
const VerilatedLockGuard lock(s_mutex); VL_MT_SAFE_EXCLUDES(m_argMutex) {
if (s_ns.s_profThreadsFilenamep) free(const_cast<char*>(s_ns.s_profThreadsFilenamep)); const VerilatedLockGuard lock(m_argMutex);
s_ns.s_profThreadsFilenamep = strdup(flagp); impp()->commandArgsAddGuts(argc, argv);
}
const char* VerilatedContext::commandArgsPlusMatch(const char* prefixp)
VL_MT_SAFE_EXCLUDES(m_argMutex) {
const std::string& match = impp()->argPlusMatch(prefixp);
static VL_THREAD_LOCAL char t_outstr[VL_VALUE_STRING_MAX_WIDTH];
if (match.empty()) return "";
char* dp = t_outstr;
for (const char* sp = match.c_str(); *sp && (dp - t_outstr) < (VL_VALUE_STRING_MAX_WIDTH - 2);)
*dp++ = *sp++;
*dp++ = '\0';
return t_outstr;
}
void VerilatedContext::internalsDump() const VL_MT_SAFE {
VL_PRINTF_MT("internalsDump:\n");
VerilatedImp::versionDump();
impp()->commandArgDump();
impp()->scopesDump();
VerilatedImp::exportsDump();
VerilatedImp::userDump();
}
//======================================================================
// VerilatedContextImp:: Methods - command line
void VerilatedContextImp::commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(m_argMutex) {
if (!m_args.m_argVecLoaded) m_args.m_argVec.clear();
for (int i = 0; i < argc; ++i) {
m_args.m_argVec.push_back(argv[i]);
commandArgVl(argv[i]);
}
m_args.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok
}
void VerilatedContextImp::commandArgDump() const VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock(m_argMutex);
VL_PRINTF_MT(" Argv:");
for (const auto& i : m_args.m_argVec) VL_PRINTF_MT(" %s", i.c_str());
VL_PRINTF_MT("\n");
}
std::string VerilatedContextImp::argPlusMatch(const char* prefixp)
VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock(m_argMutex);
// Note prefixp does not include the leading "+"
size_t len = strlen(prefixp);
if (VL_UNLIKELY(!m_args.m_argVecLoaded)) {
m_args.m_argVecLoaded = true; // Complain only once
VL_FATAL_MT("unknown", 0, "",
"%Error: Verilog called $test$plusargs or $value$plusargs without"
" testbench C first calling Verilated::commandArgs(argc,argv).");
}
for (const auto& i : m_args.m_argVec) {
if (i[0] == '+') {
if (0 == strncmp(prefixp, i.c_str() + 1, len)) return i;
}
}
return "";
}
// Return string representing current argv
// Only used by VPI so uses static storage, only supports most recent called context
std::pair<int, char**> VerilatedContextImp::argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock(m_argMutex);
static bool s_loaded = false;
static int s_argc = 0;
static char** s_argvp = nullptr;
if (VL_UNLIKELY(!s_loaded)) {
s_loaded = true;
s_argc = m_args.m_argVec.size();
s_argvp = new char*[s_argc + 1];
int in = 0;
for (const auto& i : m_args.m_argVec) {
s_argvp[in] = new char[i.length() + 1];
strcpy(s_argvp[in], i.c_str());
++in;
}
s_argvp[s_argc] = nullptr;
}
return std::make_pair(s_argc, s_argvp);
}
void VerilatedContextImp::commandArgVl(const std::string& arg) {
if (0 == strncmp(arg.c_str(), "+verilator+", strlen("+verilator+"))) {
std::string value;
if (arg == "+verilator+debug") {
Verilated::debug(4);
} else if (commandArgVlValue(arg, "+verilator+debugi+", value /*ref*/)) {
Verilated::debug(atoi(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+error+limit+", value /*ref*/)) {
errorLimit(atoi(value.c_str()));
} else if (arg == "+verilator+help") {
VerilatedImp::versionDump();
VL_PRINTF_MT("For help, please see 'verilator --help'\n");
VL_FATAL_MT("COMMAND_LINE", 0, "",
"Exiting due to command line argument (not an error)");
} else if (commandArgVlValue(arg, "+verilator+prof+threads+start+", value /*ref*/)) {
profThreadsStart(atoll(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+prof+threads+window+", value /*ref*/)) {
profThreadsWindow(atol(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+prof+threads+file+", value /*ref*/)) {
profThreadsFilename(value);
} else if (commandArgVlValue(arg, "+verilator+rand+reset+", value /*ref*/)) {
randReset(atoi(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+seed+", value /*ref*/)) {
randSeed(atoi(value.c_str()));
} else if (arg == "+verilator+noassert") {
assertOn(false);
} else if (arg == "+verilator+V") {
VerilatedImp::versionDump(); // Someday more info too
VL_FATAL_MT("COMMAND_LINE", 0, "",
"Exiting due to command line argument (not an error)");
} else if (arg == "+verilator+version") {
VerilatedImp::versionDump();
VL_FATAL_MT("COMMAND_LINE", 0, "",
"Exiting due to command line argument (not an error)");
} else {
VL_PRINTF_MT("%%Warning: Unknown +verilator runtime argument: '%s'\n", arg.c_str());
}
}
}
bool VerilatedContextImp::commandArgVlValue(const std::string& arg, const std::string& prefix,
std::string& valuer) {
size_t len = prefix.length();
if (0 == strncmp(prefix.c_str(), arg.c_str(), len)) {
valuer = arg.substr(len);
return true;
} else {
return false;
}
}
//======================================================================
// VerilatedContext:: + VerilatedContextImp:: Methods - random
void VerilatedContext::randSeed(int val) VL_MT_SAFE {
// As we have per-thread state, the epoch must be static,
// and so the rand seed's mutex must also be static
const VerilatedLockGuard lock(VerilatedContextImp::s().s_randMutex);
m_s.m_randSeed = val;
vluint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1;
// Obververs must see new epoch AFTER seed updated
#ifdef VL_THREADED
std::atomic_signal_fence(std::memory_order_release);
#endif
VerilatedContextImp::s().s_randSeedEpoch = newEpoch;
}
vluint64_t VerilatedContextImp::randSeedDefault64() const VL_MT_SAFE {
if (randSeed() != 0) {
return ((static_cast<vluint64_t>(randSeed()) << 32)
^ (static_cast<vluint64_t>(randSeed())));
} else {
return ((static_cast<vluint64_t>(vl_sys_rand32()) << 32)
^ (static_cast<vluint64_t>(vl_sys_rand32())));
}
}
//======================================================================
// VerilatedContext:: Methods - scopes
void VerilatedContext::scopesDump() const VL_MT_SAFE {
const VerilatedLockGuard lock(m_impdatap->m_nameMutex);
VL_PRINTF_MT(" scopesDump:\n");
for (const auto& i : m_impdatap->m_nameMap) {
const VerilatedScope* scopep = i.second;
scopep->scopeDump();
}
VL_PRINTF_MT("\n");
}
void VerilatedContextImp::scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope at construction
const VerilatedLockGuard lock(m_impdatap->m_nameMutex);
const auto it = m_impdatap->m_nameMap.find(scopep->name());
if (it == m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.emplace(scopep->name(), scopep);
}
void VerilatedContextImp::scopeErase(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope at destruction
const VerilatedLockGuard lock(m_impdatap->m_nameMutex);
VerilatedImp::userEraseScope(scopep);
const auto it = m_impdatap->m_nameMap.find(scopep->name());
if (it != m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.erase(it);
}
const VerilatedScope* VerilatedContext::scopeFind(const char* namep) const VL_MT_SAFE {
// Thread save only assuming this is called only after model construction completed
const VerilatedLockGuard lock(m_impdatap->m_nameMutex);
// If too slow, can assume this is only VL_MT_SAFE_POSINIT
const auto& it = m_impdatap->m_nameMap.find(namep);
if (VL_UNLIKELY(it == m_impdatap->m_nameMap.end())) return nullptr;
return it->second;
}
const VerilatedScopeNameMap* VerilatedContext::scopeNameMap() VL_MT_SAFE {
return &(impp()->m_impdatap->m_nameMap);
}
//======================================================================
// VerilatedSyms:: Methods
VerilatedSyms::VerilatedSyms(VerilatedContext* contextp)
: _vm_contextp__(contextp ? contextp : Verilated::threadContextp()) {
Verilated::threadContextp(_vm_contextp__);
#ifdef VL_THREADED
__Vm_evalMsgQp = new VerilatedEvalMsgQueue;
#endif
}
VerilatedSyms::~VerilatedSyms() {
#ifdef VL_THREADED
delete __Vm_evalMsgQp;
#endif
}
//===========================================================================
// Verilated:: Methods
void Verilated::debug(int level) VL_MT_SAFE {
s_debug = level;
if (level) {
#ifdef VL_DEBUG
VL_DEBUG_IF(VL_DBG_MSGF("- Verilated::debug is on."
" Message prefix indicates {<thread>,<sequence_number>}.\n"););
#else
VL_PRINTF_MT("- Verilated::debug attempted,"
" but compiled without VL_DEBUG, so messages suppressed.\n"
"- Suggest remake using 'make ... CPPFLAGS=-DVL_DEBUG'\n");
#endif
}
} }
const char* Verilated::catName(const char* n1, const char* n2, const char* delimiter) VL_MT_SAFE { const char* Verilated::catName(const char* n1, const char* n2, const char* delimiter) VL_MT_SAFE {
@ -2442,24 +2629,6 @@ void Verilated::runExitCallbacks() VL_MT_SAFE {
const char* Verilated::productName() VL_PURE { return VERILATOR_PRODUCT; } const char* Verilated::productName() VL_PURE { return VERILATOR_PRODUCT; }
const char* Verilated::productVersion() VL_PURE { return VERILATOR_VERSION; } const char* Verilated::productVersion() VL_PURE { return VERILATOR_VERSION; }
void Verilated::commandArgs(int argc, const char** argv) VL_MT_SAFE {
const VerilatedLockGuard lock(s_args.m_argMutex);
s_args.argc = argc;
s_args.argv = argv;
VerilatedImp::commandArgs(argc, argv);
}
const char* Verilated::commandArgsPlusMatch(const char* prefixp) VL_MT_SAFE {
const std::string& match = VerilatedImp::argPlusMatch(prefixp);
static VL_THREAD_LOCAL char t_outstr[VL_VALUE_STRING_MAX_WIDTH];
if (match.empty()) return "";
char* dp = t_outstr;
for (const char* sp = match.c_str(); *sp && (dp - t_outstr) < (VL_VALUE_STRING_MAX_WIDTH - 2);)
*dp++ = *sp++;
*dp++ = '\0';
return t_outstr;
}
void Verilated::nullPointerError(const char* filename, int linenum) VL_MT_SAFE { void Verilated::nullPointerError(const char* filename, int linenum) VL_MT_SAFE {
// Slowpath - Called only on error // Slowpath - Called only on error
VL_FATAL_MT(filename, linenum, "", "Null pointer dereferenced"); VL_FATAL_MT(filename, linenum, "", "Null pointer dereferenced");
@ -2489,22 +2658,10 @@ void Verilated::quiesce() VL_MT_SAFE {
#endif #endif
} }
void Verilated::internalsDump() VL_MT_SAFE { VerilatedImp::internalsDump(); }
void Verilated::scopesDump() VL_MT_SAFE { VerilatedImp::scopesDump(); }
const VerilatedScope* Verilated::scopeFind(const char* namep) VL_MT_SAFE {
return VerilatedImp::scopeFind(namep);
}
int Verilated::exportFuncNum(const char* namep) VL_MT_SAFE { int Verilated::exportFuncNum(const char* namep) VL_MT_SAFE {
return VerilatedImp::exportFind(namep); return VerilatedImp::exportFind(namep);
} }
const VerilatedScopeNameMap* Verilated::scopeNameMap() VL_MT_SAFE {
return VerilatedImp::scopeNameMap();
}
#ifdef VL_THREADED #ifdef VL_THREADED
void Verilated::endOfThreadMTaskGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE { void Verilated::endOfThreadMTaskGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
VL_DEBUG_IF(VL_DBG_MSGF("End of thread mtask\n");); VL_DEBUG_IF(VL_DBG_MSGF("End of thread mtask\n"););
@ -2521,138 +2678,13 @@ void Verilated::endOfEval(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
} }
#endif #endif
//===========================================================================
// VerilatedImp:: Constructors
// verilated.o may exist both in protect-lib and main module.
// Both the main module and the protect-lib refer the same instance of
// static variables such as Verilated or VerilatedImplData.
// This is important to share the state such as Verilated::gotFinish.
// But the sharing may cause double-free error when shutting down because destructors
// are called twice.
// 1st time:From protect-lib shared object on the way of unloading after exiting main()
// 2nd time:From main executable.
//
// To avoid the trouble, all member variables are enclosed in VerilatedImpU union.
// ctor nor dtor of members are not called automatically.
// VerilatedInitializer::setup() and teardown() guarantees to initialize/destruct just once.
void VerilatedImp::setup() { new (&VerilatedImp::s_s) VerilatedImpData(); }
void VerilatedImp::teardown() { VerilatedImp::s_s.~VerilatedImpU(); }
//=========================================================================== //===========================================================================
// VerilatedImp:: Methods // VerilatedImp:: Methods
std::string VerilatedImp::timeFormatSuffix() VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.m_sergMutex);
return s_s.v.m_serg.m_timeFormatSuffix;
}
void VerilatedImp::timeFormatSuffix(const std::string& value) VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.m_sergMutex);
s_s.v.m_serg.m_timeFormatSuffix = value;
}
void VerilatedImp::timeFormatUnits(int value) VL_MT_SAFE { s_s.v.m_ser.m_timeFormatUnits = value; }
void VerilatedImp::timeFormatPrecision(int value) VL_MT_SAFE {
s_s.v.m_ser.m_timeFormatPrecision = value;
}
void VerilatedImp::timeFormatWidth(int value) VL_MT_SAFE { s_s.v.m_ser.m_timeFormatWidth = value; }
void VerilatedImp::internalsDump() VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.m_argMutex);
VL_PRINTF_MT("internalsDump:\n");
versionDump();
VL_PRINTF_MT(" Argv:");
for (const auto& i : s_s.v.m_argVec) VL_PRINTF_MT(" %s", i.c_str());
VL_PRINTF_MT("\n");
scopesDump();
exportsDump();
userDump();
}
void VerilatedImp::versionDump() VL_MT_SAFE { void VerilatedImp::versionDump() VL_MT_SAFE {
VL_PRINTF_MT(" Version: %s %s\n", Verilated::productName(), Verilated::productVersion()); VL_PRINTF_MT(" Version: %s %s\n", Verilated::productName(), Verilated::productVersion());
} }
void VerilatedImp::commandArgs(int argc, const char** argv) VL_EXCLUDES(s_s.v.m_argMutex) {
const VerilatedLockGuard lock(s_s.v.m_argMutex);
s_s.v.m_argVec.clear(); // Always clear
commandArgsAddGuts(argc, argv);
}
void VerilatedImp::commandArgsAdd(int argc, const char** argv) VL_EXCLUDES(s_s.v.m_argMutex) {
const VerilatedLockGuard lock(s_s.v.m_argMutex);
commandArgsAddGuts(argc, argv);
}
void VerilatedImp::commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(s_s.v.m_argMutex) {
if (!s_s.v.m_argVecLoaded) s_s.v.m_argVec.clear();
for (int i = 0; i < argc; ++i) {
s_s.v.m_argVec.push_back(argv[i]);
commandArgVl(argv[i]);
}
s_s.v.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok
}
void VerilatedImp::commandArgVl(const std::string& arg) {
if (0 == strncmp(arg.c_str(), "+verilator+", strlen("+verilator+"))) {
std::string value;
if (arg == "+verilator+debug") {
Verilated::debug(4);
} else if (commandArgVlValue(arg, "+verilator+debugi+", value /*ref*/)) {
Verilated::debug(atoi(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+error+limit+", value /*ref*/)) {
Verilated::errorLimit(atoi(value.c_str()));
} else if (arg == "+verilator+help") {
versionDump();
VL_PRINTF_MT("For help, please see 'verilator --help'\n");
VL_FATAL_MT("COMMAND_LINE", 0, "",
"Exiting due to command line argument (not an error)");
} else if (commandArgVlValue(arg, "+verilator+prof+threads+start+", value /*ref*/)) {
Verilated::profThreadsStart(atoll(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+prof+threads+window+", value /*ref*/)) {
Verilated::profThreadsWindow(atol(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+prof+threads+file+", value /*ref*/)) {
Verilated::profThreadsFilenamep(value.c_str());
} else if (commandArgVlValue(arg, "+verilator+rand+reset+", value /*ref*/)) {
Verilated::randReset(atoi(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+seed+", value /*ref*/)) {
Verilated::randSeed(atoi(value.c_str()));
} else if (arg == "+verilator+noassert") {
Verilated::assertOn(false);
} else if (arg == "+verilator+V") {
versionDump(); // Someday more info too
VL_FATAL_MT("COMMAND_LINE", 0, "",
"Exiting due to command line argument (not an error)");
} else if (arg == "+verilator+version") {
versionDump();
VL_FATAL_MT("COMMAND_LINE", 0, "",
"Exiting due to command line argument (not an error)");
} else {
VL_PRINTF_MT("%%Warning: Unknown +verilator runtime argument: '%s'\n", arg.c_str());
}
}
}
bool VerilatedImp::commandArgVlValue(const std::string& arg, const std::string& prefix,
std::string& valuer) {
size_t len = prefix.length();
if (0 == strncmp(prefix.c_str(), arg.c_str(), len)) {
valuer = arg.substr(len);
return true;
} else {
return false;
}
}
//======================================================================
// VerilatedSyms:: Methods
VerilatedSyms::VerilatedSyms() {
#ifdef VL_THREADED
__Vm_evalMsgQp = new VerilatedEvalMsgQueue;
#endif
}
VerilatedSyms::~VerilatedSyms() {
#ifdef VL_THREADED
delete __Vm_evalMsgQp;
#endif
}
//=========================================================================== //===========================================================================
// VerilatedModule:: Methods // VerilatedModule:: Methods
@ -2706,7 +2738,7 @@ void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const
VerilatedScope::~VerilatedScope() { VerilatedScope::~VerilatedScope() {
// Memory cleanup - not called during normal operation // Memory cleanup - not called during normal operation
VerilatedImp::scopeErase(this); Verilated::threadContextp()->impp()->scopeErase(this);
if (m_namep) VL_DO_CLEAR(delete[] m_namep, m_namep = nullptr); if (m_namep) VL_DO_CLEAR(delete[] m_namep, m_namep = nullptr);
if (m_callbacksp) VL_DO_CLEAR(delete[] m_callbacksp, m_callbacksp = nullptr); if (m_callbacksp) VL_DO_CLEAR(delete[] m_callbacksp, m_callbacksp = nullptr);
if (m_varsp) VL_DO_CLEAR(delete m_varsp, m_varsp = nullptr); if (m_varsp) VL_DO_CLEAR(delete m_varsp, m_varsp = nullptr);
@ -2731,7 +2763,7 @@ void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, const
m_namep = namep; m_namep = namep;
} }
m_identifierp = identifier; m_identifierp = identifier;
VerilatedImp::scopeInsert(this); Verilated::threadContextp()->impp()->scopeInsert(this);
} }
void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE { void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE {

View File

@ -38,6 +38,9 @@
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <memory>
#include <string>
#include <vector>
// <iostream> avoided to reduce compile time // <iostream> avoided to reduce compile time
// <map> avoided and instead in verilated_heavy.h to reduce compile time // <map> avoided and instead in verilated_heavy.h to reduce compile time
// <string> avoided and instead in verilated_heavy.h to reduce compile time // <string> avoided and instead in verilated_heavy.h to reduce compile time
@ -87,6 +90,9 @@ typedef EData WData; ///< Verilated pack data, >64 bits, as an array
typedef const WData* WDataInP; ///< Array input to a function typedef const WData* WDataInP; ///< Array input to a function
typedef WData* WDataOutP; ///< Array output from a function typedef WData* WDataOutP; ///< Array output from a function
class VerilatedContextImp;
class VerilatedContextImpData;
class VerilatedCovContext;
class VerilatedEvalMsgQueue; class VerilatedEvalMsgQueue;
class VerilatedScopeNameMap; class VerilatedScopeNameMap;
class VerilatedVar; class VerilatedVar;
@ -227,12 +233,10 @@ public:
} }
} }
} }
void changeThread() { m_threadid = 0; } // Allow intentional change-of-thread
static void fatal_different() VL_MT_SAFE; static void fatal_different() VL_MT_SAFE;
#else // !VL_THREADED || !VL_DEBUG #else // !VL_THREADED || !VL_DEBUG
public: public:
void check() {} void check() {}
void changeThread() {}
#endif #endif
}; };
@ -295,21 +299,273 @@ public:
#endif #endif
// clang-format on // clang-format on
//===========================================================================
// Internal: Base class to allow virtual destruction
class VerilatedVirtualBase VL_NOT_FINAL {
public:
VerilatedVirtualBase() = default;
virtual ~VerilatedVirtualBase() = default;
};
//===========================================================================
/// Verilator simulation context
///
/// The VerilatedContext contains the information common across all models
/// that are interconnected, for example this contains the simulation time
/// and if $finish was executed.
///
/// VerilatedContexts maybe created by the user wrapper code and passed
/// when a model is created. If this is not done, then Verilator will use
/// the Verilated::defaultContextp()'s global context.
class VerilatedContext VL_NOT_FINAL {
friend class VerilatedContextImp;
protected:
// MEMBERS
// Slow path variables
mutable VerilatedMutex m_mutex; // Mutex for most s_s/s_ns members, when VL_THREADED
struct Serialized { // All these members serialized/deserialized
// No std::strings or pointers or will serialize badly!
// Fast path
bool m_assertOn = true; // Assertions are enabled
bool m_calcUnusedSigs = false; // Waves file on, need all signals calculated
bool m_fatalOnError = true; // Fatal on $stop/non-fatal error
bool m_fatalOnVpiError = true; // Fatal on vpi error/unsupported
bool m_gotError = false; // A $finish statement executed
bool m_gotFinish = false; // A $finish or $stop statement executed
vluint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy
// Slow path
vlsint8_t m_timeunit; // Time unit as 0..15
vlsint8_t m_timeprecision; // Time precision as 0..15
int m_errorCount = 0; // Number of errors
int m_errorLimit = 1; // Stop on error number
int m_randReset = 0; // Random reset: 0=all 0s, 1=all 1s, 2=random
int m_randSeed = 0; // Random seed: 0=random
enum { UNITS_NONE = 99 }; // Default based on precision
int m_timeFormatUnits = UNITS_NONE; // $timeformat units
int m_timeFormatPrecision = 0; // $timeformat number of decimal places
int m_timeFormatWidth = 20; // $timeformat character width
// CONSTRUCTORS
Serialized();
~Serialized() = default;
} m_s;
mutable VerilatedMutex m_timeDumpMutex; // Protect misc slow strings
std::string m_timeFormatSuffix VL_GUARDED_BY(m_timeDumpMutex); // $timeformat printf format
std::string m_dumpfile VL_GUARDED_BY(m_timeDumpMutex); // $dumpfile setting
struct NonSerialized { // Non-serialized information
// These are reloaded from on command-line settings, so do not need to persist
// Fast path
vluint64_t m_profThreadsStart = 1; // +prof+threads starting time
vluint32_t m_profThreadsWindow = 2; // +prof+threads window size
// Slow path
std::string m_profThreadsFilename; // +prof+threads filename
} m_ns;
mutable VerilatedMutex m_argMutex; // Protect m_argVec, m_argVecLoaded
// no need to be save-restored (serialized) the
// assumption is that the restore is allowed to pass different arguments
struct NonSerializedCommandArgs {
// Medium speed
bool m_argVecLoaded = false; // Ever loaded argument list
std::vector<std::string> m_argVec; // Aargument list
} m_args VL_GUARDED_BY(m_argMutex);
// Implementation details
std::unique_ptr<VerilatedContextImpData> m_impdatap;
// Coverage access
std::unique_ptr<VerilatedVirtualBase> m_coveragep; // Pointer for coveragep()
// File I/O
// Not serialized
mutable VerilatedMutex m_fdMutex; // Protect m_fdps, m_fdFree
std::vector<FILE*> m_fdps VL_GUARDED_BY(m_fdMutex); // File descriptors
// List of free descriptors (SLOW - FOPEN/CLOSE only)
std::vector<IData> m_fdFree VL_GUARDED_BY(m_fdMutex);
// List of free descriptors in the MCT region [4, 32)
std::vector<IData> m_fdFreeMct VL_GUARDED_BY(m_fdMutex);
private:
// CONSTRUCTORS
VL_UNCOPYABLE(VerilatedContext);
public:
/// Construct context. Also sets Verilated::threadContextp to the created context.
VerilatedContext();
~VerilatedContext();
// METHODS - User called
/// Enable assertions
void assertOn(bool flag) VL_MT_SAFE;
/// Return if assertions enabled
bool assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
/// Enable calculation of unused signals (for traces)
void calcUnusedSigs(bool flag) VL_MT_SAFE;
/// Return if calculating of unused signals (for traces)
bool calcUnusedSigs() const VL_MT_SAFE { return m_s.m_calcUnusedSigs; }
/// Record command-line arguments, for retrieval by $test$plusargs/$value$plusargs,
/// and for parsing +verilator+ run-time arguments.
/// This should be called before the first model is created.
void commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex);
void commandArgs(int argc, char** argv) VL_MT_SAFE {
commandArgs(argc, const_cast<const char**>(argv));
}
/// Add a command-line argument to existing arguments
void commandArgsAdd(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex);
/// Match plusargs with a given prefix. Returns static char* valid only for a single call
const char* commandArgsPlusMatch(const char* prefixp) VL_MT_SAFE_EXCLUDES(m_argMutex);
/// Return VerilatedCovContext, allocate if needed
/// Note if get unresolved reference then likely forgot to link verilated_cov.cpp
VerilatedCovContext* coveragep() VL_MT_SAFE;
/// Set debug level
/// Debug is currently global, but for forward compatibility have a per-context method
static void debug(int val) VL_MT_SAFE;
/// Return debug level
static int debug() VL_MT_SAFE;
/// Set current number of errors/assertions
void errorCount(int val) VL_MT_SAFE;
/// Increment current number of errors/assertions
void errorCountInc() VL_MT_SAFE;
/// Return current number of errors/assertions
int errorCount() const VL_MT_SAFE { return m_s.m_errorCount; }
/// Set number of errors/assertions before stop
void errorLimit(int val) VL_MT_SAFE;
/// Return number of errors/assertions before stop
int errorLimit() const VL_MT_SAFE { return m_s.m_errorLimit; }
/// Set to throw fatal error on $stop/non-fatal ettot
void fatalOnError(bool flag) VL_MT_SAFE;
/// Return if to throw fatal error on $stop/non-fatal
bool fatalOnError() const VL_MT_SAFE { return m_s.m_fatalOnError; }
/// Set to throw fatal error on VPI errors
void fatalOnVpiError(bool flag) VL_MT_SAFE;
/// Return if to throw fatal error on VPI errors
bool fatalOnVpiError() const VL_MT_SAFE { return m_s.m_fatalOnVpiError; }
/// Set if got a $stop or non-fatal error
void gotError(bool flag) VL_MT_SAFE;
/// Return if got a $stop or non-fatal error
bool gotError() const VL_MT_SAFE { return m_s.m_gotError; }
/// Set if got a $finish or $stop/error
void gotFinish(bool flag) VL_MT_SAFE;
/// Return if got a $finish or $stop/error
bool gotFinish() const VL_MT_SAFE { return m_s.m_gotFinish; }
/// Select initial value of otherwise uninitialized signals.
/// 0 = Set to zeros
/// 1 = Set all bits to one
/// 2 = Randomize all bits
void randReset(int val) VL_MT_SAFE;
/// Return randReset value
int randReset() VL_MT_SAFE { return m_s.m_randReset; }
/// Return default random seed
void randSeed(int val) VL_MT_SAFE;
/// Set default random seed, 0 = seed it automatically
int randSeed() const VL_MT_SAFE { return m_s.m_randSeed; }
// Time handling
/// How Verilator runtime gets the current simulation time:
///
/// * If using SystemC, time comes from the SystemC kernel-defined
/// sc_time_stamp64(). User's wrapper must not call
/// SimulationContext::time(value) nor timeInc(value).
///
/// * Else, if SimulationContext::time(value) or
/// SimulationContext::timeInc(value) is ever called with non-zero,
/// then time will come via the context. This allows multiple contexts
/// to exist and have different simulation times. This must not be used
/// with SystemC. Note Verilated::time(value) and
/// Verilated::timeInc(value) call into SimulationContext::time and
/// timeInc, operating on the thread's context.
///
/// * Else, if VL_TIME_STAMP64 is defined, time comes from the legacy
/// 'vluint64_t vl_time_stamp64()' which must a function be defined by
/// the user's wrapper.
///
/// * Else, time comes from the legacy 'double sc_time_stamp()' which
/// must be a function defined by the user's wrapper.
vluint64_t time() const VL_MT_SAFE;
/// Set current simulation time. See time() for side effect details
void time(vluint64_t value) VL_MT_SAFE { m_s.m_time = value; }
/// Advance current simulation time. See time() for side effect details
void timeInc(vluint64_t add) VL_MT_UNSAFE { m_s.m_time += add; }
/// Return time units as power-of-ten
int timeunit() const VL_MT_SAFE { return -m_s.m_timeunit; }
/// Set time units as power-of-ten
void timeunit(int value) VL_MT_SAFE;
/// Return time units as IEEE-standard text
const char* timeunitString() const VL_MT_SAFE;
/// Get time precision as power-of-ten
int timeprecision() const VL_MT_SAFE { return -m_s.m_timeprecision; }
/// Return time precision as power-of-ten
void timeprecision(int value) VL_MT_SAFE;
/// Get time precision as IEEE-standard text
const char* timeprecisionString() const VL_MT_SAFE;
/// Allow traces to at some point be enabled (disables some optimizations)
void traceEverOn(bool flag) VL_MT_SAFE {
if (flag) calcUnusedSigs(true);
}
/// 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.
void internalsDump() const 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.
void scopesDump() const VL_MT_SAFE;
public: // But for internal use only
// Internal: access to implementation class
VerilatedContextImp* impp() { return reinterpret_cast<VerilatedContextImp*>(this); }
const VerilatedContextImp* impp() const {
return reinterpret_cast<const VerilatedContextImp*>(this);
}
// Internal: $dumpfile
void dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
std::string dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
// Internal: --prof-threads related settings
void profThreadsStart(vluint64_t flag) VL_MT_SAFE;
vluint64_t profThreadsStart() const VL_MT_SAFE { return m_ns.m_profThreadsStart; }
void profThreadsWindow(vluint64_t flag) VL_MT_SAFE;
vluint32_t profThreadsWindow() const VL_MT_SAFE { return m_ns.m_profThreadsWindow; }
void profThreadsFilename(const std::string& flag) VL_MT_SAFE;
std::string profThreadsFilename() const VL_MT_SAFE;
// Internal: Find scope
const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE;
const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE;
// Internal: Serialization setup
static constexpr size_t serialized1Size() VL_PURE { return sizeof(m_s); }
void* serialized1Ptr() VL_MT_UNSAFE { return &m_s; }
};
//=========================================================================== //===========================================================================
/// Verilator symbol table base class /// Verilator symbol table base class
/// Used for internal VPI implementation, and introspection into scopes
class VerilatedSyms VL_NOT_FINAL { class VerilatedSyms VL_NOT_FINAL {
public: // But for internal use only public: // But for internal use only
// MEMBERS
// Keep first so is at zero offset for fastest code
VerilatedContext* const _vm_contextp__; // Context for current model
#ifdef VL_THREADED #ifdef VL_THREADED
VerilatedEvalMsgQueue* __Vm_evalMsgQp; VerilatedEvalMsgQueue* __Vm_evalMsgQp;
#endif #endif
VerilatedSyms(); explicit VerilatedSyms(VerilatedContext* contextp); // Pass null for default context
~VerilatedSyms(); ~VerilatedSyms();
}; };
//=========================================================================== //===========================================================================
/// Verilator global class information class /// Verilator scope information class
/// This class is initialized by main thread only. Reading post-init is thread safe. /// Used for internal VPI implementation, and introspection into scopes
class VerilatedScope final { class VerilatedScope final {
public: public:
@ -370,55 +626,29 @@ public:
class Verilated final { class Verilated final {
// MEMBERS // MEMBERS
// Slow path variables
static VerilatedMutex s_mutex; ///< Mutex for s_s/s_ns members, when VL_THREADED
static struct Serialized { // All these members serialized/deserialized // Internal Note: There should be no Serialized state in Verilated::,
// Fast path // instead serialized state should all be in VerilatedContext:: as by
bool s_calcUnusedSigs; ///< Waves file on, need all signals calculated // definition it needs to vary per-simulation
bool s_gotFinish; ///< A $finish statement executed
bool s_assertOn; ///< Assertions are enabled
bool s_fatalOnVpiError; ///< Stop on vpi error/unsupported
// Slow path
vlsint8_t s_timeunit; ///< Time unit as 0..15
vlsint8_t s_timeprecision; ///< Time precision as 0..15
int s_errorCount; ///< Number of errors
int s_errorLimit; ///< Stop on error number
int s_randReset; ///< Random reset: 0=all 0s, 1=all 1s, 2=random
int s_randSeed; ///< Random seed: 0=random
int s_randSeedEpoch; ///< Number incrementing on each reseed, 0=illegal
Serialized();
~Serialized() = default;
} s_s;
static struct NonSerialized { // Non-serialized information // Internal note: Globals may multi-construct, see verilated.cpp top.
// These are reloaded from on command-line settings, so do not need to persist
// Fast path
int s_debug = 0; ///< See accessors... only when VL_DEBUG set
vluint64_t s_profThreadsStart = 1; ///< +prof+threads starting time
vluint32_t s_profThreadsWindow = 2; ///< +prof+threads window size
// Slow path
const char* s_profThreadsFilenamep; ///< +prof+threads filename
void setup();
void teardown();
} s_ns;
// no need to be save-restored (serialized) the // Debug is reloaded from on command-line settings, so do not need to persist
// assumption is that the restore is allowed to pass different arguments static int s_debug; ///< See accessors... only when VL_DEBUG set
static struct CommandArgValues {
VerilatedMutex m_argMutex; ///< Mutex for s_args members, when VL_THREADED static VerilatedContext* s_lastContextp; ///< Last context constructed/attached
int argc = 0;
const char** argv = nullptr;
CommandArgValues() = default;
~CommandArgValues() = default;
} s_args;
// Not covered by mutex, as per-thread // Not covered by mutex, as per-thread
static VL_THREAD_LOCAL struct ThreadLocal { static VL_THREAD_LOCAL struct ThreadLocal {
// No non-POD objects here due to this:
// Internal note: Globals may multi-construct, see verilated.cpp top.
// Fast path
VerilatedContext* t_contextp = nullptr; // Thread's context
#ifdef VL_THREADED #ifdef VL_THREADED
vluint32_t t_mtaskId = 0; ///< Current mtask# executing on this thread vluint32_t t_mtaskId = 0; // mtask# executing on this thread
vluint32_t t_endOfEvalReqd // Messages maybe pending on thread, needs end-of-eval calls
= 0; ///< Messages may be pending, thread needs endOf-eval calls vluint32_t t_endOfEvalReqd = 0;
#endif #endif
const VerilatedScope* t_dpiScopep = nullptr; ///< DPI context scope const VerilatedScope* t_dpiScopep = nullptr; ///< DPI context scope
const char* t_dpiFilename = nullptr; ///< DPI context filename const char* t_dpiFilename = nullptr; ///< DPI context filename
@ -428,7 +658,6 @@ class Verilated final {
~ThreadLocal() = default; ~ThreadLocal() = default;
} t_s; } t_s;
private:
friend struct VerilatedInitializer; friend struct VerilatedInitializer;
// CONSTRUCTORS // CONSTRUCTORS
@ -443,64 +672,103 @@ public:
/// Return debug level /// Return debug level
/// When multithreaded this may not immediately react to another thread /// When multithreaded this may not immediately react to another thread
/// changing the level (no mutex) /// changing the level (no mutex)
static inline int debug() VL_MT_SAFE { return s_ns.s_debug; } static inline int debug() VL_MT_SAFE { return s_debug; }
#else #else
/// Return constant 0 debug level, so C++'s optimizer rips up /// Return constant 0 debug level, so C++'s optimizer rips up
static constexpr int debug() VL_PURE { return 0; } static constexpr int debug() VL_PURE { return 0; }
#endif #endif
/// Select initial value of otherwise uninitialized signals. /// Set the last VerilatedContext accessed
//// /// Generally threadContextp(value) should be called instead
/// 0 = Set to zeros static void lastContextp(VerilatedContext* contextp) VL_MT_SAFE { s_lastContextp = contextp; }
/// 1 = Set all bits to one /// Return the last VerilatedContext accessed
/// 2 = Randomize all bits /// Generally threadContextp() should be called instead
static void randReset(int val) VL_MT_SAFE; static VerilatedContext* lastContextp() VL_MT_SAFE {
static int randReset() VL_MT_SAFE { return s_s.s_randReset; } ///< Return randReset value if (!s_lastContextp) lastContextp(defaultContextp());
static void randSeed(int val) VL_MT_SAFE; return s_lastContextp;
static int randSeed() VL_MT_SAFE { return s_s.s_randSeed; } ///< Return randSeed value }
static vluint32_t randSeedEpoch() VL_MT_SAFE { return s_s.s_randSeedEpoch; } /// Set the VerilatedContext used by the current thread
/// Random seed extended to 64 bits, and defaulted if user seed==0
static vluint64_t randSeedDefault64() VL_MT_SAFE;
/// Enable calculation of unused signals /// If using multiple contexts, and threads are created by the user's
static void calcUnusedSigs(bool flag) VL_MT_SAFE; /// wrapper (not Verilator itself) then this must be called to set the
static bool calcUnusedSigs() VL_MT_SAFE { ///< Return calcUnusedSigs value /// context that applies to each thread
return s_s.s_calcUnusedSigs; static void threadContextp(VerilatedContext* contextp) VL_MT_SAFE {
t_s.t_contextp = contextp;
lastContextp(contextp);
} }
/// Current number of errors/assertions /// Return the VerilatedContext for the current thread
static void errorCount(int val) VL_MT_SAFE; static VerilatedContext* threadContextp() {
static void errorCountInc() VL_MT_SAFE; if (VL_UNLIKELY(!t_s.t_contextp)) t_s.t_contextp = lastContextp();
static int errorCount() VL_MT_SAFE { return s_s.s_errorCount; } return t_s.t_contextp;
/// Set number of errors/assertions before stop }
static void errorLimit(int val) VL_MT_SAFE; /// Return the global VerilatedContext, used if none created by user
static int errorLimit() VL_MT_SAFE { return s_s.s_errorLimit; } static VerilatedContext* defaultContextp() VL_MT_SAFE {
/// Did the simulation $finish? static VerilatedContext s_s;
static void gotFinish(bool flag) VL_MT_SAFE; return &s_s;
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)
#ifndef VL_NO_LEGACY
/// Call VerilatedContext::assertOn using current thread's VerilatedContext
static void assertOn(bool flag) VL_MT_SAFE { Verilated::threadContextp()->assertOn(flag); }
static bool assertOn() VL_MT_SAFE { return Verilated::threadContextp()->assertOn(); }
/// Call VerilatedContext::calcUnusedSigs using current thread's VerilatedContext
static void calcUnusedSigs(bool flag) VL_MT_SAFE {
Verilated::threadContextp()->calcUnusedSigs(flag);
}
static bool calcUnusedSigs() VL_MT_SAFE {
return Verilated::threadContextp()->calcUnusedSigs();
}
/// Call VerilatedContext::commandArgs using current thread's VerilatedContext
static void commandArgs(int argc, const char** argv) VL_MT_SAFE {
Verilated::threadContextp()->commandArgs(argc, argv);
}
static void commandArgs(int argc, char** argv) VL_MT_SAFE {
commandArgs(argc, const_cast<const char**>(argv));
}
static void commandArgsAdd(int argc, const char** argv) {
Verilated::threadContextp()->commandArgsAdd(argc, argv);
}
static const char* commandArgsPlusMatch(const char* prefixp) VL_MT_SAFE {
return Verilated::threadContextp()->commandArgsPlusMatch(prefixp);
}
/// Call VerilatedContext::errorLimit using current thread's VerilatedContext
static void errorLimit(int val) VL_MT_SAFE { Verilated::threadContextp()->errorLimit(val); }
static int errorLimit() VL_MT_SAFE { return Verilated::threadContextp()->errorLimit(); }
/// Call VerilatedContext::fatalOnError using current thread's VerilatedContext
static void fatalOnError(bool flag) VL_MT_SAFE {
Verilated::threadContextp()->fatalOnError(flag);
}
static bool fatalOnError() VL_MT_SAFE { return Verilated::threadContextp()->fatalOnError(); }
/// Call VerilatedContext::fatalOnVpiError using current thread's VerilatedContext
static void fatalOnVpiError(bool flag) VL_MT_SAFE {
Verilated::threadContextp()->fatalOnVpiError(flag);
}
static bool fatalOnVpiError() VL_MT_SAFE {
return Verilated::threadContextp()->fatalOnVpiError();
}
/// Call VerilatedContext::gotError using current thread's VerilatedContext
static void gotError(bool flag) VL_MT_SAFE { Verilated::threadContextp()->gotError(flag); }
static bool gotError() VL_MT_SAFE { return Verilated::threadContextp()->gotError(); }
/// Call VerilatedContext::gotFinish using current thread's VerilatedContext
static void gotFinish(bool flag) VL_MT_SAFE { Verilated::threadContextp()->gotFinish(flag); }
static bool gotFinish() VL_MT_SAFE { return Verilated::threadContextp()->gotFinish(); }
/// Call VerilatedContext::randReset using current thread's VerilatedContext
static void randReset(int val) VL_MT_SAFE { Verilated::threadContextp()->randReset(val); }
static int randReset() VL_MT_SAFE { return Verilated::threadContextp()->randReset(); }
/// Call VerilatedContext::randSeed using current thread's VerilatedContext
static void randSeed(int val) VL_MT_SAFE { Verilated::threadContextp()->randSeed(val); }
static int randSeed() VL_MT_SAFE { return Verilated::threadContextp()->randSeed(); }
/// Call VerilatedContext::time using current thread's VerilatedContext
static void time(vluint64_t val) VL_MT_SAFE { Verilated::threadContextp()->time(val); }
static vluint64_t time() VL_MT_SAFE { return Verilated::threadContextp()->time(); }
static void timeInc(vluint64_t add) VL_MT_UNSAFE { Verilated::threadContextp()->timeInc(add); }
static int timeunit() VL_MT_SAFE { return Verilated::threadContextp()->timeunit(); }
static int timeprecision() VL_MT_SAFE { return Verilated::threadContextp()->timeprecision(); }
/// Call VerilatedContext::tracesEverOn using current thread's VerilatedContext
static void traceEverOn(bool flag) VL_MT_SAFE { static void traceEverOn(bool flag) VL_MT_SAFE {
if (flag) calcUnusedSigs(flag); Verilated::threadContextp()->traceEverOn(flag);
} }
/// Enable/disable assertions #endif
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) VL_MT_SAFE;
static bool fatalOnVpiError() VL_MT_SAFE { return s_s.s_fatalOnVpiError; }
/// Time handling
static int timeunit() VL_MT_SAFE { return -s_s.s_timeunit; }
static const char* timeunitString() VL_MT_SAFE;
static void timeunit(int value) VL_MT_SAFE;
static int timeprecision() VL_MT_SAFE { return -s_s.s_timeprecision; }
static const char* timeprecisionString() VL_MT_SAFE;
static void timeprecision(int value) VL_MT_SAFE;
/// --prof-threads related settings
static void profThreadsStart(vluint64_t flag) VL_MT_SAFE;
static vluint64_t profThreadsStart() VL_MT_SAFE { return s_ns.s_profThreadsStart; }
static void profThreadsWindow(vluint64_t flag) VL_MT_SAFE;
static vluint32_t profThreadsWindow() VL_MT_SAFE { return s_ns.s_profThreadsWindow; }
static void profThreadsFilenamep(const char* flagp) VL_MT_SAFE;
static const char* profThreadsFilenamep() VL_MT_SAFE { return s_ns.s_profThreadsFilenamep; }
typedef void (*VoidPCb)(void*); // Callback type for below typedef void (*VoidPCb)(void*); // Callback type for below
/// Add callback to run on global flush /// Add callback to run on global flush
@ -519,18 +787,6 @@ public:
/// Run exit callbacks registered with addExitCb /// Run exit callbacks registered with addExitCb
static void runExitCallbacks() VL_MT_SAFE; static void runExitCallbacks() VL_MT_SAFE;
/// Record command line arguments, for retrieval by $test$plusargs/$value$plusargs,
/// and for parsing +verilator+ run-time arguments.
/// This should be called before the first model is created.
static void commandArgs(int argc, const char** argv) VL_MT_SAFE;
static void commandArgs(int argc, char** argv) VL_MT_SAFE {
commandArgs(argc, const_cast<const char**>(argv));
}
static void commandArgsAdd(int argc, const char** argv);
static CommandArgValues* getCommandArgs() 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) VL_MT_SAFE;
/// Return product name for (at least) VPI /// Return product name for (at least) VPI
static const char* productName() VL_PURE; static const char* productName() VL_PURE;
/// Return product version for (at least) VPI /// Return product version for (at least) VPI
@ -543,15 +799,23 @@ public:
/// This may only be called when no locks are held. /// This may only be called when no locks are held.
static void quiesce() VL_MT_SAFE; static void quiesce() VL_MT_SAFE;
#ifndef VL_NO_LEGACY
/// For debugging, print much of the Verilator internal state. /// For debugging, print much of the Verilator internal state.
/// The output of this function may change in future /// The output of this function may change in future
/// releases - contact the authors before production use. /// releases - contact the authors before production use.
static void internalsDump() VL_MT_SAFE; static void internalsDump() VL_MT_SAFE { Verilated::threadContextp()->internalsDump(); }
/// For debugging, print text list of all scope names with /// For debugging, print text list of all scope names with
/// dpiImport/Export context. This function may change in future /// dpiImport/Export context. This function may change in future
/// releases - contact the authors before production use. /// releases - contact the authors before production use.
static void scopesDump() VL_MT_SAFE; static void scopesDump() VL_MT_SAFE { Verilated::threadContextp()->scopesDump(); }
// Internal: Find scope
static const VerilatedScope* scopeFind(const char* namep) VL_MT_SAFE {
return Verilated::threadContextp()->scopeFind(namep);
}
static const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE {
return Verilated::threadContextp()->scopeNameMap();
}
#endif
public: public:
// METHODS - INTERNAL USE ONLY (but public due to what uses it) // METHODS - INTERNAL USE ONLY (but public due to what uses it)
@ -563,10 +827,6 @@ public:
static void nullPointerError(const char* filename, int linenum) VL_ATTR_NORETURN VL_MT_SAFE; static void nullPointerError(const char* filename, int linenum) VL_ATTR_NORETURN VL_MT_SAFE;
static void overWidthError(const char* signame) VL_ATTR_NORETURN VL_MT_SAFE; static void overWidthError(const char* signame) VL_ATTR_NORETURN VL_MT_SAFE;
// Internal: Find scope
static const VerilatedScope* scopeFind(const char* namep) VL_MT_SAFE;
static const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE;
// Internal: Get and set DPI context // Internal: Get and set DPI context
static const VerilatedScope* dpiScope() VL_MT_SAFE { return t_s.t_dpiScopep; } 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 dpiScope(const VerilatedScope* scopep) VL_MT_SAFE { t_s.t_dpiScopep = scopep; }
@ -582,13 +842,9 @@ public:
static int dpiLineno() VL_MT_SAFE { return t_s.t_dpiLineno; } static int dpiLineno() VL_MT_SAFE { return t_s.t_dpiLineno; }
static int exportFuncNum(const char* namep) VL_MT_SAFE; static int exportFuncNum(const char* namep) VL_MT_SAFE;
// Internal: Serialization setup
static constexpr size_t serialized1Size() VL_PURE { return sizeof(s_s); }
static constexpr void* serialized1Ptr() VL_MT_UNSAFE { return &s_s; } // For Serialize only
static size_t serialized2Size() VL_PURE;
static void* serialized2Ptr() VL_MT_UNSAFE;
#ifdef VL_THREADED #ifdef VL_THREADED
// Internal: Set the mtaskId, called when an mtask starts // Internal: Set the mtaskId, called when an mtask starts
// Per thread, so no need to be in VerilatedContext
static void mtaskId(vluint32_t id) VL_MT_SAFE { t_s.t_mtaskId = id; } static void mtaskId(vluint32_t id) VL_MT_SAFE { t_s.t_mtaskId = id; }
static vluint32_t mtaskId() VL_MT_SAFE { return t_s.t_mtaskId; } static vluint32_t mtaskId() VL_MT_SAFE { return t_s.t_mtaskId; }
static void endOfEvalReqdInc() VL_MT_SAFE { ++t_s.t_endOfEvalReqd; } static void endOfEvalReqdInc() VL_MT_SAFE { ++t_s.t_endOfEvalReqd; }
@ -608,6 +864,9 @@ private:
#endif #endif
}; };
inline void VerilatedContext::debug(int val) VL_MT_SAFE { Verilated::debug(val); }
inline int VerilatedContext::debug() VL_MT_SAFE { return Verilated::debug(); }
//========================================================================= //=========================================================================
// Extern functions -- User may override -- See verilated.cpp // Extern functions -- User may override -- See verilated.cpp
@ -686,7 +945,8 @@ inline QData VL_RDTSC_Q() {
} }
#endif #endif
extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp) VL_MT_SAFE; extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
const VerilatedContext* contextp) VL_MT_SAFE;
/// Math /// Math
extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp,
@ -862,16 +1122,40 @@ extern int VL_TIME_STR_CONVERT(const char* strp) VL_PURE;
// Already defined: extern sc_time sc_time_stamp(); // Already defined: extern sc_time sc_time_stamp();
inline vluint64_t vl_time_stamp64() { return sc_time_stamp().value(); } inline vluint64_t vl_time_stamp64() { return sc_time_stamp().value(); }
#else // Non-SystemC #else // Non-SystemC
# ifdef VL_TIME_STAMP64 # if !defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY)
extern vluint64_t vl_time_stamp64(); # ifdef VL_TIME_STAMP64
# else // vl_time_stamp64() may be optionally defined by the user to return time.
extern double sc_time_stamp(); // Verilator 4.032 and newer // On MSVC++ weak symbols are not supported so must be declared, or define
inline vluint64_t vl_time_stamp64() { return static_cast<vluint64_t>(sc_time_stamp()); } // VL_TIME_CONTEXT.
extern vluint64_t vl_time_stamp64() VL_ATTR_WEAK;
# else
// sc_time_stamp() may be optionally defined by the user to return time.
// On MSVC++ weak symbols are not supported so must be declared, or define
// VL_TIME_CONTEXT.
extern double sc_time_stamp() VL_ATTR_WEAK; // Verilator 4.032 and newer
inline vluint64_t vl_time_stamp64() {
// clang9.0.1 requires & although we really do want the weak symbol value
return VL_LIKELY(&sc_time_stamp) ? static_cast<vluint64_t>(sc_time_stamp()) : 0;
}
# endif
# endif # endif
#endif #endif
#define VL_TIME_Q() (static_cast<QData>(vl_time_stamp64())) inline vluint64_t VerilatedContext::time() const VL_MT_SAFE {
#define VL_TIME_D() (static_cast<double>(vl_time_stamp64())) // When using non-default context, fastest path is return time
if (VL_LIKELY(m_s.m_time)) return m_s.m_time;
#if defined(SYSTEMC_VERSION) || (!defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY))
// Zero time could mean really at zero, or using callback
// clang9.0.1 requires & although we really do want the weak symbol value
if (VL_LIKELY(&vl_time_stamp64)) { // else is weak symbol that is not defined
return vl_time_stamp64();
}
#endif
return 0;
}
#define VL_TIME_Q() (Verilated::threadContextp()->time())
#define VL_TIME_D() (static_cast<double>(VL_TIME_Q()))
/// Time scaled from 1-per-precision into a module's time units ("Unit"-ed, not "United") /// Time scaled from 1-per-precision into a module's time units ("Unit"-ed, not "United")
// Optimized assuming scale is always constant. // Optimized assuming scale is always constant.

View File

@ -83,11 +83,13 @@ public:
//============================================================================= //=============================================================================
// VerilatedCovImp // VerilatedCovImp
/// Implementation class for VerilatedCov. See that class for public method information. ///
/// All value and keys are indexed into a unique number. Thus we can greatly reduce /// Implementation class for VerilatedCovContext. See that class for
/// the storage requirements for otherwise identical keys. /// public method information. All value and keys are indexed into a
/// unique number. Thus we can greatly reduce the storage requirements for
/// otherwise identical keys.
class VerilatedCovImp final { class VerilatedCovImp final : public VerilatedCovContext {
private: private:
// TYPES // TYPES
typedef std::map<const std::string, int> ValueIndexMap; typedef std::map<const std::string, int> ValueIndexMap;
@ -106,12 +108,14 @@ private:
const char* m_insertFilenamep VL_GUARDED_BY(m_mutex) = nullptr; ///< Filename about to insert const char* m_insertFilenamep VL_GUARDED_BY(m_mutex) = nullptr; ///< Filename about to insert
int m_insertLineno VL_GUARDED_BY(m_mutex) = 0; ///< Line number about to insert int m_insertLineno VL_GUARDED_BY(m_mutex) = 0; ///< Line number about to insert
public:
// CONSTRUCTORS // CONSTRUCTORS
VerilatedCovImp() = default; VerilatedCovImp() = default;
VL_UNCOPYABLE(VerilatedCovImp); VL_UNCOPYABLE(VerilatedCovImp);
public: protected:
~VerilatedCovImp() { clearGuts(); } friend class VerilatedCovContext;
virtual ~VerilatedCovImp() override { clearGuts(); }
static VerilatedCovImp& imp() VL_MT_SAFE { static VerilatedCovImp& imp() VL_MT_SAFE {
static VerilatedCovImp s_singleton; static VerilatedCovImp s_singleton;
return s_singleton; return s_singleton;
@ -408,34 +412,32 @@ public:
}; };
//============================================================================= //=============================================================================
// VerilatedCov // VerilatedCovContext
void VerilatedCov::clear() VL_MT_SAFE { VerilatedCovImp::imp().clear(); } void VerilatedCovContext::clear() VL_MT_SAFE { impp()->clear(); }
void VerilatedCov::clearNonMatch(const char* matchp) VL_MT_SAFE { void VerilatedCovContext::clearNonMatch(const char* matchp) VL_MT_SAFE {
VerilatedCovImp::imp().clearNonMatch(matchp); impp()->clearNonMatch(matchp);
} }
void VerilatedCov::zero() VL_MT_SAFE { VerilatedCovImp::imp().zero(); } void VerilatedCovContext::zero() VL_MT_SAFE { impp()->zero(); }
void VerilatedCov::write(const char* filenamep) VL_MT_SAFE { void VerilatedCovContext::write(const char* filenamep) VL_MT_SAFE { impp()->write(filenamep); }
VerilatedCovImp::imp().write(filenamep); void VerilatedCovContext::_inserti(vluint32_t* itemp) VL_MT_SAFE {
impp()->inserti(new VerilatedCoverItemSpec<vluint32_t>(itemp));
} }
void VerilatedCov::_inserti(vluint32_t* itemp) VL_MT_SAFE { void VerilatedCovContext::_inserti(vluint64_t* itemp) VL_MT_SAFE {
VerilatedCovImp::imp().inserti(new VerilatedCoverItemSpec<vluint32_t>(itemp)); impp()->inserti(new VerilatedCoverItemSpec<vluint64_t>(itemp));
} }
void VerilatedCov::_inserti(vluint64_t* itemp) VL_MT_SAFE { void VerilatedCovContext::_insertf(const char* filename, int lineno) VL_MT_SAFE {
VerilatedCovImp::imp().inserti(new VerilatedCoverItemSpec<vluint64_t>(itemp)); impp()->insertf(filename, lineno);
}
void VerilatedCov::_insertf(const char* filename, int lineno) VL_MT_SAFE {
VerilatedCovImp::imp().insertf(filename, lineno);
} }
#define K(n) const char* key##n #define K(n) const char* key##n
#define A(n) const char *key##n, const char *valp##n // Argument list #define A(n) const char *key##n, const char *valp##n // Argument list
#define C(n) key##n, valp##n // Calling argument list #define C(n) key##n, valp##n // Calling argument list
#define N(n) "", "" // Null argument list #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), void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9),
A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18), A(19), A(20), A(10), A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18),
A(21), A(22), A(23), A(24), A(25), A(26), A(27), A(28), A(19), A(20), A(21), A(22), A(23), A(24), A(25), A(26), A(27),
A(29)) VL_MT_SAFE { A(28), A(29)) VL_MT_SAFE {
const char* keyps[VerilatedCovConst::MAX_KEYS] const char* keyps[VerilatedCovConst::MAX_KEYS]
= {nullptr, nullptr, nullptr, // filename,lineno,page = {nullptr, nullptr, nullptr, // filename,lineno,page
key0, key1, key2, key3, key4, key5, key6, key7, key8, key9, key0, key1, key2, key3, key4, key5, key6, key7, key8, key9,
@ -446,26 +448,26 @@ void VerilatedCov::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8)
valp0, valp1, valp2, valp3, valp4, valp5, valp6, valp7, valp8, valp9, valp0, valp1, valp2, valp3, valp4, valp5, valp6, valp7, valp8, valp9,
valp10, valp11, valp12, valp13, valp14, valp15, valp16, valp17, valp18, valp19, valp10, valp11, valp12, valp13, valp14, valp15, valp16, valp17, valp18, valp19,
valp20, valp21, valp22, valp23, valp24, valp25, valp26, valp27, valp28, valp29}; valp20, valp21, valp22, valp23, valp24, valp25, valp26, valp27, valp28, valp29};
VerilatedCovImp::imp().insertp(keyps, valps); impp()->insertp(keyps, valps);
} }
// And versions with fewer arguments (oh for a language with named parameters!) // 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), void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8),
A(9)) VL_MT_SAFE { 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), _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(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)); 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), void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9),
A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18), A(10), A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18),
A(19)) VL_MT_SAFE { 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), _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), 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)); N(25), N(26), N(27), N(28), N(29));
} }
// Backward compatibility for Verilator // Backward compatibility for Verilator
void VerilatedCov::_insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4), void VerilatedCovContext::_insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4),
const std::string& val4, A(5), A(6), A(7)) VL_MT_SAFE { const std::string& val4, A(5), A(6), A(7)) VL_MT_SAFE {
std::string val2str = vlCovCvtToStr(val2); std::string val2str = vlCovCvtToStr(val2);
std::string val3str = vlCovCvtToStr(val3); std::string val3str = vlCovCvtToStr(val3);
_insertp(C(0), C(1), key2, val2str.c_str(), key3, val3str.c_str(), key4, val4.c_str(), C(5), _insertp(C(0), C(1), key2, val2str.c_str(), key3, val3str.c_str(), key4, val4.c_str(), C(5),
@ -476,3 +478,26 @@ void VerilatedCov::_insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4),
#undef C #undef C
#undef N #undef N
#undef K #undef K
//=============================================================================
// VerilatedCov
#ifndef VL_NO_LEGACY
VerilatedCovContext* VerilatedCov::threadCovp() VL_MT_SAFE {
return Verilated::threadContextp()->coveragep();
}
#endif
//=============================================================================
// VerilatedContext
VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE {
static VerilatedMutex s_mutex;
if (VL_UNLIKELY(!m_coveragep)) {
const VerilatedLockGuard lock(s_mutex);
if (VL_LIKELY(!m_coveragep)) { // Not redundant, prevents race
m_coveragep.reset(new VerilatedCovImp);
}
}
return reinterpret_cast<VerilatedCovContext*>(m_coveragep.get());
}

View File

@ -20,11 +20,14 @@
#define VERILATOR_VERILATED_COV_H_ #define VERILATOR_VERILATED_COV_H_
#include "verilatedos.h" #include "verilatedos.h"
#include "verilated.h"
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <string> #include <string>
class VerilatedCovImp;
//============================================================================= //=============================================================================
/// Conditionally compile coverage code /// Conditionally compile coverage code
@ -68,9 +71,9 @@
/// VL_COVER_INSERT(&m_cases[i], "comment", "Coverage Case", "i", cvtToNumStr(i)); /// VL_COVER_INSERT(&m_cases[i], "comment", "Coverage Case", "i", cvtToNumStr(i));
/// } /// }
#define VL_COVER_INSERT(countp, ...) \ #define VL_COVER_INSERT(covcontextp, countp, ...) \
VL_IF_COVER(VerilatedCov::_inserti(countp); VerilatedCov::_insertf(__FILE__, __LINE__); \ VL_IF_COVER(covcontextp->_inserti(countp); covcontextp->_insertf(__FILE__, __LINE__); \
VerilatedCov::_insertp("hier", name(), __VA_ARGS__)) covcontextp->_insertp("hier", name(), __VA_ARGS__))
//============================================================================= //=============================================================================
/// Convert VL_COVER_INSERT value arguments to strings /// Convert VL_COVER_INSERT value arguments to strings
@ -83,29 +86,35 @@ template <class T> std::string vlCovCvtToStr(const T& t) VL_PURE {
//============================================================================= //=============================================================================
// VerilatedCov // VerilatedCov
/// Verilator coverage global class /// Verilator coverage per-context structure
////
/// Global class with methods affecting all coverage data.
/// All public methods in this class are thread safe. /// All public methods in this class are thread safe.
class VerilatedCov final { class VerilatedCovContext VL_NOT_FINAL : public VerilatedVirtualBase {
VL_UNCOPYABLE(VerilatedCov); VL_UNCOPYABLE(VerilatedCovContext);
public: public:
// GLOBAL METHODS // METHODS
/// Return default filename /// Return default filename
static const char* defaultFilename() VL_PURE { return "coverage.dat"; } static const char* defaultFilename() VL_PURE { return "coverage.dat"; }
/// Write all coverage data to a file /// Write all coverage data to a file
static void write(const char* filenamep = defaultFilename()) VL_MT_SAFE; void write(const char* filenamep = defaultFilename()) VL_MT_SAFE;
/// Clear coverage points (and call delete on all items)
void clear() VL_MT_SAFE;
/// Clear items not matching the provided string
void clearNonMatch(const char* matchp) VL_MT_SAFE;
/// Zero coverage points
void zero() VL_MT_SAFE;
public: // But Internal use only
/// Insert a coverage item /// Insert a coverage item
/// We accept from 1-30 key/value pairs, all as strings. /// We accept from 1-30 key/value pairs, all as strings.
/// Call _insert1, followed by _insert2 and _insert3 /// Call _insert1, followed by _insert2 and _insert3
/// Do not call directly; use VL_COVER_INSERT or higher level macros instead /// 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) // _insert1: Remember item pointer with count. (Not const, as may add zeroing function)
static void _inserti(vluint32_t* itemp) VL_MT_SAFE; void _inserti(vluint32_t* itemp) VL_MT_SAFE;
static void _inserti(vluint64_t* itemp) VL_MT_SAFE; void _inserti(vluint64_t* itemp) VL_MT_SAFE;
// _insert2: Set default filename and line number // _insert2: Set default filename and line number
static void _insertf(const char* filename, int lineno) VL_MT_SAFE; void _insertf(const char* filename, int lineno) VL_MT_SAFE;
// _insert3: Set parameters // _insert3: Set parameters
// We could have just the maximum argument version, but this compiles // We could have just the maximum argument version, but this compiles
// much slower (nearly 2x) than having smaller versions also. However // much slower (nearly 2x) than having smaller versions also. However
@ -113,25 +122,65 @@ public:
#define K(n) const char* key##n #define K(n) const char* key##n
#define A(n) const char *key##n, const char *valp##n // Argument list #define A(n) const char *key##n, const char *valp##n // Argument list
#define D(n) const char *key##n = nullptr, const char *valp##n = nullptr // Argument list #define D(n) const char *key##n = nullptr, const char *valp##n = nullptr // Argument list
static void _insertp(D(0), D(1), D(2), D(3), D(4), D(5), D(6), D(7), D(8), D(9)); void _insertp(D(0), D(1), D(2), D(3), D(4), D(5), D(6), D(7), D(8), D(9));
static void _insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10), D(11), void _insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10), D(11), D(12),
D(12), D(13), D(14), D(15), D(16), D(17), D(18), D(19)); D(13), D(14), D(15), D(16), D(17), D(18), D(19));
static void _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), void _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(12), A(13), A(14), A(15), A(16), A(17), A(18), A(19), A(20), D(21), A(13), A(14), A(15), A(16), A(17), A(18), A(19), A(20), D(21), D(22), D(23),
D(22), D(23), D(24), D(25), D(26), D(27), D(28), D(29)); D(24), D(25), D(26), D(27), D(28), D(29));
// Backward compatibility for Verilator // Backward compatibility for Verilator
static void _insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4), const std::string& val4, void _insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4), const std::string& val4, A(5),
A(5), A(6), A(7)); A(6), A(7));
#undef K #undef K
#undef A #undef A
#undef D #undef D
/// Clear coverage points (and call delete on all items)
static void clear() VL_MT_SAFE; protected:
/// Clear items not matching the provided string friend class VerilatedCovImp;
static void clearNonMatch(const char* matchp) VL_MT_SAFE; // CONSTRUCTORS
/// Zero coverage points // Internal: Only made as part of VerilatedCovImp
static void zero() VL_MT_SAFE; VerilatedCovContext() {}
virtual ~VerilatedCovContext() {}
// METHODS
// Internal: access to implementation class
VerilatedCovImp* impp() { return reinterpret_cast<VerilatedCovImp*>(this); }
}; };
//=============================================================================
// VerilatedCov
/// Verilator coverage global class
///
/// Global class that accesses via current thread's context. These are
/// provided for backward-compatibility, use VerilatedContext->coveragep()
/// instead.
#ifndef VL_NO_LEGACY
class VerilatedCov final {
VL_UNCOPYABLE(VerilatedCov);
public:
// METHODS
/// Return default filename for the current thread
static const char* defaultFilename() VL_PURE { return VerilatedCovContext::defaultFilename(); }
/// Write all coverage data to a file for the current thread
static void write(const char* filenamep = defaultFilename()) VL_MT_SAFE {
threadCovp()->write(filenamep);
}
/// Clear coverage points (and call delete on all items) for the current thread
static void clear() VL_MT_SAFE { threadCovp()->clear(); }
/// Clear items not matching the provided string for the current thread
static void clearNonMatch(const char* matchp) VL_MT_SAFE {
threadCovp()->clearNonMatch(matchp);
}
/// Zero coverage points for the current thread
static void zero() VL_MT_SAFE { threadCovp()->zero(); }
private:
/// Current thread's coverage structure
static VerilatedCovContext* threadCovp() VL_MT_SAFE;
};
#endif
#endif // Guard #endif // Guard

View File

@ -740,7 +740,7 @@ const char* svGetNameFromScope(const svScope scope) {
svScope svGetScopeFromName(const char* scopeName) { svScope svGetScopeFromName(const char* scopeName) {
// NOLINTNEXTLINE(google-readability-casting) // NOLINTNEXTLINE(google-readability-casting)
return (svScope)(VerilatedImp::scopeFind(scopeName)); return (svScope)(Verilated::threadContextp()->scopeFind(scopeName));
} }
int svPutUserData(const svScope scope, void* userKey, void* userData) { int svPutUserData(const svScope scope, void* userKey, void* userData) {

View File

@ -65,8 +65,8 @@ VerilatedFst::~VerilatedFst() {
if (m_strbuf) VL_DO_CLEAR(delete[] m_strbuf, m_strbuf = nullptr); if (m_strbuf) VL_DO_CLEAR(delete[] m_strbuf, m_strbuf = nullptr);
} }
void VerilatedFst::open(const char* filename) VL_MT_UNSAFE { void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
m_assertOne.check(); const VerilatedLockGuard lock(m_mutex);
m_fst = fstWriterCreate(filename, 1); m_fst = fstWriterCreate(filename, 1);
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4); fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref
@ -96,15 +96,16 @@ void VerilatedFst::open(const char* filename) VL_MT_UNSAFE {
if (!m_strbuf) m_strbuf = new char[maxBits() + 32]; if (!m_strbuf) m_strbuf = new char[maxBits() + 32];
} }
void VerilatedFst::close() { void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
m_assertOne.check(); const VerilatedLockGuard lock(m_mutex);
VerilatedTrace<VerilatedFst>::close(); VerilatedTrace<VerilatedFst>::closeBase();
fstWriterClose(m_fst); fstWriterClose(m_fst);
m_fst = nullptr; m_fst = nullptr;
} }
void VerilatedFst::flush() { void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedTrace<VerilatedFst>::flush(); const VerilatedLockGuard lock(m_mutex);
VerilatedTrace<VerilatedFst>::flushBase();
fstWriterFlushContext(m_fst); fstWriterFlushContext(m_fst);
} }

View File

@ -15,6 +15,7 @@
/// \brief C++ Tracing in FST Format /// \brief C++ Tracing in FST Format
/// ///
//============================================================================= //=============================================================================
// SPDIFF_OFF
#ifndef VERILATOR_VERILATED_FST_C_H_ #ifndef VERILATOR_VERILATED_FST_C_H_
#define VERILATOR_VERILATED_FST_C_H_ #define VERILATOR_VERILATED_FST_C_H_
@ -81,18 +82,19 @@ protected:
public: public:
//========================================================================= //=========================================================================
// External interface to client code // External interface to client code
// (All must be threadsafe)
explicit VerilatedFst(void* fst = nullptr); explicit VerilatedFst(void* fst = nullptr);
~VerilatedFst(); ~VerilatedFst();
// Open the file; call isOpen() to see if errors // Open the file; call isOpen() to see if errors
void open(const char* filename) VL_MT_UNSAFE; void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex);
// Close the file // Close the file
void close() VL_MT_UNSAFE; void close() VL_MT_SAFE_EXCLUDES(m_mutex);
// Flush any remaining data to this file // Flush any remaining data to this file
void flush() VL_MT_UNSAFE; void flush() VL_MT_SAFE_EXCLUDES(m_mutex);
// Return if file is open // Return if file is open
bool isOpen() const { return m_fst != nullptr; } bool isOpen() const VL_MT_SAFE { return m_fst != nullptr; }
//========================================================================= //=========================================================================
// Internal interface to Verilator generated code // Internal interface to Verilator generated code
@ -125,7 +127,6 @@ template <> void VerilatedTrace<VerilatedFst>::set_time_resolution(const std::st
// VerilatedFstC // VerilatedFstC
/// Create a FST dump file in C standalone (no SystemC) simulations. /// Create a FST dump file in C standalone (no SystemC) simulations.
/// Also derived for use in SystemC simulations. /// Also derived for use in SystemC simulations.
/// Thread safety: Unless otherwise indicated, every function is VL_MT_UNSAFE_ONE
class VerilatedFstC final { class VerilatedFstC final {
VerilatedFst m_sptrace; ///< Trace file being created VerilatedFst m_sptrace; ///< Trace file being created
@ -139,19 +140,17 @@ public:
: m_sptrace{filep} {} : m_sptrace{filep} {}
/// Destruct, flush, and close the dump /// Destruct, flush, and close the dump
~VerilatedFstC() { close(); } ~VerilatedFstC() { close(); }
/// Routines can only be called from one thread; allow next call from different thread
void changeThread() { spTrace()->changeThread(); }
// METHODS - User called // METHODS - User called
/// Return if file is open /// Return if file is open
bool isOpen() const { return m_sptrace.isOpen(); } bool isOpen() const VL_MT_SAFE { return m_sptrace.isOpen(); }
/// Open a new FST file /// Open a new FST file
void open(const char* filename) VL_MT_UNSAFE_ONE { m_sptrace.open(filename); } void open(const char* filename) VL_MT_SAFE { m_sptrace.open(filename); }
/// Close dump /// Close dump
void close() VL_MT_UNSAFE_ONE { m_sptrace.close(); } void close() VL_MT_SAFE { m_sptrace.close(); }
/// Flush dump /// Flush dump
void flush() VL_MT_UNSAFE_ONE { m_sptrace.flush(); } void flush() VL_MT_SAFE { m_sptrace.flush(); }
/// Write one cycle of dump data /// Write one cycle of dump data
void dump(vluint64_t timeui) { m_sptrace.dump(timeui); } void dump(vluint64_t timeui) { m_sptrace.dump(timeui); }
/// Write one cycle of dump data - backward compatible and to reduce /// Write one cycle of dump data - backward compatible and to reduce
@ -166,13 +165,17 @@ public:
// Set time units (s/ms, defaults to ns) // Set time units (s/ms, defaults to ns)
// Users should not need to call this, as for Verilated models, these // Users should not need to call this, as for Verilated models, these
// propage from the Verilated default timeunit // propage from the Verilated default timeunit
void set_time_unit(const char* unitp) { m_sptrace.set_time_unit(unitp); } void set_time_unit(const char* unitp) VL_MT_SAFE { m_sptrace.set_time_unit(unitp); }
void set_time_unit(const std::string& unit) { m_sptrace.set_time_unit(unit); } void set_time_unit(const std::string& unit) VL_MT_SAFE { m_sptrace.set_time_unit(unit); }
// Set time resolution (s/ms, defaults to ns) // Set time resolution (s/ms, defaults to ns)
// Users should not need to call this, as for Verilated models, these // Users should not need to call this, as for Verilated models, these
// propage from the Verilated default timeprecision // propage from the Verilated default timeprecision
void set_time_resolution(const char* unitp) { m_sptrace.set_time_resolution(unitp); } void set_time_resolution(const char* unitp) VL_MT_SAFE {
void set_time_resolution(const std::string& unit) { m_sptrace.set_time_resolution(unit); } m_sptrace.set_time_resolution(unitp);
}
void set_time_resolution(const std::string& unit) VL_MT_SAFE {
m_sptrace.set_time_resolution(unit);
}
// Internal class access // Internal class access
inline VerilatedFst* spTrace() { return &m_sptrace; }; inline VerilatedFst* spTrace() { return &m_sptrace; };

View File

@ -900,8 +900,8 @@ extern IData VL_SSCANF_INX(int lbits, const std::string& ld, const char* formatp
extern void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp, extern void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp,
...) VL_MT_SAFE; ...) VL_MT_SAFE;
extern std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE; extern std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE;
extern void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, extern void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, int width,
int width) VL_MT_SAFE; VerilatedContext* contextp) VL_MT_SAFE;
extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE; extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE;
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, CData& rdr) VL_MT_SAFE { inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, CData& rdr) VL_MT_SAFE {
WData rwp[2]; // WData must always be at least 2 WData rwp[2]; // WData must always be at least 2
@ -956,10 +956,4 @@ extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE;
extern IData VL_FGETS_NI(std::string& dest, IData fpi); extern IData VL_FGETS_NI(std::string& dest, IData fpi);
//======================================================================
// Dumping
extern const char* vl_dumpctl_filenamep(bool setit = false,
const std::string& filename = "") VL_MT_SAFE;
#endif // Guard #endif // Guard

View File

@ -20,7 +20,8 @@
#define VERILATOR_VERILATED_IMP_H_ ///< Header Guard #define VERILATOR_VERILATED_IMP_H_ ///< Header Guard
// clang-format off // clang-format off
#if !defined(VERILATOR_VERILATED_CPP_) && !defined(VERILATOR_VERILATED_DPI_CPP_) && !defined(VERILATOR_VERILATED_VPI_CPP_) #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" # error "verilated_imp.h only to be included by verilated*.cpp internals"
#endif #endif
@ -190,78 +191,238 @@ public:
} }
}; };
//======================================================================
// 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 {
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() {
static Statics s_s;
return s_s;
}
private:
// CONSTRUCTORS - no data can live here, use only VerilatedContext
VerilatedContextImp() = delete;
~VerilatedContextImp() = delete;
public: // But only for verilated*.cpp
// METHODS - extending into VerilatedContext, call via impp()->
// Random seed handling
vluint64_t randSeedDefault64() const VL_MT_SAFE;
static vluint32_t randSeedEpoch() VL_MT_SAFE { return s().s_randSeedEpoch; }
// METHODS - timeformat
int timeFormatUnits() 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() 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);
public: // But only for verilated*.cpp
// METHODS - scope name
void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE;
void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE;
public: // But only for verilated*.cpp
// METHODS - file IO
IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
if (m_fdFreeMct.empty()) return 0;
IData idx = m_fdFreeMct.back();
m_fdFreeMct.pop_back();
m_fdps[idx] = 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* fp = 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(), (FILE*)0);
m_fdFree.resize(excess);
for (std::size_t i = 0, id = start; i < m_fdFree.size(); ++i, ++id) {
m_fdFree[i] = id;
}
}
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) 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 0;
return static_cast<IData>(
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 0;
return static_cast<IData>(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 ((fdi & (1 << 31)) != 0) {
// Non-MCD case
IData idx = VL_MASK_I(31) & fdi;
if (VL_UNLIKELY(idx >= m_fdps.size())) return;
if (VL_UNLIKELY(!m_fdps[idx])) return; // Already free
fclose(m_fdps[idx]);
m_fdps[idx] = (FILE*)0;
m_fdFree.push_back(idx);
} else {
// MCD case
for (int i = 0; (fdi != 0) && (i < 31); i++, fdi >>= 1) {
if (fdi & VL_MASK_I(1)) {
fclose(m_fdps[i]);
m_fdps[i] = nullptr;
m_fdFreeMct.push_back(i);
}
}
}
}
inline 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;
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
for (size_t i = 0; (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 commandArgsAddGuts(int argc, const char** argv);
void commandArgVl(const std::string& arg);
bool commandArgVlValue(const std::string& arg, const std::string& prefix, std::string& valuer);
void commandArgDump() const VL_MT_SAFE_EXCLUDES(m_argMutex);
};
//====================================================================== //======================================================================
// VerilatedImp // VerilatedImp
class VerilatedImpData final { class VerilatedImpData final {
// Whole class is internal use only - Global information shared between verilated*.cpp files. // Whole class is internal use only - Global information shared between verilated*.cpp files.
// All only medium-speed, so use singleton function
protected: protected:
friend class Verilated; friend class Verilated;
friend class VerilatedImp; friend class VerilatedImp;
// TYPES // TYPES
typedef std::vector<std::string> ArgVec;
typedef std::map<std::pair<const void*, void*>, void*> UserMap; typedef std::map<std::pair<const void*, void*>, void*> UserMap;
typedef std::map<const char*, int, VerilatedCStrCmp> ExportNameMap; typedef std::map<const char*, int, VerilatedCStrCmp> ExportNameMap;
// MEMBERS // MEMBERS
struct Serialized { // All these members serialized/deserialized
int m_timeFormatUnits = UNITS_NONE; // $timeformat units
int m_timeFormatPrecision = 0; // $timeformat number of decimal places
int m_timeFormatWidth = 20; // $timeformat character width
enum { UNITS_NONE = 99 }; // Default based on precision
Serialized() = default;
~Serialized() = default;
} m_ser;
VerilatedMutex m_sergMutex; ///< Protect m_ser
struct SerializedG { // All these members serialized/deserialized and guarded
std::string m_timeFormatSuffix; // $timeformat printf format
} m_serg VL_GUARDED_BY(m_sergMutex);
// Nothing below here is save-restored; users expected to re-register appropriately // Nothing below here is save-restored; users expected to re-register appropriately
VerilatedMutex m_argMutex; ///< Protect m_argVec, m_argVecLoaded
/// Argument list (NOT save-restored, may want different results)
ArgVec m_argVec VL_GUARDED_BY(m_argMutex);
bool m_argVecLoaded VL_GUARDED_BY(m_argMutex); ///< Ever loaded argument list
VerilatedMutex m_userMapMutex; ///< Protect m_userMap 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> UserMap m_userMap VL_GUARDED_BY(m_userMapMutex); ///< Map of <(scope,userkey), userData>
VerilatedMutex m_nameMutex; ///< Protect m_nameMap
/// Map of <scope_name, scope pointer>
VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex);
VerilatedMutex m_hierMapMutex; ///< Protect m_hierMap VerilatedMutex m_hierMapMutex; ///< Protect m_hierMap
/// Map that represents scope hierarchy /// Map that represents scope hierarchy
// Used by hierarchyAdd, hierarchyRemove, hierarchyMap
VerilatedHierarchyMap m_hierMap VL_GUARDED_BY(m_hierMapMutex); VerilatedHierarchyMap m_hierMap VL_GUARDED_BY(m_hierMapMutex);
// Slow - somewhat static: // Slow - somewhat static:
VerilatedMutex m_exportMutex; ///< Protect m_nameMap VerilatedMutex m_exportMutex; ///< Protect m_nameMap
/// Map of <export_func_proto, func number> /// 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); ExportNameMap m_exportMap VL_GUARDED_BY(m_exportMutex);
int m_exportNext VL_GUARDED_BY(m_exportMutex); ///< Next export funcnum int m_exportNext VL_GUARDED_BY(m_exportMutex) = 0; ///< Next export funcnum
// File I/O
VerilatedMutex m_fdMutex; ///< Protect m_fdps, m_fdFree
std::vector<FILE*> m_fdps VL_GUARDED_BY(m_fdMutex); ///< File descriptors
/// List of free descriptors (SLOW - FOPEN/CLOSE only)
std::vector<IData> m_fdFree VL_GUARDED_BY(m_fdMutex);
// List of free descriptors in the MCT region [4, 32)
std::vector<IData> m_fdFreeMct VL_GUARDED_BY(m_fdMutex);
// CONSTRUCTORS // CONSTRUCTORS
VerilatedImpData() VerilatedImpData() = default;
: m_argVecLoaded{false}
, m_exportNext{0} {
m_fdps.resize(31);
std::fill(m_fdps.begin(), m_fdps.end(), (FILE*)0);
m_fdFreeMct.resize(30);
for (std::size_t i = 0, id = 1; i < m_fdFreeMct.size(); ++i, ++id) { m_fdFreeMct[i] = id; }
}
}; };
class VerilatedImp final { class VerilatedImp final {
@ -270,95 +431,62 @@ protected:
friend class Verilated; friend class Verilated;
// MEMBERS // MEMBERS
union VerilatedImpU { ///< Enclose in an union to call ctor/dtor manually static VerilatedImpData& s() { // Singleton
VerilatedImpData v; static VerilatedImpData s_s;
VerilatedImpU() {} // Can't be = default; return s_s;
~VerilatedImpU() {} // Can't be = default; }
};
static VerilatedImpU s_s; ///< Static Singleton; One and only static this
public: // But only for verilated*.cpp public: // But only for verilated*.cpp
// CONSTRUCTORS // CONSTRUCTORS
VerilatedImp() = default; VerilatedImp() = default;
~VerilatedImp() = default; ~VerilatedImp() = default;
static void setup();
static void teardown();
private: private:
VL_UNCOPYABLE(VerilatedImp); VL_UNCOPYABLE(VerilatedImp);
public: public:
// METHODS - debug // METHODS - debug
static void internalsDump() VL_MT_SAFE;
static void versionDump() VL_MT_SAFE; static void versionDump() VL_MT_SAFE;
// METHODS - arguments
public:
static void commandArgs(int argc, const char** argv) VL_EXCLUDES(s_s.v.m_argMutex);
static void commandArgsAdd(int argc, const char** argv) VL_EXCLUDES(s_s.v.m_argMutex);
static std::string argPlusMatch(const char* prefixp) VL_EXCLUDES(s_s.v.m_argMutex) {
const VerilatedLockGuard lock(s_s.v.m_argMutex);
// Note prefixp does not include the leading "+"
size_t len = strlen(prefixp);
if (VL_UNLIKELY(!s_s.v.m_argVecLoaded)) {
s_s.v.m_argVecLoaded = true; // Complain only once
VL_FATAL_MT("unknown", 0, "",
"%Error: Verilog called $test$plusargs or $value$plusargs without"
" testbench C first calling Verilated::commandArgs(argc,argv).");
}
for (const auto& i : s_s.v.m_argVec) {
if (i[0] == '+') {
if (0 == strncmp(prefixp, i.c_str() + 1, len)) return i;
}
}
return "";
}
private:
static void commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(s_s.v.m_argMutex);
static void commandArgVl(const std::string& arg);
static bool commandArgVlValue(const std::string& arg, const std::string& prefix,
std::string& valuer);
public: public:
// METHODS - user scope tracking // METHODS - user scope tracking
// We implement this as a single large map instead of one map per scope // 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 // 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. // 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 inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE { static inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.m_userMapMutex); const VerilatedLockGuard lock(s().m_userMapMutex);
const auto it = s_s.v.m_userMap.find(std::make_pair(scopep, userKey)); const auto it = s().m_userMap.find(std::make_pair(scopep, userKey));
if (it != s_s.v.m_userMap.end()) { if (it != s().m_userMap.end()) {
it->second = userData; it->second = userData;
} else { } else {
s_s.v.m_userMap.emplace(std::make_pair(scopep, userKey), userData); s().m_userMap.emplace(std::make_pair(scopep, userKey), userData);
} }
} }
static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE { static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.m_userMapMutex); const VerilatedLockGuard lock(s().m_userMapMutex);
const auto& it = vlstd::as_const(s_s.v.m_userMap).find(std::make_pair(scopep, userKey)); const auto& it = vlstd::as_const(s().m_userMap).find(std::make_pair(scopep, userKey));
if (VL_UNLIKELY(it == s_s.v.m_userMap.end())) return nullptr; if (VL_UNLIKELY(it == s().m_userMap.end())) return nullptr;
return it->second; return it->second;
} }
private: public: // But only for verilated.cpp
/// Symbol table destruction cleans up the entries for each scope. /// Symbol table destruction cleans up the entries for each scope.
static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE { static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope on destruction, so we simply iterate. // Slow ok - called once/scope on destruction, so we simply iterate.
const VerilatedLockGuard lock(s_s.v.m_userMapMutex); const VerilatedLockGuard lock(s().m_userMapMutex);
for (auto it = s_s.v.m_userMap.begin(); it != s_s.v.m_userMap.end();) { for (auto it = s().m_userMap.begin(); it != s().m_userMap.end();) {
if (it->first.first == scopep) { if (it->first.first == scopep) {
s_s.v.m_userMap.erase(it++); s().m_userMap.erase(it++);
} else { } else {
++it; ++it;
} }
} }
} }
static void userDump() VL_MT_SAFE { static void userDump() VL_MT_SAFE {
const VerilatedLockGuard lock( const VerilatedLockGuard lock(s().m_userMapMutex); // Avoid it changing in middle of dump
s_s.v.m_userMapMutex); // Avoid it changing in middle of dump
bool first = true; bool first = true;
for (const auto& i : s_s.v.m_userMap) { for (const auto& i : s().m_userMap) {
if (first) { if (first) {
VL_PRINTF_MT(" userDump:\n"); VL_PRINTF_MT(" userDump:\n");
first = false; first = false;
@ -368,54 +496,18 @@ private:
} }
} }
public: // But only for verilated*.cpp
// METHODS - scope name
static void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope at construction
const VerilatedLockGuard lock(s_s.v.m_nameMutex);
const auto it = s_s.v.m_nameMap.find(scopep->name());
if (it == s_s.v.m_nameMap.end()) s_s.v.m_nameMap.emplace(scopep->name(), scopep);
}
static inline const VerilatedScope* scopeFind(const char* namep) VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.m_nameMutex);
// If too slow, can assume this is only VL_MT_SAFE_POSINIT
const auto& it = s_s.v.m_nameMap.find(namep);
if (VL_UNLIKELY(it == s_s.v.m_nameMap.end())) return nullptr;
return it->second;
}
static void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope at destruction
const VerilatedLockGuard lock(s_s.v.m_nameMutex);
userEraseScope(scopep);
const auto it = s_s.v.m_nameMap.find(scopep->name());
if (it != s_s.v.m_nameMap.end()) s_s.v.m_nameMap.erase(it);
}
static void scopesDump() VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.m_nameMutex);
VL_PRINTF_MT(" scopesDump:\n");
for (const auto& i : s_s.v.m_nameMap) {
const VerilatedScope* scopep = i.second;
scopep->scopeDump();
}
VL_PRINTF_MT("\n");
}
static const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE_POSTINIT {
// Thread save only assuming this is called only after model construction completed
return &s_s.v.m_nameMap;
}
public: // But only for verilated*.cpp public: // But only for verilated*.cpp
// METHODS - hierarchy // METHODS - hierarchy
static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE { static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE {
// Slow ok - called at construction for VPI accessible elements // Slow ok - called at construction for VPI accessible elements
const VerilatedLockGuard lock(s_s.v.m_hierMapMutex); const VerilatedLockGuard lock(s().m_hierMapMutex);
s_s.v.m_hierMap[fromp].push_back(top); s().m_hierMap[fromp].push_back(top);
} }
static void hierarchyRemove(const VerilatedScope* fromp, static void hierarchyRemove(const VerilatedScope* fromp,
const VerilatedScope* top) VL_MT_SAFE { const VerilatedScope* top) VL_MT_SAFE {
// Slow ok - called at destruction for VPI accessible elements // Slow ok - called at destruction for VPI accessible elements
const VerilatedLockGuard lock(s_s.v.m_hierMapMutex); const VerilatedLockGuard lock(s().m_hierMapMutex);
VerilatedHierarchyMap& map = s_s.v.m_hierMap; VerilatedHierarchyMap& map = s().m_hierMap;
if (map.find(fromp) == map.end()) return; if (map.find(fromp) == map.end()) return;
auto& scopes = map[fromp]; auto& scopes = map[fromp];
const auto it = find(scopes.begin(), scopes.end(), top); const auto it = find(scopes.begin(), scopes.end(), top);
@ -423,7 +515,7 @@ public: // But only for verilated*.cpp
} }
static const VerilatedHierarchyMap* hierarchyMap() VL_MT_SAFE_POSTINIT { static const VerilatedHierarchyMap* hierarchyMap() VL_MT_SAFE_POSTINIT {
// Thread save only assuming this is called only after model construction completed // Thread save only assuming this is called only after model construction completed
return &s_s.v.m_hierMap; return &s().m_hierMap;
} }
public: // But only for verilated*.cpp public: // But only for verilated*.cpp
@ -437,19 +529,19 @@ public: // But only for verilated*.cpp
// miss at the cost of a multiply, and all lookups move to slowpath. // miss at the cost of a multiply, and all lookups move to slowpath.
static int exportInsert(const char* namep) VL_MT_SAFE { static int exportInsert(const char* namep) VL_MT_SAFE {
// Slow ok - called once/function at creation // Slow ok - called once/function at creation
const VerilatedLockGuard lock(s_s.v.m_exportMutex); const VerilatedLockGuard lock(s().m_exportMutex);
const auto it = s_s.v.m_exportMap.find(namep); const auto it = s().m_exportMap.find(namep);
if (it == s_s.v.m_exportMap.end()) { if (it == s().m_exportMap.end()) {
s_s.v.m_exportMap.emplace(namep, s_s.v.m_exportNext++); s().m_exportMap.emplace(namep, s().m_exportNext++);
return s_s.v.m_exportNext++; return s().m_exportNext++;
} else { } else {
return it->second; return it->second;
} }
} }
static int exportFind(const char* namep) VL_MT_SAFE { static int exportFind(const char* namep) VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.m_exportMutex); const VerilatedLockGuard lock(s().m_exportMutex);
const auto& it = s_s.v.m_exportMap.find(namep); const auto& it = s().m_exportMap.find(namep);
if (VL_LIKELY(it != s_s.v.m_exportMap.end())) return it->second; if (VL_LIKELY(it != s().m_exportMap.end())) return it->second;
std::string msg = (std::string("%Error: Testbench C called ") + namep std::string msg = (std::string("%Error: Testbench C called ") + namep
+ " but no such DPI export function name exists in ANY model"); + " but no such DPI export function name exists in ANY model");
VL_FATAL_MT("unknown", 0, "", msg.c_str()); VL_FATAL_MT("unknown", 0, "", msg.c_str());
@ -457,16 +549,16 @@ public: // But only for verilated*.cpp
} }
static const char* exportName(int funcnum) VL_MT_SAFE { static const char* exportName(int funcnum) VL_MT_SAFE {
// Slowpath; find name for given export; errors only so no map to reverse-map it // Slowpath; find name for given export; errors only so no map to reverse-map it
const VerilatedLockGuard lock(s_s.v.m_exportMutex); const VerilatedLockGuard lock(s().m_exportMutex);
for (const auto& i : s_s.v.m_exportMap) { for (const auto& i : s().m_exportMap) {
if (i.second == funcnum) return i.first; if (i.second == funcnum) return i.first;
} }
return "*UNKNOWN*"; return "*UNKNOWN*";
} }
static void exportsDump() VL_MT_SAFE { static void exportsDump() VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.m_exportMutex); const VerilatedLockGuard lock(s().m_exportMutex);
bool first = true; bool first = true;
for (const auto& i : s_s.v.m_exportMap) { for (const auto& i : s().m_exportMap) {
if (first) { if (first) {
VL_PRINTF_MT(" exportDump:\n"); VL_PRINTF_MT(" exportDump:\n");
first = false; first = false;
@ -476,131 +568,6 @@ public: // But only for verilated*.cpp
} }
// We don't free up m_exportMap until the end, because we can't be sure // 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. // what other models are using the assigned funcnum's.
public: // But only for verilated*.cpp
// METHODS - timeformat
static std::string timeFormatSuffix() VL_MT_SAFE;
static void timeFormatSuffix(const std::string& value) VL_MT_SAFE;
static int timeFormatUnits() VL_MT_SAFE {
if (s_s.v.m_ser.m_timeFormatUnits == VerilatedImpData::Serialized::UNITS_NONE) {
return Verilated::timeprecision();
}
return s_s.v.m_ser.m_timeFormatUnits;
}
static int timeFormatPrecision() VL_MT_SAFE { return s_s.v.m_ser.m_timeFormatPrecision; }
static int timeFormatWidth() VL_MT_SAFE { return s_s.v.m_ser.m_timeFormatWidth; }
static void timeFormatUnits(int value) VL_MT_SAFE;
static void timeFormatPrecision(int value) VL_MT_SAFE;
static void timeFormatWidth(int value) VL_MT_SAFE;
public: // But only for verilated*.cpp
// METHODS - file IO
static IData fdNewMcd(const char* filenamep) VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.m_fdMutex);
if (s_s.v.m_fdFreeMct.empty()) return 0;
IData idx = s_s.v.m_fdFreeMct.back();
s_s.v.m_fdFreeMct.pop_back();
s_s.v.m_fdps[idx] = fopen(filenamep, "w");
if (VL_UNLIKELY(!s_s.v.m_fdps[idx])) return 0;
return (1 << idx);
}
static IData fdNew(const char* filenamep, const char* modep) VL_MT_SAFE {
FILE* fp = fopen(filenamep, modep);
if (VL_UNLIKELY(!fp)) return 0;
// Bit 31 indicates it's a descriptor not a MCD
const VerilatedLockGuard lock(s_s.v.m_fdMutex);
if (s_s.v.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, s_s.v.m_fdps.size());
const std::size_t excess = 10;
s_s.v.m_fdps.resize(start + excess);
std::fill(s_s.v.m_fdps.begin() + start, s_s.v.m_fdps.end(), (FILE*)0);
s_s.v.m_fdFree.resize(excess);
for (std::size_t i = 0, id = start; i < s_s.v.m_fdFree.size(); ++i, ++id) {
s_s.v.m_fdFree[i] = id;
}
}
IData idx = s_s.v.m_fdFree.back();
s_s.v.m_fdFree.pop_back();
s_s.v.m_fdps[idx] = fp;
return (idx | (1UL << 31)); // bit 31 indicates not MCD
}
static void fdFlush(IData fdi) VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.m_fdMutex);
const VerilatedFpList fdlist = fdToFpList(fdi);
for (const auto& i : fdlist) fflush(i);
}
static IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.m_fdMutex);
const VerilatedFpList fdlist = fdToFpList(fdi);
if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
return static_cast<IData>(
fseek(*fdlist.begin(), static_cast<long>(offset), static_cast<int>(origin)));
}
static IData fdTell(IData fdi) VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.m_fdMutex);
const VerilatedFpList fdlist = fdToFpList(fdi);
if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
return static_cast<IData>(ftell(*fdlist.begin()));
}
static void fdWrite(IData fdi, const std::string& output) VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.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);
}
}
static void fdClose(IData fdi) VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.m_fdMutex);
if ((fdi & (1 << 31)) != 0) {
// Non-MCD case
IData idx = VL_MASK_I(31) & fdi;
if (VL_UNLIKELY(idx >= s_s.v.m_fdps.size())) return;
if (VL_UNLIKELY(!s_s.v.m_fdps[idx])) return; // Already free
fclose(s_s.v.m_fdps[idx]);
s_s.v.m_fdps[idx] = (FILE*)0;
s_s.v.m_fdFree.push_back(idx);
} else {
// MCD case
for (int i = 0; (fdi != 0) && (i < 31); i++, fdi >>= 1) {
if (fdi & VL_MASK_I(1)) {
fclose(s_s.v.m_fdps[i]);
s_s.v.m_fdps[i] = nullptr;
s_s.v.m_fdFreeMct.push_back(i);
}
}
}
}
static inline FILE* fdToFp(IData fdi) VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.m_fdMutex);
const VerilatedFpList fdlist = fdToFpList(fdi);
if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr;
return *fdlist.begin();
}
private:
static inline VerilatedFpList fdToFpList(IData fdi) VL_REQUIRES(s_s.v.m_fdMutex) {
VerilatedFpList fp;
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 < s_s.v.m_fdps.size())) fp.push_back(s_s.v.m_fdps[idx]);
break;
}
} else {
// MCD Case
for (size_t i = 0; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) {
if (fdi & VL_MASK_I(1)) fp.push_back(s_s.v.m_fdps[i]);
}
}
return fp;
}
}; };
//====================================================================== //======================================================================

View File

@ -16,9 +16,12 @@
/// ///
//============================================================================= //=============================================================================
#define VERILATOR_VERILATED_SAVE_CPP_
#include "verilatedos.h" #include "verilatedos.h"
#include "verilated.h" #include "verilated.h"
#include "verilated_save.h" #include "verilated_save.h"
#include "verilated_imp.h"
#include <cerrno> #include <cerrno>
#include <fcntl.h> #include <fcntl.h>
@ -43,7 +46,7 @@
// CONSTANTS // CONSTANTS
/// Value of first bytes of each file (must be multiple of 8 bytes) /// Value of first bytes of each file (must be multiple of 8 bytes)
static const char* const VLTSAVE_HEADER_STR = "verilatorsave01\n"; static const char* const VLTSAVE_HEADER_STR = "verilatorsave02\n";
/// Value of last bytes of each file (must be multiple of 8 bytes) /// Value of last bytes of each file (must be multiple of 8 bytes)
static const char* const VLTSAVE_TRAILER_STR = "vltsaved"; static const char* const VLTSAVE_TRAILER_STR = "vltsaved";
@ -77,11 +80,6 @@ void VerilatedSerialize::header() VL_MT_UNSAFE_ONE {
VerilatedSerialize& os = *this; // So can cut and paste standard << code below VerilatedSerialize& os = *this; // So can cut and paste standard << code below
assert((strlen(VLTSAVE_HEADER_STR) & 7) == 0); // Keep aligned assert((strlen(VLTSAVE_HEADER_STR) & 7) == 0); // Keep aligned
os.write(VLTSAVE_HEADER_STR, strlen(VLTSAVE_HEADER_STR)); os.write(VLTSAVE_HEADER_STR, strlen(VLTSAVE_HEADER_STR));
// Verilated doesn't do it itself, as if we're not using save/restore
// it doesn't need to compile this stuff in
os.write(Verilated::serialized1Ptr(), Verilated::serialized1Size());
os.write(Verilated::serialized2Ptr(), Verilated::serialized2Size());
} }
void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE { void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
@ -95,8 +93,6 @@ void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str()); VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
// Die before we close() as close would infinite loop // Die before we close() as close would infinite loop
} }
os.read(Verilated::serialized1Ptr(), Verilated::serialized1Size());
os.read(Verilated::serialized2Ptr(), Verilated::serialized2Size());
} }
void VerilatedSerialize::trailer() VL_MT_UNSAFE_ONE { void VerilatedSerialize::trailer() VL_MT_UNSAFE_ONE {
@ -249,3 +245,19 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
//============================================================================= //=============================================================================
// Serialization of types // Serialization of types
VerilatedSerialize& operator<<(VerilatedSerialize& os, VerilatedContext* rhsp) {
os.write(rhsp->serialized1Ptr(), rhsp->serialized1Size());
os << rhsp->impp()->timeFormatSuffix();
os << rhsp->dumpfile();
return os;
}
VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhsp) {
os.read(rhsp->serialized1Ptr(), rhsp->serialized1Size());
std::string s;
os >> s;
rhsp->impp()->timeFormatSuffix(s);
os >> s;
rhsp->dumpfile(s);
return os;
}

View File

@ -236,7 +236,7 @@ inline VerilatedSerialize& operator<<(VerilatedSerialize& os, float& rhs) {
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, float& rhs) { inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, float& rhs) {
return os.read(&rhs, sizeof(rhs)); return os.read(&rhs, sizeof(rhs));
} }
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, std::string& rhs) { inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const std::string& rhs) {
vluint32_t len = rhs.length(); vluint32_t len = rhs.length();
os << len; os << len;
return os.write(rhs.data(), len); return os.write(rhs.data(), len);
@ -247,6 +247,9 @@ inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, std::string& r
rhs.resize(len); rhs.resize(len);
return os.read((void*)rhs.data(), len); return os.read((void*)rhs.data(), len);
} }
VerilatedSerialize& operator<<(VerilatedSerialize& os, VerilatedContext* rhsp);
VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhsp);
template <class T_Key, class T_Value> template <class T_Key, class T_Value>
VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray<T_Key, T_Value>& rhs) { VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray<T_Key, T_Value>& rhs) {
os << rhs.atDefault(); os << rhs.atDefault();

View File

@ -40,12 +40,12 @@ VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
//============================================================================= //=============================================================================
// VlWorkerThread // VlWorkerThread
VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, bool profiling) VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling)
: m_waiting{false} : m_poolp{poolp}
, m_poolp{poolp}
, m_profiling{profiling} // Must init this last -- after setting up fields that it might read: , m_profiling{profiling} // Must init this last -- after setting up fields that it might read:
, m_exiting{false} , m_exiting{false}
, m_cthread{startWorker, this} {} , m_cthread{startWorker, this}
, m_contextp{contextp} {}
VlWorkerThread::~VlWorkerThread() { VlWorkerThread::~VlWorkerThread() {
m_exiting.store(true, std::memory_order_release); m_exiting.store(true, std::memory_order_release);
@ -75,12 +75,15 @@ void VlWorkerThread::workerLoop() {
if (VL_UNLIKELY(m_profiling)) m_poolp->tearDownProfilingClientThread(); if (VL_UNLIKELY(m_profiling)) m_poolp->tearDownProfilingClientThread();
} }
void VlWorkerThread::startWorker(VlWorkerThread* workerp) { workerp->workerLoop(); } void VlWorkerThread::startWorker(VlWorkerThread* workerp) {
Verilated::threadContextp(workerp->m_contextp);
workerp->workerLoop();
}
//============================================================================= //=============================================================================
// VlThreadPool // VlThreadPool
VlThreadPool::VlThreadPool(int nThreads, bool profiling) VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, bool profiling)
: m_profiling{profiling} { : m_profiling{profiling} {
// --threads N passes nThreads=N-1, as the "main" threads counts as 1 // --threads N passes nThreads=N-1, as the "main" threads counts as 1
unsigned cpus = std::thread::hardware_concurrency(); unsigned cpus = std::thread::hardware_concurrency();
@ -94,7 +97,7 @@ VlThreadPool::VlThreadPool(int nThreads, bool profiling)
} }
// Create'em // Create'em
for (int i = 0; i < nThreads; ++i) { for (int i = 0; i < nThreads; ++i) {
m_workers.push_back(new VlWorkerThread(this, profiling)); m_workers.push_back(new VlWorkerThread(this, contextp, profiling));
} }
// Set up a profile buffer for the current thread too -- on the // Set up a profile buffer for the current thread too -- on the
// assumption that it's the same thread that calls eval and may be // assumption that it's the same thread that calls eval and may be
@ -151,8 +154,9 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
fprintf(fp, "VLPROFTHREAD 1.0 # Verilator thread profile dump version 1.0\n"); fprintf(fp, "VLPROFTHREAD 1.0 # Verilator thread profile dump version 1.0\n");
fprintf(fp, "VLPROF arg --threads %" VL_PRI64 "u\n", vluint64_t(m_workers.size() + 1)); fprintf(fp, "VLPROF arg --threads %" VL_PRI64 "u\n", vluint64_t(m_workers.size() + 1));
fprintf(fp, "VLPROF arg +verilator+prof+threads+start+%" VL_PRI64 "u\n", fprintf(fp, "VLPROF arg +verilator+prof+threads+start+%" VL_PRI64 "u\n",
Verilated::profThreadsStart()); Verilated::threadContextp()->profThreadsStart());
fprintf(fp, "VLPROF arg +verilator+prof+threads+window+%u\n", Verilated::profThreadsWindow()); fprintf(fp, "VLPROF arg +verilator+prof+threads+window+%u\n",
Verilated::threadContextp()->profThreadsWindow());
fprintf(fp, "VLPROF stat yields %" VL_PRI64 "u\n", VlMTaskVertex::yields()); fprintf(fp, "VLPROF stat yields %" VL_PRI64 "u\n", VlMTaskVertex::yields());
vluint32_t thread_id = 0; vluint32_t thread_id = 0;

View File

@ -203,12 +203,13 @@ private:
bool m_profiling; // Is profiling enabled? bool m_profiling; // Is profiling enabled?
std::atomic<bool> m_exiting; // Worker thread should exit std::atomic<bool> m_exiting; // Worker thread should exit
std::thread m_cthread; // Underlying C++ thread record std::thread m_cthread; // Underlying C++ thread record
VerilatedContext* m_contextp; // Context for spawned thread
VL_UNCOPYABLE(VlWorkerThread); VL_UNCOPYABLE(VlWorkerThread);
public: public:
// CONSTRUCTORS // CONSTRUCTORS
explicit VlWorkerThread(VlThreadPool* poolp, bool profiling); explicit VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling);
~VlWorkerThread(); ~VlWorkerThread();
// METHODS // METHODS
@ -273,7 +274,7 @@ public:
// Construct a thread pool with 'nThreads' dedicated threads. The thread // Construct a thread pool with 'nThreads' dedicated threads. The thread
// pool will create these threads and make them available to execute tasks // pool will create these threads and make them available to execute tasks
// via this->workerp(index)->addTask(...) // via this->workerp(index)->addTask(...)
VlThreadPool(int nThreads, bool profiling); VlThreadPool(VerilatedContext* contextp, int nThreads, bool profiling);
~VlThreadPool(); ~VlThreadPool();
// METHODS // METHODS

View File

@ -151,7 +151,8 @@ private:
double m_timeRes; ///< Time resolution (ns/ms etc) double m_timeRes; ///< Time resolution (ns/ms etc)
double m_timeUnit; ///< Time units (ns/ms etc) double m_timeUnit; ///< Time units (ns/ms etc)
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord& cbRec); void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord& cbRec)
VL_MT_SAFE_EXCLUDES(m_mutex);
// Equivalent to 'this' but is of the sub-type 'T_Derived*'. Use 'self()->' // Equivalent to 'this' but is of the sub-type 'T_Derived*'. Use 'self()->'
// to access duck-typed functions to avoid a virtual function call. // to access duck-typed functions to avoid a virtual function call.
@ -203,7 +204,7 @@ protected:
//========================================================================= //=========================================================================
// Internals available to format specific implementations // Internals available to format specific implementations
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread VerilatedMutex m_mutex; // Ensure dump() etc only called from single thread
vluint32_t nextCode() const { return m_nextCode; } vluint32_t nextCode() const { return m_nextCode; }
vluint32_t numSignals() const { return m_numSignals; } vluint32_t numSignals() const { return m_numSignals; }
@ -225,8 +226,8 @@ protected:
// Character that splits scopes. Note whitespace are ALWAYS escapes. // Character that splits scopes. Note whitespace are ALWAYS escapes.
char scopeEscape() { return m_scopeEscape; } char scopeEscape() { return m_scopeEscape; }
void close(); void closeBase();
void flush(); void flushBase();
//========================================================================= //=========================================================================
// Virtual functions to be provided by the format specific implementation // Virtual functions to be provided by the format specific implementation
@ -247,30 +248,24 @@ public:
~VerilatedTrace(); ~VerilatedTrace();
// Set time units (s/ms, defaults to ns) // Set time units (s/ms, defaults to ns)
void set_time_unit(const char* unitp); void set_time_unit(const char* unitp) VL_MT_SAFE;
void set_time_unit(const std::string& unit); void set_time_unit(const std::string& unit) VL_MT_SAFE;
// Set time resolution (s/ms, defaults to ns) // Set time resolution (s/ms, defaults to ns)
void set_time_resolution(const char* unitp); void set_time_resolution(const char* unitp) VL_MT_SAFE;
void set_time_resolution(const std::string& unit); void set_time_resolution(const std::string& unit) VL_MT_SAFE;
// Call // Call
void dump(vluint64_t timeui); void dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex);
//========================================================================= //=========================================================================
// Non-hot path internal interface to Verilator generated code // Non-hot path internal interface to Verilator generated code
void addInitCb(initCb_t cb, void* userp) VL_MT_UNSAFE_ONE; void addInitCb(initCb_t cb, void* userp) VL_MT_SAFE;
void addFullCb(dumpCb_t cb, void* userp) VL_MT_UNSAFE_ONE; void addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
void addChgCb(dumpCb_t cb, void* userp) VL_MT_UNSAFE_ONE; void addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
void addCleanupCb(dumpCb_t cb, void* userp) VL_MT_UNSAFE_ONE; void addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
void changeThread() { m_assertOne.changeThread(); }
void module(const std::string& name) VL_MT_UNSAFE_ONE {
m_assertOne.check();
m_moduleName = name;
}
void module(const std::string& name) VL_MT_UNSAFE;
void scopeEscape(char flag) { m_scopeEscape = flag; } void scopeEscape(char flag) { m_scopeEscape = flag; }
//========================================================================= //=========================================================================

View File

@ -236,7 +236,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::shutdownWorker() {
//============================================================================= //=============================================================================
// Life cycle // Life cycle
template <> void VerilatedTrace<VL_DERIVED_T>::close() { template <> void VerilatedTrace<VL_DERIVED_T>::closeBase() {
#ifdef VL_TRACE_THREADED #ifdef VL_TRACE_THREADED
shutdownWorker(); shutdownWorker();
while (m_numTraceBuffers) { while (m_numTraceBuffers) {
@ -246,7 +246,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::close() {
#endif #endif
} }
template <> void VerilatedTrace<VL_DERIVED_T>::flush() { template <> void VerilatedTrace<VL_DERIVED_T>::flushBase() {
#ifdef VL_TRACE_THREADED #ifdef VL_TRACE_THREADED
// Hand an empty buffer to the worker thread // Hand an empty buffer to the worker thread
vluint32_t* const bufferp = getTraceBuffer(); vluint32_t* const bufferp = getTraceBuffer();
@ -262,12 +262,12 @@ template <> void VerilatedTrace<VL_DERIVED_T>::flush() {
// Callbacks to run on global events // Callbacks to run on global events
template <> void VerilatedTrace<VL_DERIVED_T>::onFlush(void* selfp) { template <> void VerilatedTrace<VL_DERIVED_T>::onFlush(void* selfp) {
// Note this calls 'flush' on the derived class // This calls 'flush' on the derived classo (which must then get any mutex)
reinterpret_cast<VL_DERIVED_T*>(selfp)->flush(); reinterpret_cast<VL_DERIVED_T*>(selfp)->flush();
} }
template <> void VerilatedTrace<VL_DERIVED_T>::onExit(void* selfp) { template <> void VerilatedTrace<VL_DERIVED_T>::onExit(void* selfp) {
// Note this calls 'close' on the derived class // This calls 'close' on the derived class (which must then get any mutex)
reinterpret_cast<VL_DERIVED_T*>(selfp)->close(); reinterpret_cast<VL_DERIVED_T*>(selfp)->close();
} }
@ -291,8 +291,8 @@ VerilatedTrace<VL_DERIVED_T>::VerilatedTrace()
, m_numTraceBuffers { 0 } , m_numTraceBuffers { 0 }
#endif #endif
{ {
set_time_unit(Verilated::timeunitString()); set_time_unit(Verilated::threadContextp()->timeunitString());
set_time_resolution(Verilated::timeprecisionString()); set_time_resolution(Verilated::threadContextp()->timeprecisionString());
} }
template <> VerilatedTrace<VL_DERIVED_T>::~VerilatedTrace() { template <> VerilatedTrace<VL_DERIVED_T>::~VerilatedTrace() {
@ -300,7 +300,7 @@ template <> VerilatedTrace<VL_DERIVED_T>::~VerilatedTrace() {
Verilated::removeFlushCb(VerilatedTrace<VL_DERIVED_T>::onFlush, this); Verilated::removeFlushCb(VerilatedTrace<VL_DERIVED_T>::onFlush, this);
Verilated::removeExitCb(VerilatedTrace<VL_DERIVED_T>::onExit, this); Verilated::removeExitCb(VerilatedTrace<VL_DERIVED_T>::onExit, this);
#ifdef VL_TRACE_THREADED #ifdef VL_TRACE_THREADED
close(); closeBase();
#endif #endif
} }
@ -308,8 +308,6 @@ template <> VerilatedTrace<VL_DERIVED_T>::~VerilatedTrace() {
// Internals available to format specific implementations // Internals available to format specific implementations
template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE { template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
m_assertOne.check();
// Note: It is possible to re-open a trace file (VCD in particular), // Note: It is possible to re-open a trace file (VCD in particular),
// so we must reset the next code here, but it must have the same number // so we must reset the next code here, but it must have the same number
// of codes on re-open // of codes on re-open
@ -376,24 +374,26 @@ template <> std::string VerilatedTrace<VL_DERIVED_T>::timeResStr() const {
//========================================================================= //=========================================================================
// External interface to client code // External interface to client code
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_unit(const char* unitp) { template <> void VerilatedTrace<VL_DERIVED_T>::set_time_unit(const char* unitp) VL_MT_SAFE {
m_timeUnit = timescaleToDouble(unitp); m_timeUnit = timescaleToDouble(unitp);
} }
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_unit(const std::string& unit) VL_MT_SAFE {
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_unit(const std::string& unit) {
set_time_unit(unit.c_str()); set_time_unit(unit.c_str());
} }
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const char* unitp) VL_MT_SAFE {
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const char* unitp) {
m_timeRes = timescaleToDouble(unitp); m_timeRes = timescaleToDouble(unitp);
} }
template <>
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const std::string& unit) { void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const std::string& unit) VL_MT_SAFE {
set_time_resolution(unit.c_str()); set_time_resolution(unit.c_str());
} }
template <> void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) { template <>
m_assertOne.check(); void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex) {
// Not really VL_MT_SAFE but more VL_MT_UNSAFE_ONE.
// This does get the mutex, but if multiple threads are trying to dump
// chances are the data being dumped will have other problems
const VerilatedLockGuard lock(m_mutex);
if (VL_UNCOVERABLE(m_timeLastDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START if (VL_UNCOVERABLE(m_timeLastDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
VL_PRINTF_MT("%%Warning: previous dump at t=%" VL_PRI64 "u, requesting t=%" VL_PRI64 VL_PRINTF_MT("%%Warning: previous dump at t=%" VL_PRI64 "u, requesting t=%" VL_PRI64
"u, dump call ignored\n", "u, dump call ignored\n",
@ -426,7 +426,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) {
m_traceBufferWritep += 3; m_traceBufferWritep += 3;
} else { } else {
// Update time point // Update time point
flush(); flushBase();
emitTimeChange(timeui); emitTimeChange(timeui);
} }
#else #else
@ -472,8 +472,9 @@ template <> void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) {
template <> template <>
void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec, void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
CallbackRecord& cbRec) { CallbackRecord& cbRec)
m_assertOne.check(); VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START
std::string msg = (std::string("Internal: ") + __FILE__ + "::" + __FUNCTION__ std::string msg = (std::string("Internal: ") + __FILE__ + "::" + __FUNCTION__
+ " called with already open file"); + " called with already open file");
@ -482,22 +483,26 @@ void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>
cbVec.push_back(cbRec); cbVec.push_back(cbRec);
} }
template <> void VerilatedTrace<VL_DERIVED_T>::addInitCb(initCb_t cb, void* userp) { template <> void VerilatedTrace<VL_DERIVED_T>::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr(cb, userp); CallbackRecord cbr(cb, userp);
addCallbackRecord(m_initCbs, cbr); addCallbackRecord(m_initCbs, cbr);
} }
template <> void VerilatedTrace<VL_DERIVED_T>::addFullCb(dumpCb_t cb, void* userp) { template <> void VerilatedTrace<VL_DERIVED_T>::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr(cb, userp); CallbackRecord cbr(cb, userp);
addCallbackRecord(m_fullCbs, cbr); addCallbackRecord(m_fullCbs, cbr);
} }
template <> void VerilatedTrace<VL_DERIVED_T>::addChgCb(dumpCb_t cb, void* userp) { template <> void VerilatedTrace<VL_DERIVED_T>::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr(cb, userp); CallbackRecord cbr(cb, userp);
addCallbackRecord(m_chgCbs, cbr); addCallbackRecord(m_chgCbs, cbr);
} }
template <> void VerilatedTrace<VL_DERIVED_T>::addCleanupCb(dumpCb_t cb, void* userp) { template <> void VerilatedTrace<VL_DERIVED_T>::addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr(cb, userp); CallbackRecord cbr(cb, userp);
addCallbackRecord(m_cleanupCbs, cbr); addCallbackRecord(m_cleanupCbs, cbr);
} }
template <> void VerilatedTrace<VL_DERIVED_T>::module(const std::string& name) VL_MT_UNSAFE {
// Called via callbacks way above in call stack, which already hold m_mutex
m_moduleName = name;
}
//========================================================================= //=========================================================================
// Hot path internal interface to Verilator generated code // Hot path internal interface to Verilator generated code

View File

@ -97,14 +97,14 @@ VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep) {
m_suffixesp = nullptr; m_suffixesp = nullptr;
} }
void VerilatedVcd::open(const char* filename) { void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
m_assertOne.check(); const VerilatedLockGuard lock(m_mutex);
if (isOpen()) return; if (isOpen()) return;
// Set member variables // Set member variables
m_filename = filename; // "" is ok, as someone may overload open m_filename = filename; // "" is ok, as someone may overload open
openNext(m_rolloverMB != 0); openNextImp(m_rolloverMB != 0);
if (!isOpen()) return; if (!isOpen()) return;
dumpHeader(); dumpHeader();
@ -113,13 +113,17 @@ void VerilatedVcd::open(const char* filename) {
m_suffixesp = &m_suffixes[0]; // Note: C++11 m_suffixes.data(); m_suffixesp = &m_suffixes[0]; // Note: C++11 m_suffixes.data();
// When using rollover, the first chunk contains the header only. // When using rollover, the first chunk contains the header only.
if (m_rolloverMB) openNext(true); if (m_rolloverMB) openNextImp(true);
} }
void VerilatedVcd::openNext(bool incFilename) { void VerilatedVcd::openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex) {
// Open next filename in concat sequence, mangle filename if // Open next filename in concat sequence, mangle filename if
// incFilename is true. // incFilename is true.
m_assertOne.check(); const VerilatedLockGuard lock(m_mutex);
openNextImp(incFilename);
}
void VerilatedVcd::openNextImp(bool incFilename) {
closePrev(); // Close existing closePrev(); // Close existing
if (incFilename) { if (incFilename) {
// Find _0000.{ext} in filename // Find _0000.{ext} in filename
@ -163,7 +167,7 @@ void VerilatedVcd::openNext(bool incFilename) {
} }
bool VerilatedVcd::preChangeDump() { bool VerilatedVcd::preChangeDump() {
if (VL_UNLIKELY(m_rolloverMB && m_wroteBytes > m_rolloverMB)) openNext(true); if (VL_UNLIKELY(m_rolloverMB && m_wroteBytes > m_rolloverMB)) openNextImp(true);
return isOpen(); return isOpen();
} }
@ -219,7 +223,7 @@ void VerilatedVcd::closePrev() {
// This function is on the flush() call path // This function is on the flush() call path
if (!isOpen()) return; if (!isOpen()) return;
VerilatedTrace<VerilatedVcd>::flush(); VerilatedTrace<VerilatedVcd>::flushBase();
bufferFlush(); bufferFlush();
m_isOpen = false; m_isOpen = false;
m_filep->close(); m_filep->close();
@ -236,9 +240,9 @@ void VerilatedVcd::closeErr() {
m_filep->close(); // May get error, just ignore it m_filep->close(); // May get error, just ignore it
} }
void VerilatedVcd::close() { void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
// This function is on the flush() call path // This function is on the flush() call path
m_assertOne.check(); const VerilatedLockGuard lock(m_mutex);
if (!isOpen()) return; if (!isOpen()) return;
if (m_evcd) { if (m_evcd) {
printStr("$vcdclose "); printStr("$vcdclose ");
@ -248,11 +252,12 @@ void VerilatedVcd::close() {
closePrev(); closePrev();
// closePrev() called VerilatedTrace<VerilatedVcd>::flush(), so we just // closePrev() called VerilatedTrace<VerilatedVcd>::flush(), so we just
// need to shut down the tracing thread here. // need to shut down the tracing thread here.
VerilatedTrace<VerilatedVcd>::close(); VerilatedTrace<VerilatedVcd>::closeBase();
} }
void VerilatedVcd::flush() { void VerilatedVcd::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedTrace<VerilatedVcd>::flush(); const VerilatedLockGuard lock(m_mutex);
VerilatedTrace<VerilatedVcd>::flushBase();
bufferFlush(); bufferFlush();
} }
@ -290,7 +295,6 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
// We add output data to m_writep. // We add output data to m_writep.
// When it gets nearly full we dump it using this routine which calls write() // When it gets nearly full we dump it using this routine which calls write()
// This is much faster than using buffered I/O // This is much faster than using buffered I/O
m_assertOne.check();
if (VL_UNLIKELY(!isOpen())) return; if (VL_UNLIKELY(!isOpen())) return;
char* wp = m_wrBufp; char* wp = m_wrBufp;
while (true) { while (true) {

View File

@ -15,6 +15,7 @@
/// \brief C++ Tracing in VCD Format /// \brief C++ Tracing in VCD Format
/// ///
//============================================================================= //=============================================================================
// SPDIFF_OFF
#ifndef VERILATOR_VERILATED_VCD_C_H_ #ifndef VERILATOR_VERILATED_VCD_C_H_
#define VERILATOR_VERILATED_VCD_C_H_ #define VERILATOR_VERILATED_VCD_C_H_
@ -28,6 +29,7 @@
class VerilatedVcd; class VerilatedVcd;
// SPDIFF_ON
//============================================================================= //=============================================================================
// VerilatedFile // VerilatedFile
/// File handling routines, which can be overrode for e.g. socket I/O /// File handling routines, which can be overrode for e.g. socket I/O
@ -84,9 +86,9 @@ private:
// We only call this once per vector, so we need enough slop for a very wide "b###" line // We only call this once per vector, so we need enough slop for a very wide "b###" line
if (VL_UNLIKELY(m_writep > m_wrFlushp)) bufferFlush(); if (VL_UNLIKELY(m_writep > m_wrFlushp)) bufferFlush();
} }
void openNextImp(bool incFilename);
void closePrev(); void closePrev();
void closeErr(); void closeErr();
void openNext();
void makeNameMap(); void makeNameMap();
void deleteNameMap(); void deleteNameMap();
void printIndent(int level_change); void printIndent(int level_change);
@ -139,15 +141,15 @@ public:
// METHODS // METHODS
// Open the file; call isOpen() to see if errors // Open the file; call isOpen() to see if errors
void open(const char* filename) VL_MT_UNSAFE_ONE; void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex);
// Open next data-only file // Open next data-only file
void openNext(bool incFilename) VL_MT_UNSAFE_ONE; void openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex);
// Close the file // Close the file
void close() VL_MT_UNSAFE_ONE; void close() VL_MT_SAFE_EXCLUDES(m_mutex);
// Flush any remaining data to this file // Flush any remaining data to this file
void flush() VL_MT_UNSAFE_ONE; void flush() VL_MT_SAFE_EXCLUDES(m_mutex);
// Return if file is open // Return if file is open
bool isOpen() const { return m_isOpen; } bool isOpen() const VL_MT_SAFE { return m_isOpen; }
//========================================================================= //=========================================================================
// Internal interface to Verilator generated code // Internal interface to Verilator generated code
@ -325,7 +327,6 @@ template <> void VerilatedTrace<VerilatedVcd>::set_time_resolution(const std::st
// VerilatedVcdC // VerilatedVcdC
/// Create a VCD dump file in C standalone (no SystemC) simulations. /// Create a VCD dump file in C standalone (no SystemC) simulations.
/// Also derived for use in SystemC simulations. /// Also derived for use in SystemC simulations.
/// Thread safety: Unless otherwise indicated, every function is VL_MT_UNSAFE_ONE
class VerilatedVcdC VL_NOT_FINAL { class VerilatedVcdC VL_NOT_FINAL {
VerilatedVcd m_sptrace; ///< Trace file being created VerilatedVcd m_sptrace; ///< Trace file being created
@ -339,30 +340,28 @@ public:
: m_sptrace{filep} {} : m_sptrace{filep} {}
/// Destruct, flush, and close the dump /// Destruct, flush, and close the dump
~VerilatedVcdC() { close(); } ~VerilatedVcdC() { close(); }
/// Routines can only be called from one thread; allow next call from different thread
void changeThread() { spTrace()->changeThread(); }
public: public:
// METHODS - User called // METHODS - User called
/// Return if file is open /// Return if file is open
bool isOpen() const { return m_sptrace.isOpen(); } bool isOpen() const VL_MT_SAFE { return m_sptrace.isOpen(); }
/// Open a new VCD file /// Open a new VCD file
/// This includes a complete header dump each time it is called, /// This includes a complete header dump each time it is called,
/// just as if this object was deleted and reconstructed. /// just as if this object was deleted and reconstructed.
void open(const char* filename) VL_MT_UNSAFE_ONE { m_sptrace.open(filename); } void open(const char* filename) VL_MT_SAFE { m_sptrace.open(filename); }
/// Continue a VCD dump by rotating to a new file name /// Continue a VCD dump by rotating to a new file name
/// The header is only in the first file created, this allows /// 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. /// "cat" to be used to combine the header plus any number of data files.
void openNext(bool incFilename = true) VL_MT_UNSAFE_ONE { m_sptrace.openNext(incFilename); } void openNext(bool incFilename = true) VL_MT_SAFE { m_sptrace.openNext(incFilename); }
/// Set size in megabytes after which new file should be created /// Set size in megabytes after which new file should be created
void rolloverMB(size_t rolloverMB) { m_sptrace.rolloverMB(rolloverMB); } void rolloverMB(size_t rolloverMB) VL_MT_SAFE { m_sptrace.rolloverMB(rolloverMB); }
/// Close dump /// Close dump
void close() VL_MT_UNSAFE_ONE { m_sptrace.close(); } void close() VL_MT_SAFE { m_sptrace.close(); }
/// Flush dump /// Flush dump
void flush() VL_MT_UNSAFE_ONE { m_sptrace.flush(); } void flush() VL_MT_SAFE { m_sptrace.flush(); }
/// Write one cycle of dump data /// Write one cycle of dump data
void dump(vluint64_t timeui) { m_sptrace.dump(timeui); } void dump(vluint64_t timeui) VL_MT_SAFE { m_sptrace.dump(timeui); }
/// Write one cycle of dump data - backward compatible and to reduce /// Write one cycle of dump data - backward compatible and to reduce
/// conversion warnings. It's better to use a vluint64_t time instead. /// conversion warnings. It's better to use a vluint64_t time instead.
void dump(double timestamp) { dump(static_cast<vluint64_t>(timestamp)); } void dump(double timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
@ -375,13 +374,15 @@ public:
// Set time units (s/ms, defaults to ns) // Set time units (s/ms, defaults to ns)
// Users should not need to call this, as for Verilated models, these // Users should not need to call this, as for Verilated models, these
// propage from the Verilated default timeunit // propage from the Verilated default timeunit
void set_time_unit(const char* unit) { m_sptrace.set_time_unit(unit); } void set_time_unit(const char* unit) VL_MT_SAFE { m_sptrace.set_time_unit(unit); }
void set_time_unit(const std::string& unit) { m_sptrace.set_time_unit(unit); } void set_time_unit(const std::string& unit) VL_MT_SAFE { m_sptrace.set_time_unit(unit); }
// Set time resolution (s/ms, defaults to ns) // Set time resolution (s/ms, defaults to ns)
// Users should not need to call this, as for Verilated models, these // Users should not need to call this, as for Verilated models, these
// propage from the Verilated default timeprecision // propage from the Verilated default timeprecision
void set_time_resolution(const char* unit) { m_sptrace.set_time_resolution(unit); } void set_time_resolution(const char* unit) VL_MT_SAFE { m_sptrace.set_time_resolution(unit); }
void set_time_resolution(const std::string& unit) { m_sptrace.set_time_resolution(unit); } void set_time_resolution(const std::string& unit) VL_MT_SAFE {
m_sptrace.set_time_resolution(unit);
}
// Internal class access // Internal class access
inline VerilatedVcd* spTrace() { return &m_sptrace; } inline VerilatedVcd* spTrace() { return &m_sptrace; }

View File

@ -667,7 +667,7 @@ class VerilatedVpiError final {
do_callbacks(); do_callbacks();
} }
void do_callbacks() { void do_callbacks() {
if (getError()->level >= vpiError && Verilated::fatalOnVpiError()) { if (getError()->level >= vpiError && Verilated::threadContextp()->fatalOnVpiError()) {
// Stop on vpi error/unsupported // Stop on vpi error/unsupported
vpi_unsupported(); vpi_unsupported();
} }
@ -1224,7 +1224,7 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
} }
{ {
// This doesn't yet follow the hierarchy in the proper way // This doesn't yet follow the hierarchy in the proper way
scopep = Verilated::scopeFind(namep); scopep = Verilated::threadContextp()->scopeFind(namep);
if (scopep) { // Whole thing found as a scope if (scopep) { // Whole thing found as a scope
if (scopep->type() == VerilatedScope::SCOPE_MODULE) { if (scopep->type() == VerilatedScope::SCOPE_MODULE) {
return (new VerilatedVpioModule(scopep))->castVpiHandle(); return (new VerilatedVpioModule(scopep))->castVpiHandle();
@ -1242,11 +1242,11 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
if (scopename.find('.') == std::string::npos) { if (scopename.find('.') == std::string::npos) {
// This is a toplevel, hence search in our TOP ports first. // This is a toplevel, hence search in our TOP ports first.
scopep = Verilated::scopeFind("TOP"); scopep = Verilated::threadContextp()->scopeFind("TOP");
if (scopep) varp = scopep->varFind(baseNamep); if (scopep) varp = scopep->varFind(baseNamep);
} }
if (!varp) { if (!varp) {
scopep = Verilated::scopeFind(scopename.c_str()); scopep = Verilated::threadContextp()->scopeFind(scopename.c_str());
if (!scopep) return nullptr; if (!scopep) return nullptr;
varp = scopep->varFind(baseNamep); varp = scopep->varFind(baseNamep);
} }
@ -1418,11 +1418,12 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
VL_VPI_ERROR_RESET_(); VL_VPI_ERROR_RESET_();
switch (property) { switch (property) {
case vpiTimePrecision: { case vpiTimePrecision: {
return Verilated::timeprecision(); return Verilated::threadContextp()->timeprecision();
} }
case vpiTimeUnit: { case vpiTimeUnit: {
VerilatedVpioScope* vop = VerilatedVpioScope::castp(object); VerilatedVpioScope* vop = VerilatedVpioScope::castp(object);
if (!vop) return Verilated::timeunit(); // Null asks for global, not unlikely if (!vop)
return Verilated::threadContextp()->timeunit(); // Null asks for global, not unlikely
return vop->scopep()->timeunit(); return vop->scopep()->timeunit();
} }
case vpiType: { case vpiType: {
@ -2022,7 +2023,8 @@ void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
} else if (time_p->type == vpiScaledRealTime) { } else if (time_p->type == vpiScaledRealTime) {
double dtime = VL_TIME_D(); double dtime = VL_TIME_D();
if (VerilatedVpioScope* vop = VerilatedVpioScope::castp(object)) { if (VerilatedVpioScope* vop = VerilatedVpioScope::castp(object)) {
int scalePow10 = Verilated::timeprecision() - vop->scopep()->timeunit(); int scalePow10
= Verilated::threadContextp()->timeprecision() - vop->scopep()->timeunit();
double scale = vl_time_multiplier(scalePow10); // e.g. 0.0001 double scale = vl_time_multiplier(scalePow10); // e.g. 0.0001
dtime *= scale; dtime *= scale;
} }
@ -2140,8 +2142,9 @@ PLI_INT32 vpi_release_handle(vpiHandle object) {
PLI_INT32 vpi_get_vlog_info(p_vpi_vlog_info vlog_info_p) VL_MT_SAFE { PLI_INT32 vpi_get_vlog_info(p_vpi_vlog_info vlog_info_p) VL_MT_SAFE {
VerilatedVpiImp::assertOneCheck(); VerilatedVpiImp::assertOneCheck();
VL_VPI_ERROR_RESET_(); VL_VPI_ERROR_RESET_();
vlog_info_p->argc = Verilated::getCommandArgs()->argc; auto argc_argv = Verilated::threadContextp()->impp()->argc_argv();
vlog_info_p->argv = const_cast<PLI_BYTE8**>(Verilated::getCommandArgs()->argv); vlog_info_p->argc = argc_argv.first;
vlog_info_p->argv = argc_argv.second;
vlog_info_p->product = const_cast<PLI_BYTE8*>(Verilated::productName()); vlog_info_p->product = const_cast<PLI_BYTE8*>(Verilated::productName());
vlog_info_p->version = const_cast<PLI_BYTE8*>(Verilated::productVersion()); vlog_info_p->version = const_cast<PLI_BYTE8*>(Verilated::productVersion());
return 1; return 1;

View File

@ -96,7 +96,8 @@ private:
: // If assertions are off, have constant propagation rip them out later : // If assertions are off, have constant propagation rip them out later
// This allows syntax errors and such to be detected normally. // This allows syntax errors and such to be detected normally.
(v3Global.opt.assertOn() (v3Global.opt.assertOn()
? static_cast<AstNode*>(new AstCMath(fl, "Verilated::assertOn()", 1)) ? static_cast<AstNode*>(
new AstCMath(fl, "vlSymsp->_vm_contextp__->assertOn()", 1))
: static_cast<AstNode*>(new AstConst(fl, AstConst::BitFalse())))), : static_cast<AstNode*>(new AstConst(fl, AstConst::BitFalse())))),
nodep, nullptr); nodep, nullptr);
newp->user1(true); // Don't assert/cover this if newp->user1(true); // Don't assert/cover this if

View File

@ -514,7 +514,7 @@ public:
virtual void visit(AstDumpCtl* nodep) override { virtual void visit(AstDumpCtl* nodep) override {
switch (nodep->ctlType()) { switch (nodep->ctlType()) {
case VDumpCtlType::FILE: case VDumpCtlType::FILE:
puts("vl_dumpctl_filenamep(true, "); puts("vlSymsp->_vm_contextp__->dumpfile(");
emitCvtPackStr(nodep->exprp()); emitCvtPackStr(nodep->exprp());
puts(");\n"); puts(");\n");
break; break;
@ -848,7 +848,7 @@ public:
putsQuoted(protect(nodep->name())); putsQuoted(protect(nodep->name()));
puts(", "); puts(", ");
putsQuoted(nodep->timeunit().ascii()); putsQuoted(nodep->timeunit().ascii());
puts(");\n"); puts(", vlSymsp->_vm_contextp__);\n");
} }
virtual void visit(AstRand* nodep) override { virtual void visit(AstRand* nodep) override {
emitOpName(nodep, nodep->emitC(), nodep->seedp(), nullptr, nullptr); emitOpName(nodep, nodep->emitC(), nodep->seedp(), nullptr, nullptr);
@ -876,7 +876,7 @@ public:
emitCvtPackStr(nodep->suffixp()); emitCvtPackStr(nodep->suffixp());
puts(", "); puts(", ");
iterateAndNextNull(nodep->widthp()); iterateAndNextNull(nodep->widthp());
puts(");\n"); puts(", vlSymsp->_vm_contextp__);\n");
} }
virtual void visit(AstNodeSimpleText* nodep) override { virtual void visit(AstNodeSimpleText* nodep) override {
if (nodep->tracking() || m_trackText) { if (nodep->tracking() || m_trackText) {
@ -1857,6 +1857,7 @@ class EmitCImp final : EmitCStmts {
void emitImp(AstNodeModule* modp); void emitImp(AstNodeModule* modp);
void emitSettleLoop(const std::string& eval_call, bool initial); void emitSettleLoop(const std::string& eval_call, bool initial);
void emitWrapEval(AstNodeModule* modp); void emitWrapEval(AstNodeModule* modp);
void emitWrapFast(AstNodeModule* modp);
void emitMTaskState(); void emitMTaskState();
void emitMTaskVertexCtors(bool* firstp); void emitMTaskVertexCtors(bool* firstp);
void emitIntTop(AstNodeModule* modp); void emitIntTop(AstNodeModule* modp);
@ -2429,11 +2430,15 @@ void EmitCImp::emitCtorImp(AstNodeModule* modp) {
modp->v3fatalSrc("constructors should be AstCFuncs instead"); modp->v3fatalSrc("constructors should be AstCFuncs instead");
} else if (optSystemC() && modp->isTop()) { } else if (optSystemC() && modp->isTop()) {
puts(prefixNameProtect(modp) + "::" + prefixNameProtect(modp) + "(sc_module_name)"); puts(prefixNameProtect(modp) + "::" + prefixNameProtect(modp) + "(sc_module_name)");
} else if (modp->isTop()) {
puts(prefixNameProtect(modp) + "::" + prefixNameProtect(modp)
+ "(VerilatedContext* _vcontextp__, const char* _vcname__)\n");
puts(" : VerilatedModule{_vcname__}\n");
first = false; // printed the first ':'
} else { } else {
puts(prefixNameProtect(modp) + "::" + prefixNameProtect(modp) puts(prefixNameProtect(modp) + "::" + prefixNameProtect(modp)
+ "(const char* __VCname)\n"); + "(const char* _vcname__)\n");
puts(" : VerilatedModule(__VCname)\n"); puts(" : VerilatedModule(_vcname__)\n");
first = false; // printed the first ':'
} }
emitVarCtors(&first); emitVarCtors(&first);
if (modp->isTop() && v3Global.opt.mtasks()) emitMTaskVertexCtors(&first); if (modp->isTop() && v3Global.opt.mtasks()) emitMTaskVertexCtors(&first);
@ -2473,8 +2478,8 @@ void EmitCImp::emitCtorImp(AstNodeModule* modp) {
// Note we create N-1 threads in the thread pool. The thread // Note we create N-1 threads in the thread pool. The thread
// that calls eval() becomes the final Nth thread for the // that calls eval() becomes the final Nth thread for the
// duration of the eval call. // duration of the eval call.
+ cvtToStr(v3Global.opt.threads() - 1) + ", " + cvtToStr(v3Global.opt.profThreads()) + string("vlSymsp->_vm_contextp__, ") + cvtToStr(v3Global.opt.threads() - 1) + ", "
+ ");\n"); + cvtToStr(v3Global.opt.profThreads()) + ");\n");
if (v3Global.opt.profThreads()) { if (v3Global.opt.profThreads()) {
puts("__Vm_profile_cycle_start = 0;\n"); puts("__Vm_profile_cycle_start = 0;\n");
@ -2493,12 +2498,12 @@ void EmitCImp::emitConfigureImp(AstNodeModule* modp) {
puts("if (false && this->__VlSymsp) {} // Prevent unused\n"); puts("if (false && this->__VlSymsp) {} // Prevent unused\n");
if (v3Global.opt.coverage()) { puts(protect("_configure_coverage") + "(vlSymsp, first);\n"); } if (v3Global.opt.coverage()) { puts(protect("_configure_coverage") + "(vlSymsp, first);\n"); }
if (modp->isTop() && !v3Global.rootp()->timeunit().isNone()) { if (modp->isTop() && !v3Global.rootp()->timeunit().isNone()) {
puts("Verilated::timeunit(" + cvtToStr(v3Global.rootp()->timeunit().powerOfTen()) puts("vlSymsp->_vm_contextp__->timeunit("
+ ");\n"); + cvtToStr(v3Global.rootp()->timeunit().powerOfTen()) + ");\n");
} }
if (modp->isTop() && !v3Global.rootp()->timeprecision().isNone()) { if (modp->isTop() && !v3Global.rootp()->timeprecision().isNone()) {
puts("Verilated::timeprecision(" + cvtToStr(v3Global.rootp()->timeprecision().powerOfTen()) puts("vlSymsp->_vm_contextp__->timeprecision("
+ ");\n"); + cvtToStr(v3Global.rootp()->timeprecision().powerOfTen()) + ");\n");
} }
puts("}\n"); puts("}\n");
splitSizeInc(10); splitSizeInc(10);
@ -2525,7 +2530,7 @@ void EmitCImp::emitCoverageImp(AstNodeModule*) {
// Used for second++ instantiation of identical bin // Used for second++ instantiation of identical bin
puts("if (!enable) count32p = &fake_zero_count;\n"); puts("if (!enable) count32p = &fake_zero_count;\n");
puts("*count32p = 0;\n"); puts("*count32p = 0;\n");
puts("VL_COVER_INSERT(count32p,"); puts("VL_COVER_INSERT(__VlSymsp->_vm_contextp__->coveragep(), count32p,");
puts(" \"filename\",filenamep,"); puts(" \"filename\",filenamep,");
puts(" \"lineno\",lineno,"); puts(" \"lineno\",lineno,");
puts(" \"column\",column,\n"); puts(" \"column\",column,\n");
@ -2585,9 +2590,15 @@ void EmitCImp::emitSavableImp(AstNodeModule* modp) {
if (de) { if (de) {
puts("os.readAssert(__Vcheckval);\n"); puts("os.readAssert(__Vcheckval);\n");
} else { } else {
puts("os<<__Vcheckval;\n"); puts("os << __Vcheckval;\n");
} }
// Save context
// If multiple models save the same context we'll save it multiple
// times, but is harmless, and doing it otherwise would break
// backwards compatibility.
puts("os " + op + " __VlSymsp->_vm_contextp__;\n");
// Save all members // Save all members
if (v3Global.opt.inhibitSim()) puts("os" + op + "__Vm_inhibitSim;\n"); if (v3Global.opt.inhibitSim()) puts("os" + op + "__Vm_inhibitSim;\n");
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
@ -2662,8 +2673,9 @@ void EmitCImp::emitTextSection(AstType type) {
void EmitCImp::emitCellCtors(AstNodeModule* modp) { void EmitCImp::emitCellCtors(AstNodeModule* modp) {
if (modp->isTop()) { if (modp->isTop()) {
// Must be before other constructors, as __vlCoverInsert calls it // Must be before other constructors, as __vlCoverInsert calls it
puts(EmitCBaseVisitor::symClassVar() + " = __VlSymsp = new " + symClassName() // Note _vcontextp__ may be nullptr, VerilatedSyms::VerilatedSyms cleans it up
+ "(this, name());\n"); puts(EmitCBaseVisitor::symClassVar() + " = __VlSymsp = new " + symClassName() + "("
+ (optSystemC() ? "nullptr" : "_vcontextp__") + ", this, name());\n");
puts(EmitCBaseVisitor::symTopAssign() + "\n"); puts(EmitCBaseVisitor::symTopAssign() + "\n");
} }
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
@ -2736,6 +2748,12 @@ void EmitCImp::emitSettleLoop(const std::string& eval_call, bool initial) {
puts("} while (VL_UNLIKELY(__Vchange));\n"); puts("} while (VL_UNLIKELY(__Vchange));\n");
} }
void EmitCImp::emitWrapFast(AstNodeModule* modp) {
puts("\nVerilatedContext* " + prefixNameProtect(modp) + "::contextp() {\n");
puts(/**/ "return __VlSymsp->_vm_contextp__;\n");
puts("}\n");
}
void EmitCImp::emitWrapEval(AstNodeModule* modp) { void EmitCImp::emitWrapEval(AstNodeModule* modp) {
puts("\nvoid " + prefixNameProtect(modp) + "::eval_step() {\n"); puts("\nvoid " + prefixNameProtect(modp) + "::eval_step() {\n");
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+++++TOP Evaluate " + prefixNameProtect(modp) puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+++++TOP Evaluate " + prefixNameProtect(modp)
@ -2759,9 +2777,10 @@ void EmitCImp::emitWrapEval(AstNodeModule* modp) {
} }
if (v3Global.opt.mtasks() && v3Global.opt.profThreads()) { if (v3Global.opt.mtasks() && v3Global.opt.profThreads()) {
puts("if (VL_UNLIKELY((Verilated::profThreadsStart() != __Vm_profile_time_finished)\n"); puts("if (VL_UNLIKELY((vlSymsp->_vm_contextp__->profThreadsStart() != "
puts(" && (VL_TIME_Q() > Verilated::profThreadsStart())\n"); "__Vm_profile_time_finished)\n");
puts(" && (Verilated::profThreadsWindow() >= 1))) {\n"); puts(" && (VL_TIME_Q() > vlSymsp->_vm_contextp__->profThreadsStart())\n");
puts(" && (vlSymsp->_vm_contextp__->profThreadsWindow() >= 1))) {\n");
// Within a profile (either starting, middle, or end) // Within a profile (either starting, middle, or end)
puts("if (vlTOPp->__Vm_profile_window_ct == 0) {\n"); // Opening file? puts("if (vlTOPp->__Vm_profile_window_ct == 0) {\n"); // Opening file?
// Start profile on this cycle. We'll capture a window worth, then // Start profile on this cycle. We'll capture a window worth, then
@ -2770,10 +2789,12 @@ void EmitCImp::emitWrapEval(AstNodeModule* modp) {
// by the time we hit the second window, we hope. // by the time we hit the second window, we hope.
puts("vlTOPp->__Vm_profile_cycle_start = VL_RDTSC_Q();\n"); puts("vlTOPp->__Vm_profile_cycle_start = VL_RDTSC_Q();\n");
// "* 2" as first half is warmup, second half is collection // "* 2" as first half is warmup, second half is collection
puts("vlTOPp->__Vm_profile_window_ct = Verilated::profThreadsWindow() * 2 + 1;\n"); puts("vlTOPp->__Vm_profile_window_ct = vlSymsp->_vm_contextp__->profThreadsWindow() * 2 + "
"1;\n");
puts("}\n"); puts("}\n");
puts("--vlTOPp->__Vm_profile_window_ct;\n"); puts("--vlTOPp->__Vm_profile_window_ct;\n");
puts("if (vlTOPp->__Vm_profile_window_ct == (Verilated::profThreadsWindow())) {\n"); puts("if (vlTOPp->__Vm_profile_window_ct == "
"(vlSymsp->_vm_contextp__->profThreadsWindow())) {\n");
// This barrier record in every threads' profile demarcates the // This barrier record in every threads' profile demarcates the
// cache-warm-up cycles before the barrier from the actual profile // cache-warm-up cycles before the barrier from the actual profile
// cycles afterward. // cycles afterward.
@ -2784,12 +2805,13 @@ void EmitCImp::emitWrapEval(AstNodeModule* modp) {
puts("else if (vlTOPp->__Vm_profile_window_ct == 0) {\n"); puts("else if (vlTOPp->__Vm_profile_window_ct == 0) {\n");
// Ending file. // Ending file.
puts("vluint64_t elapsed = VL_RDTSC_Q() - vlTOPp->__Vm_profile_cycle_start;\n"); puts("vluint64_t elapsed = VL_RDTSC_Q() - vlTOPp->__Vm_profile_cycle_start;\n");
puts("vlTOPp->__Vm_threadPoolp->profileDump(Verilated::profThreadsFilenamep(), " puts(
"elapsed);\n"); "vlTOPp->__Vm_threadPoolp->profileDump(vlSymsp->_vm_contextp__->profThreadsFilename()."
"c_str(), elapsed);\n");
// This turns off the test to enter the profiling code, but still // This turns off the test to enter the profiling code, but still
// allows the user to collect another profile by changing // allows the user to collect another profile by changing
// profThreadsStart // profThreadsStart
puts("__Vm_profile_time_finished = Verilated::profThreadsStart();\n"); puts("__Vm_profile_time_finished = vlSymsp->_vm_contextp__->profThreadsStart();\n");
puts("vlTOPp->__Vm_profile_cycle_start = 0;\n"); puts("vlTOPp->__Vm_profile_cycle_start = 0;\n");
puts("}\n"); puts("}\n");
puts("}\n"); puts("}\n");
@ -3193,21 +3215,27 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
ofp()->putsPrivate(false); // public: ofp()->putsPrivate(false); // public:
if (modp->isTop()) { if (modp->isTop()) {
puts("/// Construct the model; called by application code\n"); puts("/// Construct the model; called by application code\n");
puts("/// The special name " puts("/// If contextp is null, then the model will use the default global context\n");
" may be used to make a wrapper with a\n"); puts("/// If name is \"\", then makes a wrapper with a\n");
puts("/// single model invisible with respect to DPI scope names.\n"); puts("/// single model invisible with respect to DPI scope names.\n");
} puts(prefixNameProtect(modp) + "(VerilatedContext* contextp,"
if (VN_IS(modp, Class)) { + " const char* name = \"TOP\");\n");
// TODO move all constructor definition to e.g. V3CUse puts(prefixNameProtect(modp) + "(const char* name = \"TOP\")\n");
puts(prefixNameProtect(modp) + "();\n"); puts(" : " + prefixNameProtect(modp) + "(nullptr, name) {}\n");
} else { } else {
puts(prefixNameProtect(modp) + "(const char* name = \"TOP\");\n"); if (VN_IS(modp, Class)) {
// TODO move all constructor definition to e.g. V3CUse
puts(prefixNameProtect(modp) + "();\n");
} else {
puts(prefixNameProtect(modp) + "(const char* name = \"TOP\");\n");
}
} }
if (modp->isTop()) { if (modp->isTop()) {
puts("/// Destroy the model; called (often implicitly) by application code\n"); puts("/// Destroy the model; called (often implicitly) by application code\n");
} }
puts("~" + prefixNameProtect(modp) + "();\n"); puts("~" + prefixNameProtect(modp) + "();\n");
} }
if (v3Global.opt.trace() && modp->isTop()) { if (v3Global.opt.trace() && modp->isTop()) {
puts("/// Trace signals in the model; called by application code\n"); puts("/// Trace signals in the model; called by application code\n");
puts("void trace(" + v3Global.opt.traceClassBase() puts("void trace(" + v3Global.opt.traceClassBase()
@ -3223,6 +3251,10 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
if (modp->isTop()) { if (modp->isTop()) {
puts("\n// API METHODS\n"); puts("\n// API METHODS\n");
puts("/// Return current simulation context for this model.\n");
puts("/// Used to get to e.g. simulation time via contextp()->time()\n");
puts("VerilatedContext* contextp();\n");
string callEvalEndStep string callEvalEndStep
= (v3Global.needTraceDumper() && !optSystemC()) ? "eval_end_step(); " : ""; = (v3Global.needTraceDumper() && !optSystemC()) ? "eval_end_step(); " : "";
if (optSystemC()) { if (optSystemC()) {
@ -3340,7 +3372,10 @@ void EmitCImp::emitImp(AstNodeModule* modp) {
if (m_fast) { if (m_fast) {
emitTextSection(AstType::atScImp); emitTextSection(AstType::atScImp);
if (modp->isTop()) emitWrapEval(modp); if (modp->isTop()) {
emitWrapFast(modp);
emitWrapEval(modp);
}
} }
// Blocks // Blocks
@ -3494,10 +3529,9 @@ class EmitCTrace final : EmitCStmts {
puts("const VerilatedLockGuard lock(__VlSymsp->__Vm_dumperMutex);\n"); puts("const VerilatedLockGuard lock(__VlSymsp->__Vm_dumperMutex);\n");
puts("if (VL_UNLIKELY(!__VlSymsp->__Vm_dumperp)) {\n"); puts("if (VL_UNLIKELY(!__VlSymsp->__Vm_dumperp)) {\n");
puts("__VlSymsp->__Vm_dumperp = new " + v3Global.opt.traceClassLang() + "();\n"); puts("__VlSymsp->__Vm_dumperp = new " + v3Global.opt.traceClassLang() + "();\n");
puts("const char* cp = vl_dumpctl_filenamep();\n");
puts("trace(__VlSymsp->__Vm_dumperp, 0, 0);\n"); puts("trace(__VlSymsp->__Vm_dumperp, 0, 0);\n");
puts("__VlSymsp->__Vm_dumperp->open(vl_dumpctl_filenamep());\n"); puts("std::string dumpfile = __VlSymsp->_vm_contextp__->dumpfile();\n");
puts("__VlSymsp->__Vm_dumperp->changeThread();\n"); puts("__VlSymsp->__Vm_dumperp->open(dumpfile.c_str());\n");
puts("__VlSymsp->__Vm_dumping = true;\n"); puts("__VlSymsp->__Vm_dumping = true;\n");
puts("}\n"); puts("}\n");
puts("}\n"); puts("}\n");
@ -3524,7 +3558,7 @@ class EmitCTrace final : EmitCStmts {
+ v3Global.opt.traceClassBase() + "* tracep, uint32_t code) {\n"); + v3Global.opt.traceClassBase() + "* tracep, uint32_t code) {\n");
putsDecoration("// Callback from tracep->open()\n"); putsDecoration("// Callback from tracep->open()\n");
puts(symClassVar() + " = static_cast<" + symClassName() + "*>(userp);\n"); puts(symClassVar() + " = static_cast<" + symClassName() + "*>(userp);\n");
puts("if (!Verilated::calcUnusedSigs()) {\n"); puts("if (!vlSymsp->_vm_contextp__->calcUnusedSigs()) {\n");
puts("VL_FATAL_MT(__FILE__, __LINE__, __FILE__,\n"); puts("VL_FATAL_MT(__FILE__, __LINE__, __FILE__,\n");
puts(" \"Turning on wave traces requires Verilated::traceEverOn(true) call " puts(" \"Turning on wave traces requires Verilated::traceEverOn(true) call "
"before time 0.\");\n"); "before time 0.\");\n");

View File

@ -45,6 +45,9 @@ private:
V3OutCFile cf(filename); V3OutCFile cf(filename);
m_ofp = &cf; m_ofp = &cf;
// Not defining main_time/vl_time_stamp, so
v3Global.opt.addCFlags("-DVL_TIME_CONTEXT"); // On MSVC++ anyways
// Heavly commented output, as users are likely to look at or copy this code // Heavly commented output, as users are likely to look at or copy this code
ofp()->putsHeader(); ofp()->putsHeader();
puts("// DESCRIPTION: main() calling loop, created with Verilator --main\n"); puts("// DESCRIPTION: main() calling loop, created with Verilator --main\n");
@ -55,20 +58,16 @@ private:
puts("\n//======================\n\n"); puts("\n//======================\n\n");
puts("// Requires -DVL_TIME_STAMP64\n");
v3Global.opt.addCFlags("-DVL_TIME_STAMP64");
puts("vluint64_t main_time = 0;\n");
puts("vluint64_t vl_time_stamp64() { return main_time; }\n");
puts("\n");
puts("int main(int argc, char** argv, char**) {\n"); puts("int main(int argc, char** argv, char**) {\n");
puts("// Setup defaults and parse command line\n"); puts("// Setup context, defaults, and parse command line\n");
puts("Verilated::debug(0);\n"); puts("Verilated::debug(0);\n");
puts("Verilated::commandArgs(argc, argv);\n"); puts("const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};\n");
puts("contextp->commandArgs(argc, argv);\n");
puts("\n"); puts("\n");
puts("// Construct the Verilated model, from Vtop.h generated from Verilating\n"); puts("// Construct the Verilated model, from Vtop.h generated from Verilating\n");
puts("const std::unique_ptr<" + topClassName() + "> topp{new " + topClassName() + "};\n"); puts("const std::unique_ptr<" + topClassName() + "> topp{new " + topClassName()
+ "{contextp.get()}};\n");
puts("\n"); puts("\n");
puts("// Evaluate initials\n"); puts("// Evaluate initials\n");
@ -76,15 +75,16 @@ private:
puts("\n"); puts("\n");
puts("// Simulate until $finish\n"); puts("// Simulate until $finish\n");
puts("while (!Verilated::gotFinish()) {\n"); puts("while (!contextp->gotFinish()) {\n");
puts(/**/ "// Evaluate model\n"); puts(/**/ "// Evaluate model\n");
puts(/**/ "topp->eval();\n"); puts(/**/ "topp->eval();\n");
puts(/**/ "// Advance time\n"); puts(/**/ "// Advance time\n");
puts(/**/ "++main_time;\n"); puts(/**/ "contextp->timeInc(1);\n");
puts("}\n"); puts("}\n");
puts("\n"); puts("\n");
puts("if (!Verilated::gotFinish()) {\n"); puts("if (!contextp->gotFinish()) {\n");
puts(/**/ "VL_DEBUG_IF(VL_PRINTF(\"+ Exiting without $finish; no events left\\n\"););\n"); puts(/**/ "VL_DEBUG_IF(VL_PRINTF(\"+ Exiting without $finish; no events left\\n\"););\n");
puts("}\n"); puts("}\n");
puts("\n"); puts("\n");

View File

@ -467,7 +467,8 @@ void EmitCSyms::emitSymHdr() {
} }
puts("\n// CREATORS\n"); puts("\n// CREATORS\n");
puts(symClassName() + "(" + topClassName() + "* topp, const char* namep);\n"); puts(symClassName() + "(VerilatedContext* contextp, " + topClassName()
+ "* topp, const char* namep);\n");
puts(string("~") + symClassName() + "();\n"); puts(string("~") + symClassName() + "();\n");
for (const auto& i : m_usesVfinal) { for (const auto& i : m_usesVfinal) {
@ -643,10 +644,11 @@ void EmitCSyms::emitSymImp() {
puts("{\n"); puts("{\n");
emitScopeHier(true); emitScopeHier(true);
puts("}\n\n"); puts("}\n\n");
puts(symClassName() + "::" + symClassName() + "(" + topClassName() puts(symClassName() + "::" + symClassName() + "(VerilatedContext* contextp, " + topClassName()
+ "* topp, const char* namep)\n"); + "* topp, const char* namep)\n");
puts(" // Setup locals\n"); puts(" // Setup locals\n");
puts(" : __Vm_namep(namep)\n"); // No leak, as gets destroyed when the top is destroyed puts(" : VerilatedSyms{contextp}\n");
puts(" , __Vm_namep(namep)\n"); // No leak, as gets destroyed when the top is destroyed
if (v3Global.needTraceDumper()) { if (v3Global.needTraceDumper()) {
puts(" , __Vm_dumping(false)\n"); puts(" , __Vm_dumping(false)\n");
puts(" , __Vm_dumperp(nullptr)\n"); puts(" , __Vm_dumperp(nullptr)\n");

View File

@ -1737,15 +1737,6 @@ sub _make_main {
print $fh "#include \"verilated_save.h\"\n" if $self->{savable}; print $fh "#include \"verilated_save.h\"\n" if $self->{savable};
print $fh "std::unique_ptr<$VM_PREFIX> topp;\n"; print $fh "std::unique_ptr<$VM_PREFIX> topp;\n";
if (!$self->sc) {
if ($self->{vl_time_stamp64}) {
print $fh "vluint64_t main_time = 0;\n";
print $fh "vluint64_t vl_time_stamp64() { return main_time; }\n";
} else {
print $fh "double main_time = 0;\n";
print $fh "double sc_time_stamp() { return main_time; }\n";
}
}
if ($self->{savable}) { if ($self->{savable}) {
$fh->print("\n"); $fh->print("\n");
@ -1753,7 +1744,6 @@ sub _make_main {
$fh->print(" VL_PRINTF(\"Saving model to '%s'\\n\", filenamep);\n"); $fh->print(" VL_PRINTF(\"Saving model to '%s'\\n\", filenamep);\n");
$fh->print(" VerilatedSave os;\n"); $fh->print(" VerilatedSave os;\n");
$fh->print(" os.open(filenamep);\n"); $fh->print(" os.open(filenamep);\n");
$fh->print(" os << main_time;\n");
$fh->print(" os << *topp;\n"); $fh->print(" os << *topp;\n");
$fh->print(" os.close();\n"); $fh->print(" os.close();\n");
$fh->print("}\n"); $fh->print("}\n");
@ -1762,7 +1752,6 @@ sub _make_main {
$fh->print(" VL_PRINTF(\"Restoring model from '%s'\\n\", filenamep);\n"); $fh->print(" VL_PRINTF(\"Restoring model from '%s'\\n\", filenamep);\n");
$fh->print(" VerilatedRestore os;\n"); $fh->print(" VerilatedRestore os;\n");
$fh->print(" os.open(filenamep);\n"); $fh->print(" os.open(filenamep);\n");
$fh->print(" os >> main_time;\n");
$fh->print(" os >> *topp;\n"); $fh->print(" os >> *topp;\n");
$fh->print(" os.close();\n"); $fh->print(" os.close();\n");
$fh->print("}\n"); $fh->print("}\n");
@ -1778,14 +1767,16 @@ sub _make_main {
print $fh " sc_time sim_time($self->{sim_time}, $Self->{sc_time_resolution});\n"; print $fh " sc_time sim_time($self->{sim_time}, $Self->{sc_time_resolution});\n";
} else { } else {
print $fh "int main(int argc, char** argv, char** env) {\n"; print $fh "int main(int argc, char** argv, char** env) {\n";
print $fh " double sim_time = $self->{sim_time};\n"; print $fh " vluint64_t sim_time = $self->{sim_time};\n";
} }
print $fh " Verilated::commandArgs(argc, argv);\n";
print $fh " Verilated::debug(".($self->{verilated_debug}?1:0).");\n"; print $fh " const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};\n";
print $fh " contextp->commandArgs(argc, argv);\n";
print $fh " contextp->debug(".($self->{verilated_debug}?1:0).");\n";
print $fh " srand48(5);\n"; # Ensure determinism print $fh " srand48(5);\n"; # Ensure determinism
print $fh " Verilated::randReset(".$self->{verilated_randReset}.");\n" if defined $self->{verilated_randReset}; print $fh " contextp->randReset(".$self->{verilated_randReset}.");\n" if defined $self->{verilated_randReset};
print $fh " topp.reset(new $VM_PREFIX(\"top\"));\n"; print $fh " topp.reset(new $VM_PREFIX(\"top\"));\n";
print $fh " Verilated::internalsDump()\n;" if $self->{verilated_debug}; print $fh " contextp->internalsDump()\n;" if $self->{verilated_debug};
my $set; my $set;
if ($self->sc) { if ($self->sc) {
@ -1800,22 +1791,22 @@ sub _make_main {
if ($self->{trace}) { if ($self->{trace}) {
$fh->print("\n"); $fh->print("\n");
$fh->print("#if VM_TRACE\n"); $fh->print("#if VM_TRACE\n");
$fh->print(" Verilated::traceEverOn(true);\n"); $fh->print(" contextp->traceEverOn(true);\n");
$fh->print(" std::unique_ptr<VerilatedFstC> tfp{new VerilatedFstC};\n") if $self->{trace_format} eq 'fst-c'; $fh->print(" std::unique_ptr<VerilatedFstC> tfp{new VerilatedFstC};\n") if $self->{trace_format} eq 'fst-c';
$fh->print(" std::unique_ptr<VerilatedVcdC> tfp{new VerilatedVcdC};\n") if $self->{trace_format} eq 'vcd-c'; $fh->print(" std::unique_ptr<VerilatedVcdC> tfp{new VerilatedVcdC};\n") if $self->{trace_format} eq 'vcd-c';
$fh->print(" std::unique_ptr<VerilatedVcdSc> tfp{new VerilatedVcdSc};\n") if $self->{trace_format} eq 'vcd-sc'; $fh->print(" std::unique_ptr<VerilatedVcdSc> tfp{new VerilatedVcdSc};\n") if $self->{trace_format} eq 'vcd-sc';
$fh->print(" topp->trace(tfp.get(), 99);\n"); $fh->print(" topp->trace(tfp.get(), 99);\n");
$fh->print(" tfp->open(\"".$self->trace_filename."\");\n"); $fh->print(" tfp->open(\"".$self->trace_filename."\");\n");
if ($self->{trace} && !$self->sc) { if ($self->{trace} && !$self->sc) {
$fh->print(" if (tfp) tfp->dump(main_time);\n"); $fh->print(" if (tfp) tfp->dump(contextp->time());\n");
} }
$fh->print("#endif\n"); $fh->print("#endif\n");
} }
if ($self->{savable}) { if ($self->{savable}) {
$fh->print(" const char* save_time_strp = Verilated::commandArgsPlusMatch(\"save_time=\");\n"); $fh->print(" const char* save_time_strp = contextp->commandArgsPlusMatch(\"save_time=\");\n");
$fh->print(" unsigned int save_time = !save_time_strp[0] ? 0 : atoi(save_time_strp+strlen(\"+save_time=\"));\n"); $fh->print(" unsigned int save_time = !save_time_strp[0] ? 0 : atoi(save_time_strp+strlen(\"+save_time=\"));\n");
$fh->print(" const char* save_restore_strp = Verilated::commandArgsPlusMatch(\"save_restore=\");\n"); $fh->print(" const char* save_restore_strp = contextp->commandArgsPlusMatch(\"save_restore=\");\n");
$fh->print(" unsigned int save_restore = !save_restore_strp[0] ? 0 : 1;\n"); $fh->print(" unsigned int save_restore = !save_restore_strp[0] ? 0 : 1;\n");
} }
@ -1831,8 +1822,11 @@ sub _make_main {
_print_advance_time($self, $fh, 10); _print_advance_time($self, $fh, 10);
print $fh " }\n"; print $fh " }\n";
print $fh " while ((sc_time_stamp() < sim_time * MAIN_TIME_MULTIPLIER)\n"; my $time = $self->sc ? "sc_time_stamp()" : "contextp->time()";
print $fh " && !Verilated::gotFinish()) {\n";
print $fh " while ((${time} < sim_time * MAIN_TIME_MULTIPLIER)\n";
print $fh " && !contextp->gotFinish()) {\n";
for (my $i=0; $i<5; $i++) { for (my $i=0; $i<5; $i++) {
my $action = 0; my $action = 0;
if ($self->{inputs}{fastclk}) { if ($self->{inputs}{fastclk}) {
@ -1844,7 +1838,7 @@ sub _make_main {
$action = 1; $action = 1;
} }
if ($self->{savable}) { if ($self->{savable}) {
$fh->print(" if (sc_time_stamp() == save_time && save_time) {\n"); $fh->print(" if (save_time && ${time} == save_time) {\n");
$fh->print(" save_model(\"$self->{obj_dir}/saved.vltsv\");\n"); $fh->print(" save_model(\"$self->{obj_dir}/saved.vltsv\");\n");
$fh->print(" printf(\"Exiting after save_model\\n\");\n"); $fh->print(" printf(\"Exiting after save_model\\n\");\n");
$fh->print(" return 0;\n"); $fh->print(" return 0;\n");
@ -1853,7 +1847,7 @@ sub _make_main {
_print_advance_time($self, $fh, 1, $action); _print_advance_time($self, $fh, 1, $action);
} }
print $fh " }\n"; print $fh " }\n";
print $fh " if (!Verilated::gotFinish()) {\n"; print $fh " if (!contextp->gotFinish()) {\n";
print $fh ' vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish");',"\n"; print $fh ' vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish");',"\n";
print $fh " }\n"; print $fh " }\n";
print $fh " topp->final();\n"; print $fh " topp->final();\n";
@ -1894,11 +1888,11 @@ sub _print_advance_time {
print $fh " ${set}eval();\n"; print $fh " ${set}eval();\n";
if ($self->{trace} && !$self->sc) { if ($self->{trace} && !$self->sc) {
$fh->print("#if VM_TRACE\n"); $fh->print("#if VM_TRACE\n");
$fh->print(" if (tfp) tfp->dump(main_time);\n"); $fh->print(" if (tfp) tfp->dump(contextp->time());\n");
$fh->print("#endif // VM_TRACE\n"); $fh->print("#endif // VM_TRACE\n");
} }
} }
print $fh " main_time += ${time} * MAIN_TIME_MULTIPLIER;\n"; print $fh " contextp->timeInc(${time} * MAIN_TIME_MULTIPLIER);\n";
} }
} }
@ -2601,7 +2595,7 @@ can be used:
This can be particularly useful if checking that the Verilator model has This can be particularly useful if checking that the Verilator model has
not unexpectedly terminated. not unexpectedly terminated.
if (Verilated::gotFinish()) { if (contextp->gotFinish()) {
vl_fatal(__FILE__, __LINE__, "dut", "<error message goes here>"); vl_fatal(__FILE__, __LINE__, "dut", "<error message goes here>");
exit(1); exit(1);
} }

View File

@ -8,24 +8,23 @@
// General headers // General headers
#include "verilated.h" #include "verilated.h"
Vt_clk_inp_init* topp; void oneTest(int argc, char** argv, int seed) {
vluint64_t main_time;
double sc_time_stamp() { return main_time; }
void oneTest(int seed) {
vluint64_t sim_time = 1000; vluint64_t sim_time = 1000;
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
VL_PRINTF("== Seed=%d\n", seed); VL_PRINTF("== Seed=%d\n", seed);
#endif #endif
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
contextp->commandArgs(argc, argv);
// Randomise initial state // Randomise initial state
srand48(seed); srand48(seed);
srand48(5); srand48(5);
Verilated::randReset(123); contextp->randReset(123);
topp = new Vt_clk_inp_init("top"); // Construct the Verilated model, from Vtop.h generated from Verilating
const std::unique_ptr<Vt_clk_inp_init> topp{new Vt_clk_inp_init{contextp.get()}};
// Start not in reset // Start not in reset
topp->rst_n = 1; topp->rst_n = 1;
@ -33,33 +32,31 @@ void oneTest(int seed) {
topp->eval(); topp->eval();
// Tick for a little bit // Tick for a little bit
while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { while (contextp->time() < sim_time && !contextp->gotFinish()) {
topp->clk = 0; topp->clk = 0;
topp->eval(); topp->eval();
main_time += 5; contextp->timeInc(5);
topp->clk = 1; topp->clk = 1;
topp->eval(); topp->eval();
main_time += 5; contextp->timeInc(5);
} }
if (!Verilated::gotFinish()) { if (!contextp->gotFinish()) {
vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish"); vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish");
} }
topp->final(); topp->final();
VL_DO_DANGLING(delete topp, topp);
} }
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
Verilated::commandArgs(argc, argv);
#if VL_DEBUG #if VL_DEBUG
// Verilated::debug(1); // Verilated::debug(1);
#endif #endif
for (int seed = 123; seed < 133; ++seed) oneTest(seed); for (int seed = 123; seed < 133; ++seed) oneTest(argc, argv, seed);
return 0; return 0;
} }

View File

@ -39,16 +39,26 @@ int main() {
vluint32_t covers[1]; vluint32_t covers[1];
vluint64_t coverw[2]; vluint64_t coverw[2];
// VerilatedCovContext* covContextp = Verilated::defaultContextp()->coveragep();
VL_COVER_INSERT(&covers[0], "comment", "kept_one"); VL_COVER_INSERT(covContextp, &covers[0], "comment", "kept_one");
VL_COVER_INSERT(&coverw[0], "comment", "kept_two"); VL_COVER_INSERT(covContextp, &coverw[0], "comment", "kept_two");
VL_COVER_INSERT(&coverw[1], "comment", "lost_three"); VL_COVER_INSERT(covContextp, &coverw[1], "comment", "lost_three");
covers[0] = 100; covers[0] = 100;
coverw[0] = 210; coverw[0] = 210;
coverw[1] = 220; coverw[1] = 220;
#ifdef T_COVER_LIB
CHECK_RESULT_CSTR(covContextp->defaultFilename(), "coverage.dat");
covContextp->write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage1.dat");
covContextp->clearNonMatch("kept_");
covContextp->write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage2.dat");
covContextp->zero();
covContextp->write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage3.dat");
covContextp->clear();
covContextp->write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage4.dat");
#elif defined(T_COVER_LIB_LEGACY)
CHECK_RESULT_CSTR(VerilatedCov::defaultFilename(), "coverage.dat"); CHECK_RESULT_CSTR(VerilatedCov::defaultFilename(), "coverage.dat");
VerilatedCov::write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage1.dat"); VerilatedCov::write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage1.dat");
VerilatedCov::clearNonMatch("kept_"); VerilatedCov::clearNonMatch("kept_");
@ -57,6 +67,9 @@ int main() {
VerilatedCov::write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage3.dat"); VerilatedCov::write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage3.dat");
VerilatedCov::clear(); VerilatedCov::clear();
VerilatedCov::write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage4.dat"); VerilatedCov::write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage4.dat");
#else
#error
#endif
printf("*-* All Finished *-*\n"); printf("*-* All Finished *-*\n");
return (failure ? 10 : 0); return (failure ? 10 : 0);

View File

@ -45,19 +45,36 @@ long long get_memory_usage() {
} }
void make_and_destroy() { void make_and_destroy() {
#ifdef VL_NO_LEGACY
VerilatedContext* contextp = new VerilatedContext;
VM_PREFIX* topp = new VM_PREFIX{contextp};
#else
VM_PREFIX* topp = new VM_PREFIX; VM_PREFIX* topp = new VM_PREFIX;
#endif
Verilated::debug(0); Verilated::debug(0);
Verilated::gotFinish(0);
topp->eval(); topp->eval();
topp->clk = true; topp->clk = true;
while (!Verilated::gotFinish()) { while (!
#ifdef VL_NO_LEGACY
contextp->gotFinish()
#else
Verilated::gotFinish()
#endif
) {
#ifdef VL_NO_LEGACY
contextp->timeInc(5);
#else
main_time += 5; main_time += 5;
#endif
topp->clk = !topp->clk; topp->clk = !topp->clk;
topp->eval(); topp->eval();
} }
VL_DO_DANGLING(delete topp, topp); VL_DO_DANGLING(delete topp, topp);
#ifdef VL_NO_LEGACY
VL_DO_DANGLING(delete contextp, contextp);
#endif
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {

View File

@ -15,7 +15,7 @@ module t (clk);
cyc <= cyc + 1; cyc <= cyc + 1;
if (cyc==2) begin if (cyc==2) begin
// Not $finish; as we don't want a message to scroll by // Not $finish; as we don't want a message to scroll by
$c("Verilated::gotFinish(true);"); $c("Verilated::threadContextp()->gotFinish(true);");
end end
end end
endmodule endmodule

View File

@ -41,10 +41,10 @@ static void cycle() {
} }
int main() { int main() {
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
contextp->traceEverOn(true);
Verilated::traceEverOn(true); vcore = new VM_PREFIX{contextp.get()};
vcore = new Vt_order_multidriven;
vcd = new VerilatedVcdC; vcd = new VerilatedVcdC;
vcore->trace(vcd, 99); vcore->trace(vcd, 99);

View File

@ -14,16 +14,15 @@
#include "Vt_scope_map.h" #include "Vt_scope_map.h"
unsigned long long main_time = 0;
double sc_time_stamp() { return (double)main_time; }
const unsigned long long dt_2 = 3; const unsigned long long dt_2 = 3;
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
Vt_scope_map* top = new Vt_scope_map("top"); const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
Verilated::debug(0); Vt_scope_map* top = new Vt_scope_map{contextp.get(), "top"};
Verilated::traceEverOn(true);
contextp->debug(0);
contextp->traceEverOn(true);
VerilatedVcdC* tfp = new VerilatedVcdC; VerilatedVcdC* tfp = new VerilatedVcdC;
top->trace(tfp, 99); top->trace(tfp, 99);
@ -31,10 +30,10 @@ int main(int argc, char** argv, char** env) {
top->CLK = 0; top->CLK = 0;
top->eval(); top->eval();
tfp->dump((unsigned int)(main_time)); tfp->dump(contextp->time());
++main_time; contextp->timeInc(1);
const VerilatedScopeNameMap* scopeMapp = Verilated::scopeNameMap(); const VerilatedScopeNameMap* scopeMapp = contextp->scopeNameMap();
for (VerilatedScopeNameMap::const_iterator it = scopeMapp->begin(); it != scopeMapp->end(); for (VerilatedScopeNameMap::const_iterator it = scopeMapp->begin(); it != scopeMapp->end();
++it) { ++it) {
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
@ -106,14 +105,14 @@ int main(int argc, char** argv, char** env) {
top->CLK = 0; top->CLK = 0;
top->eval(); top->eval();
tfp->dump((unsigned int)(main_time)); tfp->dump(contextp->time());
++main_time; contextp->timeInc(1);
// Posedge on clock, expect all the public bits to flip // Posedge on clock, expect all the public bits to flip
top->CLK = 1; top->CLK = 1;
top->eval(); top->eval();
tfp->dump((unsigned int)(main_time)); tfp->dump(contextp->time());
++main_time; contextp->timeInc(1);
for (VerilatedScopeNameMap::const_iterator it = scopeMapp->begin(); it != scopeMapp->end(); for (VerilatedScopeNameMap::const_iterator it = scopeMapp->begin(); it != scopeMapp->end();
++it) { ++it) {
@ -153,8 +152,8 @@ int main(int argc, char** argv, char** env) {
top->CLK = 0; top->CLK = 0;
top->eval(); top->eval();
tfp->dump((unsigned int)(main_time)); tfp->dump(contextp->time());
++main_time; contextp->timeInc(1);
tfp->close(); tfp->close();
top->final(); top->final();

View File

@ -0,0 +1,124 @@
//
// DESCRIPTION: Verilator: Verilog Multiple Model Test Module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2020-2021 by Andreas Kuster.
// SPDX-License-Identifier: CC0-1.0
//
#include <iostream>
#include <thread>
#include <verilated.h>
#include <verilated_cov.h>
#include VM_PREFIX_INCLUDE
double sc_time_stamp() { return 0; }
VerilatedMutex outputMutex;
#ifdef T_WRAPPER_CONTEXT
#elif defined(T_WRAPPER_CONTEXT_SEQ)
VerilatedMutex sequentialMutex;
#elif defined(T_WRAPPER_CONTEXT_FST)
#else
#error "Unexpected test name"
#endif
void sim(VM_PREFIX* topp) {
#ifdef T_WRAPPER_CONTEXT_SEQ
// Run each sim sequentially
const VerilatedLockGuard seqLock(sequentialMutex);
#endif
VerilatedContext* contextp = topp->contextp();
// This test created a thread, so need to associate VerilatedContext with it
Verilated::threadContextp(contextp);
// reset
topp->clk = 0;
topp->rst = 1;
topp->stop = (topp->trace_number == 0);
topp->eval();
contextp->timeInc(1);
topp->clk = 1;
topp->eval();
contextp->timeInc(1);
topp->rst = 0;
topp->clk = 0;
topp->eval();
// simulate until done
while (!contextp->gotFinish()) {
// increment time
contextp->timeInc(1);
{
const VerilatedLockGuard lock(outputMutex);
#ifdef TEST_VERBOSE
// std::endl needed to flush output before mutex release
std::cout << "{top" << topp->trace_number
<< ", ctx=" << reinterpret_cast<void*>(contextp) << "} [" << contextp->time()
<< "]" << std::endl;
#endif
}
// toggle clk
topp->clk = !topp->clk;
// evaluate model
topp->eval();
}
std::string filename
= std::string(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage_") + topp->name() + ".dat";
contextp->coveragep()->write(filename.c_str());
}
int main(int argc, char** argv, char** env) {
// Create contexts
std::unique_ptr<VerilatedContext> context0p{new VerilatedContext};
std::unique_ptr<VerilatedContext> context1p{new VerilatedContext};
// configuration
context0p->fatalOnError(false);
context1p->fatalOnError(false);
context0p->traceEverOn(true);
context1p->traceEverOn(true);
// instantiate verilated design
std::unique_ptr<VM_PREFIX> top0p{new VM_PREFIX{context0p.get(), "top0"}};
std::unique_ptr<VM_PREFIX> top1p{new VM_PREFIX{context1p.get(), "top1"}};
top0p->trace_number = 0;
top0p->trace_number = 1;
std::cout << "Below '%Error: ... Verilog $stop' is expected as part of the test\n";
// create threads
std::thread t0(sim, top0p.get());
std::thread t1(sim, top1p.get());
// wait to finish
t0.join();
t1.join();
// check if both finished
bool pass = true;
if (top0p->done_o && top1p->done_o) {
std::cout << "*-* All Finished *-*" << std::endl;
} else {
std::cout << "Error: Early termination!" << std::endl;
pass = false;
}
// final model cleanup
top0p->final();
top1p->final();
// exit successful
return pass ? 0 : 10;
}

View File

@ -0,0 +1,33 @@
#!/usr/bin/env perl
if (!$::Driver) { use strict; use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Multiple Model Test Module
#
# Copyright 2020-2021 by Andreas Kuster. 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
scenarios(vlt_all => 1);
compile(
make_top_shell => 0,
make_main => 0,
# link threads library, add custom .cpp code, add tracing & coverage support
verilator_flags2 => ["-threads 1 --exe $Self->{t_dir}/$Self->{name}.cpp",
"--trace --coverage -cc"],
make_flags => 'CPPFLAGS_ADD=-DVL_NO_LEGACY',
);
execute(
check_finished => 1,
);
files_identical_sorted("$Self->{obj_dir}/coverage_top0.dat", "t/t_wrapper_context_top0.out");
files_identical_sorted("$Self->{obj_dir}/coverage_top1.dat", "t/t_wrapper_context_top1.out");
vcd_identical("$Self->{obj_dir}/trace0.vcd", "t/t_wrapper_context_trace0.out");
vcd_identical("$Self->{obj_dir}/trace1.vcd", "t/t_wrapper_context_trace1.out");
ok(1);
1;

View File

@ -0,0 +1,54 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This model counts from 0 to 10. It is instantiated twice in concurrent
// threads to check for race conditions/signal interference.
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2020-2021 by Andreas Kuster.
// SPDX-License-Identifier: CC0-1.0
`define STRINGIFY(x) `"x`"
module top
(
input clk,
input rst,
input [31:0] trace_number,
input stop,
output bit [31:0] counter,
output bit done_o
);
initial begin
string number;
string filename;
number.itoa(trace_number);
filename = {`STRINGIFY(`TEST_OBJ_DIR), "/trace", number, ".vcd"};
$display("Writing dumpfile '%s'", filename);
$dumpfile(filename);
$dumpvars();
end
always@(posedge clk) begin
if (rst)
counter <= 0;
else
counter <= counter + 1;
end
always_comb begin
done_o = '0;
if (stop) begin
if (counter >= 5 && stop) begin
done_o = '1;
$stop;
end
end
else begin
if (counter >= 10) begin
done_o = '1;
$finish;
end
end
end
endmodule

View File

@ -0,0 +1,29 @@
#!/usr/bin/env perl
if (!$::Driver) { use strict; use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Multiple Model Test Module
#
# Copyright 2020-2021 by Andreas Kuster. 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
scenarios(vlt_all => 1);
top_filename("t/t_wrapper_context.v");
compile(
make_top_shell => 0,
make_main => 0,
# link threads library, add custom .cpp code, add tracing & coverage support
verilator_flags2 => ["-threads 1 --exe $Self->{t_dir}/t_wrapper_context.cpp",
"--trace-fst --coverage -cc"],
make_flags => 'CPPFLAGS_ADD=-DVL_NO_LEGACY',
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,29 @@
#!/usr/bin/env perl
if (!$::Driver) { use strict; use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Multiple Model Test Module
#
# Copyright 2020-2021 by Andreas Kuster. 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
scenarios(vlt_all => 1);
top_filename("t/t_wrapper_context.v");
compile(
make_top_shell => 0,
make_main => 0,
# link threads library, add custom .cpp code, add tracing & coverage support
verilator_flags2 => ["-threads 1 --exe $Self->{t_dir}/t_wrapper_context.cpp",
"--trace --coverage -cc"],
make_flags => 'CPPFLAGS_ADD=-DVL_NO_LEGACY',
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,79 @@
# SystemC::Coverage-3
C 'ft/t_wrapper_context.vl14n22pagev_toggle/topoclkhtop0.top' 21
C 'ft/t_wrapper_context.vl15n22pagev_toggle/toporsthtop0.top' 2
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[0]htop0.top' 1
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[10]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[11]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[12]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[13]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[14]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[15]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[16]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[17]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[18]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[19]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[1]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[20]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[21]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[22]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[23]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[24]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[25]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[26]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[27]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[28]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[29]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[2]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[30]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[31]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[3]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[4]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[5]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[6]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[7]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[8]htop0.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[9]htop0.top' 0
C 'ft/t_wrapper_context.vl17n22pagev_toggle/topostophtop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[0]htop0.top' 10
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[10]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[11]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[12]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[13]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[14]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[15]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[16]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[17]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[18]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[19]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[1]htop0.top' 5
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[20]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[21]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[22]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[23]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[24]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[25]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[26]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[27]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[28]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[29]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[2]htop0.top' 2
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[30]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[31]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[3]htop0.top' 1
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[4]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[5]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[6]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[7]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[8]htop0.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[9]htop0.top' 0
C 'ft/t_wrapper_context.vl19n22pagev_toggle/topodone_ohtop0.top' 1
C 'ft/t_wrapper_context.vl22n4pagev_line/topoblockS22,25-29htop0.top' 1
C 'ft/t_wrapper_context.vl32n4pagev_line/topoblockS32htop0.top' 11
C 'ft/t_wrapper_context.vl33n7pagev_branch/topoifS33-34htop0.top' 1
C 'ft/t_wrapper_context.vl33n8pagev_branch/topoelseS36htop0.top' 10
C 'ft/t_wrapper_context.vl38n4pagev_line/topoblockS38-39htop0.top' 1
C 'ft/t_wrapper_context.vl40n7pagev_branch/topoifS40htop0.top' 0
C 'ft/t_wrapper_context.vl40n8pagev_branch/topoelseS46htop0.top' 24
C 'ft/t_wrapper_context.vl41n11pagev_line/topoelsehtop0.top' 0
C 'ft/t_wrapper_context.vl47n10pagev_branch/topoifS47-49htop0.top' 1
C 'ft/t_wrapper_context.vl47n11pagev_branch/topoelsehtop0.top' 23

View File

@ -0,0 +1,79 @@
# SystemC::Coverage-3
C 'ft/t_wrapper_context.vl14n22pagev_toggle/topoclkhtop1.top' 11
C 'ft/t_wrapper_context.vl15n22pagev_toggle/toporsthtop1.top' 2
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[0]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[10]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[11]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[12]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[13]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[14]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[15]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[16]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[17]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[18]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[19]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[1]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[20]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[21]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[22]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[23]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[24]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[25]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[26]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[27]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[28]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[29]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[2]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[30]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[31]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[3]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[4]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[5]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[6]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[7]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[8]htop1.top' 0
C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[9]htop1.top' 0
C 'ft/t_wrapper_context.vl17n22pagev_toggle/topostophtop1.top' 1
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[0]htop1.top' 5
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[10]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[11]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[12]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[13]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[14]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[15]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[16]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[17]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[18]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[19]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[1]htop1.top' 2
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[20]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[21]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[22]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[23]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[24]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[25]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[26]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[27]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[28]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[29]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[2]htop1.top' 1
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[30]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[31]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[3]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[4]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[5]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[6]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[7]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[8]htop1.top' 0
C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[9]htop1.top' 0
C 'ft/t_wrapper_context.vl19n22pagev_toggle/topodone_ohtop1.top' 1
C 'ft/t_wrapper_context.vl22n4pagev_line/topoblockS22,25-29htop1.top' 1
C 'ft/t_wrapper_context.vl32n4pagev_line/topoblockS32htop1.top' 6
C 'ft/t_wrapper_context.vl33n7pagev_branch/topoifS33-34htop1.top' 1
C 'ft/t_wrapper_context.vl33n8pagev_branch/topoelseS36htop1.top' 5
C 'ft/t_wrapper_context.vl38n4pagev_line/topoblockS38-39htop1.top' 1
C 'ft/t_wrapper_context.vl40n7pagev_branch/topoifS40htop1.top' 14
C 'ft/t_wrapper_context.vl40n8pagev_branch/topoelseS46htop1.top' 0
C 'ft/t_wrapper_context.vl41n11pagev_line/topoelsehtop1.top' 13
C 'ft/t_wrapper_context.vl47n10pagev_branch/topoifS47-49htop1.top' 0
C 'ft/t_wrapper_context.vl47n11pagev_branch/topoelsehtop1.top' 0

View File

@ -0,0 +1,59 @@
$version Generated by VerilatedVcd $end
$date Sat Mar 6 21:09:45 2021 $end
$timescale 1ps $end
$scope module top1 $end
$var wire 1 # clk $end
$var wire 32 ' counter [31:0] $end
$var wire 1 ( done_o $end
$var wire 1 $ rst $end
$var wire 1 & stop $end
$var wire 32 % trace_number [31:0] $end
$scope module top $end
$var wire 1 # clk $end
$var wire 32 ' counter [31:0] $end
$var wire 1 ( done_o $end
$var wire 1 $ rst $end
$var wire 1 & stop $end
$var wire 32 % trace_number [31:0] $end
$upscope $end
$upscope $end
$enddefinitions $end
#0
0#
1$
b00000000000000000000000000000000 %
1&
b00000000000000000000000000000000 '
0(
#1
1#
#2
0#
0$
#3
1#
b00000000000000000000000000000001 '
#4
0#
#5
1#
b00000000000000000000000000000010 '
#6
0#
#7
1#
b00000000000000000000000000000011 '
#8
0#
#9
1#
b00000000000000000000000000000100 '
#10
0#
#11
1#
b00000000000000000000000000000101 '
1(

View File

@ -0,0 +1,84 @@
$version Generated by VerilatedVcd $end
$date Sat Mar 6 21:09:47 2021 $end
$timescale 1ps $end
$scope module top0 $end
$var wire 1 # clk $end
$var wire 32 ' counter [31:0] $end
$var wire 1 ( done_o $end
$var wire 1 $ rst $end
$var wire 1 & stop $end
$var wire 32 % trace_number [31:0] $end
$scope module top $end
$var wire 1 # clk $end
$var wire 32 ' counter [31:0] $end
$var wire 1 ( done_o $end
$var wire 1 $ rst $end
$var wire 1 & stop $end
$var wire 32 % trace_number [31:0] $end
$upscope $end
$upscope $end
$enddefinitions $end
#0
0#
1$
b00000000000000000000000000000001 %
0&
b00000000000000000000000000000000 '
0(
#1
1#
#2
0#
0$
#3
1#
b00000000000000000000000000000001 '
#4
0#
#5
1#
b00000000000000000000000000000010 '
#6
0#
#7
1#
b00000000000000000000000000000011 '
#8
0#
#9
1#
b00000000000000000000000000000100 '
#10
0#
#11
1#
b00000000000000000000000000000101 '
#12
0#
#13
1#
b00000000000000000000000000000110 '
#14
0#
#15
1#
b00000000000000000000000000000111 '
#16
0#
#17
1#
b00000000000000000000000000001000 '
#18
0#
#19
1#
b00000000000000000000000000001001 '
#20
0#
#21
1#
b00000000000000000000000000001010 '
1(

View File

@ -33,11 +33,10 @@ bool got_error = false;
} }
vluint64_t main_time = 0; vluint64_t main_time = 0;
#ifdef T_WRAPPER_LEGACY_TIME64
vluint64_t vl_time_stamp64() { return main_time; }
#endif
#ifdef T_WRAPPER_LEGACY #ifdef T_WRAPPER_LEGACY
#elif defined(T_WRAPPER_LEGACY_TIME64)
vluint64_t vl_time_stamp64() { return main_time; }
#elif defined(T_WRAPPER_LEGACY_TIMED)
double sc_time_stamp() { return main_time; } double sc_time_stamp() { return main_time; }
#endif #endif
@ -60,9 +59,15 @@ int main(int argc, char** argv, char** env) {
CHECK_RESULT(Verilated::debug(), 9); CHECK_RESULT(Verilated::debug(), 9);
Verilated::debug(0); Verilated::debug(0);
Verilated::fatalOnError(true);
CHECK_RESULT(Verilated::fatalOnError(), true);
Verilated::fatalOnVpiError(true); Verilated::fatalOnVpiError(true);
CHECK_RESULT(Verilated::fatalOnVpiError(), true); CHECK_RESULT(Verilated::fatalOnVpiError(), true);
Verilated::gotError(false);
CHECK_RESULT(Verilated::gotError(), false);
Verilated::gotFinish(false); Verilated::gotFinish(false);
CHECK_RESULT(Verilated::gotFinish(), false); // Commonly used CHECK_RESULT(Verilated::gotFinish(), false); // Commonly used
@ -87,10 +92,24 @@ int main(int argc, char** argv, char** env) {
VL_PRINTF("Starting\n"); VL_PRINTF("Starting\n");
vluint64_t sim_time = 100; vluint64_t sim_time = 100;
while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { while (
#ifdef T_WRAPPER_LEGACY
Verilated::time()
#else
vl_time_stamp64()
#endif
< sim_time
&& !Verilated::gotFinish()) {
CHECK_RESULT(VL_TIME_Q(), main_time); CHECK_RESULT(VL_TIME_Q(), main_time);
CHECK_RESULT(VL_TIME_D(), main_time); CHECK_RESULT(VL_TIME_D(), main_time);
main_time += 1; main_time += 1;
#ifdef T_WRAPPER_LEGACY
Verilated::timeInc(1);
// Check reading and writing of time
Verilated::time(Verilated::time());
#endif
topp->clk = !topp->clk; topp->clk = !topp->clk;
topp->eval(); topp->eval();
} }

View File

@ -14,6 +14,7 @@ compile(
make_top_shell => 0, make_top_shell => 0,
make_main => 0, make_main => 0,
verilator_flags2 => ["--exe $Self->{t_dir}/$Self->{name}.cpp"], verilator_flags2 => ["--exe $Self->{t_dir}/$Self->{name}.cpp"],
make_flags => 'CPPFLAGS_ADD=-DVL_TIME_CONTEXT',
); );
execute( execute(

View File

@ -0,0 +1,27 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2020 by Wilson Snyder and Marlon James. 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
scenarios(vlt_all => 1);
top_filename("t/t_wrapper_legacy.v");
compile(
make_top_shell => 0,
make_main => 0,
verilator_flags2 => ["--exe $Self->{t_dir}/t_wrapper_legacy.cpp"],
make_flags => 'CPPFLAGS_ADD=-UVL_TIME_CONTEXT',
);
execute(
check_finished => 1,
);
ok(1);
1;