Browse Source

Implement MMIO device plugins.

pull/315/head
Aaron Jones 7 years ago
parent
commit
d184cd4dbf
No known key found for this signature in database GPG Key ID: F6B4DF048873A86D
  1. 2
      configure
  2. 1
      configure.ac
  3. 40
      riscv/devices.cc
  4. 14
      riscv/devices.h
  5. 91
      riscv/mmio_plugin.h
  6. 3
      riscv/riscv.mk.in
  7. 12
      riscv/sim.cc
  8. 2
      riscv/sim.h
  9. 67
      spike_main/spike.cc

2
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

1
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

40
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);
}

14
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

91
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

3
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 \

12
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);

2
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;

67
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;
}

Loading…
Cancel
Save