diff options
-rwxr-xr-x | configure | 2 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | riscv/devices.cc | 40 | ||||
-rw-r--r-- | riscv/devices.h | 14 | ||||
-rw-r--r-- | riscv/mmio_plugin.h | 91 | ||||
-rw-r--r-- | riscv/riscv.mk.in | 3 | ||||
-rw-r--r-- | riscv/sim.cc | 12 | ||||
-rw-r--r-- | riscv/sim.h | 2 | ||||
-rw-r--r-- | spike_main/spike.cc | 67 |
9 files changed, 225 insertions, 7 deletions
@@ -4366,6 +4366,8 @@ CFLAGS="-Wall -Wno-unused -g -O2" CXXFLAGS="-Wall -Wno-unused -g -O2 -std=c++11" +LDFLAGS="-Wl,--export-dynamic" + #------------------------------------------------------------------------- # MCPPBS subproject list diff --git a/configure.ac b/configure.ac index f57de82..f7a28db 100644 --- a/configure.ac +++ b/configure.ac @@ -84,6 +84,7 @@ AC_CHECK_TYPE([__int128_t], AC_SUBST([HAVE_INT128],[yes])) AC_SUBST([CFLAGS], ["-Wall -Wno-unused -g -O2"]) AC_SUBST([CXXFLAGS],["-Wall -Wno-unused -g -O2 -std=c++11"]) +AC_SUBST([LDFLAGS], ["-Wl,--export-dynamic"]) #------------------------------------------------------------------------- # MCPPBS subproject list diff --git a/riscv/devices.cc b/riscv/devices.cc index bcdd3a1..4b768b6 100644 --- a/riscv/devices.cc +++ b/riscv/devices.cc @@ -48,3 +48,43 @@ std::pair<reg_t, abstract_device_t*> bus_t::find_device(reg_t addr) it--; return std::make_pair(it->first, it->second); } + +// Type for holding all registered MMIO plugins by name. +using mmio_plugin_map_t = std::map<std::string, mmio_plugin_t>; + +// Simple singleton instance of an mmio_plugin_map_t. +static mmio_plugin_map_t& mmio_plugin_map() +{ + static mmio_plugin_map_t instance; + return instance; +} + +void register_mmio_plugin(const char* name_cstr, + const mmio_plugin_t* mmio_plugin) +{ + std::string name(name_cstr); + if (!mmio_plugin_map().emplace(name, *mmio_plugin).second) { + throw std::runtime_error("Plugin \"" + name + "\" already registered!"); + } +} + +mmio_plugin_device_t::mmio_plugin_device_t(const std::string& name, + const std::string& args) + : plugin(mmio_plugin_map().at(name)), user_data((*plugin.alloc)(args.c_str())) +{ +} + +mmio_plugin_device_t::~mmio_plugin_device_t() +{ + (*plugin.dealloc)(user_data); +} + +bool mmio_plugin_device_t::load(reg_t addr, size_t len, uint8_t* bytes) +{ + return (*plugin.load)(user_data, addr, len, bytes); +} + +bool mmio_plugin_device_t::store(reg_t addr, size_t len, const uint8_t* bytes) +{ + return (*plugin.store)(user_data, addr, len, bytes); +} diff --git a/riscv/devices.h b/riscv/devices.h index 4e4d27f..aa4c050 100644 --- a/riscv/devices.h +++ b/riscv/devices.h @@ -2,6 +2,7 @@ #define _RISCV_DEVICES_H #include "decode.h" +#include "mmio_plugin.h" #include <cstdlib> #include <string> #include <map> @@ -76,4 +77,17 @@ class clint_t : public abstract_device_t { std::vector<mtimecmp_t> mtimecmp; }; +class mmio_plugin_device_t : public abstract_device_t { + public: + mmio_plugin_device_t(const std::string& name, const std::string& args); + virtual ~mmio_plugin_device_t() override; + + virtual bool load(reg_t addr, size_t len, uint8_t* bytes) override; + virtual bool store(reg_t addr, size_t len, const uint8_t* bytes) override; + + private: + mmio_plugin_t plugin; + void* user_data; +}; + #endif diff --git a/riscv/mmio_plugin.h b/riscv/mmio_plugin.h new file mode 100644 index 0000000..f14470b --- /dev/null +++ b/riscv/mmio_plugin.h @@ -0,0 +1,91 @@ +#ifndef _RISCV_MMIO_PLUGIN_H +#define _RISCV_MMIO_PLUGIN_H + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef uint64_t reg_t; + +typedef struct { + // Allocate user data for an instance of the plugin. The parameter is a simple + // c-string containing arguments used to construct the plugin. It returns a + // void* to the allocated data. + void* (*alloc)(const char*); + + // Load a memory address of the MMIO plugin. The parameters are the user_data + // (void*), memory offset (reg_t), number of bytes to load (size_t), and the + // buffer into which the loaded data should be written (uint8_t*). Return true + // if the load is successful and false otherwise. + bool (*load)(void*, reg_t, size_t, uint8_t*); + + // Store some bytes to a memory address of the MMIO plugin. The parameters are + // the user_data (void*), memory offset (reg_t), number of bytes to store + // (size_t), and the buffer containing the data to be stored (const uint8_t*). + // Return true if the store is successful and false otherwise. + bool (*store)(void*, reg_t, size_t, const uint8_t*); + + // Deallocate the data allocated during the call to alloc. The parameter is a + // pointer to the user data allocated during the call to alloc. + void (*dealloc)(void*); +} mmio_plugin_t; + +// Register an mmio plugin with the application. This should be called by +// plugins as part of their loading process. +extern void register_mmio_plugin(const char* name_cstr, + const mmio_plugin_t* mmio_plugin); + +#ifdef __cplusplus +} + +#include <string> + +// Wrapper around the C plugin API that makes registering a C++ class with +// correctly formed constructor, load, and store functions easier. The template +// type should be the type that implements the MMIO plugin interface. Simply +// make a global mmio_plugin_registration_t and your plugin should register +// itself with the application when it is loaded because the +// mmio_plugin_registration_t constructor will be called. +template <typename T> +struct mmio_plugin_registration_t +{ + static void* alloc(const char* args) + { + return reinterpret_cast<void*>(new T(std::string(args))); + } + + static bool load(void* self, reg_t addr, size_t len, uint8_t* bytes) + { + return reinterpret_cast<T*>(self)->load(addr, len, bytes); + } + + static bool store(void* self, reg_t addr, size_t len, const uint8_t* bytes) + { + return reinterpret_cast<T*>(self)->store(addr, len, bytes); + } + + static void dealloc(void* self) + { + delete reinterpret_cast<T*>(self); + } + + mmio_plugin_registration_t(const std::string& name) + { + mmio_plugin_t plugin = { + mmio_plugin_registration_t<T>::alloc, + mmio_plugin_registration_t<T>::load, + mmio_plugin_registration_t<T>::store, + mmio_plugin_registration_t<T>::dealloc, + }; + + register_mmio_plugin(name.c_str(), &plugin); + } +}; +#endif // __cplusplus + +#endif diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in index 3057c7d..af5bbdc 100644 --- a/riscv/riscv.mk.in +++ b/riscv/riscv.mk.in @@ -20,6 +20,7 @@ riscv_hdrs = \ encoding.h \ cachesim.h \ memtracer.h \ + mmio_plugin.h \ tracer.h \ extension.h \ rocc.h \ @@ -29,6 +30,8 @@ riscv_hdrs = \ remote_bitbang.h \ jtag_dtm.h \ +riscv_install_hdrs = mmio_plugin.h + riscv_precompiled_hdrs = \ insn_template.h \ diff --git a/riscv/sim.cc b/riscv/sim.cc index 4384419..5be2bfe 100644 --- a/riscv/sim.cc +++ b/riscv/sim.cc @@ -29,19 +29,23 @@ static void handle_signal(int sig) sim_t::sim_t(const char* isa, const char* varch, size_t nprocs, bool halted, reg_t start_pc, std::vector<std::pair<reg_t, mem_t*>> mems, + std::vector<std::pair<reg_t, abstract_device_t*>> plugin_devices, const std::vector<std::string>& args, std::vector<int> const hartids, const debug_module_config_t &dm_config) - : htif_t(args), mems(mems), procs(std::max(nprocs, size_t(1))), - start_pc(start_pc), current_step(0), current_proc(0), debug(false), - histogram_enabled(false), dtb_enabled(true), remote_bitbang(NULL), - debug_module(this, dm_config) + : htif_t(args), mems(mems), plugin_devices(plugin_devices), + procs(std::max(nprocs, size_t(1))), start_pc(start_pc), current_step(0), + current_proc(0), debug(false), histogram_enabled(false), dtb_enabled(true), + remote_bitbang(NULL), debug_module(this, dm_config) { signal(SIGINT, &handle_signal); for (auto& x : mems) bus.add_device(x.first, x.second); + for (auto& x : plugin_devices) + bus.add_device(x.first, x.second); + debug_module.add_device(&bus); debug_mmu = new mmu_t(this, NULL); diff --git a/riscv/sim.h b/riscv/sim.h index a2bdd1c..57d1148 100644 --- a/riscv/sim.h +++ b/riscv/sim.h @@ -23,6 +23,7 @@ class sim_t : public htif_t, public simif_t public: sim_t(const char* isa, const char* varch, size_t _nprocs, bool halted, reg_t start_pc, std::vector<std::pair<reg_t, mem_t*>> mems, + std::vector<std::pair<reg_t, abstract_device_t*>> plugin_devices, const std::vector<std::string>& args, const std::vector<int> hartids, const debug_module_config_t &dm_config); ~sim_t(); @@ -48,6 +49,7 @@ public: private: std::vector<std::pair<reg_t, mem_t*>> mems; + std::vector<std::pair<reg_t, abstract_device_t*>> plugin_devices; mmu_t* debug_mmu; // debug port into main memory std::vector<processor_t*> procs; reg_t start_pc; diff --git a/spike_main/spike.cc b/spike_main/spike.cc index ecd14ed..83454ad 100644 --- a/spike_main/spike.cc +++ b/spike_main/spike.cc @@ -35,9 +35,16 @@ static void help(int exit_code = 1) fprintf(stderr, " --ic=<S>:<W>:<B> Instantiate a cache model with S sets,\n"); fprintf(stderr, " --dc=<S>:<W>:<B> W ways, and B-byte blocks (with S and\n"); fprintf(stderr, " --l2=<S>:<W>:<B> B both powers of 2).\n"); + fprintf(stderr, " --device=<P,B,A> Attach MMIO plugin device from an --extlib library\n"); + fprintf(stderr, " P -- Name of the MMIO plugin\n"); + fprintf(stderr, " B -- Base memory address of the device\n"); + fprintf(stderr, " A -- String arguments to pass to the plugin\n"); + fprintf(stderr, " This flag can be used multiple times.\n"); + fprintf(stderr, " The extlib flag for the library must come first.\n"); fprintf(stderr, " --log-cache-miss Generate a log of cache miss\n"); fprintf(stderr, " --extension=<name> Specify RoCC Extension\n"); fprintf(stderr, " --extlib=<name> Shared library to load\n"); + fprintf(stderr, " This flag can be used multiple times.\n"); fprintf(stderr, " --rbb-port=<port> Listen on <port> for remote bitbang connection\n"); fprintf(stderr, " --dump-dts Print device tree string and exit\n"); fprintf(stderr, " --disable-dtb Don't write the device tree blob into memory\n"); @@ -107,6 +114,7 @@ int main(int argc, char** argv) size_t nprocs = 1; reg_t start_pc = reg_t(-1); std::vector<std::pair<reg_t, mem_t*>> mems; + std::vector<std::pair<reg_t, abstract_device_t*>> plugin_devices; std::unique_ptr<icache_sim_t> ic; std::unique_ptr<dcache_sim_t> dc; std::unique_ptr<cache_sim_t> l2; @@ -140,6 +148,49 @@ int main(int argc, char** argv) } }; + auto const device_parser = [&plugin_devices](const char *s) { + const std::string str(s); + std::istringstream stream(str); + + // We are parsing a string like name,base,args. + + // Parse the name, which is simply all of the characters leading up to the + // first comma. The validity of the plugin name will be checked later. + std::string name; + std::getline(stream, name, ','); + if (name.empty()) { + throw std::runtime_error("Plugin name is empty."); + } + + // Parse the base address. First, get all of the characters up to the next + // comma (or up to the end of the string if there is no comma). Then try to + // parse that string as an integer according to the rules of strtoull. It + // could be in decimal, hex, or octal. Fail if we were able to parse a + // number but there were garbage characters after the valid number. We must + // consume the entire string between the commas. + std::string base_str; + std::getline(stream, base_str, ','); + if (base_str.empty()) { + throw std::runtime_error("Device base address is empty."); + } + char* end; + reg_t base = static_cast<reg_t>(strtoull(base_str.c_str(), &end, 0)); + if (end != &*base_str.cend()) { + throw std::runtime_error("Error parsing device base address."); + } + + // The remainder of the string is the arguments. We could use getline, but + // that could ignore newline characters in the arguments. That should be + // rare and discouraged, but handle it here anyway with this weird in_avail + // technique. The arguments are optional, so if there were no arguments + // specified we could end up with an empty string here. That's okay. + auto avail = stream.rdbuf()->in_avail(); + std::string args(avail, '\0'); + stream.readsome(&args[0], avail); + + plugin_devices.emplace_back(base, new mmio_plugin_device_t(name, args)); + }; + option_parser_t parser; parser.help(&suggest_help); parser.option('h', "help", 0, [&](const char* s){help(0);}); @@ -159,6 +210,7 @@ int main(int argc, char** argv) parser.option(0, "log-cache-miss", 0, [&](const char* s){log_cache = true;}); parser.option(0, "isa", 1, [&](const char* s){isa = s;}); parser.option(0, "varch", 1, [&](const char* s){varch = s;}); + parser.option(0, "device", 1, device_parser); parser.option(0, "extension", 1, [&](const char* s){extension = find_extension(s);}); parser.option(0, "dump-dts", 0, [&](const char *s){dump_dts = true;}); parser.option(0, "check-1905", 0, [&](const char *s){g_check_1905 = true;}); @@ -196,8 +248,8 @@ int main(int argc, char** argv) if (!*argv1) help(); - sim_t s(isa, varch, nprocs, halted, start_pc, mems, htif_args, std::move(hartids), - dm_config); + sim_t s(isa, varch, nprocs, halted, start_pc, mems, plugin_devices, htif_args, + std::move(hartids), dm_config); std::unique_ptr<remote_bitbang_t> remote_bitbang((remote_bitbang_t *) NULL); std::unique_ptr<jtag_dtm_t> jtag_dtm( new jtag_dtm_t(&s.debug_module, dmi_rti)); @@ -226,5 +278,14 @@ int main(int argc, char** argv) s.set_debug(debug); s.set_log(log); s.set_histogram(histogram); - return s.run(); + + auto return_code = s.run(); + + for (auto& mem : mems) + delete mem.second; + + for (auto& plugin_device : plugin_devices) + delete plugin_device.second; + + return return_code; } |