Introduced a unique main to the simulation. (#1368)

The main method centralizes more of the lifecycle of a simulation.
This commit is contained in:
Nandor Licker 2023-02-01 10:40:08 +02:00 committed by GitHub
parent 0c2e8dd6bc
commit 4d1876334e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 193 additions and 144 deletions

View File

@ -61,7 +61,6 @@ serial_t::serial_t(simif_t &simif,
printf("\n");
tsi_argc = argc_count + 1;
fesvr = new firesim_tsi_t(tsi_argc, tsi_argv, has_mem);
}
serial_t::~serial_t() {
@ -75,6 +74,11 @@ serial_t::~serial_t() {
}
void serial_t::init() {
// `ucontext` used by tsi cannot be created in one thread and resumed in
// another. To ensure that the tsi process is on the correct thread, it is
// built here, as the bridge constructor may be invoked from a thread other
// than the one it will run on later in meta-simulations.
fesvr = new firesim_tsi_t(tsi_argc, tsi_argv, has_mem);
write(mmio_addrs.step_size, step_size);
go();
}

View File

@ -11,16 +11,14 @@ clang_tidy_files := $(shell \
find $(firesim_base_dir) -name '*.cc' -or -name '*.h' \
| grep -v generic_vharness.cc \
| grep -v TestPointerChaser.cc \
| grep -v simif.cc \
| grep -v simif_ \
| grep -v tracerv \
| grep -v dromajo \
| grep -v serial \
| grep -v fesvr \
| grep -v firesim_top \
| grep -v generated-src \
| grep -v output \
| grep -v constructor.h \
| grep -v -F 'main.cc' \
)
clang_tidy_flags :=\

View File

@ -7,6 +7,7 @@
#include <string_view>
#include "core/simif.h"
#include "core/timing.h"
struct PEEKPOKEBRIDGEMODULE_struct {
uint64_t STEP;

View File

@ -76,7 +76,7 @@ UARTBRIDGEMODULE_checks;
#ifdef LOADMEMWIDGET_0_PRESENT
registry.add_widget(new loadmem_t(simif,
LOADMEMWIDGET_0_substruct_create,
config.mem,
conf_target.mem,
LOADMEMWIDGET_0_mem_data_chunk));
#endif // LOADMEMWIDGET_0_PRESENT

View File

@ -0,0 +1,82 @@
// See LICENSE for license details.
#include <memory>
#include "core/config.h"
#include "core/simif.h"
#include "core/simulation.h"
#include "bridges/autocounter.h"
#include "bridges/clock.h"
#include "bridges/cpu_managed_stream.h"
#include "bridges/fased_memory_timing_model.h"
#include "bridges/fpga_managed_stream.h"
#include "bridges/fpga_model.h"
#include "bridges/loadmem.h"
#include "bridges/master.h"
#include "bridges/plusargs.h"
#include "bridges/reset_pulse.h"
#include "bridges/synthesized_assertions.h"
#include "bridges/synthesized_prints.h"
#include "bridges/termination.h"
#ifdef PEEKPOKEBRIDGEMODULE_0_PRESENT
#include "bridges/peek_poke.h"
#endif
#ifdef BLOCKDEVBRIDGEMODULE_0_PRESENT
#include "bridges/blockdev.h"
#endif
#ifdef DROMAJOBRIDGEMODULE_0_PRESENT
#include "bridges/dromajo.h"
#endif
#ifdef GROUNDTESTBRIDGEMODULE_0_PRESENT
#include "bridges/groundtest.h"
#endif
#ifdef SERIALBRIDGEMODULE_0_PRESENT
#include "bridges/serial.h"
#endif
#ifdef SIMPLENICBRIDGEMODULE_0_PRESENT
#include "bridges/simplenic.h"
#endif
#ifdef TRACERVBRIDGEMODULE_0_PRESENT
#include "bridges/tracerv.h"
#endif
#ifdef UARTBRIDGEMODULE_0_PRESENT
#include "bridges/uart.h"
#endif
// The user-defined part of the driver implements this method to return
// a simulation instance implementing all simulation-specific logic.
extern std::unique_ptr<simulation_t>
create_simulation(const std::vector<std::string> &args, simif_t &simif);
// The platform-specific component of the driver implements this method
// to return a handle to the simulation.
extern std::unique_ptr<simif_t>
create_simif(const TargetConfig &config, int argc, char **argv);
// clang-format off
// Entry point of the driver.
int main(int argc, char **argv) {
std::vector<std::string> args(argv + 1, argv + argc);
auto simif_ptr = create_simif(conf_target, argc, argv);
{
auto &simif = *simif_ptr;
auto &registry = simif_ptr->get_registry();
// DOC include start: Bridge Driver Registration
// Here we instantiate our driver once for each bridge in the target
// Golden Gate emits a <BridgeModuleClassName>_<id>_PRESENT macro for each
// instance which you may use to conditionally instantiate your driver.
// This file can be included in the setup method of any top-level to pass
// an instance of each driver to the `add_bridge_driver` method. Drivers can
// be distinguished by overloading the method with the appropriate type.
#include "constructor.h"
// DOC include end: Bridge Driver Registration
}
auto sim = create_simulation(args, *simif_ptr);
return simif_ptr->run(*sim);
}

View File

@ -1,6 +1,7 @@
// See LICENSE for license details.
#include "simif.h"
#include "core/config.h"
#include "core/simulation.h"
#include "core/stream_engine.h"
@ -8,7 +9,7 @@
simif_t::simif_t(const TargetConfig &config,
const std::vector<std::string> &args)
: config(config), args(args) {
: config(config), args(args), registry() {
for (auto &arg : args) {
if (arg.find("+fastloadmem") == 0) {
fastloadmem = true;
@ -22,7 +23,7 @@ simif_t::simif_t(const TargetConfig &config,
}
}
simif_t::~simif_t() {}
simif_t::~simif_t() = default;
CPUManagedStreamIO &simif_t::get_cpu_managed_stream_io() {
std::cerr << "CPU-managed streams are not supported" << std::endl;
@ -35,14 +36,14 @@ FPGAManagedStreamIO &simif_t::get_fpga_managed_stream_io() {
}
void simif_t::target_init() {
auto &master = registry->get_widget<master_t>();
auto &loadmem = registry->get_widget<loadmem_t>();
auto &master = registry.get_widget<master_t>();
auto &loadmem = registry.get_widget<loadmem_t>();
// Do any post-constructor initialization required before requesting MMIO
while (!master.is_init_done())
;
if (auto *stream = registry->get_stream_engine()) {
if (auto *stream = registry.get_stream_engine()) {
stream->init();
}
@ -56,12 +57,9 @@ void simif_t::target_init() {
}
}
extern std::unique_ptr<simulation_t>
create_simulation(const std::vector<std::string> &args, simif_t &simif);
std::string_view simif_t::get_target_name() const { return config.target_name; }
int simif_t::simulation_run() {
registry.reset(new widget_registry_t(config, *this, args));
sim = create_simulation(args, *this);
int simif_t::run(simulation_t &sim) {
target_init();
return sim->execute_simulation_flow();
return sim.execute_simulation_flow();
}

View File

@ -23,6 +23,7 @@ class StreamEngine;
class simulation_t;
class CPUManagedStreamIO;
class FPGAManagedStreamIO;
class TargetConfig;
/** \class simif_t
*
@ -95,45 +96,45 @@ public:
* (will report a larger number).
*/
uint64_t actual_tcycle() {
return registry->get_widget<clockmodule_t>().tcycle();
return registry.get_widget<clockmodule_t>().tcycle();
}
/**
* Provides the current host cycle.
*/
uint64_t actual_hcycle() {
return registry->get_widget<clockmodule_t>().hcycle();
return registry.get_widget<clockmodule_t>().hcycle();
}
/**
* Return the name of the simulated target.
*/
std::string_view get_target_name() const { return config.target_name; }
std::string_view get_target_name() const;
/**
* Return a reference to the registry which owns all widgets.
*/
widget_registry_t &get_registry() { return *registry; }
widget_registry_t &get_registry() { return registry; }
private:
/**
* Waits for the target to be initialised.
* Runs the simulation in the context of the driver.
*
* The default implementation initialises the target and hands control to
* the driver routines, suitable for actual FPGA implementations.
*/
void target_init();
void load_mem(std::string filename);
virtual int run(simulation_t &sim);
protected:
/**
* Simulation main loop.
* Sets up the target and blocks until it is initialized.
*/
int simulation_run();
void target_init();
protected:
/**
* Target configuration.
*/
const TargetConfig config;
const TargetConfig &config;
/**
* Saved command-line arguments.
@ -143,12 +144,7 @@ protected:
/**
* Helper holding references to all bridges.
*/
std::unique_ptr<widget_registry_t> registry;
/**
* Reference to the user-defined bits of the simulation.
*/
std::unique_ptr<simulation_t> sim;
widget_registry_t registry;
/**
* Path to load DRAM contents from.

View File

@ -2,55 +2,11 @@
#include "widget_registry.h"
#include "core/bridge_driver.h"
#include "bridges/autocounter.h"
#include "bridges/clock.h"
#include "bridges/cpu_managed_stream.h"
#include "bridges/fased_memory_timing_model.h"
#include "bridges/fpga_managed_stream.h"
#include "bridges/fpga_model.h"
#include "bridges/loadmem.h"
#include "bridges/master.h"
#include "bridges/plusargs.h"
#include "bridges/reset_pulse.h"
#include "bridges/synthesized_assertions.h"
#include "bridges/synthesized_prints.h"
#include "bridges/termination.h"
#include "core/bridge_driver.h"
#include "core/stream_engine.h"
#ifdef PEEKPOKEBRIDGEMODULE_0_PRESENT
#include "bridges/peek_poke.h"
#endif
#ifdef BLOCKDEVBRIDGEMODULE_0_PRESENT
#include "bridges/blockdev.h"
#endif
#ifdef DROMAJOBRIDGEMODULE_0_PRESENT
#include "bridges/dromajo.h"
#endif
#ifdef GROUNDTESTBRIDGEMODULE_0_PRESENT
#include "bridges/groundtest.h"
#endif
#ifdef SERIALBRIDGEMODULE_0_PRESENT
#include "bridges/serial.h"
#endif
#ifdef SIMPLENICBRIDGEMODULE_0_PRESENT
#include "bridges/simplenic.h"
#endif
#ifdef TRACERVBRIDGEMODULE_0_PRESENT
#include "bridges/tracerv.h"
#endif
#ifdef UARTBRIDGEMODULE_0_PRESENT
#include "bridges/uart.h"
#endif
widget_registry_t::widget_registry_t(const TargetConfig &config,
simif_t &simif,
const std::vector<std::string> &args)
: config(config) {
widget_registry_t &registry = *this;
#include "constructor.h"
}
widget_registry_t::widget_registry_t() = default;
widget_registry_t::~widget_registry_t() = default;

View File

@ -26,10 +26,7 @@ class StreamEngine;
*/
class widget_registry_t final {
public:
widget_registry_t(const TargetConfig &config,
simif_t &simif,
const std::vector<std::string> &args);
widget_registry_t();
~widget_registry_t();
/**
@ -94,9 +91,6 @@ public:
void add_widget(StreamEngine *widget);
private:
// Target-specific configuration.
const TargetConfig config;
// Mapping from bridge kinds to the list of bridges of that kind.
using widget_list_t = std::vector<std::unique_ptr<widget_t>>;
std::unordered_map<const void *, widget_list_t> widgets;

View File

@ -4,6 +4,7 @@
#include "bridges/cpu_managed_stream.h"
#include "bridges/fpga_managed_stream.h"
#include "core/simulation.h"
#include "emul/mm.h"
#include "emul/mmio.h"
@ -49,7 +50,11 @@ simif_emul_t::simif_emul_t(const TargetConfig &config,
assert(!config.cpu_managed && "stream should be CPU or FPGA managed");
fpga_managed_stream_io.reset(new FPGAManagedStreamIOImpl(*this, *conf));
}
}
simif_emul_t::~simif_emul_t() {}
void simif_emul_t::start_driver(simulation_t &sim) {
// Set up the simulation thread.
finished = false;
exit_code = EXIT_FAILURE;
@ -57,13 +62,36 @@ simif_emul_t::simif_emul_t(const TargetConfig &config,
std::unique_lock<std::mutex> lock(rtlsim_mutex);
driver_flag = false;
rtlsim_flag = false;
thread = std::thread([&] { thread_main(); });
// Spawn the target thread.
thread = std::thread([&] {
do_tick();
// Load memories before initialising the simulation.
if (fastloadmem && !load_mem_path.empty()) {
fprintf(stdout, "[fast loadmem] %s\n", load_mem_path.c_str());
load_mems(load_mem_path.c_str());
}
// Run the simulation flow.
target_init();
exit_code = sim.execute_simulation_flow();
// Wake the target thread before returning from the simulation thread.
{
std::unique_lock<std::mutex> lock(rtlsim_mutex);
finished = true;
rtlsim_cond.notify_one();
}
});
// Wait for the target thread to yield and enter the RTL simulator.
// The target thread is waken up when the DPI tick function is
// ready to transfer data to it.
rtlsim_cond.wait(lock, [&] { return rtlsim_flag; });
}
}
simif_emul_t::~simif_emul_t() {}
void simif_emul_t::wait_write(mmio_t &mmio) {
while (!mmio.write_resp())
advance_target();
@ -190,24 +218,3 @@ bool simif_emul_t::to_sim() {
}
return finished;
}
void simif_emul_t::thread_main() {
// Switch over to the target thread and wait for the first tick.
do_tick();
// Load memories before initialising the simulation.
if (fastloadmem && !load_mem_path.empty()) {
fprintf(stdout, "[fast loadmem] %s\n", load_mem_path.c_str());
load_mems(load_mem_path.c_str());
}
// Run the simulation flow.
exit_code = simulation_run();
// Wake the target thread before returning from the simulation thread.
{
std::unique_lock<std::mutex> lock(rtlsim_mutex);
finished = true;
rtlsim_cond.notify_one();
}
}

View File

@ -39,7 +39,7 @@ public:
int end();
/**
* Simulation thread implementation.
* Start the driver thread.
*
* This thread is synchronized by the main thread driven by Verilator/VCS
* which simulates the RTL and invokes the tick function through DPI. At
@ -53,7 +53,7 @@ public:
* runs for one cycle, handling the AXI transactions, before switching control
* back to the target thread that reads outputs into the RTL.
*/
void thread_main();
void start_driver(simulation_t &sim);
/**
* Transfers control to the simulator on a tick.

View File

@ -8,25 +8,38 @@
*/
class simif_emul_vcs_t final : public simif_emul_t {
public:
simif_emul_vcs_t(const TargetConfig &config,
const std::vector<std::string> &args)
: simif_emul_t(config, args) {}
simif_emul_vcs_t(const TargetConfig &config, int argc, char **argv);
~simif_emul_vcs_t() {}
int run(simulation_t &sim);
private:
int argc;
char **argv;
};
/// Simulator instance used by DPI.
simif_emul_t *simulator = nullptr;
simif_emul_vcs_t::simif_emul_vcs_t(const TargetConfig &config,
int argc,
char **argv)
: simif_emul_t(config, std::vector<std::string>(argv + 1, argv + argc)),
argc(argc), argv(argv) {
simulator = this;
}
extern "C" {
int vcs_main(int argc, char **argv);
}
/**
* Entry point to the VCS meta-simulation hijacked from VCS itself.
*/
int main(int argc, char **argv) {
std::vector<std::string> args{argv + 1, argv + argc};
simulator = new simif_emul_vcs_t(conf_target, args);
return vcs_main(argc, argv);
int simif_emul_vcs_t::run(simulation_t &sim) {
start_driver(sim);
vcs_main(argc, argv);
}
std::unique_ptr<simif_t>
create_simif(const TargetConfig &config, int argc, char **argv) {
return std::make_unique<simif_emul_vcs_t>(config, argc, argv);
}

View File

@ -21,7 +21,7 @@ public:
~simif_emul_verilator_t();
int run();
int run(simulation_t &sim);
uint64_t get_time() const { return main_time; }
@ -62,7 +62,9 @@ simif_emul_verilator_t::~simif_emul_verilator_t() {
#endif
}
int simif_emul_verilator_t::run() {
int simif_emul_verilator_t::run(simulation_t &sim) {
start_driver(sim);
top->clock = 0;
top->reset = 1;
@ -98,8 +100,9 @@ void simif_emul_verilator_t::tick() {
top->eval();
}
int main(int argc, char **argv) {
std::unique_ptr<simif_t>
create_simif(const TargetConfig &config, int argc, char **argv) {
Verilated::commandArgs(argc, argv);
std::vector<std::string> args(argv + 1, argv + argc);
return simif_emul_verilator_t(conf_target, args).run();
return std::make_unique<simif_emul_verilator_t>(config, args);
}

View File

@ -16,8 +16,6 @@ public:
simif_f1_t(const TargetConfig &config, const std::vector<std::string> &args);
~simif_f1_t();
int run() { return simulation_run(); }
void write(size_t addr, uint32_t data) override;
uint32_t read(size_t addr) override;
@ -204,7 +202,8 @@ uint32_t simif_f1_t::is_write_ready() {
return value & 0xFFFFFFFF;
}
int main(int argc, char **argv) {
std::unique_ptr<simif_t>
create_simif(const TargetConfig &config, int argc, char **argv) {
std::vector<std::string> args(argv + 1, argv + argc);
return simif_f1_t(conf_target, args).run();
return std::make_unique<simif_f1_t>(config, args);
}

View File

@ -14,8 +14,6 @@ public:
const std::vector<std::string> &args);
~simif_f1_xsim_t();
int run() { return simulation_run(); }
void write(size_t addr, uint32_t data) override;
uint32_t read(size_t addr) override;
@ -107,7 +105,8 @@ uint32_t simif_f1_xsim_t::is_write_ready() {
return *((uint64_t *)buf);
}
int main(int argc, char **argv) {
std::unique_ptr<simif_t>
create_simif(const TargetConfig &config, int argc, char **argv) {
std::vector<std::string> args(argv + 1, argv + argc);
return simif_f1_xsim_t(conf_target, args).run();
return std::make_unique<simif_f1_xsim_t>(config, args);
}

View File

@ -19,8 +19,6 @@ public:
const std::vector<std::string> &args);
~simif_vitis_t() {}
int run() { return simulation_run(); }
void write(size_t addr, uint32_t data) override;
uint32_t read(size_t addr) override;
@ -138,7 +136,8 @@ char *simif_vitis_t::get_memory_base() {
abort();
}
int main(int argc, char **argv) {
std::unique_ptr<simif_t>
create_simif(const TargetConfig &config, int argc, char **argv) {
std::vector<std::string> args(argv + 1, argv + argc);
return simif_vitis_t(conf_target, args).run();
return std::make_unique<simif_vitis_t>(config, args);
}

View File

@ -11,11 +11,11 @@
class firesim_top_t : public systematic_scheduler_t, public simulation_t {
public:
firesim_top_t(const std::vector<std::string> &args, simif_t &sim);
~firesim_top_t() {}
~firesim_top_t() override = default;
void simulation_init();
void simulation_finish();
int simulation_run();
void simulation_init() override;
void simulation_finish() override;
int simulation_run() override;
private:
// profile interval: # of cycles to advance before profiling instrumentation