Merge branch 'master' into develop-v5

This commit is contained in:
Geza Lore 2022-07-12 17:29:45 +01:00
commit c9ac9a75a6
97 changed files with 1110 additions and 646 deletions

View File

@ -24,6 +24,9 @@ Verilator 4.225 devel
**Minor:** **Minor:**
* Fix incorrect bit op tree optimization (#3470). [algrobman]
* Fix empty string arguments to display (#3484). [Grulfen]
* Fix table misoptimizing away display (#3488). [Stefan Post]
Verilator 4.224 2022-06-19 Verilator 4.224 2022-06-19

View File

@ -10,6 +10,7 @@ Alex Chadwick
Aliaksei Chapyzhenka Aliaksei Chapyzhenka
Ameya Vikram Singh Ameya Vikram Singh
Andreas Kuster Andreas Kuster
Arkadiusz Kozdra
Chris Randall Chris Randall
Chuxuan Wang Chuxuan Wang
Conor McCullough Conor McCullough

View File

@ -86,61 +86,17 @@ Connecting to C++
In C++ output mode (:vlopt:`--cc`), the Verilator generated model class is a In C++ output mode (:vlopt:`--cc`), the Verilator generated model class is a
simple C++ class. The user must write a C++ wrapper and main loop for the simple C++ class. The user must write a C++ wrapper and main loop for the
simulation, which instantiates the model class, and link with the Verilated simulation, which instantiates the model class, and link with the Verilated
model. Here is a simple example: model.
.. code-block:: C++ Refer to ``examples/make_tracing_c`` in the distribution for a detailed
commented example.
#include <verilated.h> // Defines common routines Top level IO signals are read and written as members of the model. You
#include <iostream> // Need std::cout call the model's :code:`eval()` method to evaluate the model. When the
#include "Vtop.h" // From Verilating "top.v" simulation is complete call the model's :code:`final()` method to execute
any SystemVerilog final blocks, and complete any assertions. See
:ref:`Evaluation Loop`.
Vtop *top; // Instantiation of model
uint64_t main_time = 0; // Current simulation time
// This is a 64-bit integer to reduce wrap over issues and
// allow modulus. This is in units of the timeprecision
// used in Verilog (or from --timescale-override)
double sc_time_stamp() { // Called by $time in Verilog
return main_time; // converts to double, to match
// what SystemC does
}
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv); // Remember args
top = new Vtop; // Create model
// Do not instead make Vtop as a file-scope static
// variable, as the "C++ static initialization order fiasco"
// may cause a crash
top->reset_l = 0; // Set some inputs
while (!Verilated::gotFinish()) {
if (main_time > 10) {
top->reset_l = 1; // Deassert reset
}
if ((main_time % 10) == 1) {
top->clk = 1; // Toggle clock
}
if ((main_time % 10) == 6) {
top->clk = 0;
}
top->eval(); // Evaluate model
cout << top->out << endl; // Read a output
main_time++; // Time passes...
}
top->final(); // Done simulating
// // (Though this example doesn't get here)
delete top;
}
Note top level IO signals are read and written as members of the model. You
call the :code:`eval()` method to evaluate the model. When the simulation is
complete call the :code:`final()` method to execute any SystemVerilog final
blocks, and complete any assertions. See :ref:`Evaluation Loop`.
Connecting to SystemC Connecting to SystemC
@ -449,14 +405,15 @@ accesses the above signal "readme" would be:
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
Verilated::commandArgs(argc, argv); Verilated::commandArgs(argc, argv);
Vour* top = new Vour; const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
Verilated::internalsDump(); // See scopes to help debug const std::unique_ptr<Vour> top{new Vour{contextp.get()}};
while (!Verilated::gotFinish()) {
contextp->internalsDump(); // See scopes to help debug
while (!contextp->gotFinish()) {
top->eval(); top->eval();
VerilatedVpi::callValueCbs(); // For signal callbacks VerilatedVpi::callValueCbs(); // For signal callbacks
read_and_check(); read_and_check();
} }
delete top;
return 0; return 0;
} }
EOF EOF

View File

@ -8,7 +8,7 @@ Simulation Runtime Arguments
The following are the arguments that may be passed to a Verilated The following are the arguments that may be passed to a Verilated
executable, provided that executable calls executable, provided that executable calls
:code:`Verilated::commandArgs()`. :code:`VerilatedContext*->commandArgs(argc, argv)`.
All simulation runtime arguments begin with "+verilator", so that the All simulation runtime arguments begin with "+verilator", so that the
user's executable may skip over all "+verilator" arguments when parsing its user's executable may skip over all "+verilator" arguments when parsing its
@ -96,7 +96,7 @@ Summary:
.. option:: +verilator+noassert .. option:: +verilator+noassert
Disable assert checking per runtime argument. This is the same as Disable assert checking per runtime argument. This is the same as
calling :code:`Verilated::assertOn(false)` in the model. calling :code:`VerilatedContext*->assertOn(false)` in the model.
.. option:: +verilator+V .. option:: +verilator+V

View File

@ -478,7 +478,7 @@ $test$plusargs, $value$plusargs
.. code-block:: C++ .. code-block:: C++
Verilated::commandArgs(argc, argv); {VerilatedContext*} ->commandArgs(argc, argv);
to register the command line before calling $test$plusargs or to register the command line before calling $test$plusargs or
$value$plusargs. $value$plusargs.

View File

@ -21,11 +21,14 @@ int main(int argc, char** argv, char** env) {
// Prevent unused variable warnings // Prevent unused variable warnings
if (false && argc && argv && env) {} if (false && argc && argv && env) {}
// Construct a VerilatedContext to hold simulation time, etc.
VerilatedContext* contextp = new VerilatedContext;
// Construct the Verilated model, from Vtop.h generated from Verilating "top.v" // Construct the Verilated model, from Vtop.h generated from Verilating "top.v"
Vtop* top = new Vtop; Vtop* top = new Vtop{contextp};
// Simulate until $finish // Simulate until $finish
while (!Verilated::gotFinish()) { while (!contextp->gotFinish()) {
// Evaluate model // Evaluate model
top->eval(); top->eval();

View File

@ -66,6 +66,10 @@
#if defined(_WIN32) || defined(__MINGW32__) #if defined(_WIN32) || defined(__MINGW32__)
# include <direct.h> // mkdir # include <direct.h> // mkdir
#endif #endif
#ifdef VL_THREADED
# include "verilated_threads.h"
#endif
// clang-format on // clang-format on
// Max characters in static char string for VL_VALUE_STRING // Max characters in static char string for VL_VALUE_STRING
@ -1606,8 +1610,8 @@ IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE {
return code >> 8; // Want exit status return code >> 8; // Want exit status
} }
IData VL_TESTPLUSARGS_I(const char* formatp) VL_MT_SAFE { IData VL_TESTPLUSARGS_I(const std::string& format) VL_MT_SAFE {
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(formatp); const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(format.c_str());
return match.empty() ? 0 : 1; return match.empty() ? 0 : 1;
} }
@ -2428,6 +2432,33 @@ const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE {
return vl_time_str(timeprecision()); return vl_time_str(timeprecision());
} }
void VerilatedContext::threads(unsigned n) {
if (n == 0) VL_FATAL_MT(__FILE__, __LINE__, "", "%Error: Simulation threads must be >= 1");
if (m_threadPool) {
VL_FATAL_MT(
__FILE__, __LINE__, "",
"%Error: Cannot set simulation threads after the thread pool has been created.");
}
#if VL_THREADED
if (m_threads == n) return; // To avoid unnecessary warnings
m_threads = n;
const unsigned hardwareThreadsAvailable = std::thread::hardware_concurrency();
if (m_threads > hardwareThreadsAvailable) {
VL_PRINTF_MT("%%Warning: System has %u hardware threads but simulation thread count set "
"to %u. This will likely cause significant slowdown.\n",
hardwareThreadsAvailable, m_threads);
}
#else
if (n > 1) {
VL_PRINTF_MT("%%Warning: Verilator run-time library built without VL_THREADS. Ignoring "
"call to 'VerilatedContext::threads' with argument %u.\n",
n);
}
#endif
}
void VerilatedContext::commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) { void VerilatedContext::commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock{m_argMutex}; const VerilatedLockGuard lock{m_argMutex};
m_args.m_argVec.clear(); // Empty first, then add m_args.m_argVec.clear(); // Empty first, then add
@ -2458,6 +2489,33 @@ void VerilatedContext::internalsDump() const VL_MT_SAFE {
VerilatedImp::userDump(); VerilatedImp::userDump();
} }
void VerilatedContext::addModel(VerilatedModel* modelp) {
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
if (modelp->threads() > m_threads) {
std::ostringstream msg;
msg << "VerilatedContext has " << m_threads << " threads but model '"
<< modelp->modelName() << "' (instantiated as '" << modelp->hierName()
<< "') was Verilated with --threads " << modelp->threads() << ".\n";
const std::string str = msg.str();
VL_FATAL_MT(__FILE__, __LINE__, modelp->hierName(), str.c_str());
}
}
VerilatedVirtualBase* VerilatedContext::threadPoolp() {
if (m_threads == 1) return nullptr;
#if VL_THREADED
if (!m_threadPool) m_threadPool.reset(new VlThreadPool{this, m_threads - 1});
#endif
return m_threadPool.get();
}
VerilatedVirtualBase*
VerilatedContext::enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&)) {
if (!m_executionProfiler) m_executionProfiler.reset(construct(*this));
return m_executionProfiler.get();
}
//====================================================================== //======================================================================
// VerilatedContextImp:: Methods - command line // VerilatedContextImp:: Methods - command line
@ -2850,6 +2908,12 @@ void VerilatedImp::versionDump() VL_MT_SAFE {
VL_PRINTF_MT(" Version: %s %s\n", Verilated::productName(), Verilated::productVersion()); VL_PRINTF_MT(" Version: %s %s\n", Verilated::productName(), Verilated::productVersion());
} }
//===========================================================================
// VerilatedModel:: Methods
VerilatedModel::VerilatedModel(VerilatedContext& context)
: m_context{context} {}
//=========================================================================== //===========================================================================
// VerilatedModule:: Methods // VerilatedModule:: Methods

View File

@ -81,6 +81,7 @@
#endif #endif
// clang-format on // clang-format on
class VerilatedContext;
class VerilatedContextImp; class VerilatedContextImp;
class VerilatedContextImpData; class VerilatedContextImpData;
class VerilatedCovContext; class VerilatedCovContext;
@ -252,6 +253,33 @@ public:
#endif #endif
}; };
//=========================================================================
/// Base class of a Verilator generated (Verilated) model.
///
/// VerilatedModel is a base class of the user facing primary class generated
/// by Verilator.
class VerilatedModel VL_NOT_FINAL {
VL_UNCOPYABLE(VerilatedModel);
VerilatedContext& m_context; // The VerilatedContext this model is instantiated under
protected:
explicit VerilatedModel(VerilatedContext& context);
virtual ~VerilatedModel() = default;
public:
/// Returns the VerilatedContext this model is instantiated under
/// Used to get to e.g. simulation time via contextp()->time()
inline VerilatedContext* contextp() const { return &m_context; }
/// Returns the hierarchical name of this module instance.
virtual const char* hierName() const = 0;
/// Returns the name of this model (the name of the generated model class).
virtual const char* modelName() const = 0;
/// Returns the thread level parallelism, this model was Verilated with. Always 1 or higher.
virtual unsigned threads() const = 0;
};
//========================================================================= //=========================================================================
/// Base class for all Verilated module classes. /// Base class for all Verilated module classes.
@ -266,10 +294,6 @@ public:
const char* name() const { return m_namep; } ///< Return name of module const char* name() const { return m_namep; } ///< Return name of module
}; };
/// Declare a module, ala SC_MODULE
#define VL_MODULE(modname) class modname VL_NOT_FINAL : public VerilatedModule
// Not class final in VL_MODULE, as users might be abstracting our models (--hierarchical)
//========================================================================= //=========================================================================
// Functions overridable by user defines // Functions overridable by user defines
// (Internals however must use VL_PRINTF_MT, which calls these.) // (Internals however must use VL_PRINTF_MT, which calls these.)
@ -362,6 +386,16 @@ protected:
// Implementation details // Implementation details
const std::unique_ptr<VerilatedContextImpData> m_impdatap; const std::unique_ptr<VerilatedContextImpData> m_impdatap;
// Number of threads to use for simulation (size of m_threadPool + 1 for main thread)
#ifdef VL_THREADED
unsigned m_threads = std::thread::hardware_concurrency();
#else
const unsigned m_threads = 1;
#endif
// The thread pool shared by all models added to this context
std::unique_ptr<VerilatedVirtualBase> m_threadPool;
// The execution profiler shared by all models added to this context
std::unique_ptr<VerilatedVirtualBase> m_executionProfiler;
// Coverage access // Coverage access
std::unique_ptr<VerilatedVirtualBase> m_coveragep; // Pointer for coveragep() std::unique_ptr<VerilatedVirtualBase> m_coveragep; // Pointer for coveragep()
@ -495,6 +529,12 @@ public:
/// Get time precision as IEEE-standard text /// Get time precision as IEEE-standard text
const char* timeprecisionString() const VL_MT_SAFE; const char* timeprecisionString() const VL_MT_SAFE;
/// Get number of threads used for simulation (including the main thread)
unsigned threads() const { return m_threads; }
/// Set number of threads used for simulation (including the main thread)
/// Can only be called before the thread pool is created (before first model is added).
void threads(unsigned n);
/// Allow traces to at some point be enabled (disables some optimizations) /// Allow traces to at some point be enabled (disables some optimizations)
void traceEverOn(bool flag) VL_MT_SAFE { void traceEverOn(bool flag) VL_MT_SAFE {
if (flag) calcUnusedSigs(true); if (flag) calcUnusedSigs(true);
@ -517,6 +557,12 @@ public: // But for internal use only
return reinterpret_cast<const VerilatedContextImp*>(this); return reinterpret_cast<const VerilatedContextImp*>(this);
} }
void addModel(VerilatedModel*);
VerilatedVirtualBase* threadPoolp();
VerilatedVirtualBase*
enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&));
// Internal: $dumpfile // Internal: $dumpfile
void dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex); void dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
std::string dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex); std::string dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);

View File

@ -146,7 +146,7 @@ extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp);
extern IData VL_SYSTEM_IQ(QData lhs); extern IData VL_SYSTEM_IQ(QData lhs);
inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); } inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); }
extern IData VL_TESTPLUSARGS_I(const char* formatp); extern IData VL_TESTPLUSARGS_I(const std::string& format);
extern const char* vl_mc_scan_plusargs(const char* prefixp); // PLIish extern const char* vl_mc_scan_plusargs(const char* prefixp); // PLIish
//========================================================================= //=========================================================================

View File

@ -66,41 +66,66 @@ template <size_t N> static size_t roundUptoMultipleOf(size_t value) {
return (value + mask) & ~mask; return (value + mask) & ~mask;
} }
VlExecutionProfiler::VlExecutionProfiler() { VlExecutionProfiler::VlExecutionProfiler(VerilatedContext& context)
: m_context{context} {
// Setup profiling on main thread // Setup profiling on main thread
setupThread(0); setupThread(0);
} }
void VlExecutionProfiler::configure(const VerilatedContext& context) { void VlExecutionProfiler::configure() {
if (VL_UNLIKELY(m_enabled)) { if (VL_UNLIKELY(m_enabled)) {
--m_windowCount; --m_windowCount;
if (VL_UNLIKELY(m_windowCount == context.profExecWindow())) { if (VL_UNLIKELY(m_windowCount == m_context.profExecWindow())) {
VL_DEBUG_IF(VL_DBG_MSGF("+ profile start collection\n");); VL_DEBUG_IF(VL_DBG_MSGF("+ profile start collection\n"););
clear(); // Clear the profile after the cache warm-up cycles. clear(); // Clear the profile after the cache warm-up cycles.
m_tickBegin = VL_CPU_TICK(); m_tickBegin = VL_CPU_TICK();
} else if (VL_UNLIKELY(m_windowCount == 0)) { } else if (VL_UNLIKELY(m_windowCount == 0)) {
const uint64_t tickEnd = VL_CPU_TICK(); const uint64_t tickEnd = VL_CPU_TICK();
VL_DEBUG_IF(VL_DBG_MSGF("+ profile end\n");); VL_DEBUG_IF(VL_DBG_MSGF("+ profile end\n"););
const std::string& fileName = context.profExecFilename(); const std::string& fileName = m_context.profExecFilename();
dump(fileName.c_str(), tickEnd); dump(fileName.c_str(), tickEnd);
m_enabled = false; m_enabled = false;
} }
return; return;
} }
const uint64_t startReq = context.profExecStart() + 1; // + 1, so we can start at time 0 const uint64_t startReq = m_context.profExecStart() + 1; // + 1, so we can start at time 0
if (VL_UNLIKELY(m_lastStartReq < startReq && VL_TIME_Q() >= context.profExecStart())) { if (VL_UNLIKELY(m_lastStartReq < startReq && VL_TIME_Q() >= m_context.profExecStart())) {
VL_DEBUG_IF(VL_DBG_MSGF("+ profile start warmup\n");); VL_DEBUG_IF(VL_DBG_MSGF("+ profile start warmup\n"););
VL_DEBUG_IF(assert(m_windowCount == 0);); VL_DEBUG_IF(assert(m_windowCount == 0););
m_enabled = true; m_enabled = true;
m_windowCount = context.profExecWindow() * 2; m_windowCount = m_context.profExecWindow() * 2;
m_lastStartReq = startReq; m_lastStartReq = startReq;
} }
} }
void VlExecutionProfiler::startWorkerSetup(VlExecutionProfiler* profilep, uint32_t threadId) { VerilatedVirtualBase* VlExecutionProfiler::construct(VerilatedContext& context) {
profilep->setupThread(threadId); VlExecutionProfiler* const selfp = new VlExecutionProfiler{context};
#if VL_THREADED
if (VlThreadPool* const threadPoolp = static_cast<VlThreadPool*>(context.threadPoolp())) {
for (int i = 0; i < threadPoolp->numThreads(); ++i) {
// Data to pass to worker thread initialization
struct Data {
VlExecutionProfiler* const selfp;
const uint32_t threadId;
} data{selfp, static_cast<uint32_t>(i + 1)};
// Initialize worker thread
threadPoolp->workerp(i)->addTask(
[](void* userp, bool) {
Data* const datap = static_cast<Data*>(userp);
datap->selfp->setupThread(datap->threadId);
},
&data);
// Wait until initializationis complete
threadPoolp->workerp(i)->wait();
}
}
#endif
return selfp;
} }
void VlExecutionProfiler::setupThread(uint32_t threadId) { void VlExecutionProfiler::setupThread(uint32_t threadId) {
@ -108,10 +133,13 @@ void VlExecutionProfiler::setupThread(uint32_t threadId) {
// while profiling. // while profiling.
t_trace.reserve(RESERVED_TRACE_CAPACITY); t_trace.reserve(RESERVED_TRACE_CAPACITY);
// Register thread-local buffer in list of all buffers // Register thread-local buffer in list of all buffers
bool exists;
{ {
const VerilatedLockGuard lock{m_mutex}; const VerilatedLockGuard lock{m_mutex};
bool exists = !m_traceps.emplace(threadId, &t_trace).second; exists = !m_traceps.emplace(threadId, &t_trace).second;
assert(!exists); }
if (VL_UNLIKELY(exists)) {
VL_FATAL_MT(__FILE__, __LINE__, "", "multiple initialization of profiler on some thread");
} }
} }

View File

@ -33,13 +33,14 @@
#include <vector> #include <vector>
class VlExecutionProfiler; class VlExecutionProfiler;
class VlThreadPool;
//============================================================================= //=============================================================================
// Macros to simplify generated code // Macros to simplify generated code
#define VL_EXEC_TRACE_ADD_RECORD(vlSymsp) \ #define VL_EXEC_TRACE_ADD_RECORD(vlSymsp) \
if (VL_UNLIKELY((vlSymsp)->__Vm_executionProfiler.enabled())) \ if (VL_UNLIKELY((vlSymsp)->__Vm_executionProfilerp->enabled())) \
(vlSymsp)->__Vm_executionProfiler.addRecord() (vlSymsp)->__Vm_executionProfilerp->addRecord()
//============================================================================= //=============================================================================
// Return high-precision counter for profiling, or 0x0 if not available // Return high-precision counter for profiling, or 0x0 if not available
@ -131,7 +132,7 @@ static_assert(std::is_trivially_destructible<VlExecutionRecord>::value,
//============================================================================= //=============================================================================
// VlExecutionProfiler is for collecting profiling data about model execution // VlExecutionProfiler is for collecting profiling data about model execution
class VlExecutionProfiler final { class VlExecutionProfiler final : public VerilatedVirtualBase {
// CONSTANTS // CONSTANTS
// In order to try to avoid dynamic memory allocations during the actual profiling phase, // In order to try to avoid dynamic memory allocations during the actual profiling phase,
@ -149,6 +150,7 @@ class VlExecutionProfiler final {
using ExecutionTrace = std::vector<VlExecutionRecord>; using ExecutionTrace = std::vector<VlExecutionRecord>;
// STATE // STATE
VerilatedContext& m_context; // The context this profiler is under
static VL_THREAD_LOCAL ExecutionTrace t_trace; // thread-local trace buffers static VL_THREAD_LOCAL ExecutionTrace t_trace; // thread-local trace buffers
mutable VerilatedMutex m_mutex; mutable VerilatedMutex m_mutex;
// Map from thread id to &t_trace of given thread // Map from thread id to &t_trace of given thread
@ -162,7 +164,8 @@ class VlExecutionProfiler final {
public: public:
// CONSTRUCTOR // CONSTRUCTOR
VlExecutionProfiler(); explicit VlExecutionProfiler(VerilatedContext& context);
virtual ~VlExecutionProfiler() = default;
// METHODS // METHODS
@ -174,7 +177,7 @@ public:
return t_trace.back(); return t_trace.back();
} }
// Configure profiler (called in beginning of 'eval') // Configure profiler (called in beginning of 'eval')
void configure(const VerilatedContext&); void configure();
// Setup profiling on a particular thread; // Setup profiling on a particular thread;
void setupThread(uint32_t threadId); void setupThread(uint32_t threadId);
// Clear all profiling data // Clear all profiling data
@ -182,8 +185,8 @@ public:
// Write profiling data into file // Write profiling data into file
void dump(const char* filenamep, uint64_t tickEnd) VL_MT_SAFE_EXCLUDES(m_mutex); void dump(const char* filenamep, uint64_t tickEnd) VL_MT_SAFE_EXCLUDES(m_mutex);
// Called via VlStartWorkerCb in VlWorkerThread::startWorker // Passed to VerilatedContext to create the VlExecutionProfiler profiler instance
static void startWorkerSetup(VlExecutionProfiler* profilep, uint32_t threadId); static VerilatedVirtualBase* construct(VerilatedContext& context);
}; };
//============================================================================= //=============================================================================

View File

@ -47,11 +47,9 @@ VlMTaskVertex::VlMTaskVertex(uint32_t upstreamDepCount)
//============================================================================= //=============================================================================
// VlWorkerThread // VlWorkerThread
VlWorkerThread::VlWorkerThread(uint32_t threadId, VerilatedContext* contextp, VlWorkerThread::VlWorkerThread(VerilatedContext* contextp)
VlExecutionProfiler* profilerp, VlStartWorkerCb startCb)
: m_ready_size{0} : m_ready_size{0}
, m_cthread{startWorker, this, threadId, profilerp, startCb} , m_cthread{startWorker, this, contextp} {}
, m_contextp{contextp} {}
VlWorkerThread::~VlWorkerThread() { VlWorkerThread::~VlWorkerThread() {
shutdown(); shutdown();
@ -59,47 +57,49 @@ VlWorkerThread::~VlWorkerThread() {
m_cthread.join(); m_cthread.join();
} }
void VlWorkerThread::shutdownTask(void*, bool) { static void shutdownTask(void*, bool) {
// Deliberately empty, we use the address of this function as a magic number // Deliberately empty, we use the address of this function as a magic number
} }
void VlWorkerThread::shutdown() { addTask(shutdownTask, nullptr); }
void VlWorkerThread::wait() {
// Enqueue a task that sets this flag. Execution is in-order so this ensures completion.
std::atomic<bool> flag{false};
addTask([](void* flagp, bool) { static_cast<std::atomic<bool>*>(flagp)->store(true); }, &flag);
// Spin wait
for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) {
if (flag.load()) return;
VL_CPU_RELAX();
}
// Yield wait
while (!flag.load()) std::this_thread::yield();
}
void VlWorkerThread::workerLoop() { void VlWorkerThread::workerLoop() {
ExecRec work; ExecRec work;
// Wait for the first task without spinning, in case the thread is never actually used.
dequeWork</* SpinWait: */ false>(&work);
while (true) { while (true) {
dequeWork(&work);
if (VL_UNLIKELY(work.m_fnp == shutdownTask)) break; if (VL_UNLIKELY(work.m_fnp == shutdownTask)) break;
work.m_fnp(work.m_selfp, work.m_evenCycle); work.m_fnp(work.m_selfp, work.m_evenCycle);
// Wait for next task with spinning.
dequeWork</* SpinWait: */ true>(&work);
} }
} }
void VlWorkerThread::startWorker(VlWorkerThread* workerp, uint32_t threadId, void VlWorkerThread::startWorker(VlWorkerThread* workerp, VerilatedContext* contextp) {
VlExecutionProfiler* profilerp, VlStartWorkerCb startCb) { Verilated::threadContextp(contextp);
Verilated::threadContextp(workerp->m_contextp);
if (VL_UNLIKELY(startCb)) startCb(profilerp, threadId);
workerp->workerLoop(); workerp->workerLoop();
} }
//============================================================================= //=============================================================================
// VlThreadPool // VlThreadPool
VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, VlThreadPool::VlThreadPool(VerilatedContext* contextp, unsigned nThreads) {
VlExecutionProfiler* profilerp, VlStartWorkerCb startCb) { for (unsigned i = 0; i < nThreads; ++i) m_workers.push_back(new VlWorkerThread{contextp});
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
++nThreads;
const unsigned cpus = std::thread::hardware_concurrency();
if (cpus < nThreads) {
static int warnedOnce = 0;
if (!warnedOnce++) {
VL_PRINTF_MT("%%Warning: System has %u CPUs but model Verilated with"
" --threads %d; may run slow.\n",
cpus, nThreads);
}
}
// Create worker threads
for (uint32_t threadId = 1; threadId < nThreads; ++threadId) {
m_workers.push_back(new VlWorkerThread{threadId, contextp, profilerp, startCb});
}
} }
VlThreadPool::~VlThreadPool() { VlThreadPool::~VlThreadPool() {

View File

@ -60,9 +60,6 @@ using VlSelfP = void*;
using VlExecFnp = void (*)(VlSelfP, bool); using VlExecFnp = void (*)(VlSelfP, bool);
// VlWorkerThread::startWorker callback, used to hook in VlExecutionProfiler
using VlStartWorkerCb = void (*)(VlExecutionProfiler*, uint32_t threadId);
// Track dependencies for a single MTask. // Track dependencies for a single MTask.
class VlMTaskVertex final { class VlMTaskVertex final {
// MEMBERS // MEMBERS
@ -166,24 +163,23 @@ private:
std::atomic<size_t> m_ready_size; std::atomic<size_t> m_ready_size;
std::thread m_cthread; // Underlying C++ thread record std::thread m_cthread; // Underlying C++ thread record
VerilatedContext* const m_contextp; // Context for spawned thread
VL_UNCOPYABLE(VlWorkerThread); VL_UNCOPYABLE(VlWorkerThread);
public: public:
// CONSTRUCTORS // CONSTRUCTORS
explicit VlWorkerThread(uint32_t threadId, VerilatedContext* contextp, explicit VlWorkerThread(VerilatedContext* contextp);
VlExecutionProfiler* profilerp, VlStartWorkerCb startCb);
~VlWorkerThread(); ~VlWorkerThread();
// METHODS // METHODS
template <bool SpinWait> //
inline void dequeWork(ExecRec* workp) VL_MT_SAFE_EXCLUDES(m_mutex) { inline void dequeWork(ExecRec* workp) VL_MT_SAFE_EXCLUDES(m_mutex) {
// Spin for a while, waiting for new data // Spin for a while, waiting for new data
for (int i = 0; i < VL_LOCK_SPINS; ++i) { if VL_CONSTEXPR_CXX17 (SpinWait) {
if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) { // for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) {
break; if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) break;
VL_CPU_RELAX();
} }
VL_CPU_RELAX();
} }
VerilatedLockGuard lock{m_mutex}; VerilatedLockGuard lock{m_mutex};
while (m_ready.empty()) { while (m_ready.empty()) {
@ -197,7 +193,7 @@ public:
m_ready.erase(m_ready.begin()); m_ready.erase(m_ready.begin());
m_ready_size.fetch_sub(1, std::memory_order_relaxed); m_ready_size.fetch_sub(1, std::memory_order_relaxed);
} }
inline void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle) inline void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle = false)
VL_MT_SAFE_EXCLUDES(m_mutex) { VL_MT_SAFE_EXCLUDES(m_mutex) {
bool notify; bool notify;
{ {
@ -209,15 +205,14 @@ public:
if (notify) m_cv.notify_one(); if (notify) m_cv.notify_one();
} }
inline void shutdown() { addTask(shutdownTask, nullptr, false); } void shutdown(); // Finish current tasks, then terminate thread
static void shutdownTask(void*, bool); void wait(); // Blocks calling thread until all tasks complete in this thread
void workerLoop(); void workerLoop();
static void startWorker(VlWorkerThread* workerp, uint32_t threadId, static void startWorker(VlWorkerThread* workerp, VerilatedContext* contextp);
VlExecutionProfiler* profilerp, VlStartWorkerCb startCb);
}; };
class VlThreadPool final { class VlThreadPool final : public VerilatedVirtualBase {
// MEMBERS // MEMBERS
std::vector<VlWorkerThread*> m_workers; // our workers std::vector<VlWorkerThread*> m_workers; // our workers
@ -226,9 +221,8 @@ public:
// Construct a thread pool with 'nThreads' dedicated threads. The thread // Construct a thread pool with 'nThreads' dedicated threads. The thread
// pool will create these threads and make them available to execute tasks // pool will create these threads and make them available to execute tasks
// via this->workerp(index)->addTask(...) // via this->workerp(index)->addTask(...)
VlThreadPool(VerilatedContext* contextp, int nThreads, VlExecutionProfiler* profilerp, VlThreadPool(VerilatedContext* contextp, unsigned nThreads);
VlStartWorkerCb startCb); virtual ~VlThreadPool();
~VlThreadPool();
// METHODS // METHODS
inline int numThreads() const { return m_workers.size(); } inline int numThreads() const { return m_workers.size(); }

View File

@ -194,8 +194,6 @@ private:
static void parallelWorkerTask(void*, bool); static void parallelWorkerTask(void*, bool);
#endif #endif
using ParallelCallbackMap = std::unordered_map<VlThreadPool*, std::vector<CallbackRecord>>;
protected: protected:
uint32_t* m_sigs_oldvalp = nullptr; // Previous value store uint32_t* m_sigs_oldvalp = nullptr; // Previous value store
EData* m_sigs_enabledp = nullptr; // Bit vector of enabled codes (nullptr = all on) EData* m_sigs_enabledp = nullptr; // Bit vector of enabled codes (nullptr = all on)
@ -203,10 +201,10 @@ private:
uint64_t m_timeLastDump = 0; // Last time we did a dump uint64_t m_timeLastDump = 0; // Last time we did a dump
std::vector<bool> m_sigs_enabledVec; // Staging for m_sigs_enabledp std::vector<bool> m_sigs_enabledVec; // Staging for m_sigs_enabledp
std::vector<CallbackRecord> m_initCbs; // Routines to initialize tracing std::vector<CallbackRecord> m_initCbs; // Routines to initialize tracing
ParallelCallbackMap m_fullCbs; // Routines to perform full dump std::vector<CallbackRecord> m_fullCbs; // Routines to perform full dump
ParallelCallbackMap m_chgCbs; // Routines to perform incremental dump std::vector<CallbackRecord> m_chgCbs; // Routines to perform incremental dump
std::vector<CallbackRecord> m_cleanupCbs; // Routines to call at the end of dump std::vector<CallbackRecord> m_cleanupCbs; // Routines to call at the end of dump
std::vector<VlThreadPool*> m_threadPoolps; // All thread pools, in insertion order VerilatedContext* m_contextp = nullptr; // The context used by the traced models
bool m_fullDump = true; // Whether a full dump is required on the next call to 'dump' bool m_fullDump = true; // Whether a full dump is required on the next call to 'dump'
uint32_t m_nextCode = 0; // Next code number to assign uint32_t m_nextCode = 0; // Next code number to assign
uint32_t m_numSignals = 0; // Number of distinct signals uint32_t m_numSignals = 0; // Number of distinct signals
@ -217,16 +215,16 @@ private:
double m_timeRes = 1e-9; // Time resolution (ns/ms etc) double m_timeRes = 1e-9; // Time resolution (ns/ms etc)
double m_timeUnit = 1e-0; // Time units (ns/ms etc) double m_timeUnit = 1e-0; // Time units (ns/ms etc)
void addThreadPool(VlThreadPool* threadPoolp) VL_MT_SAFE_EXCLUDES(m_mutex); void addContext(VerilatedContext*) VL_MT_SAFE_EXCLUDES(m_mutex);
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord& cbRec) void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord&& cbRec)
VL_MT_SAFE_EXCLUDES(m_mutex); VL_MT_SAFE_EXCLUDES(m_mutex);
// Equivalent to 'this' but is of the sub-type 'T_Trace*'. Use 'self()->' // Equivalent to 'this' but is of the sub-type 'T_Trace*'. Use 'self()->'
// to access duck-typed functions to avoid a virtual function call. // to access duck-typed functions to avoid a virtual function call.
T_Trace* self() { return static_cast<T_Trace*>(this); } T_Trace* self() { return static_cast<T_Trace*>(this); }
void runParallelCallbacks(const ParallelCallbackMap& cbMap); void runCallbacks(const std::vector<CallbackRecord>& cbVec);
// Flush any remaining data for this file // Flush any remaining data for this file
static void onFlush(void* selfp) VL_MT_UNSAFE_ONE; static void onFlush(void* selfp) VL_MT_UNSAFE_ONE;
@ -341,10 +339,10 @@ public:
//========================================================================= //=========================================================================
// Non-hot path internal interface to Verilator generated code // Non-hot path internal interface to Verilator generated code
void addInitCb(initCb_t cb, void* userp) VL_MT_SAFE; void addInitCb(initCb_t cb, void* userp, VerilatedContext*) VL_MT_SAFE;
void addFullCb(dumpCb_t cb, void* userp, VlThreadPool* = nullptr) VL_MT_SAFE; void addFullCb(dumpCb_t cb, void* userp, VerilatedContext*) VL_MT_SAFE;
void addChgCb(dumpCb_t cb, void* userp, VlThreadPool* = nullptr) VL_MT_SAFE; void addChgCb(dumpCb_t cb, void* userp, VerilatedContext*) VL_MT_SAFE;
void addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE; void addCleanupCb(cleanupCb_t cb, void* userp, VerilatedContext*) VL_MT_SAFE;
void scopeEscape(char flag) { m_scopeEscape = flag; } void scopeEscape(char flag) { m_scopeEscape = flag; }

View File

@ -478,55 +478,52 @@ template <> VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWo
#endif #endif
template <> template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runParallelCallbacks(const ParallelCallbackMap& cbMap) { void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runCallbacks(const std::vector<CallbackRecord>& cbVec) {
for (VlThreadPool* threadPoolp : m_threadPoolps) {
#ifdef VL_TRACE_PARALLEL #ifdef VL_TRACE_PARALLEL
// If tracing in parallel, dispatch to the thread pool (if exists) // If tracing in parallel, dispatch to the thread pool
if (threadPoolp && threadPoolp->numThreads()) { VlThreadPool* threadPoolp = static_cast<VlThreadPool*>(m_contextp->threadPoolp());
// List of work items for thread (std::list, as ParallelWorkerData is not movable) // List of work items for thread (std::list, as ParallelWorkerData is not movable)
std::list<ParallelWorkerData> workerData; std::list<ParallelWorkerData> workerData;
// We use the whole pool + the main thread // We use the whole pool + the main thread
const unsigned threads = threadPoolp->numThreads() + 1; const unsigned threads = threadPoolp->numThreads() + 1;
// Main thread executes all jobs with index % threads == 0 // Main thread executes all jobs with index % threads == 0
std::vector<ParallelWorkerData*> mainThreadWorkerData; std::vector<ParallelWorkerData*> mainThreadWorkerData;
// The tracing callbacks to execute on this thread-pool // Enuque all the jobs
const auto& cbVec = cbMap.at(threadPoolp); for (unsigned i = 0; i < cbVec.size(); ++i) {
// Enuque all the jobs const CallbackRecord& cbr = cbVec[i];
for (unsigned i = 0; i < cbVec.size(); ++i) { // Always get the trace buffer on the main thread
const CallbackRecord& cbr = cbVec[i]; Buffer* const bufp = getTraceBuffer();
// Always get the trace buffer on the main thread // Create new work item
Buffer* const bufp = getTraceBuffer(); workerData.emplace_back(cbr.m_dumpCb, cbr.m_userp, bufp);
// Create new work item // Grab the new work item
workerData.emplace_back(cbr.m_dumpCb, cbr.m_userp, bufp); ParallelWorkerData* const itemp = &workerData.back();
// Grab the new work item // Enqueue task to thread pool, or main thread
ParallelWorkerData* const itemp = &workerData.back(); if (unsigned rem = i % threads) {
// Enqueue task to thread pool, or main thread threadPoolp->workerp(rem - 1)->addTask(parallelWorkerTask, itemp);
if (unsigned rem = i % threads) { } else {
threadPoolp->workerp(rem - 1)->addTask(parallelWorkerTask, itemp, false); mainThreadWorkerData.push_back(itemp);
} else {
mainThreadWorkerData.push_back(itemp);
}
}
// Execute main thead jobs
for (ParallelWorkerData* const itemp : mainThreadWorkerData) {
parallelWorkerTask(itemp, false);
}
// Commit all trace buffers in order
for (ParallelWorkerData& item : workerData) {
// Wait until ready
item.wait();
// Commit the buffer
commitTraceBuffer(item.m_bufp);
}
continue;
} }
}
// Execute main thead jobs
for (ParallelWorkerData* const itemp : mainThreadWorkerData) {
parallelWorkerTask(itemp, false);
}
// Commit all trace buffers in order
for (ParallelWorkerData& item : workerData) {
// Wait until ready
item.wait();
// Commit the buffer
commitTraceBuffer(item.m_bufp);
}
// Done
return;
#endif #endif
// Fall back on sequential execution // Fall back on sequential execution
for (const CallbackRecord& cbr : cbMap.at(threadPoolp)) { for (const CallbackRecord& cbr : cbVec) {
Buffer* const traceBufferp = getTraceBuffer(); Buffer* const traceBufferp = getTraceBuffer();
cbr.m_dumpCb(cbr.m_userp, traceBufferp); cbr.m_dumpCb(cbr.m_userp, traceBufferp);
commitTraceBuffer(traceBufferp); commitTraceBuffer(traceBufferp);
}
} }
} }
@ -579,9 +576,9 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
// Run the callbacks // Run the callbacks
if (VL_UNLIKELY(m_fullDump)) { if (VL_UNLIKELY(m_fullDump)) {
m_fullDump = false; // No more need for next dump to be full m_fullDump = false; // No more need for next dump to be full
runParallelCallbacks(m_fullCbs); runCallbacks(m_fullCbs);
} else { } else {
runParallelCallbacks(m_chgCbs); runCallbacks(m_chgCbs);
} }
for (uint32_t i = 0; i < m_cleanupCbs.size(); ++i) { for (uint32_t i = 0; i < m_cleanupCbs.size(); ++i) {
@ -607,18 +604,20 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
// Non-hot path internal interface to Verilator generated code // Non-hot path internal interface to Verilator generated code
template <> template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addThreadPool(VlThreadPool* threadPoolp) void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addContext(VerilatedContext* contextp)
VL_MT_SAFE_EXCLUDES(m_mutex) { VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex}; const VerilatedLockGuard lock{m_mutex};
for (VlThreadPool* const poolp : m_threadPoolps) { if (m_contextp && contextp != m_contextp) {
if (poolp == threadPoolp) return; VL_FATAL_MT(
__FILE__, __LINE__, "",
"A trace file instance can only handle models from the same simulation context");
} }
m_threadPoolps.push_back(threadPoolp); m_contextp = contextp;
} }
template <> template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec, void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
CallbackRecord& cbRec) CallbackRecord&& cbRec)
VL_MT_SAFE_EXCLUDES(m_mutex) { VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex}; const VerilatedLockGuard lock{m_mutex};
if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START
@ -630,28 +629,28 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCallbackRecord(std::vector<CallbackR
} }
template <> template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE { void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addInitCb(initCb_t cb, void* userp,
CallbackRecord cbr{cb, userp}; VerilatedContext* contextp) VL_MT_SAFE {
addCallbackRecord(m_initCbs, cbr); addContext(contextp);
addCallbackRecord(m_initCbs, CallbackRecord{cb, userp});
} }
template <> template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpCb_t cb, void* userp, void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpCb_t cb, void* userp,
VlThreadPool* threadPoolp) VL_MT_SAFE { VerilatedContext* contextp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp}; addContext(contextp);
addThreadPool(threadPoolp); addCallbackRecord(m_fullCbs, CallbackRecord{cb, userp});
addCallbackRecord(m_fullCbs[threadPoolp], cbr);
} }
template <> template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpCb_t cb, void* userp, void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpCb_t cb, void* userp,
VlThreadPool* threadPoolp) VL_MT_SAFE { VerilatedContext* contextp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp}; addContext(contextp);
addThreadPool(threadPoolp); addCallbackRecord(m_chgCbs, CallbackRecord{cb, userp});
addCallbackRecord(m_chgCbs[threadPoolp], cbr);
} }
template <> template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE { void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCleanupCb(cleanupCb_t cb, void* userp,
CallbackRecord cbr{cb, userp}; VerilatedContext* contextp) VL_MT_SAFE {
addCallbackRecord(m_cleanupCbs, cbr); addContext(contextp);
addCallbackRecord(m_cleanupCbs, CallbackRecord{cb, userp});
} }
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::pushNamePrefix(const std::string& prefix) { template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::pushNamePrefix(const std::string& prefix) {

View File

@ -234,6 +234,16 @@
# error "Verilator requires a C++11 or newer compiler" # error "Verilator requires a C++11 or newer compiler"
#endif #endif
//=========================================================================
// C++-2017
#if __cplusplus >= 201703L
# define VL_CONSTEXPR_CXX17 constexpr
#else
# define VL_CONSTEXPR_CXX17
#endif
//========================================================================= //=========================================================================
// Optimization // Optimization

View File

@ -1961,7 +1961,7 @@ private:
ASTNODE_PREFETCH(nodep->op2p()); ASTNODE_PREFETCH(nodep->op2p());
ASTNODE_PREFETCH(nodep->op3p()); ASTNODE_PREFETCH(nodep->op3p());
ASTNODE_PREFETCH(nodep->op4p()); ASTNODE_PREFETCH(nodep->op4p());
if /* TODO: 'constexpr' in C++17 */ (VisitNext) ASTNODE_PREFETCH(nodep->nextp()); if VL_CONSTEXPR_CXX17 (VisitNext) ASTNODE_PREFETCH(nodep->nextp());
// Apply function in pre-order // Apply function in pre-order
if (privateTypeTest<typename std::remove_const<T_Arg>::type>(nodep)) { if (privateTypeTest<typename std::remove_const<T_Arg>::type>(nodep)) {
@ -1977,7 +1977,7 @@ private:
} }
// Traverse 'nextp()' chain if requested // Traverse 'nextp()' chain if requested
if /* TODO: 'constexpr' in C++17 */ (VisitNext) { if VL_CONSTEXPR_CXX17 (VisitNext) {
nodep = nodep->nextp(); nodep = nodep->nextp();
} else { } else {
break; break;
@ -2000,7 +2000,7 @@ private:
ASTNODE_PREFETCH(nodep->op2p()); ASTNODE_PREFETCH(nodep->op2p());
ASTNODE_PREFETCH(nodep->op3p()); ASTNODE_PREFETCH(nodep->op3p());
ASTNODE_PREFETCH(nodep->op4p()); ASTNODE_PREFETCH(nodep->op4p());
if /* TODO: 'constexpr' in C++17 */ (VisitNext) ASTNODE_PREFETCH(nodep->nextp()); if VL_CONSTEXPR_CXX17 (VisitNext) ASTNODE_PREFETCH(nodep->nextp());
// Apply function in pre-order // Apply function in pre-order
if (privateTypeTest<typename std::remove_const<T_Arg>::type>(nodep)) { if (privateTypeTest<typename std::remove_const<T_Arg>::type>(nodep)) {
@ -2024,7 +2024,7 @@ private:
} }
// Traverse 'nextp()' chain if requested // Traverse 'nextp()' chain if requested
if /* TODO: 'constexpr' in C++17 */ (VisitNext) { if VL_CONSTEXPR_CXX17 (VisitNext) {
nodep = nodep->nextp(); nodep = nodep->nextp();
} else { } else {
break; break;

View File

@ -4568,26 +4568,21 @@ public:
class AstTestPlusArgs final : public AstNodeMath { class AstTestPlusArgs final : public AstNodeMath {
// Parents: expr // Parents: expr
// Child: variable to set. If nullptr then this is a $test$plusargs instead of $value$plusargs // Child: variable to set. If nullptr then this is a $test$plusargs instead of $value$plusargs
private:
string m_text;
public: public:
AstTestPlusArgs(FileLine* fl, const string& text) AstTestPlusArgs(FileLine* fl, AstNode* searchp)
: ASTGEN_SUPER_TestPlusArgs(fl) : ASTGEN_SUPER_TestPlusArgs(fl) {
, m_text{text} {} setOp1p(searchp);
}
ASTNODE_NODE_FUNCS(TestPlusArgs) ASTNODE_NODE_FUNCS(TestPlusArgs)
virtual string name() const override { return m_text; }
virtual string verilogKwd() const override { return "$test$plusargs"; } virtual string verilogKwd() const override { return "$test$plusargs"; }
virtual string emitVerilog() override { return verilogKwd(); } virtual string emitVerilog() override { return verilogKwd(); }
virtual string emitC() override { return "VL_VALUEPLUSARGS_%nq(%lw, %P, nullptr)"; } virtual string emitC() override { return "VL_VALUEPLUSARGS_%nq(%lw, %P, nullptr)"; }
virtual bool isGateOptimizable() const override { return false; } virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; } virtual bool isPredictOptimizable() const override { return false; }
virtual bool cleanOut() const override { return true; } virtual bool cleanOut() const override { return true; }
virtual bool same(const AstNode* samep) const override { virtual bool same(const AstNode* samep) const override { return true; }
return text() == static_cast<const AstTestPlusArgs*>(samep)->text(); AstNode* searchp() const { return op1p(); } // op1 = Search expression
} void searchp(AstNode* nodep) { setOp1p(nodep); }
string text() const { return m_text; } // * = Text to display
void text(const string& text) { m_text = text; }
}; };
class AstGenFor final : public AstNodeFor { class AstGenFor final : public AstNodeFor {

View File

@ -35,17 +35,16 @@ private:
std::vector<T_Data*> m_allocated; std::vector<T_Data*> m_allocated;
inline T_Data* getUserp(const T_Node* nodep) const { inline T_Data* getUserp(const T_Node* nodep) const {
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'. if VL_CONSTEXPR_CXX17 (T_UserN == 1) {
if (T_UserN == 1) {
const VNUser user = nodep->user1u(); const VNUser user = nodep->user1u();
return user.to<T_Data*>(); return user.to<T_Data*>();
} else if (T_UserN == 2) { } else if VL_CONSTEXPR_CXX17 (T_UserN == 2) {
const VNUser user = nodep->user2u(); const VNUser user = nodep->user2u();
return user.to<T_Data*>(); return user.to<T_Data*>();
} else if (T_UserN == 3) { } else if VL_CONSTEXPR_CXX17 (T_UserN == 3) {
const VNUser user = nodep->user3u(); const VNUser user = nodep->user3u();
return user.to<T_Data*>(); return user.to<T_Data*>();
} else if (T_UserN == 4) { } else if VL_CONSTEXPR_CXX17 (T_UserN == 4) {
const VNUser user = nodep->user4u(); const VNUser user = nodep->user4u();
return user.to<T_Data*>(); return user.to<T_Data*>();
} else { } else {
@ -55,14 +54,13 @@ private:
} }
inline void setUserp(T_Node* nodep, T_Data* userp) const { inline void setUserp(T_Node* nodep, T_Data* userp) const {
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'. if VL_CONSTEXPR_CXX17 (T_UserN == 1) {
if (T_UserN == 1) {
nodep->user1u(VNUser(userp)); nodep->user1u(VNUser(userp));
} else if (T_UserN == 2) { } else if VL_CONSTEXPR_CXX17 (T_UserN == 2) {
nodep->user2u(VNUser(userp)); nodep->user2u(VNUser(userp));
} else if (T_UserN == 3) { } else if VL_CONSTEXPR_CXX17 (T_UserN == 3) {
nodep->user3u(VNUser(userp)); nodep->user3u(VNUser(userp));
} else if (T_UserN == 4) { } else if VL_CONSTEXPR_CXX17 (T_UserN == 4) {
nodep->user4u(VNUser(userp)); nodep->user4u(VNUser(userp));
} else { } else {
nodep->user5u(VNUser(userp)); nodep->user5u(VNUser(userp));
@ -71,14 +69,13 @@ private:
protected: protected:
AstUserAllocatorBase() { AstUserAllocatorBase() {
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'. if VL_CONSTEXPR_CXX17 (T_UserN == 1) {
if (T_UserN == 1) {
VNUser1InUse::check(); VNUser1InUse::check();
} else if (T_UserN == 2) { } else if VL_CONSTEXPR_CXX17 (T_UserN == 2) {
VNUser2InUse::check(); VNUser2InUse::check();
} else if (T_UserN == 3) { } else if VL_CONSTEXPR_CXX17 (T_UserN == 3) {
VNUser3InUse::check(); VNUser3InUse::check();
} else if (T_UserN == 4) { } else if VL_CONSTEXPR_CXX17 (T_UserN == 4) {
VNUser4InUse::check(); VNUser4InUse::check();
} else { } else {
VNUser5InUse::check(); VNUser5InUse::check();

View File

@ -80,30 +80,48 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
using ResultTerm = std::tuple<AstNode*, unsigned, bool>; using ResultTerm = std::tuple<AstNode*, unsigned, bool>;
class LeafInfo final { // Leaf node (either AstConst or AstVarRef) class LeafInfo final { // Leaf node (either AstConst or AstVarRef)
// MEMBERS
bool m_polarity = true; bool m_polarity = true;
int m_lsb = 0; int m_lsb = 0; // LSB of actually used bit of m_refp->varp()
int m_msb = 0; // MSB of actually used bit of m_refp->varp()
int m_wordIdx = -1; // -1 means AstWordSel is not used. int m_wordIdx = -1; // -1 means AstWordSel is not used.
AstVarRef* m_refp = nullptr; AstVarRef* m_refp = nullptr;
const AstConst* m_constp = nullptr; const AstConst* m_constp = nullptr;
public: public:
// CONSTRUCTORS
LeafInfo() = default;
LeafInfo(const LeafInfo& other) = default;
explicit LeafInfo(int lsb)
: m_lsb{lsb} {}
// METHODS
void setLeaf(AstVarRef* refp) { void setLeaf(AstVarRef* refp) {
UASSERT(!m_refp && !m_constp, "Must be called just once"); UASSERT(!m_refp && !m_constp, "Must be called just once");
m_refp = refp; m_refp = refp;
m_msb = refp->varp()->widthMin() - 1;
} }
void setLeaf(const AstConst* constp) { void setLeaf(const AstConst* constp) {
UASSERT(!m_refp && !m_constp, "Must be called just once"); UASSERT(!m_refp && !m_constp, "Must be called just once");
m_constp = constp; m_constp = constp;
m_msb = constp->widthMin() - 1;
} }
void updateBitRange(const AstCCast* castp) {
m_msb = std::min(m_msb, m_lsb + castp->width() - 1);
}
void updateBitRange(const AstShiftR* shiftp) {
m_lsb += VN_AS(shiftp->rhsp(), Const)->toUInt();
}
void wordIdx(int i) { m_wordIdx = i; }
void polarity(bool p) { m_polarity = p; }
AstVarRef* refp() const { return m_refp; } AstVarRef* refp() const { return m_refp; }
const AstConst* constp() const { return m_constp; } const AstConst* constp() const { return m_constp; }
int wordIdx() const { return m_wordIdx; } int wordIdx() const { return m_wordIdx; }
bool polarity() const { return m_polarity; } bool polarity() const { return m_polarity; }
int lsb() const { return m_lsb; } int lsb() const { return m_lsb; }
void wordIdx(int i) { m_wordIdx = i; } int msb() const { return std::min(m_msb, varWidth() - 1); }
void lsb(int l) { m_lsb = l; }
void polarity(bool p) { m_polarity = p; }
int varWidth() const { int varWidth() const {
UASSERT(m_refp, "m_refp should be set"); UASSERT(m_refp, "m_refp should be set");
const int width = m_refp->varp()->widthMin(); const int width = m_refp->varp()->widthMin();
@ -382,7 +400,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
// Traverse down to see AstConst or AstVarRef // Traverse down to see AstConst or AstVarRef
LeafInfo findLeaf(AstNode* nodep, bool expectConst) { LeafInfo findLeaf(AstNode* nodep, bool expectConst) {
LeafInfo info; LeafInfo info{m_lsb};
{ {
VL_RESTORER(m_leafp); VL_RESTORER(m_leafp);
m_leafp = &info; m_leafp = &info;
@ -402,7 +420,10 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
virtual void visit(AstNode* nodep) override { virtual void visit(AstNode* nodep) override {
CONST_BITOP_SET_FAILED("Hit unexpected op", nodep); CONST_BITOP_SET_FAILED("Hit unexpected op", nodep);
} }
virtual void visit(AstCCast* nodep) override { iterateChildren(nodep); } virtual void visit(AstCCast* nodep) override {
iterateChildren(nodep);
if (m_leafp) m_leafp->updateBitRange(nodep);
}
virtual void visit(AstShiftR* nodep) override { virtual void visit(AstShiftR* nodep) override {
CONST_BITOP_RETURN_IF(!m_leafp, nodep); CONST_BITOP_RETURN_IF(!m_leafp, nodep);
AstConst* const constp = VN_CAST(nodep->rhsp(), Const); AstConst* const constp = VN_CAST(nodep->rhsp(), Const);
@ -410,12 +431,14 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
m_lsb += constp->toUInt(); m_lsb += constp->toUInt();
incrOps(nodep, __LINE__); incrOps(nodep, __LINE__);
iterate(nodep->lhsp()); iterate(nodep->lhsp());
m_leafp->updateBitRange(nodep);
m_lsb -= constp->toUInt(); m_lsb -= constp->toUInt();
} }
virtual void visit(AstNot* nodep) override { virtual void visit(AstNot* nodep) override {
CONST_BITOP_RETURN_IF(nodep->widthMin() != 1, nodep); CONST_BITOP_RETURN_IF(nodep->widthMin() != 1, nodep);
AstNode* lhsp = nodep->lhsp(); AstNode* lhsp = nodep->lhsp();
if (AstCCast* const castp = VN_CAST(lhsp, CCast)) lhsp = castp->lhsp(); AstCCast* const castp = VN_CAST(lhsp, CCast);
if (castp) lhsp = castp->lhsp();
CONST_BITOP_RETURN_IF(!VN_IS(lhsp, VarRef) && !VN_IS(lhsp, Xor) && !VN_IS(lhsp, RedXor) CONST_BITOP_RETURN_IF(!VN_IS(lhsp, VarRef) && !VN_IS(lhsp, Xor) && !VN_IS(lhsp, RedXor)
&& !VN_IS(lhsp, ShiftR), && !VN_IS(lhsp, ShiftR),
lhsp); lhsp);
@ -424,6 +447,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
iterateChildren(nodep); iterateChildren(nodep);
// Don't restore m_polarity for Xor as it counts parity of the entire tree // Don't restore m_polarity for Xor as it counts parity of the entire tree
if (!isXorTree()) m_polarity = !m_polarity; if (!isXorTree()) m_polarity = !m_polarity;
if (m_leafp && castp) m_leafp->updateBitRange(castp);
} }
virtual void visit(AstWordSel* nodep) override { virtual void visit(AstWordSel* nodep) override {
CONST_BITOP_RETURN_IF(!m_leafp, nodep); CONST_BITOP_RETURN_IF(!m_leafp, nodep);
@ -437,27 +461,27 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
CONST_BITOP_RETURN_IF(!m_leafp, nodep); CONST_BITOP_RETURN_IF(!m_leafp, nodep);
m_leafp->setLeaf(nodep); m_leafp->setLeaf(nodep);
m_leafp->polarity(m_polarity); m_leafp->polarity(m_polarity);
m_leafp->lsb(m_lsb);
} }
virtual void visit(AstConst* nodep) override { virtual void visit(AstConst* nodep) override {
CONST_BITOP_RETURN_IF(!m_leafp, nodep); CONST_BITOP_RETURN_IF(!m_leafp, nodep);
m_leafp->setLeaf(nodep); m_leafp->setLeaf(nodep);
m_leafp->lsb(m_lsb);
} }
virtual void visit(AstRedXor* nodep) override { virtual void visit(AstRedXor* nodep) override {
Restorer restorer{*this}; Restorer restorer{*this};
CONST_BITOP_RETURN_IF(!VN_IS(m_rootp, Xor), nodep); CONST_BITOP_RETURN_IF(!VN_IS(m_rootp, Xor), nodep);
AstNode* lhsp = nodep->lhsp(); AstNode* lhsp = nodep->lhsp();
if (const AstCCast* const castp = VN_CAST(lhsp, CCast)) lhsp = castp->lhsp(); const AstCCast* const castp = VN_CAST(lhsp, CCast);
if (castp) lhsp = castp->lhsp();
if (const AstAnd* const andp = VN_CAST(lhsp, And)) { // '^(mask & leaf)' if (const AstAnd* const andp = VN_CAST(lhsp, And)) { // '^(mask & leaf)'
CONST_BITOP_RETURN_IF(!andp, lhsp); CONST_BITOP_RETURN_IF(!andp, lhsp);
const LeafInfo& mask = findLeaf(andp->lhsp(), true); const LeafInfo& mask = findLeaf(andp->lhsp(), true);
CONST_BITOP_RETURN_IF(!mask.constp() || mask.lsb() != 0, andp->lhsp()); CONST_BITOP_RETURN_IF(!mask.constp() || mask.lsb() != 0, andp->lhsp());
const LeafInfo& ref = findLeaf(andp->rhsp(), false); LeafInfo ref = findLeaf(andp->rhsp(), false);
CONST_BITOP_RETURN_IF(!ref.refp(), andp->rhsp()); CONST_BITOP_RETURN_IF(!ref.refp(), andp->rhsp());
if (castp) ref.updateBitRange(castp);
restorer.disableRestore(); // Now all subtree succeeded restorer.disableRestore(); // Now all subtree succeeded
@ -467,7 +491,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
incrOps(andp, __LINE__); incrOps(andp, __LINE__);
// Mark all bits checked in this reduction // Mark all bits checked in this reduction
const int maxBitIdx = std::min(ref.lsb() + maskNum.width(), ref.varWidth()); const int maxBitIdx = std::min(ref.lsb() + maskNum.width(), ref.msb() + 1);
for (int bitIdx = ref.lsb(); bitIdx < maxBitIdx; ++bitIdx) { for (int bitIdx = ref.lsb(); bitIdx < maxBitIdx; ++bitIdx) {
const int maskIdx = bitIdx - ref.lsb(); const int maskIdx = bitIdx - ref.lsb();
if (maskNum.bitIs0(maskIdx)) continue; if (maskNum.bitIs0(maskIdx)) continue;
@ -475,15 +499,16 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
m_bitPolarities.emplace_back(ref, true, bitIdx); m_bitPolarities.emplace_back(ref, true, bitIdx);
} }
} else { // '^leaf' } else { // '^leaf'
const LeafInfo& ref = findLeaf(lhsp, false); LeafInfo ref = findLeaf(lhsp, false);
CONST_BITOP_RETURN_IF(!ref.refp(), lhsp); CONST_BITOP_RETURN_IF(!ref.refp(), lhsp);
if (castp) ref.updateBitRange(castp);
restorer.disableRestore(); // Now all checks passed restorer.disableRestore(); // Now all checks passed
incrOps(nodep, __LINE__); incrOps(nodep, __LINE__);
// Mark all bits checked by this comparison // Mark all bits checked by this comparison
for (int bitIdx = ref.lsb(); bitIdx < ref.varWidth(); ++bitIdx) { for (int bitIdx = ref.lsb(); bitIdx <= ref.msb(); ++bitIdx) {
m_bitPolarities.emplace_back(ref, true, bitIdx); m_bitPolarities.emplace_back(ref, true, bitIdx);
} }
} }
@ -503,7 +528,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
for (const bool right : {false, true}) { for (const bool right : {false, true}) {
Restorer restorer{*this}; Restorer restorer{*this};
LeafInfo leafInfo; LeafInfo leafInfo{m_lsb};
m_leafp = &leafInfo; m_leafp = &leafInfo;
AstNode* opp = right ? nodep->rhsp() : nodep->lhsp(); AstNode* opp = right ? nodep->rhsp() : nodep->lhsp();
const bool origFailed = m_failed; const bool origFailed = m_failed;
@ -522,7 +547,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
// The conditional on the lsb being in range is necessary for some degenerate // The conditional on the lsb being in range is necessary for some degenerate
// case, e.g.: (IData)((QData)wide[0] >> 32), or <1-bit-var> >> 1, which is // case, e.g.: (IData)((QData)wide[0] >> 32), or <1-bit-var> >> 1, which is
// just zero // just zero
if (leafInfo.lsb() < leafInfo.varWidth()) { if (leafInfo.lsb() <= leafInfo.msb()) {
m_bitPolarities.emplace_back(leafInfo, isXorTree() || leafInfo.polarity(), m_bitPolarities.emplace_back(leafInfo, isXorTree() || leafInfo.polarity(),
leafInfo.lsb()); leafInfo.lsb());
} else if (isAndTree() && leafInfo.polarity()) { } else if (isAndTree() && leafInfo.polarity()) {
@ -559,7 +584,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
incrOps(andp, __LINE__); incrOps(andp, __LINE__);
// Mark all bits checked by this comparison // Mark all bits checked by this comparison
const int maxBitIdx = std::min(ref.lsb() + maskNum.width(), ref.varWidth()); const int maxBitIdx = std::min(ref.lsb() + maskNum.width(), ref.msb() + 1);
for (int bitIdx = ref.lsb(); bitIdx < maxBitIdx; ++bitIdx) { for (int bitIdx = ref.lsb(); bitIdx < maxBitIdx; ++bitIdx) {
const int maskIdx = bitIdx - ref.lsb(); const int maskIdx = bitIdx - ref.lsb();
if (maskNum.bitIs0(maskIdx)) continue; if (maskNum.bitIs0(maskIdx)) continue;
@ -575,7 +600,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
incrOps(nodep, __LINE__); incrOps(nodep, __LINE__);
// Mark all bits checked by this comparison // Mark all bits checked by this comparison
const int maxBitIdx = std::min(ref.lsb() + compNum.width(), ref.varWidth()); const int maxBitIdx = std::min(ref.lsb() + compNum.width(), ref.msb() + 1);
for (int bitIdx = ref.lsb(); bitIdx < maxBitIdx; ++bitIdx) { for (int bitIdx = ref.lsb(); bitIdx < maxBitIdx; ++bitIdx) {
const int maskIdx = bitIdx - ref.lsb(); const int maskIdx = bitIdx - ref.lsb();
const bool polarity = compNum.bitIs1(maskIdx) != maskFlip; const bool polarity = compNum.bitIs1(maskIdx) != maskFlip;

View File

@ -573,7 +573,7 @@ public:
} }
virtual void visit(AstTestPlusArgs* nodep) override { virtual void visit(AstTestPlusArgs* nodep) override {
puts("VL_TESTPLUSARGS_I("); puts("VL_TESTPLUSARGS_I(");
putsQuoted(nodep->text()); emitCvtPackStr(nodep->searchp());
puts(")"); puts(")");
} }
virtual void visit(AstFError* nodep) override { virtual void visit(AstFError* nodep) override {

View File

@ -251,15 +251,15 @@ class EmitCHeader final : public EmitCConstInit {
emitTextSection(modp, VNType::atScHdr); emitTextSection(modp, VNType::atScHdr);
// Open class body {{{ // Open class body {{{
puts("\nclass ");
puts(prefixNameProtect(modp));
if (const AstClass* const classp = VN_CAST(modp, Class)) { if (const AstClass* const classp = VN_CAST(modp, Class)) {
puts("class ");
puts(prefixNameProtect(modp));
if (classp->extendsp()) { if (classp->extendsp()) {
puts(" : public "); puts(" : public ");
puts(prefixNameProtect(classp->extendsp()->classp())); puts(prefixNameProtect(classp->extendsp()->classp()));
} }
} else { } else {
puts("VL_MODULE(" + prefixNameProtect(modp) + ")"); puts(" final : public VerilatedModule");
} }
puts(" {\n"); puts(" {\n");
ofp()->resetPrivate(); ofp()->resetPrivate();

View File

@ -173,7 +173,7 @@ class CMakeEmitter final {
+ ".cpp"); + ".cpp");
} }
} }
if (v3Global.opt.mtasks()) { if (v3Global.opt.threads()) {
global.emplace_back("${VERILATOR_ROOT}/include/verilated_threads.cpp"); global.emplace_back("${VERILATOR_ROOT}/include/verilated_threads.cpp");
} }
if (v3Global.opt.usesProfiler()) { if (v3Global.opt.usesProfiler()) {

View File

@ -89,11 +89,12 @@ class EmitCModel final : public EmitCFunc {
puts("\n"); puts("\n");
puts("// This class is the main interface to the Verilated model\n"); puts("// This class is the main interface to the Verilated model\n");
puts("class " + topClassName() + " VL_NOT_FINAL : ");
if (optSystemC()) { if (optSystemC()) {
puts("SC_MODULE(" + topClassName() + ") {\n"); // SC_MODULE, but with multiple-inheritance of VerilatedModel
} else { puts("public ::sc_core::sc_module, ");
puts("class " + topClassName() + " VL_NOT_FINAL {\n");
} }
puts("public VerilatedModel {\n");
ofp()->resetPrivate(); ofp()->resetPrivate();
ofp()->putsPrivate(true); // private: ofp()->putsPrivate(true); // private:
@ -194,9 +195,6 @@ class EmitCModel final : public EmitCFunc {
} }
} }
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() const;\n");
if (!optSystemC()) { if (!optSystemC()) {
puts("/// Retrieve name of this model instance (as passed to constructor).\n"); puts("/// Retrieve name of this model instance (as passed to constructor).\n");
puts("const char* name() const;\n"); puts("const char* name() const;\n");
@ -221,6 +219,11 @@ class EmitCModel final : public EmitCFunc {
+ topClassName() + "& rhs);\n"); + topClassName() + "& rhs);\n");
} }
puts("\n// Abstract methods from VerilatedModel\n");
puts("const char* hierName() const override final;\n");
puts("const char* modelName() const override final;\n");
puts("unsigned threads() const override final;\n");
puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n"); puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n");
ofp()->putsEndGuard(); ofp()->putsEndGuard();
@ -235,10 +238,12 @@ class EmitCModel final : public EmitCFunc {
puts(topClassName() + "::" + topClassName()); puts(topClassName() + "::" + topClassName());
if (optSystemC()) { if (optSystemC()) {
puts("(sc_module_name /* unused */)\n"); puts("(sc_module_name /* unused */)\n");
puts(" : vlSymsp{new " + symClassName() + "(nullptr, name(), this)}\n"); puts(" : VerilatedModel{*Verilated::threadContextp()}\n");
puts(" , vlSymsp{new " + symClassName() + "(contextp(), name(), this)}\n");
} else { } else {
puts(+"(VerilatedContext* _vcontextp__, const char* _vcname__)\n"); puts(+"(VerilatedContext* _vcontextp__, const char* _vcname__)\n");
puts(" : vlSymsp{new " + symClassName() + "(_vcontextp__, _vcname__, this)}\n"); puts(" : VerilatedModel{*_vcontextp__}\n");
puts(" , vlSymsp{new " + symClassName() + "(contextp(), _vcname__, this)}\n");
} }
// Set up IO references // Set up IO references
@ -263,6 +268,8 @@ class EmitCModel final : public EmitCFunc {
puts(" , rootp{&(vlSymsp->TOP)}\n"); puts(" , rootp{&(vlSymsp->TOP)}\n");
puts("{\n"); puts("{\n");
puts("// Register model with the context\n");
puts("contextp()->addModel(this);\n");
if (optSystemC()) { if (optSystemC()) {
// Create sensitivity list for when to evaluate the model. // Create sensitivity list for when to evaluate the model.
@ -301,7 +308,7 @@ class EmitCModel final : public EmitCFunc {
if (!optSystemC()) { if (!optSystemC()) {
puts("\n"); puts("\n");
puts(topClassName() + "::" + topClassName() + "(const char* _vcname__)\n"); puts(topClassName() + "::" + topClassName() + "(const char* _vcname__)\n");
puts(" : " + topClassName() + "(nullptr, _vcname__)\n{\n}\n"); puts(" : " + topClassName() + "(Verilated::threadContextp(), _vcname__)\n{\n}\n");
} }
} }
@ -364,7 +371,7 @@ class EmitCModel final : public EmitCFunc {
} }
if (v3Global.opt.profExec()) { if (v3Global.opt.profExec()) {
puts("vlSymsp->__Vm_executionProfiler.configure(*(vlSymsp->_vm_contextp__));\n"); puts("vlSymsp->__Vm_executionProfilerp->configure();\n");
puts("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).evalBegin();\n"); puts("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).evalBegin();\n");
} }
@ -399,10 +406,6 @@ class EmitCModel final : public EmitCFunc {
} }
putSectionDelimiter("Utilities"); putSectionDelimiter("Utilities");
// ::contextp
puts("\nVerilatedContext* " + topClassName() + "::contextp() const {\n");
puts(/**/ "return vlSymsp->_vm_contextp__;\n");
puts("}\n");
if (!optSystemC()) { if (!optSystemC()) {
// ::name // ::name
@ -419,6 +422,13 @@ class EmitCModel final : public EmitCFunc {
puts("\nVL_ATTR_COLD void " + topClassName() + "::final() {\n"); puts("\nVL_ATTR_COLD void " + topClassName() + "::final() {\n");
puts(/**/ topModNameProtected + "__" + protect("_eval_final") + "(&(vlSymsp->TOP));\n"); puts(/**/ topModNameProtected + "__" + protect("_eval_final") + "(&(vlSymsp->TOP));\n");
puts("}\n"); puts("}\n");
putSectionDelimiter("Implementations of abstract methods from VerilatedModel\n");
puts("const char* " + topClassName() + "::hierName() const { return vlSymsp->name(); }\n");
puts("const char* " + topClassName() + "::modelName() const { return \"" + topClassName()
+ "\"; }\n");
puts("unsigned " + topClassName() + "::threads() const { return "
+ cvtToStr(std::max(1, v3Global.opt.threads())) + "; }\n");
} }
void emitTraceMethods(AstNodeModule* modp) { void emitTraceMethods(AstNodeModule* modp) {
@ -471,7 +481,8 @@ class EmitCModel final : public EmitCFunc {
puts(/**/ "}"); puts(/**/ "}");
} }
puts(/**/ "if (false && levels && options) {} // Prevent unused\n"); puts(/**/ "if (false && levels && options) {} // Prevent unused\n");
puts(/**/ "tfp->spTrace()->addInitCb(&" + protect("trace_init") + ", &(vlSymsp->TOP));\n"); puts(/**/ "tfp->spTrace()->addInitCb(&" + protect("trace_init")
+ ", &(vlSymsp->TOP), contextp());\n");
puts(/**/ topModNameProtected + "__" + protect("trace_register") puts(/**/ topModNameProtected + "__" + protect("trace_register")
+ "(&(vlSymsp->TOP), tfp->spTrace());\n"); + "(&(vlSymsp->TOP), tfp->spTrace());\n");

View File

@ -446,17 +446,17 @@ void EmitCSyms::emitSymHdr() {
if (v3Global.hasEvents()) puts("std::vector<VlEvent*> __Vm_triggeredEvents;\n"); if (v3Global.hasEvents()) puts("std::vector<VlEvent*> __Vm_triggeredEvents;\n");
puts("bool __Vm_didInit = false;\n"); puts("bool __Vm_didInit = false;\n");
if (v3Global.opt.profExec()) {
puts("\n// EXECUTION PROFILING\n");
puts("VlExecutionProfiler __Vm_executionProfiler;\n");
}
if (v3Global.opt.mtasks()) { if (v3Global.opt.mtasks()) {
puts("\n// MULTI-THREADING\n"); puts("\n// MULTI-THREADING\n");
puts("VlThreadPool* const __Vm_threadPoolp;\n"); puts("VlThreadPool* const __Vm_threadPoolp;\n");
puts("bool __Vm_even_cycle = false;\n"); puts("bool __Vm_even_cycle = false;\n");
} }
if (v3Global.opt.profExec()) {
puts("\n// EXECUTION PROFILING\n");
puts("VlExecutionProfiler* const __Vm_executionProfilerp;\n");
}
puts("\n// MODULE INSTANCE STATE\n"); puts("\n// MODULE INSTANCE STATE\n");
for (const auto& i : m_scopes) { for (const auto& i : m_scopes) {
const AstScope* const scopep = i.first; const AstScope* const scopep = i.first;
@ -690,7 +690,6 @@ void EmitCSyms::emitSymImp() {
puts("_vm_pgoProfiler.write(\"" + topClassName() puts("_vm_pgoProfiler.write(\"" + topClassName()
+ "\", _vm_contextp__->profVltFilename());\n"); + "\", _vm_contextp__->profVltFilename());\n");
} }
if (v3Global.opt.mtasks()) puts("delete __Vm_threadPoolp;\n");
puts("}\n\n"); puts("}\n\n");
// Constructor // Constructor
@ -722,12 +721,13 @@ void EmitCSyms::emitSymImp() {
// Note we create N-1 threads in the thread pool. The thread // Note we create N-1 threads in the thread pool. The thread
// that calls eval() becomes the final Nth thread for the // that calls eval() becomes the final Nth thread for the
// duration of the eval call. // duration of the eval call.
puts(" , __Vm_threadPoolp{new VlThreadPool{_vm_contextp__, " puts(" , __Vm_threadPoolp{static_cast<VlThreadPool*>(contextp->threadPoolp())}\n");
+ cvtToStr(v3Global.opt.threads() - 1) + ", " }
+ (v3Global.opt.profExec()
? "&__Vm_executionProfiler, &VlExecutionProfiler::startWorkerSetup" if (v3Global.opt.profExec()) {
: "nullptr, nullptr") puts(" , "
+ "}}\n"); "__Vm_executionProfilerp{static_cast<VlExecutionProfiler*>(contextp->"
"enableExecutionProfiler(&VlExecutionProfiler::construct))}\n");
} }
puts(" // Setup module instances\n"); puts(" // Setup module instances\n");

View File

@ -116,7 +116,7 @@ public:
putMakeClassEntry(of, v3Global.opt.traceSourceLang() + ".cpp"); putMakeClassEntry(of, v3Global.opt.traceSourceLang() + ".cpp");
} }
} }
if (v3Global.opt.mtasks()) putMakeClassEntry(of, "verilated_threads.cpp"); if (v3Global.opt.threads()) putMakeClassEntry(of, "verilated_threads.cpp");
if (v3Global.opt.usesProfiler()) { if (v3Global.opt.usesProfiler()) {
putMakeClassEntry(of, "verilated_profiler.cpp"); putMakeClassEntry(of, "verilated_profiler.cpp");
} }

View File

@ -225,11 +225,6 @@ private:
m_hash += nodep->text(); m_hash += nodep->text();
}); });
} }
virtual void visit(AstTestPlusArgs* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->text();
});
}
virtual void visit(AstAddrOfCFunc* nodep) override { virtual void visit(AstAddrOfCFunc* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { // m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
iterateNull(nodep->funcp()); iterateNull(nodep->funcp());

View File

@ -217,6 +217,13 @@ private:
iterateAndNextNull(nodep->msbp()); iterateAndNextNull(nodep->msbp());
} }
} }
virtual void visit(AstTestPlusArgs* nodep) override {
VL_RESTORER(m_setRefLvalue);
{
m_setRefLvalue = VAccess::NOCHANGE;
iterateAndNextNull(nodep->searchp());
}
}
virtual void visit(AstValuePlusArgs* nodep) override { virtual void visit(AstValuePlusArgs* nodep) override {
VL_RESTORER(m_setRefLvalue); VL_RESTORER(m_setRefLvalue);
{ {

View File

@ -319,43 +319,45 @@ private:
while (argp) { while (argp) {
if (skipCount) { if (skipCount) {
argp = argp->nextp(); argp = argp->nextp();
skipCount--; --skipCount;
continue; continue;
} }
const AstConst* const constp = VN_CAST(argp, Const); const AstConst* const constp = VN_CAST(argp, Const);
const bool isFromString = (constp) ? constp->num().isFromString() : false; const bool isFromString = (constp) ? constp->num().isFromString() : false;
if (isFromString) { if (isFromString) {
const int numchars = argp->dtypep()->width() / 8; const int numchars = argp->dtypep()->width() / 8;
string str(numchars, ' '); if (!constp->num().toString().empty()) {
// now scan for % operators string str(numchars, ' ');
bool inpercent = false; // now scan for % operators
for (int i = 0; i < numchars; i++) { bool inpercent = false;
const int ii = numchars - i - 1; for (int i = 0; i < numchars; i++) {
const char c = constp->num().dataByte(ii); const int ii = numchars - i - 1;
str[i] = c; const char c = constp->num().dataByte(ii);
if (!inpercent && c == '%') { str[i] = c;
inpercent = true; if (!inpercent && c == '%') {
} else if (inpercent) { inpercent = true;
inpercent = false; } else if (inpercent) {
switch (c) { inpercent = false;
case '0': // FALLTHRU switch (c) {
case '1': // FALLTHRU case '0': // FALLTHRU
case '2': // FALLTHRU case '1': // FALLTHRU
case '3': // FALLTHRU case '2': // FALLTHRU
case '4': // FALLTHRU case '3': // FALLTHRU
case '5': // FALLTHRU case '4': // FALLTHRU
case '6': // FALLTHRU case '5': // FALLTHRU
case '7': // FALLTHRU case '6': // FALLTHRU
case '8': // FALLTHRU case '7': // FALLTHRU
case '9': // FALLTHRU case '8': // FALLTHRU
case '.': inpercent = true; break; case '9': // FALLTHRU
case '%': break; case '.': inpercent = true; break;
default: case '%': break;
if (V3Number::displayedFmtLegal(c, isScan)) ++skipCount; default:
if (V3Number::displayedFmtLegal(c, isScan)) ++skipCount;
}
} }
} }
newFormat.append(str);
} }
newFormat.append(str);
AstNode* const nextp = argp->nextp(); AstNode* const nextp = argp->nextp();
argp->unlinkFrBack(); argp->unlinkFrBack();
VL_DO_DANGLING(pushDeletep(argp), argp); VL_DO_DANGLING(pushDeletep(argp), argp);

View File

@ -15,7 +15,7 @@
//************************************************************************* //*************************************************************************
// //
// void example_usage() { // void example_usage() {
// SimulateVisitor simvis (false, false); // SimulateVisitor simvis{false, false};
// simvis.clear(); // simvis.clear();
// // Set all inputs to the constant // // Set all inputs to the constant
// for (deque<AstVarScope*>::iterator it = m_inVarps.begin(); it!=m_inVarps.end(); ++it) { // for (deque<AstVarScope*>::iterator it = m_inVarps.begin(); it!=m_inVarps.end(); ++it) {
@ -100,6 +100,7 @@ private:
bool m_anyAssignDly; ///< True if found a delayed assignment bool m_anyAssignDly; ///< True if found a delayed assignment
bool m_anyAssignComb; ///< True if found a non-delayed assignment bool m_anyAssignComb; ///< True if found a non-delayed assignment
bool m_inDlyAssign; ///< Under delayed assignment bool m_inDlyAssign; ///< Under delayed assignment
bool m_isOutputter; // Creates output
int m_instrCount; ///< Number of nodes int m_instrCount; ///< Number of nodes
int m_dataCount; ///< Bytes of data int m_dataCount; ///< Bytes of data
AstJumpGo* m_jumpp; ///< Jump label we're branching from AstJumpGo* m_jumpp; ///< Jump label we're branching from
@ -130,7 +131,7 @@ private:
const int width = itemp->width(); const int width = itemp->width();
const int lsb = itemp->lsb(); const int lsb = itemp->lsb();
const int msb = lsb + width - 1; const int msb = lsb + width - 1;
V3Number fieldNum(nump, width); V3Number fieldNum{nump, width};
fieldNum.opSel(*nump, msb, lsb); fieldNum.opSel(*nump, msb, lsb);
out << itemp->name() << ": "; out << itemp->name() << ": ";
if (AstNodeDType* const childTypep = itemp->subDTypep()) { if (AstNodeDType* const childTypep = itemp->subDTypep()) {
@ -152,7 +153,7 @@ private:
const int width = childTypep->width(); const int width = childTypep->width();
const int lsb = width * element; const int lsb = width * element;
const int msb = lsb + width - 1; const int msb = lsb + width - 1;
V3Number fieldNum(nump, width); V3Number fieldNum{nump, width};
fieldNum.opSel(*nump, msb, lsb); fieldNum.opSel(*nump, msb, lsb);
const int arrayElem = arrayp->lo() + element; const int arrayElem = arrayp->lo() + element;
out << arrayElem << " = " << prettyNumber(&fieldNum, childTypep); out << arrayElem << " = " << prettyNumber(&fieldNum, childTypep);
@ -205,6 +206,7 @@ public:
AstNode* whyNotNodep() const { return m_whyNotNodep; } AstNode* whyNotNodep() const { return m_whyNotNodep; }
bool isAssignDly() const { return m_anyAssignDly; } bool isAssignDly() const { return m_anyAssignDly; }
bool isOutputter() const { return m_isOutputter; }
int instrCount() const { return m_instrCount; } int instrCount() const { return m_instrCount; }
int dataCount() const { return m_dataCount; } int dataCount() const { return m_dataCount; }
@ -236,7 +238,7 @@ private:
} }
if (allocNewConst) { if (allocNewConst) {
// Need to allocate new constant // Need to allocate new constant
constp = new AstConst(nodep->fileline(), AstConst::DtypedValue(), nodep->dtypep(), 0); constp = new AstConst{nodep->fileline(), AstConst::DtypedValue{}, nodep->dtypep(), 0};
// Mark as in use, add to free list for later reuse // Mark as in use, add to free list for later reuse
constp->user2(1); constp->user2(1);
freeList.push_back(constp); freeList.push_back(constp);
@ -342,15 +344,16 @@ private:
nodep->user2p((void*)valuep); nodep->user2p((void*)valuep);
} }
void checkNodeInfo(AstNode* nodep) { void checkNodeInfo(AstNode* nodep, bool ignorePredict = false) {
if (m_checkOnly) { if (m_checkOnly) {
m_instrCount += nodep->instrCount(); m_instrCount += nodep->instrCount();
m_dataCount += nodep->width(); m_dataCount += nodep->width();
} }
if (!nodep->isPredictOptimizable()) { if (!ignorePredict && !nodep->isPredictOptimizable()) {
// UINFO(9, " !predictopt " << nodep << endl); // UINFO(9, " !predictopt " << nodep << endl);
clearOptimizable(nodep, "Isn't predictable"); clearOptimizable(nodep, "Isn't predictable");
} }
if (nodep->isOutputter()) m_isOutputter = true;
} }
void badNodeType(AstNode* nodep) { void badNodeType(AstNode* nodep) {
@ -683,15 +686,15 @@ private:
initp = vscpnump; initp = vscpnump;
} else { // Assignment to unassigned variable, all bits are X } else { // Assignment to unassigned variable, all bits are X
// TODO generic initialization which builds X/arrays by recursion // TODO generic initialization which builds X/arrays by recursion
AstConst* const outconstp = new AstConst( AstConst* const outconstp = new AstConst{
nodep->fileline(), AstConst::WidthedValue(), basicp->widthMin(), 0); nodep->fileline(), AstConst::WidthedValue{}, basicp->widthMin(), 0};
if (basicp->isZeroInit()) { if (basicp->isZeroInit()) {
outconstp->num().setAllBits0(); outconstp->num().setAllBits0();
} else { } else {
outconstp->num().setAllBitsX(); outconstp->num().setAllBitsX();
} }
initp = new AstInitArray(nodep->fileline(), arrayp, outconstp); initp = new AstInitArray{nodep->fileline(), arrayp, outconstp};
m_reclaimValuesp.push_back(initp); m_reclaimValuesp.push_back(initp);
} }
const uint32_t index = fetchConst(selp->bitp())->toUInt(); const uint32_t index = fetchConst(selp->bitp())->toUInt();
@ -706,7 +709,7 @@ private:
} }
void handleAssignSel(AstNodeAssign* nodep, AstSel* selp) { void handleAssignSel(AstNodeAssign* nodep, AstSel* selp) {
AstVarRef* varrefp = nullptr; AstVarRef* varrefp = nullptr;
V3Number lsb(nodep); V3Number lsb{nodep};
iterateAndNextNull(nodep->rhsp()); // Value to assign iterateAndNextNull(nodep->rhsp()); // Value to assign
handleAssignSelRecurse(nodep, selp, varrefp /*ref*/, lsb /*ref*/, 0); handleAssignSelRecurse(nodep, selp, varrefp /*ref*/, lsb /*ref*/, 0);
if (!m_checkOnly && optimizable()) { if (!m_checkOnly && optimizable()) {
@ -719,8 +722,8 @@ private:
} else if (AstConst* const vscpnump = fetchConstNull(vscp)) { } else if (AstConst* const vscpnump = fetchConstNull(vscp)) {
outconstp = vscpnump; outconstp = vscpnump;
} else { // Assignment to unassigned variable, all bits are X or 0 } else { // Assignment to unassigned variable, all bits are X or 0
outconstp = new AstConst(nodep->fileline(), AstConst::WidthedValue(), outconstp = new AstConst{nodep->fileline(), AstConst::WidthedValue{},
varrefp->varp()->widthMin(), 0); varrefp->varp()->widthMin(), 0};
if (varrefp->varp()->basicp() && varrefp->varp()->basicp()->isZeroInit()) { if (varrefp->varp()->basicp() && varrefp->varp()->basicp()->isZeroInit()) {
outconstp->num().setAllBits0(); outconstp->num().setAllBits0();
} else { } else {
@ -742,7 +745,7 @@ private:
lsbRef = fetchConst(selp->lsbp())->num(); lsbRef = fetchConst(selp->lsbp())->num();
return; // And presumably still optimizable() return; // And presumably still optimizable()
} else if (AstSel* const subselp = VN_CAST(selp->lhsp(), Sel)) { } else if (AstSel* const subselp = VN_CAST(selp->lhsp(), Sel)) {
V3Number sublsb(nodep); V3Number sublsb{nodep};
handleAssignSelRecurse(nodep, subselp, outVarrefpRef, sublsb /*ref*/, depth + 1); handleAssignSelRecurse(nodep, subselp, outVarrefpRef, sublsb /*ref*/, depth + 1);
if (optimizable()) { if (optimizable()) {
lsbRef = sublsb; lsbRef = sublsb;
@ -756,6 +759,7 @@ private:
virtual void visit(AstNodeAssign* nodep) override { virtual void visit(AstNodeAssign* nodep) override {
if (jumpingOver(nodep)) return; if (jumpingOver(nodep)) return;
if (!optimizable()) return; // Accelerate if (!optimizable()) return; // Accelerate
checkNodeInfo(nodep);
if (VN_IS(nodep, AssignForce)) { if (VN_IS(nodep, AssignForce)) {
clearOptimizable(nodep, "Force"); clearOptimizable(nodep, "Force");
} else if (VN_IS(nodep, AssignDly)) { } else if (VN_IS(nodep, AssignDly)) {
@ -829,7 +833,7 @@ private:
if (hit) break; if (hit) break;
iterateAndNextNull(ep); iterateAndNextNull(ep);
if (optimizable()) { if (optimizable()) {
V3Number match(nodep, 1); V3Number match{nodep, 1};
match.opEq(fetchConst(nodep->exprp())->num(), fetchConst(ep)->num()); match.opEq(fetchConst(nodep->exprp())->num(), fetchConst(ep)->num());
if (match.isNeqZero()) { if (match.isNeqZero()) {
iterateAndNextNull(itemp->bodysp()); iterateAndNextNull(itemp->bodysp());
@ -970,6 +974,7 @@ private:
if (jumpingOver(nodep)) return; if (jumpingOver(nodep)) return;
if (!optimizable()) return; // Accelerate if (!optimizable()) return; // Accelerate
UINFO(5, " FUNCREF " << nodep << endl); UINFO(5, " FUNCREF " << nodep << endl);
checkNodeInfo(nodep);
if (!m_params) { if (!m_params) {
badNodeType(nodep); badNodeType(nodep);
return; return;
@ -1053,6 +1058,7 @@ private:
virtual void visit(AstSFormatF* nodep) override { virtual void visit(AstSFormatF* nodep) override {
if (jumpingOver(nodep)) return; if (jumpingOver(nodep)) return;
if (!optimizable()) return; // Accelerate if (!optimizable()) return; // Accelerate
checkNodeInfo(nodep);
iterateChildren(nodep); iterateChildren(nodep);
if (m_params) { if (m_params) {
AstNode* nextArgp = nodep->exprsp(); AstNode* nextArgp = nodep->exprsp();
@ -1097,7 +1103,7 @@ private:
} }
AstConst* const resultConstp AstConst* const resultConstp
= new AstConst(nodep->fileline(), AstConst::String(), result); = new AstConst{nodep->fileline(), AstConst::String{}, result};
setValue(nodep, resultConstp); setValue(nodep, resultConstp);
m_reclaimValuesp.push_back(resultConstp); m_reclaimValuesp.push_back(resultConstp);
} }
@ -1106,6 +1112,9 @@ private:
virtual void visit(AstDisplay* nodep) override { virtual void visit(AstDisplay* nodep) override {
if (jumpingOver(nodep)) return; if (jumpingOver(nodep)) return;
if (!optimizable()) return; // Accelerate if (!optimizable()) return; // Accelerate
// We ignore isPredictOptimizable as $display is often in constant
// functions and we want them to work if used with parameters
checkNodeInfo(nodep, /*display:*/ true);
iterateChildren(nodep); iterateChildren(nodep);
if (m_params) { if (m_params) {
AstConst* const textp = fetchConst(nodep->fmtp()); AstConst* const textp = fetchConst(nodep->fmtp());
@ -1155,6 +1164,7 @@ public:
m_anyAssignComb = false; m_anyAssignComb = false;
m_anyAssignDly = false; m_anyAssignDly = false;
m_inDlyAssign = false; m_inDlyAssign = false;
m_isOutputter = false;
m_instrCount = 0; m_instrCount = 0;
m_dataCount = 0; m_dataCount = 0;
m_jumpp = nullptr; m_jumpp = nullptr;

View File

@ -92,8 +92,8 @@ public:
= elemDType->isString() = elemDType->isString()
? elemDType ? elemDType
: v3Global.rootp()->findBitDType(width, width, VSigning::UNSIGNED); : v3Global.rootp()->findBitDType(width, width, VSigning::UNSIGNED);
AstUnpackArrayDType* const tableDTypep AstUnpackArrayDType* const tableDTypep = new AstUnpackArrayDType{
= new AstUnpackArrayDType(m_fl, subDTypep, new AstRange(m_fl, size, 0)); m_fl, subDTypep, new AstRange{m_fl, static_cast<int>(size), 0}};
v3Global.rootp()->typeTablep()->addTypesp(tableDTypep); v3Global.rootp()->typeTablep()->addTypesp(tableDTypep);
// Create table initializer (with default value 0) // Create table initializer (with default value 0)
AstConst* const defaultp = elemDType->isString() AstConst* const defaultp = elemDType->isString()
@ -106,7 +106,7 @@ public:
UASSERT_OBJ(!m_varScopep, m_fl, "Table variable already created"); UASSERT_OBJ(!m_varScopep, m_fl, "Table variable already created");
// Default value is zero/empty string so don't add it // Default value is zero/empty string so don't add it
if (value.isString() ? value.toString().empty() : value.isEqZero()) return; if (value.isString() ? value.toString().empty() : value.isEqZero()) return;
m_initp->addIndexValuep(index, new AstConst(m_fl, value)); m_initp->addIndexValuep(index, new AstConst{m_fl, value});
} }
AstVarScope* varScopep() { AstVarScope* varScopep() {
@ -225,6 +225,9 @@ private:
if (!m_outWidthBytes || !m_inWidthBits) { if (!m_outWidthBytes || !m_inWidthBits) {
chkvis.clearOptimizable(nodep, "Table has no outputs"); chkvis.clearOptimizable(nodep, "Table has no outputs");
} }
if (chkvis.isOutputter()) {
chkvis.clearOptimizable(nodep, "Table creates display output");
}
UINFO(4, " Test: Opt=" << (chkvis.optimizable() ? "OK" : "NO") << ", Instrs=" UINFO(4, " Test: Opt=" << (chkvis.optimizable() ? "OK" : "NO") << ", Instrs="
<< chkvis.instrCount() << " Data=" << chkvis.dataCount() << chkvis.instrCount() << " Data=" << chkvis.dataCount()
<< " in width (bits)=" << m_inWidthBits << " out width (bytes)=" << " in width (bits)=" << m_inWidthBits << " out width (bytes)="
@ -247,14 +250,14 @@ private:
// We will need a table index variable, create it here. // We will need a table index variable, create it here.
AstVar* const indexVarp AstVar* const indexVarp
= new AstVar(fl, VVarType::BLOCKTEMP, "__Vtableidx" + cvtToStr(m_modTables), = new AstVar{fl, VVarType::BLOCKTEMP, "__Vtableidx" + cvtToStr(m_modTables),
VFlagBitPacked(), m_inWidthBits); VFlagBitPacked{}, static_cast<int>(m_inWidthBits)};
m_modp->addStmtp(indexVarp); m_modp->addStmtp(indexVarp);
AstVarScope* const indexVscp = new AstVarScope(indexVarp->fileline(), m_scopep, indexVarp); AstVarScope* const indexVscp = new AstVarScope{indexVarp->fileline(), m_scopep, indexVarp};
m_scopep->addVarp(indexVscp); m_scopep->addVarp(indexVscp);
// The 'output assigned' table builder // The 'output assigned' table builder
TableBuilder outputAssignedTableBuilder(fl); TableBuilder outputAssignedTableBuilder{fl};
outputAssignedTableBuilder.setTableSize( outputAssignedTableBuilder.setTableSize(
nodep->findBitDType(m_outVarps.size(), m_outVarps.size(), VSigning::UNSIGNED), nodep->findBitDType(m_outVarps.size(), m_outVarps.size(), VSigning::UNSIGNED),
VL_MASK_I(m_inWidthBits)); VL_MASK_I(m_inWidthBits));
@ -311,7 +314,7 @@ private:
<< simvis.whyNotMessage()); << simvis.whyNotMessage());
// Build output value tables and the assigned flags table // Build output value tables and the assigned flags table
V3Number outputAssignedMask(nodep, m_outVarps.size(), 0); V3Number outputAssignedMask{nodep, static_cast<int>(m_outVarps.size()), 0};
for (TableOutputVar& tov : m_outVarps) { for (TableOutputVar& tov : m_outVarps) {
if (V3Number* const outnump = simvis.fetchOutNumberNull(tov.varScopep())) { if (V3Number* const outnump = simvis.fetchOutNumberNull(tov.varScopep())) {
UINFO(8, " Output " << tov.name() << " = " << *outnump << endl); UINFO(8, " Output " << tov.name() << " = " << *outnump << endl);
@ -333,21 +336,21 @@ private:
// First var in inVars becomes the LSB of the concat // First var in inVars becomes the LSB of the concat
AstNode* concatp = nullptr; AstNode* concatp = nullptr;
for (AstVarScope* invscp : m_inVarps) { for (AstVarScope* invscp : m_inVarps) {
AstVarRef* const refp = new AstVarRef(fl, invscp, VAccess::READ); AstVarRef* const refp = new AstVarRef{fl, invscp, VAccess::READ};
if (concatp) { if (concatp) {
concatp = new AstConcat(fl, refp, concatp); concatp = new AstConcat{fl, refp, concatp};
} else { } else {
concatp = refp; concatp = refp;
} }
} }
return new AstAssign(fl, new AstVarRef(fl, indexVscp, VAccess::WRITE), concatp); return new AstAssign{fl, new AstVarRef{fl, indexVscp, VAccess::WRITE}, concatp};
} }
AstArraySel* select(FileLine* fl, AstVarScope* fromp, AstVarScope* indexp) { AstArraySel* select(FileLine* fl, AstVarScope* fromp, AstVarScope* indexp) {
AstVarRef* const fromRefp = new AstVarRef(fl, fromp, VAccess::READ); AstVarRef* const fromRefp = new AstVarRef{fl, fromp, VAccess::READ};
AstVarRef* const indexRefp = new AstVarRef(fl, indexp, VAccess::READ); AstVarRef* const indexRefp = new AstVarRef{fl, indexp, VAccess::READ};
return new AstArraySel(fl, fromRefp, indexRefp); return new AstArraySel{fl, fromRefp, indexRefp};
} }
void createOutputAssigns(AstNode* nodep, AstNode* stmtsp, AstVarScope* indexVscp, void createOutputAssigns(AstNode* nodep, AstNode* stmtsp, AstVarScope* indexVscp,
@ -362,12 +365,12 @@ private:
// If this output is unassigned on some code paths, wrap the assignment in an If // If this output is unassigned on some code paths, wrap the assignment in an If
if (tov.mayBeUnassigned()) { if (tov.mayBeUnassigned()) {
V3Number outputChgMask(nodep, m_outVarps.size(), 0); V3Number outputChgMask{nodep, static_cast<int>(m_outVarps.size()), 0};
outputChgMask.setBit(tov.ord(), 1); outputChgMask.setBit(tov.ord(), 1);
AstNode* const condp AstNode* const condp
= new AstAnd(fl, select(fl, outputAssignedTableVscp, indexVscp), = new AstAnd{fl, select(fl, outputAssignedTableVscp, indexVscp),
new AstConst(fl, outputChgMask)); new AstConst{fl, outputChgMask}};
outsetp = new AstIf(fl, condp, outsetp); outsetp = new AstIf{fl, condp, outsetp};
} }
stmtsp->addNext(outsetp); stmtsp->addNext(outsetp);

View File

@ -512,8 +512,10 @@ private:
m_regFuncp->addStmtsp(new AstText(flp, "tracep->addChgCb(", true)); m_regFuncp->addStmtsp(new AstText(flp, "tracep->addChgCb(", true));
} }
m_regFuncp->addStmtsp(new AstAddrOfCFunc(flp, funcp)); m_regFuncp->addStmtsp(new AstAddrOfCFunc(flp, funcp));
const string threadPool{m_parallelism > 1 ? "vlSymsp->__Vm_threadPoolp" : "nullptr"}; m_regFuncp->addStmtsp(new AstText(flp, ", vlSelf", true));
m_regFuncp->addStmtsp(new AstText(flp, ", vlSelf, " + threadPool + ");\n", true)); m_regFuncp->addStmtsp(
new AstText(flp, ", vlSelf->vlSymsp->__Vm_modelp->contextp()", true));
m_regFuncp->addStmtsp(new AstText(flp, ");\n", true));
} else { } else {
// Sub functions // Sub functions
funcp->argTypes(v3Global.opt.traceClassBase() + "::Buffer* bufp"); funcp->argTypes(v3Global.opt.traceClassBase() + "::Buffer* bufp");
@ -700,7 +702,8 @@ private:
// Register it // Register it
m_regFuncp->addStmtsp(new AstText(fl, "tracep->addCleanupCb(", true)); m_regFuncp->addStmtsp(new AstText(fl, "tracep->addCleanupCb(", true));
m_regFuncp->addStmtsp(new AstAddrOfCFunc(fl, cleanupFuncp)); m_regFuncp->addStmtsp(new AstAddrOfCFunc(fl, cleanupFuncp));
m_regFuncp->addStmtsp(new AstText(fl, ", vlSelf);\n", true)); m_regFuncp->addStmtsp(
new AstText(fl, ", vlSelf, vlSelf->vlSymsp->__Vm_modelp->contextp());\n", true));
// Clear global activity flag // Clear global activity flag
cleanupFuncp->addStmtsp( cleanupFuncp->addStmtsp(

View File

@ -448,7 +448,6 @@ private:
// Widths: Constant, terminal // Widths: Constant, terminal
virtual void visit(AstTime* nodep) override { nodep->dtypeSetUInt64(); } virtual void visit(AstTime* nodep) override { nodep->dtypeSetUInt64(); }
virtual void visit(AstTimeD* nodep) override { nodep->dtypeSetDouble(); } virtual void visit(AstTimeD* nodep) override { nodep->dtypeSetDouble(); }
virtual void visit(AstTestPlusArgs* nodep) override { nodep->dtypeSetSigned32(); }
virtual void visit(AstScopeName* nodep) override { virtual void visit(AstScopeName* nodep) override {
nodep->dtypeSetUInt64(); // A pointer, but not that it matters nodep->dtypeSetUInt64(); // A pointer, but not that it matters
} }
@ -4352,6 +4351,12 @@ private:
userIterateAndNext(nodep->lsbp(), WidthVP(SELF, BOTH).p()); userIterateAndNext(nodep->lsbp(), WidthVP(SELF, BOTH).p());
userIterateAndNext(nodep->msbp(), WidthVP(SELF, BOTH).p()); userIterateAndNext(nodep->msbp(), WidthVP(SELF, BOTH).p());
} }
virtual void visit(AstTestPlusArgs* nodep) override {
if (m_vup->prelim()) {
userIterateAndNext(nodep->searchp(), WidthVP{SELF, BOTH}.p());
nodep->dtypeChgWidthSigned(32, 1, VSigning::SIGNED); // Spec says integer return
}
}
virtual void visit(AstValuePlusArgs* nodep) override { virtual void visit(AstValuePlusArgs* nodep) override {
if (m_vup->prelim()) { if (m_vup->prelim()) {
userIterateAndNext(nodep->searchp(), WidthVP(SELF, BOTH).p()); userIterateAndNext(nodep->searchp(), WidthVP(SELF, BOTH).p());

View File

@ -3918,7 +3918,7 @@ system_f_call_or_t<nodep>: // IEEE: part of system_tf_call (can be task or
| yD_STABLE '(' expr ',' expr ')' { $$ = $3; BBUNSUP($1, "Unsupported: $stable and clock arguments"); } | yD_STABLE '(' expr ',' expr ')' { $$ = $3; BBUNSUP($1, "Unsupported: $stable and clock arguments"); }
| yD_TAN '(' expr ')' { $$ = new AstTanD($1,$3); } | yD_TAN '(' expr ')' { $$ = new AstTanD($1,$3); }
| yD_TANH '(' expr ')' { $$ = new AstTanhD($1,$3); } | yD_TANH '(' expr ')' { $$ = new AstTanhD($1,$3); }
| yD_TESTPLUSARGS '(' str ')' { $$ = new AstTestPlusArgs($1,*$3); } | yD_TESTPLUSARGS '(' expr ')' { $$ = new AstTestPlusArgs($1, $3); }
| yD_TIME parenE { $$ = new AstTime($1, VTimescale(VTimescale::NONE)); } | yD_TIME parenE { $$ = new AstTime($1, VTimescale(VTimescale::NONE)); }
| yD_TYPENAME '(' exprOrDataType ')' { $$ = new AstAttrOf($1, VAttrType::TYPENAME, $3); } | yD_TYPENAME '(' exprOrDataType ')' { $$ = new AstAttrOf($1, VAttrType::TYPENAME, $3); }
| yD_UNGETC '(' expr ',' expr ')' { $$ = new AstFUngetC($1, $5, $3); } // Arg swap to file first | yD_UNGETC '(' expr ',' expr ')' { $$ = new AstFUngetC($1, $5, $3); } // Arg swap to file first

View File

@ -578,6 +578,8 @@ sub new {
make_pli => 0, # need to compile pli make_pli => 0, # need to compile pli
sc_time_resolution => "SC_PS", # Keep - PS is SystemC default sc_time_resolution => "SC_PS", # Keep - PS is SystemC default
sim_time => 1100, sim_time => 1100,
threads => -1, # --threads (negative means auto based on scenario)
context_threads => 0, # Number of threads to allocate in the context
benchmark => $opt_benchmark, benchmark => $opt_benchmark,
verbose => $opt_verbose, verbose => $opt_verbose,
run_env => '', run_env => '',
@ -902,6 +904,7 @@ sub compile_vlt_flags {
@{$param{verilator_flags}}, @{$param{verilator_flags}},
@{$param{verilator_flags2}}, @{$param{verilator_flags2}},
@{$param{verilator_flags3}}); @{$param{verilator_flags3}});
die "%Error: specify threads via 'threads =>' argument, not as a command line option" unless ($checkflags !~ /(^|\s)-?-threads\s/ && $checkflags !~ /(^|\s)-?-no-threads($|\s)/);
$self->{sc} = 1 if ($checkflags =~ /-sc\b/); $self->{sc} = 1 if ($checkflags =~ /-sc\b/);
$self->{trace} = ($opt_trace || $checkflags =~ /-trace\b/ $self->{trace} = ($opt_trace || $checkflags =~ /-trace\b/
|| $checkflags =~ /-trace-fst\b/); || $checkflags =~ /-trace-fst\b/);
@ -920,8 +923,7 @@ sub compile_vlt_flags {
unshift @verilator_flags, "--rr" if $opt_rr; unshift @verilator_flags, "--rr" if $opt_rr;
unshift @verilator_flags, "--x-assign unique"; # More likely to be buggy unshift @verilator_flags, "--x-assign unique"; # More likely to be buggy
unshift @verilator_flags, "--trace" if $opt_trace; unshift @verilator_flags, "--trace" if $opt_trace;
my $threads = ::calc_threads($Vltmt_threads); unshift @verilator_flags, "--threads $param{threads}" if $param{threads} >= 0;
unshift @verilator_flags, "--threads $threads" if $param{vltmt} && $checkflags !~ /-threads /;
unshift @verilator_flags, "--trace-threads 2" if $param{vltmt} && $checkflags =~ /-trace-fst /; unshift @verilator_flags, "--trace-threads 2" if $param{vltmt} && $checkflags =~ /-trace-fst /;
unshift @verilator_flags, "--debug-partition" if $param{vltmt}; unshift @verilator_flags, "--debug-partition" if $param{vltmt};
unshift @verilator_flags, "-CFLAGS -ggdb -LDFLAGS -ggdb" if $opt_gdbsim; unshift @verilator_flags, "-CFLAGS -ggdb -LDFLAGS -ggdb" if $opt_gdbsim;
@ -972,6 +974,13 @@ sub compile {
return 1 if $self->errors || $self->skips || $self->unsupporteds; return 1 if $self->errors || $self->skips || $self->unsupporteds;
$self->oprint("Compile\n") if $self->{verbose}; $self->oprint("Compile\n") if $self->{verbose};
die "%Error: 'threads =>' argument must be <= 1 for vlt scenario" if $param{vlt} && $param{threads} > 1;
# Compute automatic parameter values
$param{threads} = ::calc_threads($Vltmt_threads) if $param{threads} < 0 && $param{vltmt};
$param{context_threads} = $param{threads} >= 1 ? $param{threads} : 1 if !$param{context_threads};
$self->{threads} = $param{threads};
$self->{context_threads} = $param{context_threads};
compile_vlt_cmd(%param); compile_vlt_cmd(%param);
if (!$param{make_top_shell}) { if (!$param{make_top_shell}) {
@ -1791,6 +1800,7 @@ sub _make_main {
} }
print $fh " const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};\n"; print $fh " const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};\n";
print $fh " contextp->threads($self->{context_threads});\n";
print $fh " contextp->commandArgs(argc, argv);\n"; print $fh " contextp->commandArgs(argc, argv);\n";
print $fh " contextp->debug(" . ($self->{verilated_debug} ? 1 : 0) . ");\n"; print $fh " contextp->debug(" . ($self->{verilated_debug} ? 1 : 0) . ");\n";
print $fh " srand48(5);\n"; # Ensure determinism print $fh " srand48(5);\n"; # Ensure determinism

View File

@ -19,7 +19,7 @@ execute(
); );
if ($Self->{vlt}) { if ($Self->{vlt}) {
file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 11); file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 14);
} }
ok(1); ok(1);
1; 1;

View File

@ -62,7 +62,7 @@ module t(/*AUTOARG*/
$write("[%0t] cyc==%0d crc=%x sum=%x\n", $time, cyc, crc, sum); $write("[%0t] cyc==%0d crc=%x sum=%x\n", $time, cyc, crc, sum);
if (crc !== 64'hc77bb9b3784ea091) $stop; if (crc !== 64'hc77bb9b3784ea091) $stop;
// What checksum will we end up with (above print should match) // What checksum will we end up with (above print should match)
`define EXPECTED_SUM 64'hdccb9e7b8b638233 `define EXPECTED_SUM 64'hde21e019a3e12039
if (sum !== `EXPECTED_SUM) $stop; if (sum !== `EXPECTED_SUM) $stop;
$write("*-* All Finished *-*\n"); $write("*-* All Finished *-*\n");
@ -86,10 +86,11 @@ module Test(/*AUTOARG*/
logic bug3182_out; logic bug3182_out;
logic bug3197_out; logic bug3197_out;
logic bug3445_out; logic bug3445_out;
logic bug3470_out;
output logic o; output logic o;
logic [7:0] tmp; logic [8:0] tmp;
assign o = ^tmp; assign o = ^tmp;
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
@ -113,11 +114,13 @@ module Test(/*AUTOARG*/
tmp[5] <= bug3182_out; tmp[5] <= bug3182_out;
tmp[6] <= bug3197_out; tmp[6] <= bug3197_out;
tmp[7] <= bug3445_out; tmp[7] <= bug3445_out;
tmp[8] <= bug3470_out;
end end
bug3182 i_bug3182(.in(d[4:0]), .out(bug3182_out)); bug3182 i_bug3182(.in(d[4:0]), .out(bug3182_out));
bug3197 i_bug3197(.clk(clk), .in(d), .out(bug3197_out)); bug3197 i_bug3197(.clk(clk), .in(d), .out(bug3197_out));
bug3445 i_bug3445(.clk(clk), .in(d), .out(bug3445_out)); bug3445 i_bug3445(.clk(clk), .in(d), .out(bug3445_out));
bug3470 i_bug3470(.clk(clk), .in(d), .out(bug3470_out));
endmodule endmodule
@ -203,3 +206,32 @@ module bug3445(input wire clk, input wire [31:0] in, output wire out);
assign out = result0 ^ result1 ^ (result2 | result3); assign out = result0 ^ result1 ^ (result2 | result3);
endmodule endmodule
// Bug3470
// CCast had been ignored in bit op tree optimization
// Assume the following HDL input:
// (^d[38:32]) ^ (^d[31:0])
// where d is logic [38:0]
// ^d[31:0] becomes REDXOR(CCast(uint32_t, d)),
// but CCast was ignored and interpreted as ^d[38:0].
// Finally (^d[38:32]) ^ (^d31:0]) was wrongly transformed to
// (^d[38:32]) ^ (^d[38:0])
// -> (^d[38:32]) ^ ((^d[38:32]) ^ (^d[31:0]))
// -> ^d[31:0]
// Of course the correct result is ^d[38:0] = ^d
module bug3470(input wire clk, input wire [31:0] in, output wire out);
logic [38:0] d;
always_ff @(posedge clk)
d <= {d[6:0], in};
logic tmp, expected;
always_ff @(posedge clk) begin
tmp <= ^(d >> 32) ^ (^d[31:0]);
expected <= ^d;
end
always @(posedge clk)
if (tmp != expected) $stop;
assign out = tmp;
endmodule

View File

@ -11,7 +11,8 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(vltmt => 1); # Note issue shows up with --threads scenarios(vltmt => 1); # Note issue shows up with --threads
compile( compile(
verilator_flags2 => ['--compiler clang --threads 2 -Wno-UNOPTTHREADS'], verilator_flags2 => ['--compiler clang -Wno-UNOPTTHREADS'],
threads => 2
); );
ok(1); ok(1);

View File

@ -1,5 +1,7 @@
Merge: Merge:
This should merge This should merge
Merge:
This should also merge
f f
1=1 a=top.t 1=1 1=1 b=top.t 1=1 1=1 a=top.t 1=1 1=1 b=top.t 1=1
pre pre

View File

@ -20,7 +20,7 @@ execute(
); );
file_grep("$Self->{obj_dir}/$Self->{VM_PREFIX}__stats.txt", file_grep("$Self->{obj_dir}/$Self->{VM_PREFIX}__stats.txt",
qr/Node count, DISPLAY \s+ 41 \s+ 27 \s+ 27 \s+ 6/); qr/Node count, DISPLAY \s+ 44 \s+ 27 \s+ 27 \s+ 6/);
ok(1); ok(1);
1; 1;

View File

@ -19,6 +19,10 @@ module t (/*AUTOARG*/);
$write("should "); $write("should ");
$display("merge"); $display("merge");
$display("Merge:");
$write("This ", "", "should ", "", "also ");
$display("merge");
$display("f"); $display("f");
$write(" 1=%0d a=%m 1=%0d", one, one); $write(" 1=%0d a=%m 1=%0d", one, one);
$display(" 1=%0d b=%m 1=%0d", one, one); $display(" 1=%0d b=%m 1=%0d", one, one);

View File

@ -14,7 +14,8 @@ scenarios(vltmt => 1);
top_filename("t/t_gen_alw.v"); top_filename("t/t_gen_alw.v");
compile( compile(
v_flags2 => ["--debug --debugi 5 --threads 2"] v_flags2 => ["--debug --debugi 5"],
threads => 2
); );
foreach my $dotname ("linkcells", "task_call", "gate_simp", "gate_opt", foreach my $dotname ("linkcells", "task_call", "gate_simp", "gate_opt",

View File

@ -26,11 +26,9 @@ using std::hex;
using std::setfill; using std::setfill;
using std::setw; using std::setw;
double sc_time_stamp() { return 0; }
// Convenience function to check we didn't finish unexpectedly // Convenience function to check we didn't finish unexpectedly
static void checkFinish(const char* msg) { static void checkFinish(VerilatedContext* contextp, const char* msg) {
if (Verilated::gotFinish()) { if (contextp->gotFinish()) {
vl_fatal(__FILE__, __LINE__, "dut", msg); vl_fatal(__FILE__, __LINE__, "dut", msg);
exit(1); exit(1);
} }
@ -61,7 +59,9 @@ static void checkResult(bool p, const char* msg_fail) {
// Main function instantiates the model and steps through the test. // Main function instantiates the model and steps through the test.
int main() { int main() {
Vt_dpi_accessors* dut = new Vt_dpi_accessors("dut"); const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
const std::unique_ptr<VM_PREFIX> dut{new VM_PREFIX{contextp.get(), "dut"}};
svScope scope = svGetScopeFromName("dut.t"); svScope scope = svGetScopeFromName("dut.t");
if (!scope) vl_fatal(__FILE__, __LINE__, "dut", "No svGetScopeFromName result"); if (!scope) vl_fatal(__FILE__, __LINE__, "dut", "No svGetScopeFromName result");
svSetScope(scope); svSetScope(scope);
@ -112,7 +112,7 @@ int main() {
cout << "===============================\n"; cout << "===============================\n";
#endif #endif
for (int i = 0; !Verilated::gotFinish() && (i < 4); i++) { for (int i = 0; !contextp->gotFinish() && (i < 4); i++) {
dut->clk = 1 - dut->clk; dut->clk = 1 - dut->clk;
a = (int)a_read(); a = (int)a_read();
logReg(dut->clk, "read a", a, " (before clk)"); logReg(dut->clk, "read a", a, " (before clk)");
@ -130,7 +130,7 @@ int main() {
"Test of scalar register reading failed."); "Test of scalar register reading failed.");
} }
checkFinish("t_dpi_accessors unexpected finish"); checkFinish(contextp.get(), "t_dpi_accessors unexpected finish");
// Check we can read a vector register. // Check we can read a vector register.
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
@ -138,7 +138,7 @@ int main() {
cout << "===============================\n"; cout << "===============================\n";
#endif #endif
for (int i = 0; !Verilated::gotFinish() && (i < 4); i++) { for (int i = 0; !contextp->gotFinish() && (i < 4); i++) {
dut->clk = 1 - dut->clk; dut->clk = 1 - dut->clk;
b = (int)b_read(); b = (int)b_read();
logRegHex(dut->clk, "read b", 8, b, " (before clk)"); logRegHex(dut->clk, "read b", 8, b, " (before clk)");
@ -153,7 +153,7 @@ int main() {
"Test of vector register reading failed."); "Test of vector register reading failed.");
} }
checkFinish("t_dpi_accessors unexpected finish"); checkFinish(contextp.get(), "t_dpi_accessors unexpected finish");
// Test we can read an array element // Test we can read an array element
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
@ -162,7 +162,7 @@ int main() {
cout << "=============================\n"; cout << "=============================\n";
#endif #endif
for (int i = 0; !Verilated::gotFinish() && (i < 4); i++) { for (int i = 0; !contextp->gotFinish() && (i < 4); i++) {
dut->clk = 1 - dut->clk; dut->clk = 1 - dut->clk;
mem32 = (int)mem32_read(); mem32 = (int)mem32_read();
logRegHex(dut->clk, "read mem32", 8, mem32, " (before clk)"); logRegHex(dut->clk, "read mem32", 8, mem32, " (before clk)");
@ -177,7 +177,7 @@ int main() {
checkResult(mem32 == 0x20, "Test of array element reading failed."); checkResult(mem32 == 0x20, "Test of array element reading failed.");
} }
checkFinish("t_dpi_accessors unexpected finish"); checkFinish(contextp.get(), "t_dpi_accessors unexpected finish");
// Check we can read a scalar wire // Check we can read a scalar wire
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
@ -186,7 +186,7 @@ int main() {
cout << "===========================\n"; cout << "===========================\n";
#endif #endif
for (int i = 0; !Verilated::gotFinish() && (i < 4); i++) { for (int i = 0; !contextp->gotFinish() && (i < 4); i++) {
dut->clk = 1 - dut->clk; dut->clk = 1 - dut->clk;
a = (int)a_read(); a = (int)a_read();
c = (int)c_read(); c = (int)c_read();
@ -206,7 +206,7 @@ int main() {
checkResult(c == (1 - a), "Test of scalar wire reading failed."); checkResult(c == (1 - a), "Test of scalar wire reading failed.");
} }
checkFinish("t_dpi_accessors unexpected finish"); checkFinish(contextp.get(), "t_dpi_accessors unexpected finish");
// Check we can read a vector wire // Check we can read a vector wire
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
@ -215,7 +215,7 @@ int main() {
cout << "===========================\n"; cout << "===========================\n";
#endif #endif
for (int i = 0; !Verilated::gotFinish() && (i < 4); i++) { for (int i = 0; !contextp->gotFinish() && (i < 4); i++) {
dut->clk = 1 - dut->clk; dut->clk = 1 - dut->clk;
b = (int)b_read(); b = (int)b_read();
d = (int)d_read(); d = (int)d_read();
@ -236,7 +236,7 @@ int main() {
checkResult(d == ((~b) & 0xff), "Test of vector wire reading failed."); checkResult(d == ((~b) & 0xff), "Test of vector wire reading failed.");
} }
checkFinish("t_dpi_accessors unexpected finish"); checkFinish(contextp.get(), "t_dpi_accessors unexpected finish");
// Check we can write a scalar register // Check we can write a scalar register
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
@ -245,7 +245,7 @@ int main() {
cout << "===============================\n"; cout << "===============================\n";
#endif #endif
for (int i = 0; !Verilated::gotFinish() && (i < 4); i++) { for (int i = 0; !contextp->gotFinish() && (i < 4); i++) {
dut->clk = 1 - dut->clk; dut->clk = 1 - dut->clk;
a = 1 - (int)a_read(); a = 1 - (int)a_read();
a_write(reinterpret_cast<const svBitVecVal*>(&a)); a_write(reinterpret_cast<const svBitVecVal*>(&a));
@ -265,7 +265,7 @@ int main() {
"Test of scalar register writing failed."); "Test of scalar register writing failed.");
} }
checkFinish("t_dpi_accessors unexpected finish"); checkFinish(contextp.get(), "t_dpi_accessors unexpected finish");
// Check we can write a vector register // Check we can write a vector register
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
@ -274,7 +274,7 @@ int main() {
cout << "===============================\n"; cout << "===============================\n";
#endif #endif
for (int i = 0; !Verilated::gotFinish() && (i < 4); i++) { for (int i = 0; !contextp->gotFinish() && (i < 4); i++) {
dut->clk = 1 - dut->clk; dut->clk = 1 - dut->clk;
b = (int)b_read() - 1; b = (int)b_read() - 1;
b_write(reinterpret_cast<const svBitVecVal*>(&b)); b_write(reinterpret_cast<const svBitVecVal*>(&b));
@ -294,7 +294,7 @@ int main() {
"Test of vector register writing failed."); "Test of vector register writing failed.");
} }
checkFinish("t_dpi_accessors unexpected finish"); checkFinish(contextp.get(), "t_dpi_accessors unexpected finish");
// Test we can write an array element // Test we can write an array element
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
@ -303,7 +303,7 @@ int main() {
cout << "=============================\n"; cout << "=============================\n";
#endif #endif
for (int i = 0; !Verilated::gotFinish() && (i < 4); i++) { for (int i = 0; !contextp->gotFinish() && (i < 4); i++) {
dut->clk = 1 - dut->clk; dut->clk = 1 - dut->clk;
mem32 = (int)mem32_read() - 1; mem32 = (int)mem32_read() - 1;
mem32_write(reinterpret_cast<const svBitVecVal*>(&mem32)); mem32_write(reinterpret_cast<const svBitVecVal*>(&mem32));
@ -323,7 +323,7 @@ int main() {
checkResult(mem32_after == mem32, "Test of array element writing failed."); checkResult(mem32_after == mem32, "Test of array element writing failed.");
} }
checkFinish("t_dpi_accessors unexpected finish"); checkFinish(contextp.get(), "t_dpi_accessors unexpected finish");
// Check we can read a vector register slice // Check we can read a vector register slice
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
@ -332,7 +332,7 @@ int main() {
cout << "=====================================\n"; cout << "=====================================\n";
#endif #endif
for (int i = 0; !Verilated::gotFinish() && (i < 4); i++) { for (int i = 0; !contextp->gotFinish() && (i < 4); i++) {
dut->clk = 1 - dut->clk; dut->clk = 1 - dut->clk;
b = (int)b_read(); b = (int)b_read();
int b_slice = (int)b_slice_read(); int b_slice = (int)b_slice_read();
@ -350,7 +350,7 @@ int main() {
checkResult(b_slice == (b & 0x0f), "Test of vector register slice reading failed."); checkResult(b_slice == (b & 0x0f), "Test of vector register slice reading failed.");
} }
checkFinish("t_dpi_accessors unexpected finish"); checkFinish(contextp.get(), "t_dpi_accessors unexpected finish");
// Test we can read an array element slice // Test we can read an array element slice
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
@ -359,7 +359,7 @@ int main() {
cout << "===================================\n"; cout << "===================================\n";
#endif #endif
for (int i = 0; !Verilated::gotFinish() && (i < 4); i++) { for (int i = 0; !contextp->gotFinish() && (i < 4); i++) {
dut->clk = 1 - dut->clk; dut->clk = 1 - dut->clk;
mem32 = (int)mem32_read(); mem32 = (int)mem32_read();
int mem32_slice = (int)mem32_slice_read(); int mem32_slice = (int)mem32_slice_read();
@ -379,7 +379,7 @@ int main() {
"Test of array element slice reading failed."); "Test of array element slice reading failed.");
} }
checkFinish("t_dpi_accessors unexpected finish"); checkFinish(contextp.get(), "t_dpi_accessors unexpected finish");
// Check we can read a vector wire slice // Check we can read a vector wire slice
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
@ -388,7 +388,7 @@ int main() {
cout << "=================================\n"; cout << "=================================\n";
#endif #endif
for (int i = 0; !Verilated::gotFinish() && (i < 4); i++) { for (int i = 0; !contextp->gotFinish() && (i < 4); i++) {
dut->clk = 1 - dut->clk; dut->clk = 1 - dut->clk;
b = (int)b_read(); b = (int)b_read();
d = (int)d_read(); d = (int)d_read();
@ -410,7 +410,7 @@ int main() {
checkResult(d_slice == ((d & 0x7e) >> 1), "Test of vector wire slice reading failed."); checkResult(d_slice == ((d & 0x7e) >> 1), "Test of vector wire slice reading failed.");
} }
checkFinish("t_dpi_accessors unexpected finish"); checkFinish(contextp.get(), "t_dpi_accessors unexpected finish");
// Check we can write a vector register slice // Check we can write a vector register slice
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
@ -419,7 +419,7 @@ int main() {
cout << "=====================================\n"; cout << "=====================================\n";
#endif #endif
for (int i = 0; !Verilated::gotFinish() && (i < 4); i++) { for (int i = 0; !contextp->gotFinish() && (i < 4); i++) {
dut->clk = 1 - dut->clk; dut->clk = 1 - dut->clk;
b = (int)b_read(); b = (int)b_read();
@ -449,7 +449,7 @@ int main() {
logRegHex(dut->clk, "read b [3:0]", 4, b_slice, " (after clk)"); logRegHex(dut->clk, "read b [3:0]", 4, b_slice, " (after clk)");
} }
checkFinish("t_dpi_accessors unexpected finish"); checkFinish(contextp.get(), "t_dpi_accessors unexpected finish");
// Test we can write an array element slice // Test we can write an array element slice
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
@ -458,7 +458,7 @@ int main() {
cout << "===================================\n"; cout << "===================================\n";
#endif #endif
for (int i = 0; !Verilated::gotFinish() && (i < 4); i++) { for (int i = 0; !contextp->gotFinish() && (i < 4); i++) {
dut->clk = 1 - dut->clk; dut->clk = 1 - dut->clk;
mem32 = (int)mem32_read(); mem32 = (int)mem32_read();
@ -494,7 +494,7 @@ int main() {
"Test of array element slice writing failed."); "Test of array element slice writing failed.");
} }
checkFinish("t_dpi_accessors unexpected finish"); checkFinish(contextp.get(), "t_dpi_accessors unexpected finish");
// Check we can read complex registers // Check we can read complex registers
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
@ -503,7 +503,7 @@ int main() {
cout << "================================\n"; cout << "================================\n";
#endif #endif
for (int i = 0; !Verilated::gotFinish() && (i < 4); i++) { for (int i = 0; !contextp->gotFinish() && (i < 4); i++) {
dut->clk = 1 - dut->clk; dut->clk = 1 - dut->clk;
b = (int)b_read(); b = (int)b_read();
@ -540,9 +540,9 @@ int main() {
cout << endl; cout << endl;
#endif #endif
checkFinish("t_dpi_accessors unexpected finish"); checkFinish(contextp.get(), "t_dpi_accessors unexpected finish");
for (int i = 0; !Verilated::gotFinish() && (i < 4); i++) { for (int i = 0; !contextp->gotFinish() && (i < 4); i++) {
dut->clk = 1 - dut->clk; dut->clk = 1 - dut->clk;
e = 0x05 | (i << 4); e = 0x05 | (i << 4);
@ -574,7 +574,7 @@ int main() {
"Test of complex register reading l2 failed."); "Test of complex register reading l2 failed.");
} }
checkFinish("t_dpi_accessors unexpected finish"); checkFinish(contextp.get(), "t_dpi_accessors unexpected finish");
// Test we can write a complex register // Test we can write a complex register
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
@ -583,7 +583,7 @@ int main() {
cout << "================================\n"; cout << "================================\n";
#endif #endif
for (int i = 0; !Verilated::gotFinish() && (i < 4); i++) { for (int i = 0; !contextp->gotFinish() && (i < 4); i++) {
dut->clk = 1 - dut->clk; dut->clk = 1 - dut->clk;
b = (int)b_read(); b = (int)b_read();
@ -632,9 +632,9 @@ int main() {
cout << endl; cout << endl;
#endif #endif
checkFinish("t_dpi_accessors unexpected finish"); checkFinish(contextp.get(), "t_dpi_accessors unexpected finish");
for (int i = 0; !Verilated::gotFinish() && (i < 4); i++) { for (int i = 0; !contextp->gotFinish() && (i < 4); i++) {
dut->clk = 1 - dut->clk; dut->clk = 1 - dut->clk;
e = (int)e_read(); e = (int)e_read();
@ -671,11 +671,10 @@ int main() {
logRegHex(dut->clk, "read l2", 8, l2, " (before clk)"); logRegHex(dut->clk, "read l2", 8, l2, " (before clk)");
} }
checkFinish("t_dpi_accessors unexpected finish"); checkFinish(contextp.get(), "t_dpi_accessors unexpected finish");
// Tidy up // Tidy up
dut->final(); dut->final();
VL_DO_DANGLING(delete dut, dut);
cout << "*-* All Finished *-*\n"; cout << "*-* All Finished *-*\n";
} }

View File

@ -110,39 +110,39 @@ void mon_eval() {
//====================================================================== //======================================================================
unsigned int main_time = 0;
double sc_time_stamp() { return main_time; }
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
uint64_t sim_time = 1100; const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
Verilated::commandArgs(argc, argv);
Verilated::debug(0);
VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out uint64_t sim_time = 1100;
contextp->commandArgs(argc, argv);
contextp->debug(0);
const std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX{contextp.get(),
// Note null name - we're flattening it out
""}};
// clang-format off // clang-format off
#ifdef VERILATOR #ifdef VERILATOR
# ifdef TEST_VERBOSE # ifdef TEST_VERBOSE
Verilated::scopesDump(); contextp->scopesDump();
# endif # endif
#endif #endif
// clang-format on // clang-format on
topp->eval(); topp->eval();
topp->clk = 0; topp->clk = 0;
main_time += 10; contextp->timeInc(10);
while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { while (contextp->time() < sim_time && !contextp->gotFinish()) {
main_time += 1; contextp->timeInc(1);
topp->eval(); topp->eval();
topp->clk = !topp->clk; topp->clk = !topp->clk;
// mon_do(); // mon_do();
} }
if (!Verilated::gotFinish()) { if (!contextp->gotFinish()) {
vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish"); vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish");
} }
topp->final(); topp->final();
VL_DO_DANGLING(delete topp, topp);
return 0; return 0;
} }

View File

@ -22,6 +22,8 @@ mkdir $child_dir;
(VM_PREFIX => "$Self->{VM_PREFIX}_child", (VM_PREFIX => "$Self->{VM_PREFIX}_child",
top_filename => "$Self->{name}_child.v", top_filename => "$Self->{name}_child.v",
verilator_flags => ["-cc", "-Mdir", "${child_dir}", "--debug-check"], verilator_flags => ["-cc", "-Mdir", "${child_dir}", "--debug-check"],
# Can't use multi threading (like hier blocks), but needs to be thread safe
threads => $Self->{vltmt} ? 1 : 0,
); );
run(logfile => "${child_dir}/vlt_compile.log", run(logfile => "${child_dir}/vlt_compile.log",

View File

@ -18,8 +18,9 @@ scenarios(vlt_all => 1);
top_filename("t/t_gen_alw.v"); top_filename("t/t_gen_alw.v");
compile( compile(
v_flags2 => ["--prof-exec"],
# Checks below care about thread count, so use 2 (minimum reasonable) # Checks below care about thread count, so use 2 (minimum reasonable)
v_flags2 => ["--prof-exec", ($Self->{vltmt} ? "--threads 2" : "")] threads => $Self->{vltmt} ? 2 : 0
); );
execute( execute(

View File

@ -0,0 +1,43 @@
//
// DESCRIPTION: Verilator: Verilog Multiple Model Test Module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Geza Lore.
// SPDX-License-Identifier: CC0-1.0
//
#include <memory>
#include "verilated.h"
#include "Vt_gantt_two.h"
int main(int argc, char** argv, char** env) {
srand48(5);
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
#ifdef VL_THREADED
contextp->threads(2);
#endif
contextp->commandArgs(argc, argv);
contextp->debug(0);
std::unique_ptr<Vt_gantt_two> topap{new Vt_gantt_two{contextp.get(), "topa"}};
std::unique_ptr<Vt_gantt_two> topbp{new Vt_gantt_two{contextp.get(), "topb"}};
topap->clk = false;
topap->eval();
topbp->clk = false;
topbp->eval();
contextp->timeInc(10);
while ((contextp->time() < 1100) && !contextp->gotFinish()) {
topap->clk = !topap->clk;
topap->eval();
topbp->clk = !topbp->clk;
topbp->eval();
contextp->timeInc(5);
}
if (!contextp->gotFinish()) {
vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish");
}
return 0;
}

61
test_regress/t/t_gantt_two.pl Executable file
View File

@ -0,0 +1,61 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
# Test for bin/verilator_gantt,
scenarios(vlt_all => 1);
# It doesn't really matter what test
# we use, so long as it runs several cycles,
# enough for the profiling to happen:
top_filename("t/t_gen_alw.v");
compile(
make_top_shell => 0,
make_main => 0,
v_flags2 => ["--prof-exec --exe $Self->{t_dir}/$Self->{name}.cpp"],
# Checks below care about thread count, so use 2 (minimum reasonable)
threads => $Self->{vltmt} ? 2 : 0,
make_flags => 'CPPFLAGS_ADD=-DVL_NO_LEGACY',
);
execute(
all_run_flags => ["+verilator+prof+exec+start+4",
" +verilator+prof+exec+window+4",
" +verilator+prof+exec+file+$Self->{obj_dir}/profile_exec.dat",
" +verilator+prof+vlt+file+$Self->{obj_dir}/profile.vlt",
],
check_finished => 1,
);
# For now, verilator_gantt still reads from STDIN
# (probably it should take a file, gantt.dat like verilator_profcfunc)
# The profiling data still goes direct to the runtime's STDOUT
# (maybe that should go to a separate file - gantt.dat?)
run(cmd => ["$ENV{VERILATOR_ROOT}/bin/verilator_gantt",
"$Self->{obj_dir}/profile_exec.dat",
"--vcd $Self->{obj_dir}/profile_exec.vcd",
"| tee $Self->{obj_dir}/gantt.log"],
);
if ($Self->{vltmt}) {
file_grep("$Self->{obj_dir}/gantt.log", qr/Total threads += 2/i);
file_grep("$Self->{obj_dir}/gantt.log", qr/Total mtasks += 7/i);
} else {
file_grep("$Self->{obj_dir}/gantt.log", qr/Total threads += 1/i);
file_grep("$Self->{obj_dir}/gantt.log", qr/Total mtasks += 0/i);
}
file_grep("$Self->{obj_dir}/gantt.log", qr/Total evals += 4/i);
# Diff to itself, just to check parsing
vcd_identical("$Self->{obj_dir}/profile_exec.vcd", "$Self->{obj_dir}/profile_exec.vcd");
ok(1);
1;

View File

@ -18,11 +18,12 @@ scenarios(vlt_all => 1);
compile( compile(
v_flags2 => ['t/t_hier_block.cpp'], v_flags2 => ['t/t_hier_block.cpp'],
verilator_flags2 => ['--stats', ($Self->{vltmt} ? ' --threads 6' : ''), verilator_flags2 => ['--stats',
'--hierarchical', '--hierarchical',
'--Wno-TIMESCALEMOD', '--Wno-TIMESCALEMOD',
'--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus"' '--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus"'
], ],
threads => $Self->{vltmt} ? 6 : 0
); );
execute( execute(

View File

@ -13,13 +13,17 @@
#include "Vt_hier_block.h" #include "Vt_hier_block.h"
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
std::unique_ptr<Vt_hier_block> top{new Vt_hier_block("top")}; const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
Verilated::commandArgs(argc, argv); #if VL_THREADED
for (int i = 0; i < 100 && !Verilated::gotFinish(); ++i) { contextp->threads(6);
#endif
contextp->commandArgs(argc, argv);
std::unique_ptr<Vt_hier_block> top{new Vt_hier_block{contextp.get(), "top"}};
for (int i = 0; i < 100 && !contextp->gotFinish(); ++i) {
top->eval(); top->eval();
top->clk ^= 1; top->clk ^= 1;
} }
if (!Verilated::gotFinish()) { if (!contextp->gotFinish()) {
vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish"); vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish");
} }
top->final(); top->final();

View File

@ -23,8 +23,8 @@ compile(
v_flags2 => ['t/t_hier_block.cpp'], v_flags2 => ['t/t_hier_block.cpp'],
verilator_flags2 => ['--stats', verilator_flags2 => ['--stats',
'+define+USE_VLT', 't/t_hier_block_vlt.vlt', '+define+USE_VLT', 't/t_hier_block_vlt.vlt',
'--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus"', '--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus"'],
($Self->{vltmt} ? ' --threads 6' : '')], threads => $Self->{vltmt} ? 6 : 0
); );
execute( execute(

View File

@ -22,9 +22,9 @@ compile(
verilator_flags2 => ['--sc', verilator_flags2 => ['--sc',
'--stats', '--stats',
'--hierarchical', '--hierarchical',
($Self->{vltmt} ? ' --threads 6' : ''),
'--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus"' '--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus"'
], ],
threads => $Self->{vltmt} ? 6 : 0
); );
execute( execute(

View File

@ -22,11 +22,11 @@ compile(
verilator_flags2 => ['--sc', verilator_flags2 => ['--sc',
'--stats', '--stats',
'--hierarchical', '--hierarchical',
($Self->{vltmt} ? ' --threads 6' : ''),
'--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus"', '--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus"',
"--CFLAGS", '"-O0 -ggdb"', "--CFLAGS", '"-O0 -ggdb"',
"--trace-fst" "--trace-fst"
], ],
threads => $Self->{vltmt} ? 6 : 0
); );
execute( execute(

View File

@ -22,11 +22,11 @@ compile(
verilator_flags2 => ['--sc', verilator_flags2 => ['--sc',
'--stats', '--stats',
'--hierarchical', '--hierarchical',
($Self->{vltmt} ? ' --threads 6' : ''),
'--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus"', '--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus"',
"--CFLAGS", '"-O0 -ggdb"', "--CFLAGS", '"-O0 -ggdb"',
"--trace" "--trace"
], ],
threads => $Self->{vltmt} ? 6 : 0
); );
execute( execute(

View File

@ -17,12 +17,12 @@ top_filename("t/t_hier_block.v");
compile( compile(
v_flags2 => ['t/t_hier_block.cpp'], v_flags2 => ['t/t_hier_block.cpp'],
verilator_flags2 => [($Self->{vltmt} ? ' --threads 6' : ''), verilator_flags2 => ['--hierarchical',
'--hierarchical',
'--Wno-TIMESCALEMOD', '--Wno-TIMESCALEMOD',
'--trace-fst', '--trace-fst',
'--no-trace-underscore', # To avoid handle mismatches '--no-trace-underscore', # To avoid handle mismatches
], ],
threads => $Self->{vltmt} ? 6 : 0
); );
execute( execute(

View File

@ -17,12 +17,12 @@ top_filename("t/t_hier_block.v");
compile( compile(
v_flags2 => ['t/t_hier_block.cpp'], v_flags2 => ['t/t_hier_block.cpp'],
verilator_flags2 => [($Self->{vltmt} ? ' --threads 6' : ''), verilator_flags2 => ['--hierarchical',
'--hierarchical',
'--Wno-TIMESCALEMOD', '--Wno-TIMESCALEMOD',
'--trace', '--trace',
'--no-trace-underscore', # To avoid handle mismatches '--no-trace-underscore', # To avoid handle mismatches
], ],
threads => $Self->{vltmt} ? 6 : 0
); );
execute( execute(

View File

@ -22,8 +22,8 @@ compile(
'--hierarchical', '--hierarchical',
'+define+SHOW_TIMESCALE', '+define+SHOW_TIMESCALE',
'+define+USE_VLT', 't/t_hier_block_vlt.vlt', '+define+USE_VLT', 't/t_hier_block_vlt.vlt',
'--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus"', '--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus"'],
($Self->{vltmt} ? ' --threads 6' : '')], threads => $Self->{vltmt} ? 6 : 0
); );
execute( execute(

View File

@ -14,9 +14,9 @@ top_filename("t/t_inst_tree.v");
my $default_vltmt_threads = $Self->get_default_vltmt_threads(); my $default_vltmt_threads = $Self->get_default_vltmt_threads();
compile( compile(
verilator_flags2 => ['--stats', "$Self->{t_dir}/$Self->{name}.vlt", verilator_flags2 => ['--stats', "$Self->{t_dir}/$Self->{name}.vlt"],
# Force 3 threads even if we have fewer cores # Force 3 threads even if we have fewer cores
$Self->{vltmt} ? "--threads $default_vltmt_threads" : ""] threads => $Self->{vltmt} ? $default_vltmt_threads : 0
); );
sub checkRelativeRefs { sub checkRelativeRefs {

View File

@ -48,11 +48,12 @@ void make_and_destroy() {
#ifdef VL_NO_LEGACY #ifdef VL_NO_LEGACY
VerilatedContext* contextp = new VerilatedContext; VerilatedContext* contextp = new VerilatedContext;
VM_PREFIX* topp = new VM_PREFIX{contextp}; VM_PREFIX* topp = new VM_PREFIX{contextp};
contextp->debug(0);
#else #else
VM_PREFIX* topp = new VM_PREFIX; VM_PREFIX* topp = new VM_PREFIX;
Verilated::debug(0);
#endif #endif
Verilated::debug(0);
topp->eval(); topp->eval();
topp->clk = true; topp->clk = true;
while (! while (!

View File

@ -56,10 +56,11 @@ while (1) {
compile( compile(
verilator_flags2 => ["$secret_dir/secret.sv", verilator_flags2 => ["$secret_dir/secret.sv",
($Self->{vltmt} ? ' --threads 1' : ''),
"-LDFLAGS", "-LDFLAGS",
"'-Wl,-rpath,$abs_secret_dir -L$abs_secret_dir -l$secret_prefix'"], "'-Wl,-rpath,$abs_secret_dir -L$abs_secret_dir -l$secret_prefix'"],
xsim_flags2 => ["$secret_dir/secret.sv"], xsim_flags2 => ["$secret_dir/secret.sv"],
threads => $Self->{vltmt} ? 1 : 0,
context_threads => $Self->{vltmt} ? 6 : 1
); );
execute( execute(

View File

@ -0,0 +1,12 @@
Clocked
Clocked
Clocked
Clocked
Clocked
Clocked
Clocked
Clocked
Clocked
Clocked
Clocked
*-* All Finished *-*

View File

@ -0,0 +1,23 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(simulator => 1);
compile(
verilator_flags2 => ["--stats"],
);
execute(
check_finished => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,44 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t (/*AUTOARG*/
// Outputs
test,
// Inputs
clk
);
input clk;
output reg [5:0] test;
parameter STATE1 = 6'b000001;
parameter STATE2 = 6'b000010;
parameter STATE3 = 6'b000100;
parameter STATE4 = 6'b001000;
parameter STATE5 = 6'b010000;
parameter STATE6 = 6'b100000;
always @(posedge clk) begin
$display("Clocked");
case (test)
STATE1: test <= STATE2;
STATE2: test <= STATE3;
STATE3: test <= STATE4;
STATE4: test <= STATE5;
STATE5: test <= STATE6;
default: test <= STATE1;
endcase
end
int cyc;
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 10) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(vltmt => 1); scenarios(vltmt => 1);
compile( compile(
v_flags2 => ["--threads 2"], threads => 2,
fails => 1, fails => 1,
expect_filename => $Self->{golden_filename}, expect_filename => $Self->{golden_filename},
); );

View File

@ -14,7 +14,8 @@ scenarios(vltmt => 1);
top_filename("t/t_gen_alw.v"); top_filename("t/t_gen_alw.v");
compile( compile(
v_flags2 => ["--prof-pgo --threads 2"] v_flags2 => ["--prof-pgo"],
threads => 2
); );
execute( execute(
@ -30,8 +31,8 @@ file_grep("$Self->{obj_dir}/profile.vlt", qr/profile_data/i);
compile( compile(
# Intentinally no --prof-pgo here to make sure profile data can be read in # Intentinally no --prof-pgo here to make sure profile data can be read in
# without it (that is: --prof-pgo has no effect on profile_data hash names) # without it (that is: --prof-pgo has no effect on profile_data hash names)
v_flags2 => ["--threads 2", v_flags2 => [" $Self->{obj_dir}/profile.vlt"],
" $Self->{obj_dir}/profile.vlt"], threads => 2
); );
execute( execute(

View File

@ -14,8 +14,8 @@ scenarios(simulator => 1);
# %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads. # %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads.
# So use 6 threads here though it's not optimal in performace wise, but ok. # So use 6 threads here though it's not optimal in performace wise, but ok.
compile( compile(
verilator_flags2 => ['--stats' . ($Self->{vltmt} ? ' --threads 6' : ''), verilator_flags2 => ['--stats', "$Self->{t_dir}/t_split_var_0.vlt"],
"$Self->{t_dir}/t_split_var_0.vlt"], threads => $Self->{vltmt} ? 6 : 0
); );
execute( execute(

View File

@ -15,8 +15,8 @@ top_filename("t/t_split_var_0.v");
# %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads. # %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads.
# So use 6 threads here though it's not optimal in performace wise, but ok. # So use 6 threads here though it's not optimal in performace wise, but ok.
compile( compile(
verilator_flags2 => ['--cc --trace --stats' . ($Self->{vltmt} ? ' --threads 6' : ''), verilator_flags2 => ['--cc --trace --stats +define+TEST_ATTRIBUTES'],
'+define+TEST_ATTRIBUTES'], threads => $Self->{vltmt} ? 6 : 0
); );
execute( execute(

View File

@ -22,6 +22,12 @@ module t;
//if ($test$plusargs("")!==1) $stop; // Simulators differ in this answer //if ($test$plusargs("")!==1) $stop; // Simulators differ in this answer
if ($test$plusargs("NOTTHERE")!==0) $stop; if ($test$plusargs("NOTTHERE")!==0) $stop;
sv_in = "PLUS";
`ifdef VERILATOR
if ($c1(0)) sv_in = "NEVER"; // Prevent constant propagation
`endif
if ($test$plusargs(sv_in)!==1) $stop;
p_i = 10; p_i = 10;
if ($value$plusargs("NOTTHERE%d", p_i) !== 0) $stop; if ($value$plusargs("NOTTHERE%d", p_i) !== 0) $stop;
if ($value$plusargs("NOTTHERE%0d", p_i) !== 0) $stop; if ($value$plusargs("NOTTHERE%0d", p_i) !== 0) $stop;

View File

@ -13,7 +13,8 @@ scenarios(simulator => 1);
top_filename("t/t_threads_counter.v"); top_filename("t/t_threads_counter.v");
compile( compile(
verilator_flags2 => ['--cc --no-threads'], verilator_flags2 => ['--cc'],
threads => 0,
); );
execute( execute(

View File

@ -13,7 +13,8 @@ scenarios(vltmt => 1);
top_filename("t/t_threads_counter.v"); top_filename("t/t_threads_counter.v");
compile( compile(
verilator_flags2 => ['--cc --threads 1'], verilator_flags2 => ['--cc'],
threads => 1
); );
execute( execute(

View File

@ -13,7 +13,8 @@ scenarios(vltmt => 1);
top_filename("t/t_threads_counter.v"); top_filename("t/t_threads_counter.v");
compile( compile(
verilator_flags2 => ['--cc --threads 2'], verilator_flags2 => ['--cc'],
threads => 2
); );
execute( execute(

View File

@ -13,7 +13,8 @@ scenarios(vltmt => 1);
top_filename("t/t_threads_counter.v"); top_filename("t/t_threads_counter.v");
compile( compile(
verilator_flags2 => ['--cc --threads 4'], verilator_flags2 => ['--cc'],
threads => 4
); );
execute( execute(

View File

@ -10,19 +10,16 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(vltmt => 1); scenarios(vltmt => 1);
if ($Self->cfg_with_m32) {
skip("Does not work with -m32 (resource unavailable)");
}
compile( compile(
verilator_flags2 => ['--cc --threads 1024'], verilator_flags2 => ['--cc'],
threads => 4,
context_threads => 2
); );
execute( execute(
check_finished => 1, fails => 1
); );
file_grep($Self->{run_log_filename}, qr/System has .* CPUs but.*--threads 1024/); file_grep($Self->{run_log_filename}, qr/%Error: .*\/verilated\.cpp:\d+: VerilatedContext has 2 threads but model 'Vt_threads_crazy' \(instantiated as 'top'\) was Verilated with --threads 4\./);
ok(1); ok(1);
1; 1;

View File

@ -0,0 +1,36 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003-2009 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(vlt_all => 1);
if ($Self->cfg_with_m32) {
skip("Does not work with -m32 (resource unavailable)");
}
top_filename("t/t_threads_crazy.v");
compile(
verilator_flags2 => ['--cc'],
threads => $Self->{vltmt} ? 2 : 0,
context_threads => 1024
);
execute(
check_finished => 1,
);
if ($Self->{vltmt}) {
file_grep($Self->{run_log_filename}, qr/System has \d+ hardware threads but simulation thread count set to 1024\. This will likely cause significant slowdown\./);
} else {
file_grep($Self->{run_log_filename}, qr/Verilator run-time library built without VL_THREADS\. Ignoring call to 'VerilatedContext::threads' with argument 1024\./);
}
ok(1);
1;

View File

@ -13,7 +13,8 @@ scenarios(vltmt => 1);
top_filename("t/t_threads_counter.v"); top_filename("t/t_threads_counter.v");
compile( compile(
verilator_flags2 => ['--cc --threads 2 --debug-nondeterminism --no-skip-identical'], verilator_flags2 => ['--cc --debug-nondeterminism --no-skip-identical'],
threads => 2
); );
execute( execute(

View File

@ -14,8 +14,8 @@ scenarios(simulator => 1);
# %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads. # %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads.
# Strangely, asking for more threads makes it go away. # Strangely, asking for more threads makes it go away.
compile( compile(
verilator_flags2 => ['--cc --trace --trace-params -Wno-LITENDIAN', verilator_flags2 => ['--cc --trace --trace-params -Wno-LITENDIAN'],
($Self->{vltmt} ? '--threads 6' : '')], threads => $Self->{vltmt} ? 6 : 0
); );
execute( execute(

View File

@ -16,8 +16,8 @@ top_filename("t/t_trace_litendian.v");
# %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads. # %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads.
# Strangely, asking for more threads makes it go away. # Strangely, asking for more threads makes it go away.
compile( compile(
verilator_flags2 => ['--cc --trace-fst --trace-params -Wno-LITENDIAN', verilator_flags2 => ['--cc --trace-fst --trace-params -Wno-LITENDIAN'],
($Self->{vltmt} ? '--threads 6' : '')], threads => $Self->{vltmt} ? 6 : 0
); );
execute( execute(

View File

@ -20,8 +20,8 @@ else {
# %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads. # %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads.
# Strangely, asking for more threads makes it go away. # Strangely, asking for more threads makes it go away.
compile( compile(
verilator_flags2 => ['--sc --trace-fst --trace-params -Wno-LITENDIAN', verilator_flags2 => ['--sc --trace-fst --trace-params -Wno-LITENDIAN'],
($Self->{vltmt} ? '--threads 6' : '')], threads => $Self->{vltmt} ? 6 : 0
); );
execute( execute(

View File

@ -25,21 +25,21 @@
VM_PREFIX* ap; VM_PREFIX* ap;
Vt_trace_two_b* bp; Vt_trace_two_b* bp;
uint64_t main_time = 0;
double sc_time_stamp() { return main_time; }
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
uint64_t sim_time = 1100; uint64_t sim_time = 1100;
Verilated::commandArgs(argc, argv); contextp->commandArgs(argc, argv);
Verilated::debug(0); contextp->debug(0);
Verilated::traceEverOn(true); contextp->traceEverOn(true);
srand48(5); srand48(5);
ap = new VM_PREFIX("topa"); ap = new VM_PREFIX{contextp.get(), "topa"};
bp = new Vt_trace_two_b("topb"); bp = new Vt_trace_two_b{contextp.get(), "topb"};
// clang-format off // clang-format off
#ifdef TEST_HDR_TRACE #ifdef TEST_HDR_TRACE
Verilated::traceEverOn(true); contextp->traceEverOn(true);
# ifdef TEST_FST # ifdef TEST_FST
VerilatedFstC* tfp = new VerilatedFstC; VerilatedFstC* tfp = new VerilatedFstC;
ap->trace(tfp, 99); ap->trace(tfp, 99);
@ -59,14 +59,14 @@ int main(int argc, char** argv, char** env) {
bp->eval_step(); bp->eval_step();
ap->eval_end_step(); ap->eval_end_step();
bp->eval_end_step(); bp->eval_end_step();
if (tfp) tfp->dump(main_time); if (tfp) tfp->dump(contextp->time());
#endif #endif
{ {
ap->clk = false; ap->clk = false;
main_time += 10; contextp->timeInc(10);
} }
while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { while (contextp->time() < sim_time && !contextp->gotFinish()) {
ap->clk = !ap->clk; ap->clk = !ap->clk;
bp->clk = ap->clk; bp->clk = ap->clk;
ap->eval_step(); ap->eval_step();
@ -74,11 +74,11 @@ int main(int argc, char** argv, char** env) {
ap->eval_end_step(); ap->eval_end_step();
bp->eval_end_step(); bp->eval_end_step();
#ifdef TEST_HDR_TRACE #ifdef TEST_HDR_TRACE
if (tfp) tfp->dump(main_time); if (tfp) tfp->dump(contextp->time());
#endif #endif
main_time += 5; contextp->timeInc(5);
} }
if (!Verilated::gotFinish()) { if (!contextp->gotFinish()) {
vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish"); vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish");
} }
ap->final(); ap->final();

View File

@ -8,8 +8,6 @@
VM_PREFIX* tb = nullptr; VM_PREFIX* tb = nullptr;
double sc_time_stamp() { return 0; }
bool check() { bool check() {
bool pass; bool pass;
int c = (tb->A >> tb->SEL) & 0x1; int c = (tb->A >> tb->SEL) & 0x1;

View File

@ -7,8 +7,6 @@
VM_PREFIX* tb = nullptr; VM_PREFIX* tb = nullptr;
double sc_time_stamp() { return 0; }
int main() { int main() {
Verilated::debug(0); Verilated::debug(0);
tb = new VM_PREFIX("tb"); tb = new VM_PREFIX("tb");

View File

@ -8,7 +8,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Version 2.0. # Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(vlt => 1); scenarios(vltmt => 1);
my $root = ".."; my $root = "..";
@ -17,10 +17,10 @@ compile(
verilator_flags2 => ["--cc", verilator_flags2 => ["--cc",
"--coverage-toggle --coverage-line --coverage-user", "--coverage-toggle --coverage-line --coverage-user",
"--trace --vpi ", "--trace --vpi ",
"--threads 2",
"--trace-threads 1", "--trace-threads 1",
"--prof-exec", "--prof-pgo", "--prof-exec", "--prof-pgo",
"$root/include/verilated_save.cpp"], "$root/include/verilated_save.cpp"],
threads => 2
); );
execute( execute(

View File

@ -16,7 +16,8 @@ my $root = "..";
compile( compile(
# Can't use --coverage and --savable together, so cheat and compile inline # Can't use --coverage and --savable together, so cheat and compile inline
verilator_flags2 => ["--cc --coverage-toggle --coverage-line --coverage-user --trace --threads 1 --vpi $root/include/verilated_save.cpp"], verilator_flags2 => ["--cc --coverage-toggle --coverage-line --coverage-user --trace --vpi $root/include/verilated_save.cpp"],
threads => 1
); );
execute( execute(

View File

@ -143,11 +143,15 @@ static void register_filler_cb() {
double sc_time_stamp() { return main_time; } double sc_time_stamp() { return main_time; }
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
uint64_t sim_time = 100; const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
Verilated::commandArgs(argc, argv);
Verilated::debug(0);
VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out uint64_t sim_time = 100;
contextp->commandArgs(argc, argv);
contextp->debug(0);
const std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX{contextp.get(),
// Note null name - we're flattening it out
""}};
reregister_value_cb(); reregister_value_cb();
TEST_CHECK_NZ(vh_value_cb); TEST_CHECK_NZ(vh_value_cb);
@ -158,7 +162,7 @@ int main(int argc, char** argv, char** env) {
topp->eval(); topp->eval();
topp->clk = 0; topp->clk = 0;
while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { while (main_time < sim_time && !contextp->gotFinish()) {
main_time += 1; main_time += 1;
if (verbose) VL_PRINTF("Sim Time %d got_error %d\n", main_time, errors); if (verbose) VL_PRINTF("Sim Time %d got_error %d\n", main_time, errors);
topp->clk = !topp->clk; topp->clk = !topp->clk;
@ -168,11 +172,10 @@ int main(int argc, char** argv, char** env) {
if (errors) vl_stop(__FILE__, __LINE__, "TOP-cpp"); if (errors) vl_stop(__FILE__, __LINE__, "TOP-cpp");
} }
if (!Verilated::gotFinish()) { if (!contextp->gotFinish()) {
vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish"); vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish");
} }
topp->final(); topp->final();
VL_DO_DANGLING(delete topp, topp);
return errors ? 10 : 0; return errors ? 10 : 0;
} }

View File

@ -43,7 +43,6 @@ bool callbacks_expected_called[CB_COUNT] = {false};
std::vector<int>::const_iterator cb_iter; std::vector<int>::const_iterator cb_iter;
std::vector<CallbackState>::const_iterator state_iter; std::vector<CallbackState>::const_iterator state_iter;
unsigned int main_time = 0;
bool got_error = false; bool got_error = false;
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
@ -245,27 +244,29 @@ static int register_test_callback() {
return 0; return 0;
} }
double sc_time_stamp() { return main_time; }
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
uint64_t sim_time = 100; uint64_t sim_time = 100;
bool cbs_called; bool cbs_called;
Verilated::commandArgs(argc, argv); contextp->commandArgs(argc, argv);
VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out const std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX{contextp.get(),
// Note null name - we're flattening it out
""}};
if (verbose) VL_PRINTF("-- { Sim Time %d } --\n", main_time); if (verbose) VL_PRINTF("-- { Sim Time %" PRId64 " } --\n", contextp->time());
register_test_callback(); register_test_callback();
topp->eval(); topp->eval();
topp->clk = 0; topp->clk = 0;
main_time += 1; contextp->timeInc(1);
while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { while (contextp->time() < sim_time && !contextp->gotFinish()) {
if (verbose) { if (verbose) {
VL_PRINTF("-- { Sim Time %d , Callback %s (%d) , Testcase State %d } --\n", main_time, VL_PRINTF("-- { Sim Time %" PRId64 " , Callback %s (%d) , Testcase State %d } --\n",
cb_reason_to_string(*cb_iter), *cb_iter, *state_iter); contextp->time(), cb_reason_to_string(*cb_iter), *cb_iter, *state_iter);
} }
topp->eval(); topp->eval();
@ -285,14 +286,17 @@ int main(int argc, char** argv, char** env) {
VerilatedVpi::callTimedCbs(); VerilatedVpi::callTimedCbs();
main_time = VerilatedVpi::cbNextDeadline(); int64_t next_time = VerilatedVpi::cbNextDeadline();
if (main_time == -1 && !Verilated::gotFinish()) { contextp->time(next_time);
if (verbose) VL_PRINTF("-- { Sim Time %d , No more testcases } --\n", main_time); if (next_time == -1 && !contextp->gotFinish()) {
if (verbose)
VL_PRINTF("-- { Sim Time %" PRId64 " , No more testcases } --\n",
contextp->time());
if (got_error) { if (got_error) {
vl_stop(__FILE__, __LINE__, "TOP-cpp"); vl_stop(__FILE__, __LINE__, "TOP-cpp");
} else { } else {
VL_PRINTF("*-* All Finished *-*\n"); VL_PRINTF("*-* All Finished *-*\n");
Verilated::gotFinish(true); contextp->gotFinish(true);
} }
} }
@ -302,11 +306,10 @@ int main(int argc, char** argv, char** env) {
topp->clk = !topp->clk; topp->clk = !topp->clk;
} }
if (!Verilated::gotFinish()) { if (!contextp->gotFinish()) {
vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish"); vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish");
} }
topp->final(); topp->final();
VL_DO_DANGLING(delete topp, topp);
return 0; return 0;
} }

View File

@ -40,8 +40,6 @@
#define TEST_MSG \ #define TEST_MSG \
if (0) printf if (0) printf
unsigned int main_time = 0;
//====================================================================== //======================================================================
#define CHECK_RESULT_VH(got, exp) \ #define CHECK_RESULT_VH(got, exp) \
@ -240,22 +238,25 @@ void vpi_compat_bootstrap(void) {
void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0};
#else #else
double sc_time_stamp() { return main_time; }
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
uint64_t sim_time = 1100; const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
Verilated::commandArgs(argc, argv);
Verilated::debug(0);
Vt_vpi_get* topp = new Vt_vpi_get(""); // Note null name - we're flattening it out uint64_t sim_time = 1100;
contextp->commandArgs(argc, argv);
contextp->debug(0);
const std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX{contextp.get(),
// Note null name - we're flattening it out
""}};
#ifdef VERILATOR #ifdef VERILATOR
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
Verilated::scopesDump(); contextp->scopesDump();
#endif #endif
#endif #endif
#if VM_TRACE #if VM_TRACE
Verilated::traceEverOn(true); contextp->traceEverOn(true);
VL_PRINTF("Enabling waves...\n"); VL_PRINTF("Enabling waves...\n");
VerilatedVcdC* tfp = new VerilatedVcdC; VerilatedVcdC* tfp = new VerilatedVcdC;
topp->trace(tfp, 99); topp->trace(tfp, 99);
@ -264,19 +265,19 @@ int main(int argc, char** argv, char** env) {
topp->eval(); topp->eval();
topp->clk = 0; topp->clk = 0;
main_time += 10; contextp->timeInc(10);
while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { while (contextp->time() < sim_time && !contextp->gotFinish()) {
main_time += 1; contextp->timeInc(1);
topp->eval(); topp->eval();
VerilatedVpi::callValueCbs(); VerilatedVpi::callValueCbs();
topp->clk = !topp->clk; topp->clk = !topp->clk;
// mon_do(); // mon_do();
#if VM_TRACE #if VM_TRACE
if (tfp) tfp->dump(main_time); if (tfp) tfp->dump(contextp->time());
#endif #endif
} }
if (!Verilated::gotFinish()) { if (!contextp->gotFinish()) {
vl_fatal(FILENM, __LINE__, "main", "%Error: Timeout; never got a $finish"); vl_fatal(FILENM, __LINE__, "main", "%Error: Timeout; never got a $finish");
} }
topp->final(); topp->final();
@ -285,7 +286,6 @@ int main(int argc, char** argv, char** env) {
if (tfp) tfp->close(); if (tfp) tfp->close();
#endif #endif
VL_DO_DANGLING(delete topp, topp);
return 0; return 0;
} }

View File

@ -41,7 +41,6 @@
#define DEBUG \ #define DEBUG \
if (0) printf if (0) printf
unsigned int main_time = 0;
int errors = 0; int errors = 0;
//====================================================================== //======================================================================
@ -250,24 +249,27 @@ void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0};
#else #else
double sc_time_stamp() { return main_time; }
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
uint64_t sim_time = 1100; const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
Verilated::commandArgs(argc, argv);
Verilated::debug(0);
// we're going to be checking for these errors do don't crash out
Verilated::fatalOnVpiError(0);
VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out uint64_t sim_time = 1100;
contextp->commandArgs(argc, argv);
contextp->debug(0);
// we're going to be checking for these errors do don't crash out
contextp->fatalOnVpiError(0);
const std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX{contextp.get(),
// Note null name - we're flattening it out
""}};
#ifdef VERILATOR #ifdef VERILATOR
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
Verilated::scopesDump(); contextp->scopesDump();
#endif #endif
#endif #endif
#if VM_TRACE #if VM_TRACE
Verilated::traceEverOn(true); contextp->traceEverOn(true);
VL_PRINTF("Enabling waves...\n"); VL_PRINTF("Enabling waves...\n");
VerilatedVcdC* tfp = new VerilatedVcdC; VerilatedVcdC* tfp = new VerilatedVcdC;
topp->trace(tfp, 99); topp->trace(tfp, 99);
@ -276,19 +278,19 @@ int main(int argc, char** argv, char** env) {
topp->eval(); topp->eval();
topp->clk = 0; topp->clk = 0;
main_time += 10; contextp->timeInc(10);
while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { while (contextp->time() < sim_time && !contextp->gotFinish()) {
main_time += 1; contextp->timeInc(1);
topp->eval(); topp->eval();
VerilatedVpi::callValueCbs(); VerilatedVpi::callValueCbs();
topp->clk = !topp->clk; topp->clk = !topp->clk;
// mon_do(); // mon_do();
#if VM_TRACE #if VM_TRACE
if (tfp) tfp->dump(main_time); if (tfp) tfp->dump(contextp->time());
#endif #endif
} }
if (!Verilated::gotFinish()) { if (!contextp->gotFinish()) {
vl_fatal(FILENM, __LINE__, "main", "%Error: Timeout; never got a $finish"); vl_fatal(FILENM, __LINE__, "main", "%Error: Timeout; never got a $finish");
} }
topp->final(); topp->final();
@ -297,7 +299,6 @@ int main(int argc, char** argv, char** env) {
if (tfp) tfp->close(); if (tfp) tfp->close();
#endif #endif
VL_DO_DANGLING(delete topp, topp);
return 0; return 0;
} }

View File

@ -40,8 +40,6 @@
#define DEBUG \ #define DEBUG \
if (0) printf if (0) printf
unsigned int main_time = 0;
#define CHECK_RESULT_NZ(got) \ #define CHECK_RESULT_NZ(got) \
if (!(got)) { \ if (!(got)) { \
printf("%%Error: %s:%d: GOT = NULL EXP = !NULL\n", FILENM, __LINE__); \ printf("%%Error: %s:%d: GOT = NULL EXP = !NULL\n", FILENM, __LINE__); \
@ -172,27 +170,36 @@ void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0};
#else #else
double sc_time_stamp() { return main_time; }
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
uint64_t sim_time = 1100; const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
Verilated::commandArgs(argc, argv);
Verilated::debug(0); uint64_t sim_time = 1100;
// we're going to be checking for these errors do don't crash out contextp->commandArgs(argc, argv);
Verilated::fatalOnVpiError(0); contextp->debug(0);
// we're going to be checking for these errors do don't crash out
contextp->fatalOnVpiError(0);
{
// Construct and destroy
const std::unique_ptr<VM_PREFIX> topp{
new VM_PREFIX{contextp.get(),
// Note null name - we're flattening it out
""}};
}
VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out
// Test second construction // Test second construction
delete topp; const std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX{contextp.get(),
topp = new VM_PREFIX(""); // Note null name - we're flattening it out
""}};
#ifdef VERILATOR #ifdef VERILATOR
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
Verilated::scopesDump(); contextp->scopesDump();
#endif #endif
#endif #endif
#if VM_TRACE #if VM_TRACE
Verilated::traceEverOn(true); contextp->traceEverOn(true);
VL_PRINTF("Enabling waves...\n"); VL_PRINTF("Enabling waves...\n");
VerilatedVcdC* tfp = new VerilatedVcdC; VerilatedVcdC* tfp = new VerilatedVcdC;
topp->trace(tfp, 99); topp->trace(tfp, 99);
@ -201,19 +208,19 @@ int main(int argc, char** argv, char** env) {
topp->eval(); topp->eval();
topp->clk = 0; topp->clk = 0;
main_time += 10; contextp->timeInc(10);
while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { while (contextp->time() < sim_time && !contextp->gotFinish()) {
main_time += 1; contextp->timeInc(1);
topp->eval(); topp->eval();
VerilatedVpi::callValueCbs(); VerilatedVpi::callValueCbs();
topp->clk = !topp->clk; topp->clk = !topp->clk;
// mon_do(); // mon_do();
#if VM_TRACE #if VM_TRACE
if (tfp) tfp->dump(main_time); if (tfp) tfp->dump(contextp->time());
#endif #endif
} }
if (!Verilated::gotFinish()) { if (!contextp->gotFinish()) {
vl_fatal(FILENM, __LINE__, "main", "%Error: Timeout; never got a $finish"); vl_fatal(FILENM, __LINE__, "main", "%Error: Timeout; never got a $finish");
} }
topp->final(); topp->final();
@ -222,7 +229,6 @@ int main(int argc, char** argv, char** env) {
if (tfp) tfp->close(); if (tfp) tfp->close();
#endif #endif
VL_DO_DANGLING(delete topp, topp);
return 0; return 0;
} }

View File

@ -40,8 +40,6 @@
#define DEBUG \ #define DEBUG \
if (0) printf if (0) printf
unsigned int main_time = 0;
//====================================================================== //======================================================================
#define CHECK_RESULT_VH(got, exp) \ #define CHECK_RESULT_VH(got, exp) \
@ -240,24 +238,27 @@ void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0};
#else #else
double sc_time_stamp() { return main_time; }
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
uint64_t sim_time = 1100; const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
Verilated::commandArgs(argc, argv);
Verilated::debug(0);
// we're going to be checking for these errors do don't crash out
Verilated::fatalOnVpiError(0);
VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out uint64_t sim_time = 1100;
contextp->commandArgs(argc, argv);
contextp->debug(0);
// we're going to be checking for these errors do don't crash out
contextp->fatalOnVpiError(0);
const std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX{contextp.get(),
// Note null name - we're flattening it out
""}};
#ifdef VERILATOR #ifdef VERILATOR
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
Verilated::scopesDump(); contextp->scopesDump();
#endif #endif
#endif #endif
#if VM_TRACE #if VM_TRACE
Verilated::traceEverOn(true); contextp->traceEverOn(true);
VL_PRINTF("Enabling waves...\n"); VL_PRINTF("Enabling waves...\n");
VerilatedVcdC* tfp = new VerilatedVcdC; VerilatedVcdC* tfp = new VerilatedVcdC;
topp->trace(tfp, 99); topp->trace(tfp, 99);
@ -266,19 +267,19 @@ int main(int argc, char** argv, char** env) {
topp->eval(); topp->eval();
topp->clk = 0; topp->clk = 0;
main_time += 10; contextp->timeInc(10);
while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { while (contextp->time() < sim_time && !contextp->gotFinish()) {
main_time += 1; contextp->timeInc(1);
topp->eval(); topp->eval();
VerilatedVpi::callValueCbs(); VerilatedVpi::callValueCbs();
topp->clk = !topp->clk; topp->clk = !topp->clk;
// mon_do(); // mon_do();
#if VM_TRACE #if VM_TRACE
if (tfp) tfp->dump(main_time); if (tfp) tfp->dump(contextp->time());
#endif #endif
} }
if (!Verilated::gotFinish()) { if (!contextp->gotFinish()) {
vl_fatal(FILENM, __LINE__, "main", "%Error: Timeout; never got a $finish"); vl_fatal(FILENM, __LINE__, "main", "%Error: Timeout; never got a $finish");
} }
topp->final(); topp->final();
@ -287,7 +288,6 @@ int main(int argc, char** argv, char** env) {
if (tfp) tfp->close(); if (tfp) tfp->close();
#endif #endif
VL_DO_DANGLING(delete topp, topp);
return 0; return 0;
} }

View File

@ -22,27 +22,27 @@
#include <iostream> #include <iostream>
unsigned int main_time = 0;
//====================================================================== //======================================================================
double sc_time_stamp() { return main_time; }
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
uint64_t sim_time = 1100; const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
Verilated::commandArgs(argc, argv);
Verilated::debug(0);
VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out uint64_t sim_time = 1100;
contextp->commandArgs(argc, argv);
contextp->debug(0);
const std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX{contextp.get(),
// Note null name - we're flattening it out
""}};
// clang-format off // clang-format off
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
Verilated::scopesDump(); contextp->scopesDump();
#endif #endif
// clang-format on // clang-format on
#if VM_TRACE #if VM_TRACE
Verilated::traceEverOn(true); contextp->traceEverOn(true);
VL_PRINTF("Enabling waves...\n"); VL_PRINTF("Enabling waves...\n");
VerilatedVcdC* tfp = new VerilatedVcdC; VerilatedVcdC* tfp = new VerilatedVcdC;
topp->trace(tfp, 99); topp->trace(tfp, 99);
@ -54,24 +54,24 @@ int main(int argc, char** argv, char** env) {
topp->eval(); topp->eval();
topp->clk = 0; topp->clk = 0;
while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { while (vl_time_stamp64() < sim_time && !contextp->gotFinish()) {
main_time += 1; contextp->timeInc(1);
topp->eval(); topp->eval();
VerilatedVpi::callValueCbs(); VerilatedVpi::callValueCbs();
VerilatedVpi::callTimedCbs(); VerilatedVpi::callTimedCbs();
if (main_time > 20) { // Else haven't registered callbacks if (contextp->time() > 20) { // Else haven't registered callbacks
TEST_CHECK_EQ(VerilatedVpi::cbNextDeadline(), main_time + 1); TEST_CHECK_EQ(VerilatedVpi::cbNextDeadline(), contextp->time() + 1);
} }
if ((main_time % 5) == 0) topp->clk = !topp->clk; if ((contextp->time() % 5) == 0) topp->clk = !topp->clk;
// mon_do(); // mon_do();
#if VM_TRACE #if VM_TRACE
if (tfp) tfp->dump(main_time); if (tfp) tfp->dump(contextp->time());
#endif #endif
} }
VerilatedVpi::callCbs(cbEndOfSimulation); VerilatedVpi::callCbs(cbEndOfSimulation);
if (!Verilated::gotFinish()) { if (!contextp->gotFinish()) {
vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish"); vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish");
} }
topp->final(); topp->final();
@ -80,6 +80,5 @@ int main(int argc, char** argv, char** env) {
if (tfp) tfp->close(); if (tfp) tfp->close();
#endif #endif
VL_DO_DANGLING(delete topp, topp);
return errors ? 10 : 0; return errors ? 10 : 0;
} }

View File

@ -28,7 +28,6 @@
#define DEBUG \ #define DEBUG \
if (0) printf if (0) printf
unsigned int main_time = 0;
unsigned int callback_count = 0; unsigned int callback_count = 0;
//====================================================================== //======================================================================
@ -184,24 +183,27 @@ extern "C" int mon_check() {
//====================================================================== //======================================================================
double sc_time_stamp() { return main_time; }
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
uint64_t sim_time = 1100; const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
Verilated::commandArgs(argc, argv);
Verilated::debug(0);
// we're going to be checking for these errors do don't crash out
Verilated::fatalOnVpiError(0);
VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out uint64_t sim_time = 1100;
contextp->commandArgs(argc, argv);
contextp->debug(0);
// we're going to be checking for these errors do don't crash out
contextp->fatalOnVpiError(0);
const std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX{contextp.get(),
// Note null name - we're flattening it out
""}};
#ifdef VERILATOR #ifdef VERILATOR
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
Verilated::scopesDump(); contextp->scopesDump();
#endif #endif
#endif #endif
#if VM_TRACE #if VM_TRACE
Verilated::traceEverOn(true); contextp->traceEverOn(true);
VL_PRINTF("Enabling waves...\n"); VL_PRINTF("Enabling waves...\n");
VerilatedVcdC* tfp = new VerilatedVcdC; VerilatedVcdC* tfp = new VerilatedVcdC;
topp->trace(tfp, 99); topp->trace(tfp, 99);
@ -210,20 +212,20 @@ int main(int argc, char** argv, char** env) {
topp->eval(); topp->eval();
topp->clk = 0; topp->clk = 0;
main_time += 10; contextp->timeInc(10);
while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { while (contextp->time() < sim_time && !contextp->gotFinish()) {
main_time += 1; contextp->timeInc(1);
topp->eval(); topp->eval();
// VerilatedVpi::callValueCbs(); // Make sure can link without verilated_vpi.h included // VerilatedVpi::callValueCbs(); // Make sure can link without verilated_vpi.h included
topp->clk = !topp->clk; topp->clk = !topp->clk;
// mon_do(); // mon_do();
#if VM_TRACE #if VM_TRACE
if (tfp) tfp->dump(main_time); if (tfp) tfp->dump(contextp->time());
#endif #endif
} }
if (!callback_count) vl_fatal(FILENM, __LINE__, "main", "%Error: never got callbacks"); if (!callback_count) vl_fatal(FILENM, __LINE__, "main", "%Error: never got callbacks");
if (!Verilated::gotFinish()) { if (!contextp->gotFinish()) {
vl_fatal(FILENM, __LINE__, "main", "%Error: Timeout; never got a $finish"); vl_fatal(FILENM, __LINE__, "main", "%Error: Timeout; never got a $finish");
} }
topp->final(); topp->final();
@ -232,6 +234,5 @@ int main(int argc, char** argv, char** env) {
if (tfp) tfp->close(); if (tfp) tfp->close();
#endif #endif
VL_DO_DANGLING(delete topp, topp);
return 0; return 0;
} }

View File

@ -693,20 +693,24 @@ void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0};
double sc_time_stamp() { return main_time; } double sc_time_stamp() { return main_time; }
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
uint64_t sim_time = 1100; const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
Verilated::commandArgs(argc, argv);
Verilated::debug(0);
VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out uint64_t sim_time = 1100;
contextp->commandArgs(argc, argv);
contextp->debug(0);
const std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX{contextp.get(),
// Note null name - we're flattening it out
""}};
#ifdef VERILATOR #ifdef VERILATOR
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
Verilated::scopesDump(); contextp->scopesDump();
#endif #endif
#endif #endif
#if VM_TRACE #if VM_TRACE
Verilated::traceEverOn(true); contextp->traceEverOn(true);
VL_PRINTF("Enabling waves...\n"); VL_PRINTF("Enabling waves...\n");
VerilatedVcdC* tfp = new VerilatedVcdC; VerilatedVcdC* tfp = new VerilatedVcdC;
topp->trace(tfp, 99); topp->trace(tfp, 99);
@ -717,7 +721,7 @@ int main(int argc, char** argv, char** env) {
topp->clk = 0; topp->clk = 0;
main_time += 10; main_time += 10;
while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { while (vl_time_stamp64() < sim_time && !contextp->gotFinish()) {
main_time += 1; main_time += 1;
topp->eval(); topp->eval();
VerilatedVpi::callValueCbs(); VerilatedVpi::callValueCbs();
@ -731,7 +735,7 @@ int main(int argc, char** argv, char** env) {
CHECK_RESULT(callback_count_half, 250); CHECK_RESULT(callback_count_half, 250);
CHECK_RESULT(callback_count_quad, 2); CHECK_RESULT(callback_count_quad, 2);
CHECK_RESULT(callback_count_strs, callback_count_strs_max); CHECK_RESULT(callback_count_strs, callback_count_strs_max);
if (!Verilated::gotFinish()) { if (!contextp->gotFinish()) {
vl_fatal(FILENM, __LINE__, "main", "%Error: Timeout; never got a $finish"); vl_fatal(FILENM, __LINE__, "main", "%Error: Timeout; never got a $finish");
} }
topp->final(); topp->final();
@ -740,7 +744,6 @@ int main(int argc, char** argv, char** env) {
if (tfp) tfp->close(); if (tfp) tfp->close();
#endif #endif
VL_DO_DANGLING(delete topp, topp);
return 0; return 0;
} }

View File

@ -37,7 +37,6 @@
#include "TestVpi.h" #include "TestVpi.h"
int errors = 0; int errors = 0;
unsigned int main_time = 0;
unsigned int callback_count_zero_time = 0; unsigned int callback_count_zero_time = 0;
unsigned int callback_count_start_of_sim = 0; unsigned int callback_count_start_of_sim = 0;
@ -105,25 +104,27 @@ void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0};
#else #else
double sc_time_stamp() { return main_time; }
int main(int argc, char** argv, char** env) { int main(int argc, char** argv, char** env) {
uint64_t sim_time = 1100; const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
Verilated::commandArgs(argc, argv);
Verilated::debug(0);
VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out uint64_t sim_time = 1100;
contextp->commandArgs(argc, argv);
contextp->debug(0);
const std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX{contextp.get(),
// Note null name - we're flattening it out
""}};
// clang-format off // clang-format off
#ifdef VERILATOR #ifdef VERILATOR
# ifdef TEST_VERBOSE # ifdef TEST_VERBOSE
Verilated::scopesDump(); contextp->scopesDump();
# endif # endif
#endif #endif
// clang-format on // clang-format on
#if VM_TRACE #if VM_TRACE
Verilated::traceEverOn(true); contextp->traceEverOn(true);
VL_PRINTF("Enabling waves...\n"); VL_PRINTF("Enabling waves...\n");
VerilatedVcdC* tfp = new VerilatedVcdC; VerilatedVcdC* tfp = new VerilatedVcdC;
topp->trace(tfp, 99); topp->trace(tfp, 99);
@ -146,23 +147,23 @@ int main(int argc, char** argv, char** env) {
topp->eval(); topp->eval();
topp->clk = 0; topp->clk = 0;
main_time += 1; contextp->timeInc(1);
while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { while (contextp->time() < sim_time && !contextp->gotFinish()) {
main_time += 1; contextp->timeInc(1);
topp->eval(); topp->eval();
VerilatedVpi::callValueCbs(); VerilatedVpi::callValueCbs();
VerilatedVpi::callTimedCbs(); VerilatedVpi::callTimedCbs();
topp->clk = !topp->clk; topp->clk = !topp->clk;
// mon_do(); // mon_do();
#if VM_TRACE #if VM_TRACE
if (tfp) tfp->dump(main_time); if (tfp) tfp->dump(contextp->time());
#endif #endif
} }
VerilatedVpi::callCbs(cbEndOfSimulation); VerilatedVpi::callCbs(cbEndOfSimulation);
if (!Verilated::gotFinish()) { if (!contextp->gotFinish()) {
vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish"); vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish");
} }
topp->final(); topp->final();
@ -171,7 +172,6 @@ int main(int argc, char** argv, char** env) {
if (tfp) tfp->close(); if (tfp) tfp->close();
#endif #endif
VL_DO_DANGLING(delete topp, topp);
return 0; return 0;
} }

View File

@ -92,6 +92,8 @@ int main(int argc, char** argv, char** env) {
std::unique_ptr<VerilatedContext> context1p{new VerilatedContext}; std::unique_ptr<VerilatedContext> context1p{new VerilatedContext};
// configuration // configuration
context0p->threads(1);
context1p->threads(1);
context0p->fatalOnError(false); context0p->fatalOnError(false);
context1p->fatalOnError(false); context1p->fatalOnError(false);
context0p->traceEverOn(true); context0p->traceEverOn(true);

View File

@ -14,8 +14,9 @@ compile(
make_top_shell => 0, make_top_shell => 0,
make_main => 0, make_main => 0,
# link threads library, add custom .cpp code, add tracing & coverage support # link threads library, add custom .cpp code, add tracing & coverage support
verilator_flags2 => ["-threads 1 --exe $Self->{t_dir}/$Self->{name}.cpp", verilator_flags2 => ["--exe $Self->{t_dir}/$Self->{name}.cpp",
"--trace --coverage -cc"], "--trace --coverage -cc"],
threads => 1,
make_flags => 'CPPFLAGS_ADD=-DVL_NO_LEGACY', make_flags => 'CPPFLAGS_ADD=-DVL_NO_LEGACY',
); );

View File

@ -16,8 +16,9 @@ compile(
make_top_shell => 0, make_top_shell => 0,
make_main => 0, make_main => 0,
# link threads library, add custom .cpp code, add tracing & coverage support # link threads library, add custom .cpp code, add tracing & coverage support
verilator_flags2 => ["-threads 1 --exe $Self->{t_dir}/t_wrapper_context.cpp", verilator_flags2 => ["--exe $Self->{t_dir}/t_wrapper_context.cpp",
"--trace-fst --coverage -cc"], "--trace-fst --coverage -cc"],
threads => 1,
make_flags => 'CPPFLAGS_ADD=-DVL_NO_LEGACY', make_flags => 'CPPFLAGS_ADD=-DVL_NO_LEGACY',
); );

View File

@ -16,8 +16,9 @@ compile(
make_top_shell => 0, make_top_shell => 0,
make_main => 0, make_main => 0,
# link threads library, add custom .cpp code, add tracing & coverage support # link threads library, add custom .cpp code, add tracing & coverage support
verilator_flags2 => ["-threads 1 --exe $Self->{t_dir}/t_wrapper_context.cpp", verilator_flags2 => ["--exe $Self->{t_dir}/t_wrapper_context.cpp",
"--trace --coverage -cc"], "--trace --coverage -cc"],
threads => 1,
make_flags => 'CPPFLAGS_ADD=-DVL_NO_LEGACY', make_flags => 'CPPFLAGS_ADD=-DVL_NO_LEGACY',
); );