aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xconfigure2
-rw-r--r--configure.ac1
-rw-r--r--riscv/devices.cc40
-rw-r--r--riscv/devices.h14
-rw-r--r--riscv/mmio_plugin.h91
-rw-r--r--riscv/riscv.mk.in3
-rw-r--r--riscv/sim.cc12
-rw-r--r--riscv/sim.h2
-rw-r--r--spike_main/spike.cc67
9 files changed, 225 insertions, 7 deletions
diff --git a/configure b/configure
index 4fa32bd..dae4e03 100755
--- a/configure
+++ b/configure
@@ -4364,6 +4364,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 2eb623a..ffed440 100644
--- a/riscv/sim.cc
+++ b/riscv/sim.cc
@@ -26,19 +26,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 ef9a014..744e664 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");
@@ -104,6 +111,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;
@@ -137,6 +145,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);});
@@ -156,6 +207,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, "disable-dtb", 0, [&](const char *s){dtb_enabled = false;});
@@ -191,8 +243,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));
@@ -221,5 +273,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;
}