diff --git a/Changes b/Changes index 586103716..be771165c 100644 --- a/Changes +++ b/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. diff --git a/bin/verilator b/bin/verilator index 6571d6360..c710b3361 100755 --- a/bin/verilator +++ b/bin/verilator @@ -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::...'"? diff --git a/examples/make_protect_lib/sim_main.cpp b/examples/make_protect_lib/sim_main.cpp index 4d15ea360..b3096b4c5 100644 --- a/examples/make_protect_lib/sim_main.cpp +++ b/examples/make_protect_lib/sim_main.cpp @@ -16,25 +16,24 @@ #include #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 diff --git a/examples/make_tracing_c/sim_main.cpp b/examples/make_tracing_c/sim_main.cpp index c38115e2b..781594439 100644 --- a/examples/make_tracing_c/sim_main.cpp +++ b/examples/make_tracing_c/sim_main.cpp @@ -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 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 top{new Vtop}; + const std::unique_ptr 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 diff --git a/include/verilated.cpp b/include/verilated.cpp index d842d16ce..0421b0438 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -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(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(seed)); + Verilated::threadContextp()->randSeed(static_cast(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(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(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 {,}.\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(Verilated::randSeed()) << 32) - ^ (static_cast(Verilated::randSeed()))); - } else { - return ((static_cast(vl_sys_rand32()) << 32) - ^ (static_cast(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(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 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(randSeed()) << 32) + ^ (static_cast(randSeed()))); + } else { + return ((static_cast(vl_sys_rand32()) << 32) + ^ (static_cast(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 {,}.\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 { diff --git a/include/verilated.h b/include/verilated.h index 1543a4223..193470e33 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include // avoided to reduce compile time // avoided and instead in verilated_heavy.h to reduce compile time // 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 m_argVec; // Aargument list + } m_args VL_GUARDED_BY(m_argMutex); + + // Implementation details + std::unique_ptr m_impdatap; + // Coverage access + std::unique_ptr m_coveragep; // Pointer for coveragep() + + // File I/O + // Not serialized + mutable VerilatedMutex m_fdMutex; // Protect m_fdps, m_fdFree + std::vector m_fdps VL_GUARDED_BY(m_fdMutex); // File descriptors + // List of free descriptors (SLOW - FOPEN/CLOSE only) + std::vector m_fdFree VL_GUARDED_BY(m_fdMutex); + // List of free descriptors in the MCT region [4, 32) + std::vector 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(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(this); } + const VerilatedContextImp* impp() const { + return reinterpret_cast(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(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(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(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(sc_time_stamp()) : 0; +} +# endif # endif #endif -#define VL_TIME_Q() (static_cast(vl_time_stamp64())) -#define VL_TIME_D() (static_cast(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(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. diff --git a/include/verilated_cov.cpp b/include/verilated_cov.cpp index 69b9bc009..85904d9d6 100644 --- a/include/verilated_cov.cpp +++ b/include/verilated_cov.cpp @@ -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 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(itemp)); } -void VerilatedCov::_inserti(vluint32_t* itemp) VL_MT_SAFE { - VerilatedCovImp::imp().inserti(new VerilatedCoverItemSpec(itemp)); +void VerilatedCovContext::_inserti(vluint64_t* itemp) VL_MT_SAFE { + impp()->inserti(new VerilatedCoverItemSpec(itemp)); } -void VerilatedCov::_inserti(vluint64_t* itemp) VL_MT_SAFE { - VerilatedCovImp::imp().inserti(new VerilatedCoverItemSpec(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(m_coveragep.get()); +} diff --git a/include/verilated_cov.h b/include/verilated_cov.h index 048426765..450b4c071 100644 --- a/include/verilated_cov.h +++ b/include/verilated_cov.h @@ -20,11 +20,14 @@ #define VERILATOR_VERILATED_COV_H_ #include "verilatedos.h" +#include "verilated.h" #include #include #include +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 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(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 diff --git a/include/verilated_dpi.cpp b/include/verilated_dpi.cpp index 00d13172d..5a9e8aa74 100644 --- a/include/verilated_dpi.cpp +++ b/include/verilated_dpi.cpp @@ -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) { diff --git a/include/verilated_fst_c.cpp b/include/verilated_fst_c.cpp index 8842023c0..cfbec316d 100644 --- a/include/verilated_fst_c.cpp +++ b/include/verilated_fst_c.cpp @@ -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::close(); +void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock(m_mutex); + VerilatedTrace::closeBase(); fstWriterClose(m_fst); m_fst = nullptr; } -void VerilatedFst::flush() { - VerilatedTrace::flush(); +void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock(m_mutex); + VerilatedTrace::flushBase(); fstWriterFlushContext(m_fst); } diff --git a/include/verilated_fst_c.h b/include/verilated_fst_c.h index 4c757a816..64efe75c1 100644 --- a/include/verilated_fst_c.h +++ b/include/verilated_fst_c.h @@ -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::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; }; diff --git a/include/verilated_heavy.h b/include/verilated_heavy.h index cf86d4be7..cfc013b2c 100644 --- a/include/verilated_heavy.h +++ b/include/verilated_heavy.h @@ -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 diff --git a/include/verilated_imp.h b/include/verilated_imp.h index 75cb683b5..4c462d0f1 100644 --- a/include/verilated_imp.h +++ b/include/verilated_imp.h @@ -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 + // 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 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(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( + fseek(*fdlist.begin(), static_cast(offset), static_cast(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(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 ArgVec; typedef std::map, void*> UserMap; typedef std::map 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 - 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 + // 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 m_fdps VL_GUARDED_BY(m_fdMutex); ///< File descriptors - /// List of free descriptors (SLOW - FOPEN/CLOSE only) - std::vector m_fdFree VL_GUARDED_BY(m_fdMutex); - // List of free descriptors in the MCT region [4, 32) - std::vector 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(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( - fseek(*fdlist.begin(), static_cast(offset), static_cast(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(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; - } }; //====================================================================== diff --git a/include/verilated_save.cpp b/include/verilated_save.cpp index 7743859af..ed71dc4b9 100644 --- a/include/verilated_save.cpp +++ b/include/verilated_save.cpp @@ -16,9 +16,12 @@ /// //============================================================================= +#define VERILATOR_VERILATED_SAVE_CPP_ + #include "verilatedos.h" #include "verilated.h" #include "verilated_save.h" +#include "verilated_imp.h" #include #include @@ -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; +} diff --git a/include/verilated_save.h b/include/verilated_save.h index d66757c7f..a8bc78fdf 100644 --- a/include/verilated_save.h +++ b/include/verilated_save.h @@ -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 VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray& rhs) { os << rhs.atDefault(); diff --git a/include/verilated_threads.cpp b/include/verilated_threads.cpp index 132e4e29a..6eb241fac 100644 --- a/include/verilated_threads.cpp +++ b/include/verilated_threads.cpp @@ -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; diff --git a/include/verilated_threads.h b/include/verilated_threads.h index 3082898a8..ded328b5b 100644 --- a/include/verilated_threads.h +++ b/include/verilated_threads.h @@ -203,12 +203,13 @@ private: bool m_profiling; // Is profiling enabled? std::atomic 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 diff --git a/include/verilated_trace.h b/include/verilated_trace.h index 37664caf2..d5f9a5cb6 100644 --- a/include/verilated_trace.h +++ b/include/verilated_trace.h @@ -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& cbVec, CallbackRecord& cbRec); + void addCallbackRecord(std::vector& 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; } //========================================================================= diff --git a/include/verilated_trace_imp.cpp b/include/verilated_trace_imp.cpp index 04caffa2b..032596bf9 100644 --- a/include/verilated_trace_imp.cpp +++ b/include/verilated_trace_imp.cpp @@ -236,7 +236,7 @@ template <> void VerilatedTrace::shutdownWorker() { //============================================================================= // Life cycle -template <> void VerilatedTrace::close() { +template <> void VerilatedTrace::closeBase() { #ifdef VL_TRACE_THREADED shutdownWorker(); while (m_numTraceBuffers) { @@ -246,7 +246,7 @@ template <> void VerilatedTrace::close() { #endif } -template <> void VerilatedTrace::flush() { +template <> void VerilatedTrace::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::flush() { // Callbacks to run on global events template <> void VerilatedTrace::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(selfp)->flush(); } template <> void VerilatedTrace::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(selfp)->close(); } @@ -291,8 +291,8 @@ VerilatedTrace::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::~VerilatedTrace() { @@ -300,7 +300,7 @@ template <> VerilatedTrace::~VerilatedTrace() { Verilated::removeFlushCb(VerilatedTrace::onFlush, this); Verilated::removeExitCb(VerilatedTrace::onExit, this); #ifdef VL_TRACE_THREADED - close(); + closeBase(); #endif } @@ -308,8 +308,6 @@ template <> VerilatedTrace::~VerilatedTrace() { // Internals available to format specific implementations template <> void VerilatedTrace::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::timeResStr() const { //========================================================================= // External interface to client code -template <> void VerilatedTrace::set_time_unit(const char* unitp) { +template <> void VerilatedTrace::set_time_unit(const char* unitp) VL_MT_SAFE { m_timeUnit = timescaleToDouble(unitp); } - -template <> void VerilatedTrace::set_time_unit(const std::string& unit) { +template <> void VerilatedTrace::set_time_unit(const std::string& unit) VL_MT_SAFE { set_time_unit(unit.c_str()); } - -template <> void VerilatedTrace::set_time_resolution(const char* unitp) { +template <> void VerilatedTrace::set_time_resolution(const char* unitp) VL_MT_SAFE { m_timeRes = timescaleToDouble(unitp); } - -template <> void VerilatedTrace::set_time_resolution(const std::string& unit) { +template <> +void VerilatedTrace::set_time_resolution(const std::string& unit) VL_MT_SAFE { set_time_resolution(unit.c_str()); } -template <> void VerilatedTrace::dump(vluint64_t timeui) { - m_assertOne.check(); +template <> +void VerilatedTrace::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::dump(vluint64_t timeui) { m_traceBufferWritep += 3; } else { // Update time point - flush(); + flushBase(); emitTimeChange(timeui); } #else @@ -472,8 +472,9 @@ template <> void VerilatedTrace::dump(vluint64_t timeui) { template <> void VerilatedTrace::addCallbackRecord(std::vector& 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::addCallbackRecord(std::vector cbVec.push_back(cbRec); } -template <> void VerilatedTrace::addInitCb(initCb_t cb, void* userp) { +template <> void VerilatedTrace::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE { CallbackRecord cbr(cb, userp); addCallbackRecord(m_initCbs, cbr); } -template <> void VerilatedTrace::addFullCb(dumpCb_t cb, void* userp) { +template <> void VerilatedTrace::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE { CallbackRecord cbr(cb, userp); addCallbackRecord(m_fullCbs, cbr); } -template <> void VerilatedTrace::addChgCb(dumpCb_t cb, void* userp) { +template <> void VerilatedTrace::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE { CallbackRecord cbr(cb, userp); addCallbackRecord(m_chgCbs, cbr); } -template <> void VerilatedTrace::addCleanupCb(dumpCb_t cb, void* userp) { +template <> void VerilatedTrace::addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE { CallbackRecord cbr(cb, userp); addCallbackRecord(m_cleanupCbs, cbr); } +template <> void VerilatedTrace::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 diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index d0f1f6d8c..81b9666e7 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -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::flush(); + VerilatedTrace::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::flush(), so we just // need to shut down the tracing thread here. - VerilatedTrace::close(); + VerilatedTrace::closeBase(); } -void VerilatedVcd::flush() { - VerilatedTrace::flush(); +void VerilatedVcd::flush() VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock(m_mutex); + VerilatedTrace::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) { diff --git a/include/verilated_vcd_c.h b/include/verilated_vcd_c.h index ed02cbda1..37adbdd50 100644 --- a/include/verilated_vcd_c.h +++ b/include/verilated_vcd_c.h @@ -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::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(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; } diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 6814e819e..64b16fb83 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -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(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(Verilated::productName()); vlog_info_p->version = const_cast(Verilated::productVersion()); return 1; diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 8ed6d686a..5ee211b09 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -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(new AstCMath(fl, "Verilated::assertOn()", 1)) + ? static_cast( + new AstCMath(fl, "vlSymsp->_vm_contextp__->assertOn()", 1)) : static_cast(new AstConst(fl, AstConst::BitFalse())))), nodep, nullptr); newp->user1(true); // Don't assert/cover this if diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 511c41e54..c82d39dec 100644 --- a/src/V3EmitC.cpp +++ b/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"); diff --git a/src/V3EmitCMain.cpp b/src/V3EmitCMain.cpp index 768694416..a7260fb8f 100644 --- a/src/V3EmitCMain.cpp +++ b/src/V3EmitCMain.cpp @@ -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 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"); diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index f5554b8f9..352b3292b 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -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"); diff --git a/test_regress/driver.pl b/test_regress/driver.pl index 06c4d01f6..0f45bb4df 100755 --- a/test_regress/driver.pl +++ b/test_regress/driver.pl @@ -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 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 tfp{new VerilatedFstC};\n") if $self->{trace_format} eq 'fst-c'; $fh->print(" std::unique_ptr tfp{new VerilatedVcdC};\n") if $self->{trace_format} eq 'vcd-c'; $fh->print(" std::unique_ptr 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", ""); exit(1); } diff --git a/test_regress/t/t_clk_inp_init.cpp b/test_regress/t/t_clk_inp_init.cpp index 79c85635a..4d2e17463 100644 --- a/test_regress/t/t_clk_inp_init.cpp +++ b/test_regress/t/t_clk_inp_init.cpp @@ -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 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 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; } diff --git a/test_regress/t/t_cover_lib_c.cpp b/test_regress/t/t_cover_lib_c.cpp index 4f7b08dfe..f9de90ded 100644 --- a/test_regress/t/t_cover_lib_c.cpp +++ b/test_regress/t/t_cover_lib_c.cpp @@ -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); diff --git a/test_regress/t/t_leak.cpp b/test_regress/t/t_leak.cpp index 9f640d00e..f9bdf3bda 100644 --- a/test_regress/t/t_leak.cpp +++ b/test_regress/t/t_leak.cpp @@ -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[]) { diff --git a/test_regress/t/t_leak.v b/test_regress/t/t_leak.v index b85a8099d..25ef75492 100644 --- a/test_regress/t/t_leak.v +++ b/test_regress/t/t_leak.v @@ -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 diff --git a/test_regress/t/t_order_multidriven.cpp b/test_regress/t/t_order_multidriven.cpp index 9a5993967..1b9f6ef9a 100644 --- a/test_regress/t/t_order_multidriven.cpp +++ b/test_regress/t/t_order_multidriven.cpp @@ -41,10 +41,10 @@ static void cycle() { } int main() { + const std::unique_ptr 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); diff --git a/test_regress/t/t_scope_map.cpp b/test_regress/t/t_scope_map.cpp index 912150699..a978627a7 100644 --- a/test_regress/t/t_scope_map.cpp +++ b/test_regress/t/t_scope_map.cpp @@ -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 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(); diff --git a/test_regress/t/t_wrapper_context.cpp b/test_regress/t/t_wrapper_context.cpp new file mode 100644 index 000000000..d898c212d --- /dev/null +++ b/test_regress/t/t_wrapper_context.cpp @@ -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 +#include + +#include +#include + +#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(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 context0p{new VerilatedContext}; + std::unique_ptr context1p{new VerilatedContext}; + + // configuration + context0p->fatalOnError(false); + context1p->fatalOnError(false); + context0p->traceEverOn(true); + context1p->traceEverOn(true); + + // instantiate verilated design + std::unique_ptr top0p{new VM_PREFIX{context0p.get(), "top0"}}; + std::unique_ptr 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; +} diff --git a/test_regress/t/t_wrapper_context.pl b/test_regress/t/t_wrapper_context.pl new file mode 100755 index 000000000..e95e77250 --- /dev/null +++ b/test_regress/t/t_wrapper_context.pl @@ -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; diff --git a/test_regress/t/t_wrapper_context.v b/test_regress/t/t_wrapper_context.v new file mode 100644 index 000000000..e892d6138 --- /dev/null +++ b/test_regress/t/t_wrapper_context.v @@ -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 diff --git a/test_regress/t/t_wrapper_context_fst.pl b/test_regress/t/t_wrapper_context_fst.pl new file mode 100755 index 000000000..8b40a7b2d --- /dev/null +++ b/test_regress/t/t_wrapper_context_fst.pl @@ -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; diff --git a/test_regress/t/t_wrapper_context_seq.pl b/test_regress/t/t_wrapper_context_seq.pl new file mode 100755 index 000000000..8ddf958f8 --- /dev/null +++ b/test_regress/t/t_wrapper_context_seq.pl @@ -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; diff --git a/test_regress/t/t_wrapper_context_top0.out b/test_regress/t/t_wrapper_context_top0.out new file mode 100644 index 000000000..4affa5cef --- /dev/null +++ b/test_regress/t/t_wrapper_context_top0.out @@ -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 diff --git a/test_regress/t/t_wrapper_context_top1.out b/test_regress/t/t_wrapper_context_top1.out new file mode 100644 index 000000000..84013beb4 --- /dev/null +++ b/test_regress/t/t_wrapper_context_top1.out @@ -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 diff --git a/test_regress/t/t_wrapper_context_trace0.out b/test_regress/t/t_wrapper_context_trace0.out new file mode 100644 index 000000000..cc90048e5 --- /dev/null +++ b/test_regress/t/t_wrapper_context_trace0.out @@ -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( diff --git a/test_regress/t/t_wrapper_context_trace1.out b/test_regress/t/t_wrapper_context_trace1.out new file mode 100644 index 000000000..3fa5258a3 --- /dev/null +++ b/test_regress/t/t_wrapper_context_trace1.out @@ -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( diff --git a/test_regress/t/t_wrapper_legacy.cpp b/test_regress/t/t_wrapper_legacy.cpp index d93356a6a..152e81f96 100644 --- a/test_regress/t/t_wrapper_legacy.cpp +++ b/test_regress/t/t_wrapper_legacy.cpp @@ -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(); } diff --git a/test_regress/t/t_wrapper_legacy.pl b/test_regress/t/t_wrapper_legacy.pl index 293973bb4..bdcde3470 100755 --- a/test_regress/t/t_wrapper_legacy.pl +++ b/test_regress/t/t_wrapper_legacy.pl @@ -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( diff --git a/test_regress/t/t_wrapper_legacy_timed.pl b/test_regress/t/t_wrapper_legacy_timed.pl new file mode 100755 index 000000000..edde6839d --- /dev/null +++ b/test_regress/t/t_wrapper_legacy_timed.pl @@ -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;