** 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:
parent
caa9c99837
commit
2cad22a22a
7
Changes
7
Changes
|
@ -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.
|
||||
|
||||
|
|
|
@ -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::...'"?
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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; };
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
||||
//=========================================================================
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
106
src/V3EmitC.cpp
106
src/V3EmitC.cpp
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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[]) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -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
|
|
@ -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;
|
|
@ -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;
|
|
@ -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
|
|
@ -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
|
|
@ -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(
|
|
@ -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(
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
Loading…
Reference in New Issue