Make VerilatorSimCtrl class a singleton

The VerilatorSimCtrl class was always intended to be used only once in
an application, since it sets up a global signal handler and needs to be
accessible from DPI modules. This accessibility was achieved through a
global variable `simctrl`.

With this commit the VerilatorSimCtrl is switched to a singleton class.
The instance is accessible through `VerilatorSimCtrl::GetInstance()`.
The downside of that approach is that we loose the constructor
arguments, and need to deal with a class which potentially hasn't top or
the clock and reset signals set.
This commit is contained in:
Philipp Wagner 2019-11-28 17:31:59 +00:00 committed by Philipp Wagner
parent afdee5c596
commit 82e0faf50b
5 changed files with 104 additions and 61 deletions

View file

@ -14,8 +14,9 @@ int simutil_verilator_set_mem(int index, const svLogicVecVal *val) { return 0; }
int main(int argc, char **argv) {
tb_cs_registers top;
VerilatorSimCtrl simctrl(top, top.clk_i, top.in_rst_ni,
VerilatorSimCtrlFlags::ResetPolarityNegative);
VerilatorSimCtrl &simctrl = VerilatorSimCtrl::GetInstance();
simctrl.SetTop(&top, &top.clk_i, &top.in_rst_ni,
VerilatorSimCtrlFlags::ResetPolarityNegative);
return simctrl.Exec(argc, argv);
}

View file

@ -7,8 +7,9 @@
int main(int argc, char **argv) {
ibex_riscv_compliance top;
VerilatorSimCtrl simctrl(top, top.IO_CLK, top.IO_RST_N,
VerilatorSimCtrlFlags::ResetPolarityNegative);
VerilatorSimCtrl &simctrl = VerilatorSimCtrl::GetInstance();
simctrl.SetTop(&top, &top.IO_CLK, &top.IO_RST_N,
VerilatorSimCtrlFlags::ResetPolarityNegative);
simctrl.RegisterMemoryArea("ram", "TOP.ibex_riscv_compliance.u_ram");

View file

@ -27,39 +27,13 @@ struct BufferDesc {
size_t length;
};
// Static pointer to a single simctrl instance used by SignalHandler
static VerilatorSimCtrl *simctrl = nullptr;
static void SignalHandler(int sig) {
if (!simctrl) {
return;
}
switch (sig) {
case SIGINT:
simctrl->RequestStop(true);
break;
case SIGUSR1:
if (simctrl->TracingEnabled()) {
simctrl->TraceOff();
} else {
simctrl->TraceOn();
}
break;
}
}
/**
* Get the current simulation time
*
* Called by $time in Verilog, converts to double, to match what SystemC does
*/
double sc_time_stamp() {
if (simctrl) {
return simctrl->GetTime();
} else {
return 0;
}
return VerilatorSimCtrl::GetInstance().GetTime();
}
// DPI Exports
@ -80,12 +54,13 @@ extern void simutil_verilator_memload(const char *file);
extern int simutil_verilator_set_mem(int index, const svLogicVecVal *val);
}
VerilatorSimCtrl::VerilatorSimCtrl(VerilatedToplevel &top, CData &sig_clk,
CData &sig_rst, VerilatorSimCtrlFlags flags)
: top_(top),
sig_clk_(sig_clk),
sig_rst_(sig_rst),
flags_(flags),
VerilatorSimCtrl& VerilatorSimCtrl::GetInstance() {
static VerilatorSimCtrl instance;
return instance;
}
VerilatorSimCtrl::VerilatorSimCtrl()
: top_(nullptr),
time_(0),
tracing_enabled_(false),
tracing_enabled_changed_(false),
@ -99,6 +74,15 @@ VerilatorSimCtrl::VerilatorSimCtrl(VerilatedToplevel &top, CData &sig_clk,
term_after_cycles_(0),
callback_(nullptr) {}
void VerilatorSimCtrl::SetTop(VerilatedToplevel *top, CData *sig_clk,
CData *sig_rst,
VerilatorSimCtrlFlags flags) {
top_ = top;
sig_clk_ = sig_clk;
sig_rst_ = sig_rst;
flags_ = flags;
}
int VerilatorSimCtrl::Exec(int argc, char **argv) {
RegisterSignalHandler();
@ -141,9 +125,6 @@ void VerilatorSimCtrl::RunSimulation() {
void VerilatorSimCtrl::RegisterSignalHandler() {
struct sigaction sigIntHandler;
// Point the static simctrl pointer at this object
simctrl = this;
sigIntHandler.sa_handler = SignalHandler;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
@ -152,6 +133,23 @@ void VerilatorSimCtrl::RegisterSignalHandler() {
sigaction(SIGUSR1, &sigIntHandler, NULL);
}
void VerilatorSimCtrl::SignalHandler(int sig) {
VerilatorSimCtrl &simctrl = VerilatorSimCtrl::GetInstance();
switch (sig) {
case SIGINT:
simctrl.RequestStop(true);
break;
case SIGUSR1:
if (simctrl.TracingEnabled()) {
simctrl.TraceOff();
} else {
simctrl.TraceOn();
}
break;
}
}
void VerilatorSimCtrl::RequestStop(bool simulation_success) {
request_stop_ = true;
simulation_success_ &= simulation_success;
@ -177,8 +175,15 @@ bool VerilatorSimCtrl::TraceOff() {
return tracing_enabled_;
}
std::string VerilatorSimCtrl::GetName() const {
if (top_) {
return top_->name();
}
return "unknown";
}
void VerilatorSimCtrl::PrintHelp() const {
std::cout << "Execute a simulation model for " << top_.name()
std::cout << "Execute a simulation model for " << GetName()
<< "\n"
"\n";
if (tracing_possible_) {
@ -529,14 +534,16 @@ void VerilatorSimCtrl::SetOnClockCallback(SimCtrlCallBack callback) {
}
void VerilatorSimCtrl::Run() {
assert(top_ && "Use SetTop() first.");
// We always need to enable this as tracing can be enabled at runtime
if (tracing_possible_) {
Verilated::traceEverOn(true);
top_.trace(tracer_, 99, 0);
top_->trace(tracer_, 99, 0);
}
// Evaluate all initial blocks, including the DPI setup routines
top_.eval();
top_->eval();
std::cout << std::endl
<< "Simulation running, end by pressing CTRL-c." << std::endl;
@ -552,13 +559,13 @@ void VerilatorSimCtrl::Run() {
UnsetReset();
}
sig_clk_ = !sig_clk_;
*sig_clk_ = !*sig_clk_;
if (sig_clk_ && (callback_ != nullptr)) {
if (*sig_clk_ && (callback_ != nullptr)) {
callback_(time_);
}
top_.eval();
top_->eval();
time_++;
Trace();
@ -580,7 +587,7 @@ void VerilatorSimCtrl::Run() {
}
}
top_.final();
top_->final();
time_end_ = std::chrono::steady_clock::now();
if (TracingEverEnabled()) {
@ -590,17 +597,17 @@ void VerilatorSimCtrl::Run() {
void VerilatorSimCtrl::SetReset() {
if (flags_ & ResetPolarityNegative) {
sig_rst_ = 0;
*sig_rst_ = 0;
} else {
sig_rst_ = 1;
*sig_rst_ = 1;
}
}
void VerilatorSimCtrl::UnsetReset() {
if (flags_ & ResetPolarityNegative) {
sig_rst_ = 1;
*sig_rst_ = 1;
} else {
sig_rst_ = 0;
*sig_rst_ = 0;
}
}

View file

@ -39,16 +39,28 @@ struct MemArea {
*/
class VerilatorSimCtrl {
public:
VerilatorSimCtrl(VerilatedToplevel &top, CData &clk, CData &rst_n,
VerilatorSimCtrlFlags flags = Defaults);
/**
* Get the simulation controller instance
*
* @see SetTop()
*/
static VerilatorSimCtrl& GetInstance();
VerilatorSimCtrl(VerilatorSimCtrl const&) = delete;
void operator=(VerilatorSimCtrl const&) = delete;
/**
* Set the top-level design
*/
void SetTop(VerilatedToplevel *top, CData *sig_clk, CData *sig_rst,
VerilatorSimCtrlFlags flags = Defaults);
/**
* Setup and run the simulation (all in one)
*
* Use this function as high-level entry point, suitable for most use cases.
*
* Exec() can be used only once per process as it registers a global signal
* handler.
* SetTop() must be called before this function.
*
* This function performs the following tasks:
* 1. Sets up a signal handler to enable tracing to be turned on/off during
@ -64,7 +76,7 @@ class VerilatorSimCtrl {
/**
* A helper function to execute a standard set of run commands.
*
* This function performs the followind tasks:
* This function performs the following tasks:
* 1. Prints some tracer-related helper messages
* 2. Runs the simulation
* 3. Prints some further helper messages and statistics once the simulation
@ -110,6 +122,13 @@ class VerilatorSimCtrl {
*/
unsigned long GetTime() const { return time_; }
/**
* Get a name for this simulation
*
* This name is typically the name of the top-level.
*/
std::string GetName() const;
/**
* Get the simulation result
*/
@ -181,9 +200,9 @@ class VerilatorSimCtrl {
void SetOnClockCallback(SimCtrlCallBack callback);
private:
VerilatedToplevel &top_;
CData &sig_clk_;
CData &sig_rst_;
VerilatedToplevel *top_;
CData *sig_clk_;
CData *sig_rst_;
VerilatorSimCtrlFlags flags_;
unsigned long time_;
bool tracing_enabled_;
@ -201,11 +220,25 @@ class VerilatorSimCtrl {
int term_after_cycles_;
SimCtrlCallBack callback_;
/**
* Default constructor
*
* Use GetInstance() instead.
*/
VerilatorSimCtrl();
/**
* Register the signal handler
*/
void RegisterSignalHandler();
/**
* Signal handler callback
*
* Use RegisterSignalHandler() to setup.
*/
static void SignalHandler(int sig);
/**
* Parse command line arguments
*

View file

@ -10,8 +10,9 @@
int main(int argc, char **argv) {
ibex_simple_system top;
VerilatorSimCtrl simctrl(top, top.IO_CLK, top.IO_RST_N,
VerilatorSimCtrlFlags::ResetPolarityNegative);
VerilatorSimCtrl &simctrl = VerilatorSimCtrl::GetInstance();
simctrl.SetTop(&top, &top.IO_CLK, &top.IO_RST_N,
VerilatorSimCtrlFlags::ResetPolarityNegative);
simctrl.RegisterMemoryArea("ram", "TOP.ibex_simple_system.u_ram");