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!
* 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.

View File

@ -1953,7 +1953,8 @@ Displays program version and exits.
=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
cd test_our
@ -1968,21 +1969,24 @@ We'll compile this example into C++.
#include "Vour.h"
#include "verilated.h"
int main(int argc, char** argv, char** env) {
Verilated::commandArgs(argc, argv);
Vour* top = new Vour;
while (!Verilated::gotFinish()) { top->eval(); }
VerilatedContext* contextp = new VerilatedContext;
contextp->commandArgs(argc, argv);
Vour* top = new Vour{contextp};
while (!contextp->gotFinish()) { top->eval(); }
delete top;
delete contextp;
return 0;
}
EOF
See the README in the source kit for various ways to install or point to
Verilator binaries. In brief, if you installed Verilator using the package
manager of your operating system, or did a "make install" to place
Verilator into your default path, you do not need anything special in your
environment, and should not have VERILATOR_ROOT set. However, if you
installed Verilator from sources and want to run Verilator out of where you
compiled Verilator, you need to point to the kit:
Next we need Verilator installed. See the README in the source kit for
various ways to install or point to Verilator binaries. In brief, if you
installed Verilator using the package manager of your operating system, or
did a "make install" to place Verilator into your default path, you do not
need anything special in your environment, and should not have
VERILATOR_ROOT set. However, if you installed Verilator from sources and
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
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
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
evaluate, just call designp->eval().
1. When there is a single design instantiated at the C++ level that needs
to evaluate within a given context, just call designp->eval().
2. When there are multiple designs instantiated at the C++ level that
need to evaluate, call first_designp->eval_step() then ->eval_step() on all
other designs. Then call ->eval_end_step() on the first design then all
other designs. If there is only a single design, you would call
eval_step() then eval_end_step(); in fact eval() described above is just a
wrapper which calls these two functions.
2. When there are multiple designs instantiated at the C++ level that need
to evaluate within a context, call first_designp->eval_step() then
->eval_step() on all other designs. Then call ->eval_end_step() on the
first design then all other designs. If there is only a single design, you
would call eval_step() then eval_end_step(); in fact eval() described above
is just a wrapper which calls these two functions.
When eval() is called Verilator looks for changes in clock signals and
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()'"?
In C++ (non SystemC) code you need to define this function so that the
simulator knows the current time. See the "CONNECTING TO C++" examples.
In 4.112 and later, using the timeInc function is recommended instead. See
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::...'"?

View File

@ -16,25 +16,24 @@
#include <verilated_vcd_c.h>
#endif
vluint64_t main_time = 0;
double sc_time_stamp() { return main_time; }
int main(int argc, char** argv, char** env) {
if (false && argc && argv && env) {}
Verilated::debug(0);
Verilated::randReset(2);
Verilated::commandArgs(argc, argv);
// Construct context to hold simulation time, etc
VerilatedContext* contextp = new VerilatedContext;
contextp->debug(0);
contextp->randReset(2);
contextp->commandArgs(argc, argv);
// Construct the Verilated model, including the secret module
Vtop* top = new Vtop;
Vtop* top = new Vtop{contextp};
#if VM_TRACE
// When tracing, the contents of the secret module will not be seen
VerilatedVcdC* tfp = nullptr;
const char* flag = Verilated::commandArgsPlusMatch("trace");
const char* flag = contextp->commandArgsPlusMatch("trace");
if (flag && 0 == strcmp(flag, "+trace")) {
Verilated::traceEverOn(true);
contextp->traceEverOn(true);
VL_PRINTF("Enabling waves into logs/vlt_dump.vcd...\n");
tfp = new VerilatedVcdC;
top->trace(tfp, 99);
@ -46,12 +45,12 @@ int main(int argc, char** argv, char** env) {
top->clk = 0;
// Simulate until $finish
while (!Verilated::gotFinish()) {
main_time++;
while (!contextp->gotFinish()) {
contextp->timeInc(1);
top->clk = ~top->clk & 0x1;
top->eval();
#if VM_TRACE
if (tfp) tfp->dump(main_time);
if (tfp) tfp->dump(contextp->time());
#endif
}
@ -69,6 +68,8 @@ int main(int argc, char** argv, char** env) {
// Destroy model
delete top;
top = nullptr;
delete contextp;
contextp = nullptr;
// Return good completion status
// 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 "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) {
// 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
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
// May be overridden by commandArgs argument parsing
Verilated::debug(0);
contextp->debug(0);
// Randomization reset policy
// May be overridden by commandArgs argument parsing
Verilated::randReset(2);
contextp->randReset(2);
// Verilator must compute traced signals
Verilated::traceEverOn(true);
contextp->traceEverOn(true);
// Pass arguments so Verilated code can see them, e.g. $value$plusargs
// 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".
// Using unique_ptr is similar to "Vtop* top = new Vtop" then deleting at end.
// "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
top->reset_l = !0;
@ -60,8 +62,19 @@ int main(int argc, char** argv, char** env) {
top->in_wide[2] = 0x3;
// Simulate until $finish
while (!Verilated::gotFinish()) {
main_time++; // Time passes...
while (!contextp->gotFinish()) {
// 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
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
// reset is not sampled there.
if (!top->clk) {
if (main_time > 1 && main_time < 10) {
if (contextp->time() > 1 && contextp->time() < 10) {
top->reset_l = !1; // Assert reset
} else {
top->reset_l = !0; // Deassert reset
@ -89,8 +102,8 @@ int main(int argc, char** argv, char** env) {
// Read outputs
VL_PRINTF("[%" VL_PRI64 "d] clk=%x rstl=%x iquad=%" VL_PRI64 "x"
" -> 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],
top->out_wide[1], top->out_wide[0]);
contextp->time(), top->clk, top->reset_l, top->in_quad, top->out_quad,
top->out_wide[2], top->out_wide[1], top->out_wide[0]);
}
// 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)
#if VM_COVERAGE
Verilated::mkdir("logs");
VerilatedCov::write("logs/coverage.dat");
contextp->coveragep()->write("logs/coverage.dat");
#endif
// Return good completion status

View File

@ -78,41 +78,16 @@ static_assert(sizeof(vluint64_t) == 8, "vluint8_t is missized");
//===========================================================================
// Global variables
// Internal note: Globals may multi-construct, see verilated.cpp top.
// Slow path variables
VerilatedMutex Verilated::s_mutex;
// Fast path, keep together
int Verilated::s_debug = 0;
VerilatedContext* Verilated::s_lastContextp = nullptr;
// Keep below together in one cache line
Verilated::Serialized Verilated::s_s;
Verilated::NonSerialized Verilated::s_ns;
// Internal note: Globals may multi-construct, see verilated.cpp top.
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
// 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) {}
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
"- %s:%d: Verilog $finish\n", filename, linenum);
if (Verilated::gotFinish()) {
if (Verilated::threadContextp()->gotFinish()) {
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
"- %s:%d: Second verilog $finish, exiting\n", filename, linenum);
Verilated::runFlushCallbacks();
Verilated::runExitCallbacks();
exit(0);
}
Verilated::gotFinish(true);
Verilated::threadContextp()->gotFinish(true);
}
#endif
#ifndef VL_USER_STOP ///< Define this to override this function
void vl_stop(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE {
Verilated::gotFinish(true);
Verilated::runFlushCallbacks();
vl_fatal(filename, linenum, hier, "Verilog $stop");
const char* const msg = "Verilog $stop";
Verilated::threadContextp()->gotError(true);
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
#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 {
if (false && hier) {}
Verilated::gotFinish(true);
Verilated::threadContextp()->gotError(true);
Verilated::threadContextp()->gotFinish(true);
if (filename && filename[0]) {
// Not VL_PRINTF_MT, already on main thread
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
void vl_stop_maybe(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_UNSAFE {
Verilated::errorCountInc();
if (maybe && Verilated::errorCount() < Verilated::errorLimit()) {
Verilated::threadContextp()->errorCountInc();
if (maybe
&& Verilated::threadContextp()->errorCount() < Verilated::threadContextp()->errorLimit()) {
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
"-Info: %s:%d: %s\n", filename, linenum,
"Verilog $stop, ignored due to +verilator+error+limit");
@ -279,34 +267,6 @@ void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE {
}
#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.
@ -324,13 +284,14 @@ static vluint32_t vl_sys_rand32() VL_MT_UNSAFE {
}
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 vluint32_t t_seedEpoch = 0;
// For speed, we use a thread-local epoch number to know when to reseed
if (VL_UNLIKELY(t_seedEpoch != Verilated::randSeedEpoch())) {
// Set epoch before state, in case races with new seeding
t_seedEpoch = Verilated::randSeedEpoch();
t_state[0] = Verilated::randSeedDefault64();
// A thread always belongs to a single context, so this works out ok
if (VL_UNLIKELY(t_seedEpoch != VerilatedContextImp::randSeedEpoch())) {
// Set epoch before state, to avoid race case with new seeding
t_seedEpoch = VerilatedContextImp::randSeedEpoch();
t_state[0] = Verilated::threadContextp()->impp()->randSeedDefault64();
t_state[1] = t_state[0];
// Fix state as algorithm is slow to randomize if many zeros
// 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
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);
}
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;
if (Verilated::randReset() != 1) { // if 2, randomize
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
data = VL_RANDOM_I(obits);
}
data &= VL_MASK_I(obits);
return data;
}
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;
if (Verilated::randReset() != 1) { // if 2, randomize
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
data = VL_RANDOM_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) {
// Double may lose precision, but sc_time_stamp has similar limit
std::string suffix = VerilatedImp::timeFormatSuffix();
int userUnits = VerilatedImp::timeFormatUnits(); // 0..-15
int fracDigits = VerilatedImp::timeFormatPrecision(); // 0..N
int prec = Verilated::timeprecision(); // 0..-15
std::string suffix = Verilated::threadContextp()->impp()->timeFormatSuffix();
int userUnits = Verilated::threadContextp()->impp()->timeFormatUnits(); // 0..-15
int fracDigits = Verilated::threadContextp()->impp()->timeFormatPrecision(); // 0..N
int prec = Verilated::threadContextp()->timeprecision(); // 0..-15
int shift = prec - userUnits + fracDigits; // 0..-15
double shiftd = vl_time_multiplier(shift);
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);
if (lbits) {} // UNUSED - always 64
if (fmt == '^') { // Realtime
if (!widthSet) width = VerilatedImp::timeFormatWidth();
if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth();
output += _vl_vsformat_time(t_tmp, d, left, width);
} else {
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;
}
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);
break;
}
@ -1261,7 +1222,7 @@ done:
FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE {
// 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 {
@ -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) {
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 {
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 {
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 {
// 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 {
@ -1451,7 +1412,7 @@ void VL_FWRITEF(IData fpi, const char* formatp, ...) VL_MT_SAFE {
_vl_vsformat(t_output, formatp, 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 {
@ -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 {
const std::string& match = VerilatedImp::argPlusMatch(formatp);
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(formatp);
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();
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();
if (match.empty()) return 0;
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 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];
if (match.empty()) return nullptr;
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);
}
//===========================================================================
// 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
@ -2210,99 +2152,119 @@ double vl_time_multiplier(int scale) VL_PURE {
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,
Verilated::timeprecisionString());
contextp->timeprecisionString());
}
void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix,
int width) VL_MT_SAFE {
VerilatedImp::timeFormatUnits(units);
VerilatedImp::timeFormatPrecision(precision);
VerilatedImp::timeFormatSuffix(suffix);
VerilatedImp::timeFormatWidth(width);
void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, int width,
VerilatedContext* contextp) VL_MT_SAFE {
contextp->impp()->timeFormatUnits(units);
contextp->impp()->timeFormatPrecision(precision);
contextp->impp()->timeFormatSuffix(suffix);
contextp->impp()->timeFormatWidth(width);
}
//===========================================================================
// Verilated:: Methods
//======================================================================
// VerilatedContext:: Methods
void Verilated::debug(int level) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex);
s_ns.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
VerilatedContext::VerilatedContext()
: m_impdatap{new VerilatedContextImpData} {
Verilated::lastContextp(this);
Verilated::threadContextp(this);
m_ns.m_profThreadsFilename = "profile_threads.dat";
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;
}
// 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 {
const VerilatedLockGuard lock(s_mutex);
s_s.s_randReset = val;
void VerilatedContext::errorCount(int val) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
m_s.m_errorCount = val;
}
void Verilated::randSeed(int val) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex);
s_s.s_randSeed = val;
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;
void VerilatedContext::errorCountInc() VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
++m_s.m_errorCount;
}
vluint64_t Verilated::randSeedDefault64() VL_MT_SAFE {
if (Verilated::randSeed() != 0) {
return ((static_cast<vluint64_t>(Verilated::randSeed()) << 32)
^ (static_cast<vluint64_t>(Verilated::randSeed())));
} else {
return ((static_cast<vluint64_t>(vl_sys_rand32()) << 32)
^ (static_cast<vluint64_t>(vl_sys_rand32())));
}
void VerilatedContext::errorLimit(int val) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
m_s.m_errorLimit = val;
}
void Verilated::calcUnusedSigs(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex);
s_s.s_calcUnusedSigs = flag;
void VerilatedContext::fatalOnError(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
m_s.m_fatalOnError = flag;
}
void Verilated::errorCount(int val) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex);
s_s.s_errorCount = val;
void VerilatedContext::fatalOnVpiError(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
m_s.m_fatalOnVpiError = flag;
}
void Verilated::errorCountInc() VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex);
++s_s.s_errorCount;
void VerilatedContext::gotError(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
m_s.m_gotError = flag;
}
void Verilated::errorLimit(int val) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex);
s_s.s_errorLimit = val;
void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
m_s.m_gotFinish = flag;
}
void Verilated::gotFinish(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex);
s_s.s_gotFinish = flag;
void VerilatedContext::profThreadsStart(vluint64_t flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
m_ns.m_profThreadsStart = flag;
}
void Verilated::assertOn(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex);
s_s.s_assertOn = flag;
void VerilatedContext::profThreadsWindow(vluint64_t flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
m_ns.m_profThreadsWindow = flag;
}
void Verilated::fatalOnVpiError(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex);
s_s.s_fatalOnVpiError = flag;
void VerilatedContext::profThreadsFilename(const std::string& flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
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
const VerilatedLockGuard lock(s_mutex);
s_s.s_timeunit = value;
const VerilatedLockGuard lock(m_mutex);
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
const VerilatedLockGuard lock(s_mutex);
s_s.s_timeprecision = value;
const VerilatedLockGuard lock(m_mutex);
m_s.m_timeprecision = value;
#ifdef SYSTEMC_VERSION
sc_time sc_res = sc_get_time_resolution();
int sc_prec = 99;
@ -2331,18 +2293,243 @@ void Verilated::timeprecision(int value) VL_MT_SAFE {
}
#endif
}
void Verilated::profThreadsStart(vluint64_t flag) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex);
s_ns.s_profThreadsStart = flag;
const char* VerilatedContext::timeunitString() const VL_MT_SAFE { return vl_time_str(timeunit()); }
const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE {
return vl_time_str(timeprecision());
}
void Verilated::profThreadsWindow(vluint64_t flag) VL_MT_SAFE {
const VerilatedLockGuard lock(s_mutex);
s_ns.s_profThreadsWindow = flag;
void VerilatedContext::commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) {
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 {
const VerilatedLockGuard lock(s_mutex);
if (s_ns.s_profThreadsFilenamep) free(const_cast<char*>(s_ns.s_profThreadsFilenamep));
s_ns.s_profThreadsFilenamep = strdup(flagp);
void VerilatedContext::commandArgsAdd(int argc, const char** argv)
VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock(m_argMutex);
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 {
@ -2442,24 +2629,6 @@ void Verilated::runExitCallbacks() VL_MT_SAFE {
const char* Verilated::productName() VL_PURE { return VERILATOR_PRODUCT; }
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 {
// Slowpath - Called only on error
VL_FATAL_MT(filename, linenum, "", "Null pointer dereferenced");
@ -2489,22 +2658,10 @@ void Verilated::quiesce() VL_MT_SAFE {
#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 {
return VerilatedImp::exportFind(namep);
}
const VerilatedScopeNameMap* Verilated::scopeNameMap() VL_MT_SAFE {
return VerilatedImp::scopeNameMap();
}
#ifdef VL_THREADED
void Verilated::endOfThreadMTaskGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
VL_DEBUG_IF(VL_DBG_MSGF("End of thread mtask\n"););
@ -2521,138 +2678,13 @@ void Verilated::endOfEval(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
}
#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
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 {
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
@ -2706,7 +2738,7 @@ void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const
VerilatedScope::~VerilatedScope() {
// 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_callbacksp) VL_DO_CLEAR(delete[] m_callbacksp, m_callbacksp = 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_identifierp = identifier;
VerilatedImp::scopeInsert(this);
Verilated::threadContextp()->impp()->scopeInsert(this);
}
void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE {

View File

@ -38,6 +38,9 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <string>
#include <vector>
// <iostream> avoided 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
@ -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 WData* WDataOutP; ///< Array output from a function
class VerilatedContextImp;
class VerilatedContextImpData;
class VerilatedCovContext;
class VerilatedEvalMsgQueue;
class VerilatedScopeNameMap;
class VerilatedVar;
@ -227,12 +233,10 @@ public:
}
}
}
void changeThread() { m_threadid = 0; } // Allow intentional change-of-thread
static void fatal_different() VL_MT_SAFE;
#else // !VL_THREADED || !VL_DEBUG
public:
void check() {}
void changeThread() {}
#endif
};
@ -295,21 +299,273 @@ public:
#endif
// 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
/// Used for internal VPI implementation, and introspection into scopes
class VerilatedSyms VL_NOT_FINAL {
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
VerilatedEvalMsgQueue* __Vm_evalMsgQp;
#endif
VerilatedSyms();
explicit VerilatedSyms(VerilatedContext* contextp); // Pass null for default context
~VerilatedSyms();
};
//===========================================================================
/// Verilator global class information class
/// This class is initialized by main thread only. Reading post-init is thread safe.
/// Verilator scope information class
/// Used for internal VPI implementation, and introspection into scopes
class VerilatedScope final {
public:
@ -370,55 +626,29 @@ public:
class Verilated final {
// 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
// Fast path
bool s_calcUnusedSigs; ///< Waves file on, need all signals calculated
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;
// Internal Note: There should be no Serialized state in Verilated::,
// instead serialized state should all be in VerilatedContext:: as by
// definition it needs to vary per-simulation
static struct NonSerialized { // Non-serialized information
// 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;
// Internal note: Globals may multi-construct, see verilated.cpp top.
// no need to be save-restored (serialized) the
// assumption is that the restore is allowed to pass different arguments
static struct CommandArgValues {
VerilatedMutex m_argMutex; ///< Mutex for s_args members, when VL_THREADED
int argc = 0;
const char** argv = nullptr;
CommandArgValues() = default;
~CommandArgValues() = default;
} s_args;
// Debug is reloaded from on command-line settings, so do not need to persist
static int s_debug; ///< See accessors... only when VL_DEBUG set
static VerilatedContext* s_lastContextp; ///< Last context constructed/attached
// Not covered by mutex, as per-thread
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
vluint32_t t_mtaskId = 0; ///< Current mtask# executing on this thread
vluint32_t t_endOfEvalReqd
= 0; ///< Messages may be pending, thread needs endOf-eval calls
vluint32_t t_mtaskId = 0; // mtask# executing on this thread
// Messages maybe pending on thread, needs end-of-eval calls
vluint32_t t_endOfEvalReqd = 0;
#endif
const VerilatedScope* t_dpiScopep = nullptr; ///< DPI context scope
const char* t_dpiFilename = nullptr; ///< DPI context filename
@ -428,7 +658,6 @@ class Verilated final {
~ThreadLocal() = default;
} t_s;
private:
friend struct VerilatedInitializer;
// CONSTRUCTORS
@ -443,64 +672,103 @@ public:
/// Return debug level
/// When multithreaded this may not immediately react to another thread
/// changing the level (no mutex)
static inline int debug() VL_MT_SAFE { return s_ns.s_debug; }
static inline int debug() VL_MT_SAFE { return s_debug; }
#else
/// Return constant 0 debug level, so C++'s optimizer rips up
static constexpr int debug() VL_PURE { return 0; }
#endif
/// Select initial value of otherwise uninitialized signals.
////
/// 0 = Set to zeros
/// 1 = Set all bits to one
/// 2 = Randomize all bits
static void randReset(int val) VL_MT_SAFE;
static int randReset() VL_MT_SAFE { return s_s.s_randReset; } ///< Return randReset value
static void randSeed(int val) VL_MT_SAFE;
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; }
/// Random seed extended to 64 bits, and defaulted if user seed==0
static vluint64_t randSeedDefault64() VL_MT_SAFE;
/// Set the last VerilatedContext accessed
/// Generally threadContextp(value) should be called instead
static void lastContextp(VerilatedContext* contextp) VL_MT_SAFE { s_lastContextp = contextp; }
/// Return the last VerilatedContext accessed
/// Generally threadContextp() should be called instead
static VerilatedContext* lastContextp() VL_MT_SAFE {
if (!s_lastContextp) lastContextp(defaultContextp());
return s_lastContextp;
}
/// Set the VerilatedContext used by the current thread
/// Enable calculation of unused signals
static void calcUnusedSigs(bool flag) VL_MT_SAFE;
static bool calcUnusedSigs() VL_MT_SAFE { ///< Return calcUnusedSigs value
return s_s.s_calcUnusedSigs;
/// If using multiple contexts, and threads are created by the user's
/// wrapper (not Verilator itself) then this must be called to set the
/// context that applies to each thread
static void threadContextp(VerilatedContext* contextp) VL_MT_SAFE {
t_s.t_contextp = contextp;
lastContextp(contextp);
}
/// Current number of errors/assertions
static void errorCount(int val) VL_MT_SAFE;
static void errorCountInc() VL_MT_SAFE;
static int errorCount() VL_MT_SAFE { return s_s.s_errorCount; }
/// Set number of errors/assertions before stop
static void errorLimit(int val) VL_MT_SAFE;
static int errorLimit() VL_MT_SAFE { return s_s.s_errorLimit; }
/// Did the simulation $finish?
static void gotFinish(bool flag) VL_MT_SAFE;
static bool gotFinish() VL_MT_SAFE { return s_s.s_gotFinish; } ///< Return if got a $finish
/// Allow traces to at some point be enabled (disables some optimizations)
/// Return the VerilatedContext for the current thread
static VerilatedContext* threadContextp() {
if (VL_UNLIKELY(!t_s.t_contextp)) t_s.t_contextp = lastContextp();
return t_s.t_contextp;
}
/// Return the global VerilatedContext, used if none created by user
static VerilatedContext* defaultContextp() VL_MT_SAFE {
static VerilatedContext s_s;
return &s_s;
}
#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 {
if (flag) calcUnusedSigs(flag);
Verilated::threadContextp()->traceEverOn(flag);
}
/// Enable/disable assertions
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; }
#endif
typedef void (*VoidPCb)(void*); // Callback type for below
/// Add callback to run on global flush
@ -519,18 +787,6 @@ public:
/// Run exit callbacks registered with addExitCb
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
static const char* productName() VL_PURE;
/// Return product version for (at least) VPI
@ -543,15 +799,23 @@ public:
/// This may only be called when no locks are held.
static void quiesce() VL_MT_SAFE;
#ifndef VL_NO_LEGACY
/// For debugging, print much of the Verilator internal state.
/// The output of this function may change in future
/// releases - contact the authors before production use.
static void internalsDump() VL_MT_SAFE;
static void internalsDump() VL_MT_SAFE { Verilated::threadContextp()->internalsDump(); }
/// For debugging, print text list of all scope names with
/// dpiImport/Export context. This function may change in future
/// releases - contact the authors before production use.
static void scopesDump() 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:
// 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 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
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; }
@ -582,13 +842,9 @@ public:
static int dpiLineno() VL_MT_SAFE { return t_s.t_dpiLineno; }
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
// 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 vluint32_t mtaskId() VL_MT_SAFE { return t_s.t_mtaskId; }
static void endOfEvalReqdInc() VL_MT_SAFE { ++t_s.t_endOfEvalReqd; }
@ -608,6 +864,9 @@ private:
#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
@ -686,7 +945,8 @@ inline QData VL_RDTSC_Q() {
}
#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
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();
inline vluint64_t vl_time_stamp64() { return sc_time_stamp().value(); }
#else // Non-SystemC
# ifdef VL_TIME_STAMP64
extern vluint64_t vl_time_stamp64();
# else
extern double sc_time_stamp(); // Verilator 4.032 and newer
inline vluint64_t vl_time_stamp64() { return static_cast<vluint64_t>(sc_time_stamp()); }
# if !defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY)
# ifdef VL_TIME_STAMP64
// vl_time_stamp64() 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 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
#define VL_TIME_Q() (static_cast<QData>(vl_time_stamp64()))
#define VL_TIME_D() (static_cast<double>(vl_time_stamp64()))
inline vluint64_t VerilatedContext::time() const VL_MT_SAFE {
// 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")
// Optimized assuming scale is always constant.

View File

@ -83,11 +83,13 @@ public:
//=============================================================================
// 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
/// the storage requirements for otherwise identical keys.
///
/// Implementation class for VerilatedCovContext. See that class for
/// 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:
// TYPES
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
int m_insertLineno VL_GUARDED_BY(m_mutex) = 0; ///< Line number about to insert
public:
// CONSTRUCTORS
VerilatedCovImp() = default;
VL_UNCOPYABLE(VerilatedCovImp);
public:
~VerilatedCovImp() { clearGuts(); }
protected:
friend class VerilatedCovContext;
virtual ~VerilatedCovImp() override { clearGuts(); }
static VerilatedCovImp& imp() VL_MT_SAFE {
static VerilatedCovImp s_singleton;
return s_singleton;
@ -408,34 +412,32 @@ public:
};
//=============================================================================
// VerilatedCov
// VerilatedCovContext
void VerilatedCov::clear() VL_MT_SAFE { VerilatedCovImp::imp().clear(); }
void VerilatedCov::clearNonMatch(const char* matchp) VL_MT_SAFE {
VerilatedCovImp::imp().clearNonMatch(matchp);
void VerilatedCovContext::clear() VL_MT_SAFE { impp()->clear(); }
void VerilatedCovContext::clearNonMatch(const char* matchp) VL_MT_SAFE {
impp()->clearNonMatch(matchp);
}
void VerilatedCov::zero() VL_MT_SAFE { VerilatedCovImp::imp().zero(); }
void VerilatedCov::write(const char* filenamep) VL_MT_SAFE {
VerilatedCovImp::imp().write(filenamep);
void VerilatedCovContext::zero() VL_MT_SAFE { impp()->zero(); }
void VerilatedCovContext::write(const char* filenamep) VL_MT_SAFE { impp()->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 {
VerilatedCovImp::imp().inserti(new VerilatedCoverItemSpec<vluint32_t>(itemp));
void VerilatedCovContext::_inserti(vluint64_t* itemp) VL_MT_SAFE {
impp()->inserti(new VerilatedCoverItemSpec<vluint64_t>(itemp));
}
void VerilatedCov::_inserti(vluint64_t* itemp) VL_MT_SAFE {
VerilatedCovImp::imp().inserti(new VerilatedCoverItemSpec<vluint64_t>(itemp));
}
void VerilatedCov::_insertf(const char* filename, int lineno) VL_MT_SAFE {
VerilatedCovImp::imp().insertf(filename, lineno);
void VerilatedCovContext::_insertf(const char* filename, int lineno) VL_MT_SAFE {
impp()->insertf(filename, lineno);
}
#define K(n) const char* key##n
#define A(n) const char *key##n, const char *valp##n // Argument list
#define C(n) key##n, valp##n // Calling 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),
A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18), A(19), A(20),
A(21), A(22), A(23), A(24), A(25), A(26), A(27), A(28),
A(29)) VL_MT_SAFE {
void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9),
A(10), A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18),
A(19), A(20), A(21), A(22), A(23), A(24), A(25), A(26), A(27),
A(28), A(29)) VL_MT_SAFE {
const char* keyps[VerilatedCovConst::MAX_KEYS]
= {nullptr, nullptr, nullptr, // filename,lineno,page
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,
valp10, valp11, valp12, valp13, valp14, valp15, valp16, valp17, valp18, valp19,
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!)
void VerilatedCov::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8),
A(9)) VL_MT_SAFE {
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 {
_insertp(C(0), C(1), C(2), C(3), C(4), C(5), C(6), C(7), C(8), C(9), N(10), N(11), N(12),
N(13), N(14), N(15), N(16), N(17), N(18), N(19), N(20), N(21), N(22), N(23), N(24),
N(25), N(26), N(27), N(28), N(29));
}
void VerilatedCov::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10),
A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18),
A(19)) VL_MT_SAFE {
void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9),
A(10), A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18),
A(19)) VL_MT_SAFE {
_insertp(C(0), C(1), C(2), C(3), C(4), C(5), C(6), C(7), C(8), C(9), C(10), C(11), C(12),
C(13), C(14), C(15), C(16), C(17), C(18), C(19), N(20), N(21), N(22), N(23), N(24),
N(25), N(26), N(27), N(28), N(29));
}
// Backward compatibility for Verilator
void VerilatedCov::_insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4),
const std::string& val4, A(5), A(6), A(7)) VL_MT_SAFE {
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 {
std::string val2str = vlCovCvtToStr(val2);
std::string val3str = vlCovCvtToStr(val3);
_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 N
#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_
#include "verilatedos.h"
#include "verilated.h"
#include <iostream>
#include <sstream>
#include <string>
class VerilatedCovImp;
//=============================================================================
/// Conditionally compile coverage code
@ -68,9 +71,9 @@
/// VL_COVER_INSERT(&m_cases[i], "comment", "Coverage Case", "i", cvtToNumStr(i));
/// }
#define VL_COVER_INSERT(countp, ...) \
VL_IF_COVER(VerilatedCov::_inserti(countp); VerilatedCov::_insertf(__FILE__, __LINE__); \
VerilatedCov::_insertp("hier", name(), __VA_ARGS__))
#define VL_COVER_INSERT(covcontextp, countp, ...) \
VL_IF_COVER(covcontextp->_inserti(countp); covcontextp->_insertf(__FILE__, __LINE__); \
covcontextp->_insertp("hier", name(), __VA_ARGS__))
//=============================================================================
/// Convert VL_COVER_INSERT value arguments to strings
@ -83,29 +86,35 @@ template <class T> std::string vlCovCvtToStr(const T& t) VL_PURE {
//=============================================================================
// VerilatedCov
/// Verilator coverage global class
////
/// Global class with methods affecting all coverage data.
/// Verilator coverage per-context structure
/// All public methods in this class are thread safe.
class VerilatedCov final {
VL_UNCOPYABLE(VerilatedCov);
class VerilatedCovContext VL_NOT_FINAL : public VerilatedVirtualBase {
VL_UNCOPYABLE(VerilatedCovContext);
public:
// GLOBAL METHODS
// METHODS
/// Return default filename
static const char* defaultFilename() VL_PURE { return "coverage.dat"; }
/// Write all coverage data to a file
static void write(const char* filenamep = defaultFilename()) 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
/// We accept from 1-30 key/value pairs, all as strings.
/// Call _insert1, followed by _insert2 and _insert3
/// Do not call directly; use VL_COVER_INSERT or higher level macros instead
// _insert1: Remember item pointer with count. (Not const, as may add zeroing function)
static void _inserti(vluint32_t* itemp) VL_MT_SAFE;
static void _inserti(vluint64_t* itemp) VL_MT_SAFE;
void _inserti(vluint32_t* itemp) VL_MT_SAFE;
void _inserti(vluint64_t* itemp) VL_MT_SAFE;
// _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
// We could have just the maximum argument version, but this compiles
// much slower (nearly 2x) than having smaller versions also. However
@ -113,25 +122,65 @@ public:
#define K(n) const char* key##n
#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
static 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),
D(12), 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),
A(12), A(13), A(14), A(15), A(16), A(17), A(18), A(19), A(20), D(21),
D(22), D(23), D(24), D(25), D(26), D(27), D(28), D(29));
void _insertp(D(0), D(1), D(2), D(3), D(4), D(5), D(6), D(7), D(8), D(9));
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(13), D(14), D(15), D(16), D(17), D(18), D(19));
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(13), A(14), A(15), A(16), A(17), A(18), A(19), A(20), D(21), D(22), D(23),
D(24), D(25), D(26), D(27), D(28), D(29));
// 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,
A(5), A(6), A(7));
void _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));
#undef K
#undef A
#undef D
/// Clear coverage points (and call delete on all items)
static void clear() VL_MT_SAFE;
/// Clear items not matching the provided string
static void clearNonMatch(const char* matchp) VL_MT_SAFE;
/// Zero coverage points
static void zero() VL_MT_SAFE;
protected:
friend class VerilatedCovImp;
// CONSTRUCTORS
// Internal: Only made as part of VerilatedCovImp
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

View File

@ -740,7 +740,7 @@ const char* svGetNameFromScope(const svScope scope) {
svScope svGetScopeFromName(const char* scopeName) {
// NOLINTNEXTLINE(google-readability-casting)
return (svScope)(VerilatedImp::scopeFind(scopeName));
return (svScope)(Verilated::threadContextp()->scopeFind(scopeName));
}
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);
}
void VerilatedFst::open(const char* filename) VL_MT_UNSAFE {
m_assertOne.check();
void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
m_fst = fstWriterCreate(filename, 1);
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
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];
}
void VerilatedFst::close() {
m_assertOne.check();
VerilatedTrace<VerilatedFst>::close();
void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
VerilatedTrace<VerilatedFst>::closeBase();
fstWriterClose(m_fst);
m_fst = nullptr;
}
void VerilatedFst::flush() {
VerilatedTrace<VerilatedFst>::flush();
void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
VerilatedTrace<VerilatedFst>::flushBase();
fstWriterFlushContext(m_fst);
}

View File

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

View File

@ -20,7 +20,8 @@
#define VERILATOR_VERILATED_IMP_H_ ///< Header Guard
// 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"
#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
class VerilatedImpData final {
// Whole class is internal use only - Global information shared between verilated*.cpp files.
// All only medium-speed, so use singleton function
protected:
friend class Verilated;
friend class VerilatedImp;
// TYPES
typedef std::vector<std::string> ArgVec;
typedef std::map<std::pair<const void*, void*>, void*> UserMap;
typedef std::map<const char*, int, VerilatedCStrCmp> ExportNameMap;
// 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
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
// For userInsert, userFind. As indexed by pointer is common across contexts.
UserMap m_userMap VL_GUARDED_BY(m_userMapMutex); ///< Map of <(scope,userkey), userData>
VerilatedMutex m_nameMutex; ///< Protect m_nameMap
/// Map of <scope_name, scope pointer>
VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex);
VerilatedMutex m_hierMapMutex; ///< Protect m_hierMap
/// Map that represents scope hierarchy
// Used by hierarchyAdd, hierarchyRemove, hierarchyMap
VerilatedHierarchyMap m_hierMap VL_GUARDED_BY(m_hierMapMutex);
// Slow - somewhat static:
VerilatedMutex m_exportMutex; ///< Protect m_nameMap
/// Map of <export_func_proto, func number>
// Used by exportInsert, exportFind, exportName.
// Export numbers same across all contexts as just a string-to-number conversion
ExportNameMap m_exportMap VL_GUARDED_BY(m_exportMutex);
int m_exportNext VL_GUARDED_BY(m_exportMutex); ///< 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);
int m_exportNext VL_GUARDED_BY(m_exportMutex) = 0; ///< Next export funcnum
// CONSTRUCTORS
VerilatedImpData()
: 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; }
}
VerilatedImpData() = default;
};
class VerilatedImp final {
@ -270,95 +431,62 @@ protected:
friend class Verilated;
// MEMBERS
union VerilatedImpU { ///< Enclose in an union to call ctor/dtor manually
VerilatedImpData v;
VerilatedImpU() {} // Can't be = default;
~VerilatedImpU() {} // Can't be = default;
};
static VerilatedImpU s_s; ///< Static Singleton; One and only static this
static VerilatedImpData& s() { // Singleton
static VerilatedImpData s_s;
return s_s;
}
public: // But only for verilated*.cpp
// CONSTRUCTORS
VerilatedImp() = default;
~VerilatedImp() = default;
static void setup();
static void teardown();
private:
VL_UNCOPYABLE(VerilatedImp);
public:
// METHODS - debug
static void internalsDump() 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:
// 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
// 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 {
const VerilatedLockGuard lock(s_s.v.m_userMapMutex);
const auto it = s_s.v.m_userMap.find(std::make_pair(scopep, userKey));
if (it != s_s.v.m_userMap.end()) {
const VerilatedLockGuard lock(s().m_userMapMutex);
const auto it = s().m_userMap.find(std::make_pair(scopep, userKey));
if (it != s().m_userMap.end()) {
it->second = userData;
} else {
s_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 {
const VerilatedLockGuard lock(s_s.v.m_userMapMutex);
const auto& it = vlstd::as_const(s_s.v.m_userMap).find(std::make_pair(scopep, userKey));
if (VL_UNLIKELY(it == s_s.v.m_userMap.end())) return nullptr;
const VerilatedLockGuard lock(s().m_userMapMutex);
const auto& it = vlstd::as_const(s().m_userMap).find(std::make_pair(scopep, userKey));
if (VL_UNLIKELY(it == s().m_userMap.end())) return nullptr;
return it->second;
}
private:
public: // But only for verilated.cpp
/// Symbol table destruction cleans up the entries for each scope.
static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope on destruction, so we simply iterate.
const VerilatedLockGuard lock(s_s.v.m_userMapMutex);
for (auto it = s_s.v.m_userMap.begin(); it != s_s.v.m_userMap.end();) {
const VerilatedLockGuard lock(s().m_userMapMutex);
for (auto it = s().m_userMap.begin(); it != s().m_userMap.end();) {
if (it->first.first == scopep) {
s_s.v.m_userMap.erase(it++);
s().m_userMap.erase(it++);
} else {
++it;
}
}
}
static void userDump() VL_MT_SAFE {
const VerilatedLockGuard lock(
s_s.v.m_userMapMutex); // Avoid it changing in middle of dump
const VerilatedLockGuard lock(s().m_userMapMutex); // Avoid it changing in middle of dump
bool first = true;
for (const auto& i : s_s.v.m_userMap) {
for (const auto& i : s().m_userMap) {
if (first) {
VL_PRINTF_MT(" userDump:\n");
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
// METHODS - hierarchy
static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE {
// Slow ok - called at construction for VPI accessible elements
const VerilatedLockGuard lock(s_s.v.m_hierMapMutex);
s_s.v.m_hierMap[fromp].push_back(top);
const VerilatedLockGuard lock(s().m_hierMapMutex);
s().m_hierMap[fromp].push_back(top);
}
static void hierarchyRemove(const VerilatedScope* fromp,
const VerilatedScope* top) VL_MT_SAFE {
// Slow ok - called at destruction for VPI accessible elements
const VerilatedLockGuard lock(s_s.v.m_hierMapMutex);
VerilatedHierarchyMap& map = s_s.v.m_hierMap;
const VerilatedLockGuard lock(s().m_hierMapMutex);
VerilatedHierarchyMap& map = s().m_hierMap;
if (map.find(fromp) == map.end()) return;
auto& scopes = map[fromp];
const auto it = find(scopes.begin(), scopes.end(), top);
@ -423,7 +515,7 @@ public: // But only for verilated*.cpp
}
static const VerilatedHierarchyMap* hierarchyMap() VL_MT_SAFE_POSTINIT {
// 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
@ -437,19 +529,19 @@ public: // But only for verilated*.cpp
// miss at the cost of a multiply, and all lookups move to slowpath.
static int exportInsert(const char* namep) VL_MT_SAFE {
// Slow ok - called once/function at creation
const VerilatedLockGuard lock(s_s.v.m_exportMutex);
const auto it = s_s.v.m_exportMap.find(namep);
if (it == s_s.v.m_exportMap.end()) {
s_s.v.m_exportMap.emplace(namep, s_s.v.m_exportNext++);
return s_s.v.m_exportNext++;
const VerilatedLockGuard lock(s().m_exportMutex);
const auto it = s().m_exportMap.find(namep);
if (it == s().m_exportMap.end()) {
s().m_exportMap.emplace(namep, s().m_exportNext++);
return s().m_exportNext++;
} else {
return it->second;
}
}
static int exportFind(const char* namep) VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.m_exportMutex);
const auto& it = s_s.v.m_exportMap.find(namep);
if (VL_LIKELY(it != s_s.v.m_exportMap.end())) return it->second;
const VerilatedLockGuard lock(s().m_exportMutex);
const auto& it = s().m_exportMap.find(namep);
if (VL_LIKELY(it != s().m_exportMap.end())) return it->second;
std::string msg = (std::string("%Error: Testbench C called ") + namep
+ " but no such DPI export function name exists in ANY model");
VL_FATAL_MT("unknown", 0, "", msg.c_str());
@ -457,16 +549,16 @@ public: // But only for verilated*.cpp
}
static const char* exportName(int funcnum) VL_MT_SAFE {
// Slowpath; find name for given export; errors only so no map to reverse-map it
const VerilatedLockGuard lock(s_s.v.m_exportMutex);
for (const auto& i : s_s.v.m_exportMap) {
const VerilatedLockGuard lock(s().m_exportMutex);
for (const auto& i : s().m_exportMap) {
if (i.second == funcnum) return i.first;
}
return "*UNKNOWN*";
}
static void exportsDump() VL_MT_SAFE {
const VerilatedLockGuard lock(s_s.v.m_exportMutex);
const VerilatedLockGuard lock(s().m_exportMutex);
bool first = true;
for (const auto& i : s_s.v.m_exportMap) {
for (const auto& i : s().m_exportMap) {
if (first) {
VL_PRINTF_MT(" exportDump:\n");
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
// 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 "verilated.h"
#include "verilated_save.h"
#include "verilated_imp.h"
#include <cerrno>
#include <fcntl.h>
@ -43,7 +46,7 @@
// CONSTANTS
/// 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)
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
assert((strlen(VLTSAVE_HEADER_STR) & 7) == 0); // Keep aligned
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 {
@ -95,8 +93,6 @@ void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
// 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 {
@ -249,3 +245,19 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
//=============================================================================
// 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) {
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();
os << len;
return os.write(rhs.data(), len);
@ -247,6 +247,9 @@ inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, std::string& r
rhs.resize(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>
VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray<T_Key, T_Value>& rhs) {
os << rhs.atDefault();

View File

@ -40,12 +40,12 @@ VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
//=============================================================================
// VlWorkerThread
VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, bool profiling)
: m_waiting{false}
, m_poolp{poolp}
VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling)
: m_poolp{poolp}
, m_profiling{profiling} // Must init this last -- after setting up fields that it might read:
, m_exiting{false}
, m_cthread{startWorker, this} {}
, m_cthread{startWorker, this}
, m_contextp{contextp} {}
VlWorkerThread::~VlWorkerThread() {
m_exiting.store(true, std::memory_order_release);
@ -75,12 +75,15 @@ void VlWorkerThread::workerLoop() {
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(int nThreads, bool profiling)
VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, bool profiling)
: m_profiling{profiling} {
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
unsigned cpus = std::thread::hardware_concurrency();
@ -94,7 +97,7 @@ VlThreadPool::VlThreadPool(int nThreads, bool profiling)
}
// Create'em
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
// 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, "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",
Verilated::profThreadsStart());
fprintf(fp, "VLPROF arg +verilator+prof+threads+window+%u\n", Verilated::profThreadsWindow());
Verilated::threadContextp()->profThreadsStart());
fprintf(fp, "VLPROF arg +verilator+prof+threads+window+%u\n",
Verilated::threadContextp()->profThreadsWindow());
fprintf(fp, "VLPROF stat yields %" VL_PRI64 "u\n", VlMTaskVertex::yields());
vluint32_t thread_id = 0;

View File

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

View File

@ -151,7 +151,8 @@ private:
double m_timeRes; ///< Time resolution (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()->'
// to access duck-typed functions to avoid a virtual function call.
@ -203,7 +204,7 @@ protected:
//=========================================================================
// 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 numSignals() const { return m_numSignals; }
@ -225,8 +226,8 @@ protected:
// Character that splits scopes. Note whitespace are ALWAYS escapes.
char scopeEscape() { return m_scopeEscape; }
void close();
void flush();
void closeBase();
void flushBase();
//=========================================================================
// Virtual functions to be provided by the format specific implementation
@ -247,30 +248,24 @@ public:
~VerilatedTrace();
// Set time units (s/ms, defaults to ns)
void set_time_unit(const char* unitp);
void set_time_unit(const std::string& unit);
void set_time_unit(const char* unitp) VL_MT_SAFE;
void set_time_unit(const std::string& unit) VL_MT_SAFE;
// Set time resolution (s/ms, defaults to ns)
void set_time_resolution(const char* unitp);
void set_time_resolution(const std::string& unit);
void set_time_resolution(const char* unitp) VL_MT_SAFE;
void set_time_resolution(const std::string& unit) VL_MT_SAFE;
// 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
void addInitCb(initCb_t cb, void* userp) VL_MT_UNSAFE_ONE;
void addFullCb(dumpCb_t cb, void* userp) VL_MT_UNSAFE_ONE;
void addChgCb(dumpCb_t cb, void* userp) VL_MT_UNSAFE_ONE;
void addCleanupCb(dumpCb_t cb, void* userp) VL_MT_UNSAFE_ONE;
void changeThread() { m_assertOne.changeThread(); }
void module(const std::string& name) VL_MT_UNSAFE_ONE {
m_assertOne.check();
m_moduleName = name;
}
void addInitCb(initCb_t cb, void* userp) VL_MT_SAFE;
void addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
void addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
void addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
void module(const std::string& name) VL_MT_UNSAFE;
void scopeEscape(char flag) { m_scopeEscape = flag; }
//=========================================================================

View File

@ -236,7 +236,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::shutdownWorker() {
//=============================================================================
// Life cycle
template <> void VerilatedTrace<VL_DERIVED_T>::close() {
template <> void VerilatedTrace<VL_DERIVED_T>::closeBase() {
#ifdef VL_TRACE_THREADED
shutdownWorker();
while (m_numTraceBuffers) {
@ -246,7 +246,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::close() {
#endif
}
template <> void VerilatedTrace<VL_DERIVED_T>::flush() {
template <> void VerilatedTrace<VL_DERIVED_T>::flushBase() {
#ifdef VL_TRACE_THREADED
// Hand an empty buffer to the worker thread
vluint32_t* const bufferp = getTraceBuffer();
@ -262,12 +262,12 @@ template <> void VerilatedTrace<VL_DERIVED_T>::flush() {
// Callbacks to run on global events
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();
}
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();
}
@ -291,8 +291,8 @@ VerilatedTrace<VL_DERIVED_T>::VerilatedTrace()
, m_numTraceBuffers { 0 }
#endif
{
set_time_unit(Verilated::timeunitString());
set_time_resolution(Verilated::timeprecisionString());
set_time_unit(Verilated::threadContextp()->timeunitString());
set_time_resolution(Verilated::threadContextp()->timeprecisionString());
}
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::removeExitCb(VerilatedTrace<VL_DERIVED_T>::onExit, this);
#ifdef VL_TRACE_THREADED
close();
closeBase();
#endif
}
@ -308,8 +308,6 @@ template <> VerilatedTrace<VL_DERIVED_T>::~VerilatedTrace() {
// Internals available to format specific implementations
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),
// so we must reset the next code here, but it must have the same number
// of codes on re-open
@ -376,24 +374,26 @@ template <> std::string VerilatedTrace<VL_DERIVED_T>::timeResStr() const {
//=========================================================================
// 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);
}
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_unit(const std::string& unit) {
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_unit(const std::string& unit) VL_MT_SAFE {
set_time_unit(unit.c_str());
}
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const char* unitp) {
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const char* unitp) VL_MT_SAFE {
m_timeRes = timescaleToDouble(unitp);
}
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const std::string& unit) {
template <>
void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const std::string& unit) VL_MT_SAFE {
set_time_resolution(unit.c_str());
}
template <> void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) {
m_assertOne.check();
template <>
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
VL_PRINTF_MT("%%Warning: previous dump at t=%" VL_PRI64 "u, requesting t=%" VL_PRI64
"u, dump call ignored\n",
@ -426,7 +426,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) {
m_traceBufferWritep += 3;
} else {
// Update time point
flush();
flushBase();
emitTimeChange(timeui);
}
#else
@ -472,8 +472,9 @@ template <> void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) {
template <>
void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
CallbackRecord& cbRec) {
m_assertOne.check();
CallbackRecord& cbRec)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START
std::string msg = (std::string("Internal: ") + __FILE__ + "::" + __FUNCTION__
+ " called with already open file");
@ -482,22 +483,26 @@ void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>
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);
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);
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);
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);
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

View File

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

View File

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

View File

@ -667,7 +667,7 @@ class VerilatedVpiError final {
do_callbacks();
}
void do_callbacks() {
if (getError()->level >= vpiError && Verilated::fatalOnVpiError()) {
if (getError()->level >= vpiError && Verilated::threadContextp()->fatalOnVpiError()) {
// Stop on vpi error/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
scopep = Verilated::scopeFind(namep);
scopep = Verilated::threadContextp()->scopeFind(namep);
if (scopep) { // Whole thing found as a scope
if (scopep->type() == VerilatedScope::SCOPE_MODULE) {
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) {
// 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 (!varp) {
scopep = Verilated::scopeFind(scopename.c_str());
scopep = Verilated::threadContextp()->scopeFind(scopename.c_str());
if (!scopep) return nullptr;
varp = scopep->varFind(baseNamep);
}
@ -1418,11 +1418,12 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
VL_VPI_ERROR_RESET_();
switch (property) {
case vpiTimePrecision: {
return Verilated::timeprecision();
return Verilated::threadContextp()->timeprecision();
}
case vpiTimeUnit: {
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();
}
case vpiType: {
@ -2022,7 +2023,8 @@ void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
} else if (time_p->type == vpiScaledRealTime) {
double dtime = VL_TIME_D();
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
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 {
VerilatedVpiImp::assertOneCheck();
VL_VPI_ERROR_RESET_();
vlog_info_p->argc = Verilated::getCommandArgs()->argc;
vlog_info_p->argv = const_cast<PLI_BYTE8**>(Verilated::getCommandArgs()->argv);
auto argc_argv = Verilated::threadContextp()->impp()->argc_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->version = const_cast<PLI_BYTE8*>(Verilated::productVersion());
return 1;

View File

@ -96,7 +96,8 @@ private:
: // If assertions are off, have constant propagation rip them out later
// This allows syntax errors and such to be detected normally.
(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())))),
nodep, nullptr);
newp->user1(true); // Don't assert/cover this if

View File

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

View File

@ -45,6 +45,9 @@ private:
V3OutCFile cf(filename);
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
ofp()->putsHeader();
puts("// DESCRIPTION: main() calling loop, created with Verilator --main\n");
@ -55,20 +58,16 @@ private:
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("// Setup defaults and parse command line\n");
puts("// Setup context, defaults, and parse command line\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("// 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("// Evaluate initials\n");
@ -76,15 +75,16 @@ private:
puts("\n");
puts("// Simulate until $finish\n");
puts("while (!Verilated::gotFinish()) {\n");
puts("while (!contextp->gotFinish()) {\n");
puts(/**/ "// Evaluate model\n");
puts(/**/ "topp->eval();\n");
puts(/**/ "// Advance time\n");
puts(/**/ "++main_time;\n");
puts(/**/ "contextp->timeInc(1);\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("}\n");
puts("\n");

View File

@ -467,7 +467,8 @@ void EmitCSyms::emitSymHdr() {
}
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");
for (const auto& i : m_usesVfinal) {
@ -643,10 +644,11 @@ void EmitCSyms::emitSymImp() {
puts("{\n");
emitScopeHier(true);
puts("}\n\n");
puts(symClassName() + "::" + symClassName() + "(" + topClassName()
puts(symClassName() + "::" + symClassName() + "(VerilatedContext* contextp, " + topClassName()
+ "* topp, const char* namep)\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()) {
puts(" , __Vm_dumping(false)\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 "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}) {
$fh->print("\n");
@ -1753,7 +1744,6 @@ sub _make_main {
$fh->print(" VL_PRINTF(\"Saving model to '%s'\\n\", filenamep);\n");
$fh->print(" VerilatedSave os;\n");
$fh->print(" os.open(filenamep);\n");
$fh->print(" os << main_time;\n");
$fh->print(" os << *topp;\n");
$fh->print(" os.close();\n");
$fh->print("}\n");
@ -1762,7 +1752,6 @@ sub _make_main {
$fh->print(" VL_PRINTF(\"Restoring model from '%s'\\n\", filenamep);\n");
$fh->print(" VerilatedRestore os;\n");
$fh->print(" os.open(filenamep);\n");
$fh->print(" os >> main_time;\n");
$fh->print(" os >> *topp;\n");
$fh->print(" os.close();\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";
} else {
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 " 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 " Verilated::internalsDump()\n;" if $self->{verilated_debug};
print $fh " contextp->internalsDump()\n;" if $self->{verilated_debug};
my $set;
if ($self->sc) {
@ -1800,22 +1791,22 @@ sub _make_main {
if ($self->{trace}) {
$fh->print("\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<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(" topp->trace(tfp.get(), 99);\n");
$fh->print(" tfp->open(\"".$self->trace_filename."\");\n");
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");
}
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(" 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");
}
@ -1831,8 +1822,11 @@ sub _make_main {
_print_advance_time($self, $fh, 10);
print $fh " }\n";
print $fh " while ((sc_time_stamp() < sim_time * MAIN_TIME_MULTIPLIER)\n";
print $fh " && !Verilated::gotFinish()) {\n";
my $time = $self->sc ? "sc_time_stamp()" : "contextp->time()";
print $fh " while ((${time} < sim_time * MAIN_TIME_MULTIPLIER)\n";
print $fh " && !contextp->gotFinish()) {\n";
for (my $i=0; $i<5; $i++) {
my $action = 0;
if ($self->{inputs}{fastclk}) {
@ -1844,7 +1838,7 @@ sub _make_main {
$action = 1;
}
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(" printf(\"Exiting after save_model\\n\");\n");
$fh->print(" return 0;\n");
@ -1853,7 +1847,7 @@ sub _make_main {
_print_advance_time($self, $fh, 1, $action);
}
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 " }\n";
print $fh " topp->final();\n";
@ -1894,11 +1888,11 @@ sub _print_advance_time {
print $fh " ${set}eval();\n";
if ($self->{trace} && !$self->sc) {
$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");
}
}
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
not unexpectedly terminated.
if (Verilated::gotFinish()) {
if (contextp->gotFinish()) {
vl_fatal(__FILE__, __LINE__, "dut", "<error message goes here>");
exit(1);
}

View File

@ -8,24 +8,23 @@
// General headers
#include "verilated.h"
Vt_clk_inp_init* topp;
vluint64_t main_time;
double sc_time_stamp() { return main_time; }
void oneTest(int seed) {
void oneTest(int argc, char** argv, int seed) {
vluint64_t sim_time = 1000;
#ifdef TEST_VERBOSE
VL_PRINTF("== Seed=%d\n", seed);
#endif
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
contextp->commandArgs(argc, argv);
// Randomise initial state
srand48(seed);
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
topp->rst_n = 1;
@ -33,33 +32,31 @@ void oneTest(int seed) {
topp->eval();
// Tick for a little bit
while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) {
while (contextp->time() < sim_time && !contextp->gotFinish()) {
topp->clk = 0;
topp->eval();
main_time += 5;
contextp->timeInc(5);
topp->clk = 1;
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");
}
topp->final();
VL_DO_DANGLING(delete topp, topp);
}
int main(int argc, char** argv, char** env) {
Verilated::commandArgs(argc, argv);
#if VL_DEBUG
// Verilated::debug(1);
#endif
for (int seed = 123; seed < 133; ++seed) oneTest(seed);
for (int seed = 123; seed < 133; ++seed) oneTest(argc, argv, seed);
return 0;
}

View File

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

View File

@ -45,19 +45,36 @@ long long get_memory_usage() {
}
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;
#endif
Verilated::debug(0);
Verilated::gotFinish(0);
topp->eval();
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;
#endif
topp->clk = !topp->clk;
topp->eval();
}
VL_DO_DANGLING(delete topp, topp);
#ifdef VL_NO_LEGACY
VL_DO_DANGLING(delete contextp, contextp);
#endif
}
int main(int argc, char* argv[]) {

View File

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

View File

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

View File

@ -14,16 +14,15 @@
#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;
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);
Verilated::traceEverOn(true);
Vt_scope_map* top = new Vt_scope_map{contextp.get(), "top"};
contextp->debug(0);
contextp->traceEverOn(true);
VerilatedVcdC* tfp = new VerilatedVcdC;
top->trace(tfp, 99);
@ -31,10 +30,10 @@ int main(int argc, char** argv, char** env) {
top->CLK = 0;
top->eval();
tfp->dump((unsigned int)(main_time));
++main_time;
tfp->dump(contextp->time());
contextp->timeInc(1);
const VerilatedScopeNameMap* scopeMapp = Verilated::scopeNameMap();
const VerilatedScopeNameMap* scopeMapp = contextp->scopeNameMap();
for (VerilatedScopeNameMap::const_iterator it = scopeMapp->begin(); it != scopeMapp->end();
++it) {
#ifdef TEST_VERBOSE
@ -106,14 +105,14 @@ int main(int argc, char** argv, char** env) {
top->CLK = 0;
top->eval();
tfp->dump((unsigned int)(main_time));
++main_time;
tfp->dump(contextp->time());
contextp->timeInc(1);
// Posedge on clock, expect all the public bits to flip
top->CLK = 1;
top->eval();
tfp->dump((unsigned int)(main_time));
++main_time;
tfp->dump(contextp->time());
contextp->timeInc(1);
for (VerilatedScopeNameMap::const_iterator it = scopeMapp->begin(); it != scopeMapp->end();
++it) {
@ -153,8 +152,8 @@ int main(int argc, char** argv, char** env) {
top->CLK = 0;
top->eval();
tfp->dump((unsigned int)(main_time));
++main_time;
tfp->dump(contextp->time());
contextp->timeInc(1);
tfp->close();
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;
#ifdef T_WRAPPER_LEGACY_TIME64
vluint64_t vl_time_stamp64() { return main_time; }
#endif
#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; }
#endif
@ -60,9 +59,15 @@ int main(int argc, char** argv, char** env) {
CHECK_RESULT(Verilated::debug(), 9);
Verilated::debug(0);
Verilated::fatalOnError(true);
CHECK_RESULT(Verilated::fatalOnError(), true);
Verilated::fatalOnVpiError(true);
CHECK_RESULT(Verilated::fatalOnVpiError(), true);
Verilated::gotError(false);
CHECK_RESULT(Verilated::gotError(), false);
Verilated::gotFinish(false);
CHECK_RESULT(Verilated::gotFinish(), false); // Commonly used
@ -87,10 +92,24 @@ int main(int argc, char** argv, char** env) {
VL_PRINTF("Starting\n");
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_D(), main_time);
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->eval();
}

View File

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