Browse Source

Merge pull request #1374 from riscv-software-src/devices

Use device factories to control dts-construction/dts-parsing/device-instantiation
pull/1391/head
Jerry Zhao 3 years ago
committed by GitHub
parent
commit
d8b6fc534f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      ci-tests/testlib.c
  2. 5
      fdt/fdt.mk.in
  3. 32
      riscv/abstract_device.h
  4. 43
      riscv/clint.cc
  5. 4
      riscv/debug_module.cc
  6. 2
      riscv/debug_module.h
  7. 46
      riscv/devices.cc
  8. 53
      riscv/devices.h
  9. 80
      riscv/dts.cc
  10. 26
      riscv/dts.h
  11. 91
      riscv/mmio_plugin.h
  12. 44
      riscv/ns16550.cc
  13. 39
      riscv/plic.cc
  14. 2
      riscv/riscv.mk.in
  15. 147
      riscv/sim.cc
  16. 19
      riscv/sim.h
  17. 61
      spike_main/spike.cc

2
ci-tests/testlib.c

@ -28,7 +28,7 @@ int main()
hartids,
false,
4);
std::vector<std::pair<reg_t, abstract_device_t*>> plugin_devices;
std::vector<const device_factory_t*> plugin_devices;
std::vector<std::string> htif_args {"pk", "hello"};
debug_module_config_t dm_config = {
.progbufsize = 2,

5
fdt/fdt.mk.in

@ -1,5 +1,10 @@
fdt_subproject_deps = \
fdt_install_shared_lib = yes
fdt_install_hdrs = \
libfdt.h \
fdt_c_srcs = \
fdt.c \
fdt_ro.c \

32
riscv/abstract_device.h

@ -2,14 +2,46 @@
#define _RISCV_ABSTRACT_DEVICE_H
#include "decode.h"
#include "common.h"
#include <cstdint>
#include <cstddef>
#include <string>
#include <map>
#include <stdexcept>
class sim_t;
class abstract_device_t {
public:
virtual bool load(reg_t addr, size_t len, uint8_t* bytes) = 0;
virtual bool store(reg_t addr, size_t len, const uint8_t* bytes) = 0;
virtual ~abstract_device_t() {}
virtual void tick(reg_t UNUSED rtc_ticks) {}
};
// factory for devices which should show up in the DTS, and can be
// parameterized by parsing the DTS
class device_factory_t {
public:
virtual abstract_device_t* parse_from_fdt(const void* fdt, const sim_t* sim, reg_t* base) const = 0;
virtual std::string generate_dts(const sim_t* sim) const = 0;
virtual ~device_factory_t() {}
};
// Type for holding all registered MMIO plugins by name.
using mmio_device_map_t = std::map<std::string, const device_factory_t*>;
mmio_device_map_t& mmio_device_map();
#define REGISTER_DEVICE(name, parse, generate) \
class name##_factory_t : public device_factory_t { \
public: \
name##_factory_t() { \
std::string str(#name); \
if (!mmio_device_map().emplace(str, this).second) throw std::runtime_error("Plugin \"" + str + "\" already registered"); \
}; \
name##_t* parse_from_fdt(const void* fdt, const sim_t* sim, reg_t* base) const override { return parse(fdt, sim, base); } \
std::string generate_dts(const sim_t* sim) const override { return generate(sim); } \
}; const device_factory_t *name##_factory = new name##_factory_t();
#endif

43
riscv/clint.cc

@ -1,9 +1,12 @@
#include <sys/time.h>
#include <sstream>
#include "devices.h"
#include "processor.h"
#include "simif.h"
#include "sim.h"
#include "dts.h"
clint_t::clint_t(simif_t* sim, uint64_t freq_hz, bool real_time)
clint_t::clint_t(const simif_t* sim, uint64_t freq_hz, bool real_time)
: sim(sim), freq_hz(freq_hz), real_time(real_time), mtime(0)
{
struct timeval base;
@ -12,7 +15,7 @@ clint_t::clint_t(simif_t* sim, uint64_t freq_hz, bool real_time)
real_time_ref_secs = base.tv_sec;
real_time_ref_usecs = base.tv_usec;
increment(0);
tick(0);
}
/* 0000 msip hart 0
@ -34,7 +37,7 @@ bool clint_t::load(reg_t addr, size_t len, uint8_t* bytes)
if (len > 8)
return false;
increment(0);
tick(0);
if (addr >= MSIP_BASE && addr < MTIMECMP_BASE) {
if (len == 8) {
@ -90,11 +93,11 @@ bool clint_t::store(reg_t addr, size_t len, const uint8_t* bytes)
} else {
return false;
}
increment(0);
tick(0);
return true;
}
void clint_t::increment(reg_t inc)
void clint_t::tick(reg_t rtc_ticks)
{
if (real_time) {
struct timeval now;
@ -104,7 +107,7 @@ void clint_t::increment(reg_t inc)
diff_usecs = ((now.tv_sec - real_time_ref_secs) * 1000000) + (now.tv_usec - real_time_ref_usecs);
mtime = diff_usecs * freq_hz / 1000000;
} else {
mtime += inc;
mtime += rtc_ticks;
}
for (const auto& [hart_id, hart] : sim->get_harts()) {
@ -112,3 +115,31 @@ void clint_t::increment(reg_t inc)
hart->state.mip->backdoor_write_with_mask(MIP_MTIP, mtime >= mtimecmp[hart_id] ? MIP_MTIP : 0);
}
}
clint_t* clint_parse_from_fdt(const void* fdt, const sim_t* sim, reg_t* base) {
if (fdt_parse_clint(fdt, base, "riscv,clint0") == 0)
return new clint_t(sim,
sim->CPU_HZ / sim->INSNS_PER_RTC_TICK,
sim->get_cfg().real_time_clint());
else
return nullptr;
}
std::string clint_generate_dts(const sim_t* sim) {
std::stringstream s;
s << std::hex
<< " clint@" << CLINT_BASE << " {\n"
" compatible = \"riscv,clint0\";\n"
" interrupts-extended = <" << std::dec;
for (size_t i = 0; i < sim->get_cfg().nprocs(); i++)
s << "&CPU" << i << "_intc 3 &CPU" << i << "_intc 7 ";
reg_t clintbs = CLINT_BASE;
reg_t clintsz = CLINT_SIZE;
s << std::hex << ">;\n"
" reg = <0x" << (clintbs >> 32) << " 0x" << (clintbs & (uint32_t)-1) <<
" 0x" << (clintsz >> 32) << " 0x" << (clintsz & (uint32_t)-1) << ">;\n"
" };\n";
return s.str();
}
REGISTER_DEVICE(clint, clint_parse_from_fdt, clint_generate_dts)

4
riscv/debug_module.cc

@ -118,10 +118,6 @@ void debug_module_t::reset()
challenge = random();
}
void debug_module_t::add_device(bus_t *bus) {
bus->add_device(DEBUG_START, this);
}
bool debug_module_t::load(reg_t addr, size_t len, uint8_t* bytes)
{
addr = DEBUG_START + addr;

2
riscv/debug_module.h

@ -113,8 +113,6 @@ class debug_module_t : public abstract_device_t
debug_module_t(simif_t *sim, const debug_module_config_t &config);
~debug_module_t();
void add_device(bus_t *bus);
bool load(reg_t addr, size_t len, uint8_t* bytes);
bool store(reg_t addr, size_t len, const uint8_t* bytes);

46
riscv/devices.cc

@ -2,6 +2,12 @@
#include "mmu.h"
#include <stdexcept>
mmio_device_map_t& mmio_device_map()
{
static mmio_device_map_t device_map;
return device_map;
}
void bus_t::add_device(reg_t addr, abstract_device_t* dev)
{
// Searching devices via lower_bound/upper_bound
@ -51,46 +57,6 @@ std::pair<reg_t, abstract_device_t*> bus_t::find_device(reg_t addr)
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);
}
mem_t::mem_t(reg_t size)
: sz(size)
{

53
riscv/devices.h

@ -2,7 +2,6 @@
#define _RISCV_DEVICES_H
#include "decode.h"
#include "mmio_plugin.h"
#include "abstract_device.h"
#include "abstract_interrupt_controller.h"
#include "platform.h"
@ -17,8 +16,8 @@ class simif_t;
class bus_t : public abstract_device_t {
public:
bool load(reg_t addr, size_t len, uint8_t* bytes);
bool store(reg_t addr, size_t len, const uint8_t* bytes);
bool load(reg_t addr, size_t len, uint8_t* bytes) override;
bool store(reg_t addr, size_t len, const uint8_t* bytes) override;
void add_device(reg_t addr, abstract_device_t* dev);
std::pair<reg_t, abstract_device_t*> find_device(reg_t addr);
@ -30,8 +29,8 @@ class bus_t : public abstract_device_t {
class rom_device_t : public abstract_device_t {
public:
rom_device_t(std::vector<char> data);
bool load(reg_t addr, size_t len, uint8_t* bytes);
bool store(reg_t addr, size_t len, const uint8_t* bytes);
bool load(reg_t addr, size_t len, uint8_t* bytes) override;
bool store(reg_t addr, size_t len, const uint8_t* bytes) override;
const std::vector<char>& contents() { return data; }
private:
std::vector<char> data;
@ -43,8 +42,8 @@ class mem_t : public abstract_device_t {
mem_t(const mem_t& that) = delete;
~mem_t();
bool load(reg_t addr, size_t len, uint8_t* bytes) { return load_store(addr, len, bytes, false); }
bool store(reg_t addr, size_t len, const uint8_t* bytes) { return load_store(addr, len, const_cast<uint8_t*>(bytes), true); }
bool load(reg_t addr, size_t len, uint8_t* bytes) override { return load_store(addr, len, bytes, false); }
bool store(reg_t addr, size_t len, const uint8_t* bytes) override { return load_store(addr, len, const_cast<uint8_t*>(bytes), true); }
char* contents(reg_t addr);
reg_t size() { return sz; }
void dump(std::ostream& o);
@ -58,18 +57,18 @@ class mem_t : public abstract_device_t {
class clint_t : public abstract_device_t {
public:
clint_t(simif_t*, uint64_t freq_hz, bool real_time);
bool load(reg_t addr, size_t len, uint8_t* bytes);
bool store(reg_t addr, size_t len, const uint8_t* bytes);
clint_t(const simif_t*, uint64_t freq_hz, bool real_time);
bool load(reg_t addr, size_t len, uint8_t* bytes) override;
bool store(reg_t addr, size_t len, const uint8_t* bytes) override;
size_t size() { return CLINT_SIZE; }
void increment(reg_t inc);
void tick(reg_t rtc_ticks) override;
uint64_t get_mtimecmp(reg_t hartid) { return mtimecmp[hartid]; }
uint64_t get_mtime() { return mtime; }
private:
typedef uint64_t mtime_t;
typedef uint64_t mtimecmp_t;
typedef uint32_t msip_t;
simif_t* sim;
const simif_t* sim;
uint64_t freq_hz;
bool real_time;
uint64_t real_time_ref_secs;
@ -97,10 +96,10 @@ struct plic_context_t {
class plic_t : public abstract_device_t, public abstract_interrupt_controller_t {
public:
plic_t(simif_t*, uint32_t ndev);
bool load(reg_t addr, size_t len, uint8_t* bytes);
bool store(reg_t addr, size_t len, const uint8_t* bytes);
void set_interrupt_level(uint32_t id, int lvl);
plic_t(const simif_t*, uint32_t ndev);
bool load(reg_t addr, size_t len, uint8_t* bytes) override;
bool store(reg_t addr, size_t len, const uint8_t* bytes) override;
void set_interrupt_level(uint32_t id, int lvl) override;
size_t size() { return PLIC_SIZE; }
private:
std::vector<plic_context_t> contexts;
@ -127,14 +126,13 @@ class plic_t : public abstract_device_t, public abstract_interrupt_controller_t
class ns16550_t : public abstract_device_t {
public:
ns16550_t(class bus_t *bus, abstract_interrupt_controller_t *intctrl,
ns16550_t(abstract_interrupt_controller_t *intctrl,
uint32_t interrupt_id, uint32_t reg_shift, uint32_t reg_io_width);
bool load(reg_t addr, size_t len, uint8_t* bytes);
bool store(reg_t addr, size_t len, const uint8_t* bytes);
void tick(void);
bool load(reg_t addr, size_t len, uint8_t* bytes) override;
bool store(reg_t addr, size_t len, const uint8_t* bytes) override;
void tick(reg_t rtc_ticks) override;
size_t size() { return NS16550_SIZE; }
private:
class bus_t *bus;
abstract_interrupt_controller_t *intctrl;
uint32_t interrupt_id;
uint32_t reg_shift;
@ -158,19 +156,6 @@ class ns16550_t : public abstract_device_t {
static const int MAX_BACKOFF = 16;
};
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;
};
template<typename T>
void write_little_endian_reg(T* word, reg_t addr, size_t len, const uint8_t* bytes)
{

80
riscv/dts.cc

@ -17,7 +17,8 @@ std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz,
const char* bootargs,
size_t pmpregions,
std::vector<processor_t*> procs,
std::vector<std::pair<reg_t, mem_t*>> mems)
std::vector<std::pair<reg_t, mem_t*>> mems,
std::string device_nodes)
{
std::stringstream s;
s << std::dec <<
@ -85,47 +86,8 @@ std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz,
" #size-cells = <2>;\n"
" compatible = \"ucbbar,spike-bare-soc\", \"simple-bus\";\n"
" ranges;\n"
" clint@" << CLINT_BASE << " {\n"
" compatible = \"riscv,clint0\";\n"
" interrupts-extended = <" << std::dec;
for (size_t i = 0; i < procs.size(); i++)
s << "&CPU" << i << "_intc 3 &CPU" << i << "_intc 7 ";
reg_t clintbs = CLINT_BASE;
reg_t clintsz = CLINT_SIZE;
s << std::hex << ">;\n"
" reg = <0x" << (clintbs >> 32) << " 0x" << (clintbs & (uint32_t)-1) <<
" 0x" << (clintsz >> 32) << " 0x" << (clintsz & (uint32_t)-1) << ">;\n"
" };\n"
" PLIC: plic@" << PLIC_BASE << " {\n"
" compatible = \"riscv,plic0\";\n"
" #address-cells = <2>;\n"
" interrupts-extended = <" << std::dec;
for (size_t i = 0; i < procs.size(); i++)
s << "&CPU" << i << "_intc 11 &CPU" << i << "_intc 9 ";
reg_t plicbs = PLIC_BASE;
reg_t plicsz = PLIC_SIZE;
s << std::hex << ">;\n"
" reg = <0x" << (plicbs >> 32) << " 0x" << (plicbs & (uint32_t)-1) <<
" 0x" << (plicsz >> 32) << " 0x" << (plicsz & (uint32_t)-1) << ">;\n"
" riscv,ndev = <0x" << PLIC_NDEV << ">;\n"
" riscv,max-priority = <0x" << ((1U << PLIC_PRIO_BITS) - 1) << ">;\n"
" #interrupt-cells = <1>;\n"
" interrupt-controller;\n"
" };\n"
" SERIAL0: ns16550@" << NS16550_BASE << " {\n"
" compatible = \"ns16550a\";\n"
" clock-frequency = <" << std::dec << (cpu_hz/insns_per_rtc_tick) << ">;\n"
" interrupt-parent = <&PLIC>;\n"
" interrupts = <" << std::dec << NS16550_INTERRUPT_ID;
reg_t ns16550bs = NS16550_BASE;
reg_t ns16550sz = NS16550_SIZE;
s << std::hex << ">;\n"
" reg = <0x" << (ns16550bs >> 32) << " 0x" << (ns16550bs & (uint32_t)-1) <<
" 0x" << (ns16550sz >> 32) << " 0x" << (ns16550sz & (uint32_t)-1) << ">;\n"
" reg-shift = <0x" << NS16550_REG_SHIFT << ">;\n"
" reg-io-width = <0x" << NS16550_REG_IO_WIDTH << ">;\n"
" };\n"
" };\n"
<< device_nodes
<< " };\n"
" htif {\n"
" compatible = \"ucb,htif0\";\n"
" };\n"
@ -215,8 +177,8 @@ std::string dts_compile(const std::string& dts)
return dtb.str();
}
static int fdt_get_node_addr_size(void *fdt, int node, reg_t *addr,
unsigned long *size, const char *field)
int fdt_get_node_addr_size(const void *fdt, int node, reg_t *addr,
unsigned long *size, const char *field)
{
int parent, len, i;
int cell_addr, cell_size;
@ -259,7 +221,7 @@ static int fdt_get_node_addr_size(void *fdt, int node, reg_t *addr,
return 0;
}
static int check_cpu_node(void *fdt, int cpu_offset)
static int check_cpu_node(const void *fdt, int cpu_offset)
{
int len;
const void *prop;
@ -276,22 +238,22 @@ static int check_cpu_node(void *fdt, int cpu_offset)
return 0;
}
int fdt_get_offset(void *fdt, const char *field)
int fdt_get_offset(const void *fdt, const char *field)
{
return fdt_path_offset(fdt, field);
}
int fdt_get_first_subnode(void *fdt, int node)
int fdt_get_first_subnode(const void *fdt, int node)
{
return fdt_first_subnode(fdt, node);
}
int fdt_get_next_subnode(void *fdt, int node)
int fdt_get_next_subnode(const void *fdt, int node)
{
return fdt_next_subnode(fdt, node);
}
int fdt_parse_clint(void *fdt, reg_t *clint_addr,
int fdt_parse_clint(const void *fdt, reg_t *clint_addr,
const char *compatible)
{
int nodeoffset, rc;
@ -307,7 +269,7 @@ int fdt_parse_clint(void *fdt, reg_t *clint_addr,
return 0;
}
int fdt_parse_plic(void *fdt, reg_t *plic_addr, uint32_t *ndev,
int fdt_parse_plic(const void *fdt, reg_t *plic_addr, uint32_t *ndev,
const char *compatible)
{
int nodeoffset, len, rc;
@ -329,8 +291,9 @@ int fdt_parse_plic(void *fdt, reg_t *plic_addr, uint32_t *ndev,
return 0;
}
int fdt_parse_ns16550(void *fdt, reg_t *ns16550_addr,
int fdt_parse_ns16550(const void *fdt, reg_t *ns16550_addr,
uint32_t *reg_shift, uint32_t *reg_io_width,
uint32_t* reg_int_id,
const char *compatible)
{
int nodeoffset, len, rc;
@ -362,10 +325,19 @@ int fdt_parse_ns16550(void *fdt, reg_t *ns16550_addr,
}
}
reg_p = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "interrupts", &len);
if (reg_int_id) {
if (reg_p) {
*reg_int_id = fdt32_to_cpu(*reg_p);
} else {
*reg_int_id = NS16550_INTERRUPT_ID;
}
}
return 0;
}
int fdt_parse_pmp_num(void *fdt, int cpu_offset, reg_t *pmp_num)
int fdt_parse_pmp_num(const void *fdt, int cpu_offset, reg_t *pmp_num)
{
int rc;
@ -380,7 +352,7 @@ int fdt_parse_pmp_num(void *fdt, int cpu_offset, reg_t *pmp_num)
return 0;
}
int fdt_parse_pmp_alignment(void *fdt, int cpu_offset, reg_t *pmp_align)
int fdt_parse_pmp_alignment(const void *fdt, int cpu_offset, reg_t *pmp_align)
{
int rc;
@ -395,7 +367,7 @@ int fdt_parse_pmp_alignment(void *fdt, int cpu_offset, reg_t *pmp_align)
return 0;
}
int fdt_parse_mmu_type(void *fdt, int cpu_offset, const char **mmu_type)
int fdt_parse_mmu_type(const void *fdt, int cpu_offset, const char **mmu_type)
{
assert(mmu_type);

26
riscv/dts.h

@ -4,7 +4,6 @@
#include "devices.h"
#include "processor.h"
#include "mmu.h"
#include <string>
std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz,
@ -12,22 +11,25 @@ std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz,
const char* bootargs,
size_t pmpregions,
std::vector<processor_t*> procs,
std::vector<std::pair<reg_t, mem_t*>> mems);
std::vector<std::pair<reg_t, mem_t*>> mems,
std::string device_nodes);
std::string dts_compile(const std::string& dts);
int fdt_get_offset(void *fdt, const char *field);
int fdt_get_first_subnode(void *fdt, int node);
int fdt_get_next_subnode(void *fdt, int node);
int fdt_get_node_addr_size(const void *fdt, int node, reg_t *addr,
unsigned long *size, const char *field);
int fdt_get_offset(const void *fdt, const char *field);
int fdt_get_first_subnode(const void *fdt, int node);
int fdt_get_next_subnode(const void *fdt, int node);
int fdt_parse_clint(void *fdt, reg_t *clint_addr,
int fdt_parse_clint(const void *fdt, reg_t *clint_addr,
const char *compatible);
int fdt_parse_plic(void *fdt, reg_t *plic_addr, uint32_t *ndev,
int fdt_parse_plic(const void *fdt, reg_t *plic_addr, uint32_t *ndev,
const char *compatible);
int fdt_parse_ns16550(void *fdt, reg_t *ns16550_addr,
uint32_t *reg_shift, uint32_t *reg_io_width,
int fdt_parse_ns16550(const void *fdt, reg_t *ns16550_addr,
uint32_t *reg_shift, uint32_t *reg_io_width, uint32_t* reg_int_id,
const char *compatible);
int fdt_parse_pmp_num(void *fdt, int cpu_offset, reg_t *pmp_num);
int fdt_parse_pmp_alignment(void *fdt, int cpu_offset, reg_t *pmp_align);
int fdt_parse_mmu_type(void *fdt, int cpu_offset, const char **mmu_type);
int fdt_parse_pmp_num(const void *fdt, int cpu_offset, reg_t *pmp_num);
int fdt_parse_pmp_alignment(const void *fdt, int cpu_offset, reg_t *pmp_align);
int fdt_parse_mmu_type(const void *fdt, int cpu_offset, const char **mmu_type);
#endif

91
riscv/mmio_plugin.h

@ -1,91 +0,0 @@
#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

44
riscv/ns16550.cc

@ -1,7 +1,10 @@
#include <sys/time.h>
#include <sstream>
#include "devices.h"
#include "processor.h"
#include "term.h"
#include "sim.h"
#include "dts.h"
#define UART_QUEUE_SIZE 64
@ -69,9 +72,9 @@
#define UART_SCR 7 /* I/O: Scratch Register */
ns16550_t::ns16550_t(class bus_t *bus, abstract_interrupt_controller_t *intctrl,
ns16550_t::ns16550_t(abstract_interrupt_controller_t *intctrl,
uint32_t interrupt_id, uint32_t reg_shift, uint32_t reg_io_width)
: bus(bus), intctrl(intctrl), interrupt_id(interrupt_id), reg_shift(reg_shift), reg_io_width(reg_io_width), backoff_counter(0)
: intctrl(intctrl), interrupt_id(interrupt_id), reg_shift(reg_shift), reg_io_width(reg_io_width), backoff_counter(0)
{
ier = 0;
iir = UART_IIR_NO_INT;
@ -292,7 +295,7 @@ bool ns16550_t::store(reg_t addr, size_t len, const uint8_t* bytes)
return ret;
}
void ns16550_t::tick(void)
void ns16550_t::tick(reg_t UNUSED rtc_ticks)
{
if (!(fcr & UART_FCR_ENABLE_FIFO) ||
(mcr & UART_MCR_LOOP) ||
@ -317,3 +320,38 @@ void ns16550_t::tick(void)
lsr |= UART_LSR_DR;
update_interrupt();
}
std::string ns16550_generate_dts(const sim_t* sim)
{
std::stringstream s;
s << std::hex
<< " SERIAL0: ns16550@" << NS16550_BASE << " {\n"
" compatible = \"ns16550a\";\n"
" clock-frequency = <" << std::dec << (sim->CPU_HZ/sim->INSNS_PER_RTC_TICK) << ">;\n"
" interrupt-parent = <&PLIC>;\n"
" interrupts = <" << std::dec << NS16550_INTERRUPT_ID;
reg_t ns16550bs = NS16550_BASE;
reg_t ns16550sz = NS16550_SIZE;
s << std::hex << ">;\n"
" reg = <0x" << (ns16550bs >> 32) << " 0x" << (ns16550bs & (uint32_t)-1) <<
" 0x" << (ns16550sz >> 32) << " 0x" << (ns16550sz & (uint32_t)-1) << ">;\n"
" reg-shift = <0x" << NS16550_REG_SHIFT << ">;\n"
" reg-io-width = <0x" << NS16550_REG_IO_WIDTH << ">;\n"
" };\n";
return s.str();
}
ns16550_t* ns16550_parse_from_fdt(const void* fdt, const sim_t* sim, reg_t* base)
{
uint32_t ns16550_shift, ns16550_io_width, ns16550_int_id;
if (fdt_parse_ns16550(fdt, base,
&ns16550_shift, &ns16550_io_width, &ns16550_int_id,
"ns16550a") == 0) {
abstract_interrupt_controller_t* intctrl = sim->get_intctrl();
return new ns16550_t(intctrl, ns16550_int_id, ns16550_shift, ns16550_io_width);
} else {
return nullptr;
}
}
REGISTER_DEVICE(ns16550, ns16550_parse_from_fdt, ns16550_generate_dts)

39
riscv/plic.cc

@ -1,7 +1,10 @@
#include <sys/time.h>
#include <sstream>
#include "devices.h"
#include "processor.h"
#include "simif.h"
#include "sim.h"
#include "dts.h"
#define PLIC_MAX_CONTEXTS 15872
@ -70,7 +73,7 @@
#define REG_SIZE 0x1000000
plic_t::plic_t(simif_t* sim, uint32_t ndev)
plic_t::plic_t(const simif_t* sim, uint32_t ndev)
: num_ids(ndev + 1), num_ids_word(((ndev + 1) + (32 - 1)) / 32),
max_prio((1UL << PLIC_PRIO_BITS) - 1), priority{}, level{}
{
@ -388,3 +391,37 @@ bool plic_t::store(reg_t addr, size_t len, const uint8_t* bytes)
return ret;
}
std::string plic_generate_dts(const sim_t* sim)
{
std::stringstream s;
s << std::hex
<< " PLIC: plic@" << PLIC_BASE << " {\n"
" compatible = \"riscv,plic0\";\n"
" #address-cells = <2>;\n"
" interrupts-extended = <" << std::dec;
for (size_t i = 0; i < sim->get_cfg().nprocs(); i++)
s << "&CPU" << i << "_intc 11 &CPU" << i << "_intc 9 ";
reg_t plicbs = PLIC_BASE;
reg_t plicsz = PLIC_SIZE;
s << std::hex << ">;\n"
" reg = <0x" << (plicbs >> 32) << " 0x" << (plicbs & (uint32_t)-1) <<
" 0x" << (plicsz >> 32) << " 0x" << (plicsz & (uint32_t)-1) << ">;\n"
" riscv,ndev = <0x" << PLIC_NDEV << ">;\n"
" riscv,max-priority = <0x" << ((1U << PLIC_PRIO_BITS) - 1) << ">;\n"
" #interrupt-cells = <1>;\n"
" interrupt-controller;\n"
" };\n";
return s.str();
}
plic_t* plic_parse_from_fdt(const void* fdt, const sim_t* sim, reg_t* base)
{
uint32_t plic_ndev;
if (fdt_parse_plic(fdt, base, &plic_ndev, "riscv,plic0") == 0)
return new plic_t(sim, plic_ndev);
else
return nullptr;
}
REGISTER_DEVICE(plic, plic_parse_from_fdt, plic_generate_dts)

2
riscv/riscv.mk.in

@ -28,13 +28,13 @@ riscv_install_hdrs = \
decode.h \
devices.h \
disasm.h \
dts.h \
encoding.h \
entropy_source.h \
extension.h \
isa_parser.h \
log_file.h \
memtracer.h \
mmio_plugin.h \
mmu.h \
platform.h \
processor.h \

147
riscv/sim.cc

@ -32,9 +32,13 @@ static void handle_signal(int sig)
const size_t sim_t::INTERLEAVE;
extern device_factory_t* clint_factory;
extern device_factory_t* plic_factory;
extern device_factory_t* ns16550_factory;
sim_t::sim_t(const cfg_t *cfg, bool halted,
std::vector<std::pair<reg_t, mem_t*>> mems,
std::vector<std::pair<reg_t, abstract_device_t*>> plugin_devices,
std::vector<const device_factory_t*> plugin_device_factories,
const std::vector<std::string>& args,
const debug_module_config_t &dm_config,
const char *log_path,
@ -45,7 +49,6 @@ sim_t::sim_t(const cfg_t *cfg, bool halted,
isa(cfg->isa(), cfg->priv()),
cfg(cfg),
mems(mems),
plugin_devices(plugin_devices),
procs(std::max(cfg->nprocs(), size_t(1))),
dtb_enabled(dtb_enabled),
log_file(log_path),
@ -66,10 +69,7 @@ sim_t::sim_t(const cfg_t *cfg, bool halted,
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);
bus.add_device(DEBUG_START, &debug_module);
socketif = NULL;
#ifdef HAVE_BOOST_ASIO
@ -89,9 +89,9 @@ sim_t::sim_t(const cfg_t *cfg, bool halted,
#ifndef RISCV_ENABLE_DUAL_ENDIAN
if (cfg->endianness != endianness_little) {
fputs("Big-endian support has not been prroperly enabled; "
"please rebuild the riscv-isa-sim project using "
"\"configure --enable-dual-endian\".\n",
stderr);
"please rebuild the riscv-isa-sim project using "
"\"configure --enable-dual-endian\".\n",
stderr);
abort();
}
#endif
@ -107,45 +107,73 @@ sim_t::sim_t(const cfg_t *cfg, bool halted,
// When running without using a dtb, skip the fdt-based configuration steps
if (!dtb_enabled) return;
// Load dtb_file if provided, otherwise self-generate a dts/dtb
make_dtb(dtb_file);
void *fdt = (void *)dtb.c_str();
// Only make a CLINT (Core-Local INTerrupt controller) if one is specified in
// the device tree configuration.
// Only make a CLINT (Core-Local INTerrupt controller) and PLIC (Platform-
// Level-Interrupt-Controller) if they are specified in the device tree
// configuration.
//
// This isn't *quite* as general as we could get (because you might have one
// that's not bus-accessible), but it should handle the normal use cases. In
// particular, the default device tree configuration that you get without
// setting the dtb_file argument has one.
reg_t clint_base;
if (fdt_parse_clint(fdt, &clint_base, "riscv,clint0") == 0) {
clint.reset(new clint_t(this, CPU_HZ / INSNS_PER_RTC_TICK, cfg->real_time_clint()));
bus.add_device(clint_base, clint.get());
}
std::vector<const device_factory_t*> device_factories = {
clint_factory, // clint must be element 0
plic_factory, // plic must be element 1
ns16550_factory};
device_factories.insert(device_factories.end(),
plugin_device_factories.begin(),
plugin_device_factories.end());
// pointer to wired interrupt controller
abstract_interrupt_controller_t *intctrl = NULL;
// Load dtb_file if provided, otherwise self-generate a dts/dtb
if (dtb_file) {
std::ifstream fin(dtb_file, std::ios::binary);
if (!fin.good()) {
std::cerr << "can't find dtb file: " << dtb_file << std::endl;
exit(-1);
}
std::stringstream strstream;
strstream << fin.rdbuf();
dtb = strstream.str();
} else {
std::pair<reg_t, reg_t> initrd_bounds = cfg->initrd_bounds();
std::string device_nodes;
for (const device_factory_t *factory : device_factories)
device_nodes.append(factory->generate_dts(this));
dts = make_dts(INSNS_PER_RTC_TICK, CPU_HZ,
initrd_bounds.first, initrd_bounds.second,
cfg->bootargs(), cfg->pmpregions, procs, mems,
device_nodes);
dtb = dts_compile(dts);
}
// create plic
reg_t plic_base;
uint32_t plic_ndev;
if (fdt_parse_plic(fdt, &plic_base, &plic_ndev, "riscv,plic0") == 0) {
plic.reset(new plic_t(this, plic_ndev));
bus.add_device(plic_base, plic.get());
intctrl = plic.get();
int fdt_code = fdt_check_header(dtb.c_str());
if (fdt_code) {
std::cerr << "Failed to read DTB from ";
if (!dtb_file) {
std::cerr << "auto-generated DTS string";
} else {
std::cerr << "`" << dtb_file << "'";
}
std::cerr << ": " << fdt_strerror(fdt_code) << ".\n";
exit(-1);
}
// create ns16550
reg_t ns16550_base;
uint32_t ns16550_shift, ns16550_io_width;
if (fdt_parse_ns16550(fdt, &ns16550_base,
&ns16550_shift, &ns16550_io_width, "ns16550a") == 0) {
assert(intctrl);
ns16550.reset(new ns16550_t(&bus, intctrl, NS16550_INTERRUPT_ID,
ns16550_shift, ns16550_io_width));
bus.add_device(ns16550_base, ns16550.get());
void *fdt = (void *)dtb.c_str();
for (size_t i = 0; i < device_factories.size(); i++) {
const device_factory_t *factory = device_factories[i];
reg_t device_base = 0;
abstract_device_t* device = factory->parse_from_fdt(fdt, this, &device_base);
if (device) {
assert(device_base);
bus.add_device(device_base, device);
std::shared_ptr<abstract_device_t> dev_ptr(device);
devices.push_back(dev_ptr);
if (i == 0) // clint_factory
clint = std::static_pointer_cast<clint_t>(dev_ptr);
else if (i == 1) // plic_factory
plic = std::static_pointer_cast<plic_t>(dev_ptr);
}
}
//per core attribute
@ -242,8 +270,8 @@ void sim_t::step(size_t n)
procs[current_proc]->get_mmu()->yield_load_reservation();
if (++current_proc == procs.size()) {
current_proc = 0;
if (clint) clint->increment(INTERLEAVE / INSNS_PER_RTC_TICK);
if (ns16550) ns16550->tick();
reg_t rtc_ticks = INTERLEAVE / INSNS_PER_RTC_TICK;
for (auto &dev : devices) dev->tick(rtc_ticks);
}
}
}
@ -299,40 +327,6 @@ bool sim_t::mmio_store(reg_t paddr, size_t len, const uint8_t* bytes)
return bus.store(paddr, len, bytes);
}
void sim_t::make_dtb(const char* dtb_file)
{
if (dtb_file) {
std::ifstream fin(dtb_file, std::ios::binary);
if (!fin.good()) {
std::cerr << "can't find dtb file: " << dtb_file << std::endl;
exit(-1);
}
std::stringstream strstream;
strstream << fin.rdbuf();
dtb = strstream.str();
} else {
std::pair<reg_t, reg_t> initrd_bounds = cfg->initrd_bounds();
dts = make_dts(INSNS_PER_RTC_TICK, CPU_HZ,
initrd_bounds.first, initrd_bounds.second,
cfg->bootargs(), cfg->pmpregions, procs, mems);
dtb = dts_compile(dts);
}
int fdt_code = fdt_check_header(dtb.c_str());
if (fdt_code) {
std::cerr << "Failed to read DTB from ";
if (!dtb_file) {
std::cerr << "auto-generated DTS string";
} else {
std::cerr << "`" << dtb_file << "'";
}
std::cerr << ": " << fdt_strerror(fdt_code) << ".\n";
exit(-1);
}
}
void sim_t::set_rom()
{
const int reset_vec_size = 8;
@ -374,8 +368,9 @@ void sim_t::set_rom()
const int align = 0x1000;
rom.resize((rom.size() + align - 1) / align * align);
boot_rom.reset(new rom_device_t(rom));
std::shared_ptr<rom_device_t> boot_rom(new rom_device_t(rom));
bus.add_device(DEFAULT_RSTVEC, boot_rom.get());
devices.push_back(boot_rom);
}
char* sim_t::addr_to_mem(reg_t paddr) {

19
riscv/sim.h

@ -27,7 +27,7 @@ class sim_t : public htif_t, public simif_t
public:
sim_t(const cfg_t *cfg, bool halted,
std::vector<std::pair<reg_t, mem_t*>> mems,
std::vector<std::pair<reg_t, abstract_device_t*>> plugin_devices,
std::vector<const device_factory_t*> plugin_device_factories,
const std::vector<std::string>& args,
const debug_module_config_t &dm_config, const char *log_path,
bool dtb_enabled, const char *dtb_file,
@ -52,6 +52,7 @@ public:
}
const char* get_dts() { return dts.c_str(); }
processor_t* get_core(size_t i) { return procs.at(i); }
abstract_interrupt_controller_t* get_intctrl() const { assert(plic.get()); return plic.get(); }
virtual const cfg_t &get_cfg() const override { return *cfg; }
virtual const std::map<size_t, processor_t*>& get_harts() const override { return harts; }
@ -59,21 +60,23 @@ public:
// Callback for processors to let the simulation know they were reset.
virtual void proc_reset(unsigned id) override;
static const size_t INTERLEAVE = 5000;
static const size_t INSNS_PER_RTC_TICK = 100; // 10 MHz clock for 1 BIPS core
static const size_t CPU_HZ = 1000000000; // 1GHz CPU
private:
isa_parser_t isa;
const cfg_t * const cfg;
std::vector<std::pair<reg_t, mem_t*>> mems;
std::vector<std::pair<reg_t, abstract_device_t*>> plugin_devices;
std::vector<processor_t*> procs;
std::map<size_t, processor_t*> harts;
std::pair<reg_t, reg_t> initrd_range;
std::string dts;
std::string dtb;
bool dtb_enabled;
std::unique_ptr<rom_device_t> boot_rom;
std::unique_ptr<clint_t> clint;
std::unique_ptr<plic_t> plic;
std::unique_ptr<ns16550_t> ns16550;
std::vector<std::shared_ptr<abstract_device_t>> devices;
std::shared_ptr<clint_t> clint;
std::shared_ptr<plic_t> plic;
bus_t bus;
log_file_t log_file;
@ -84,9 +87,6 @@ private:
processor_t* get_core(const std::string& i);
void step(size_t n); // step through simulation
static const size_t INTERLEAVE = 5000;
static const size_t INSNS_PER_RTC_TICK = 100; // 10 MHz clock for 1 BIPS core
static const size_t CPU_HZ = 1000000000; // 1GHz CPU
size_t current_step;
size_t current_proc;
bool debug;
@ -99,7 +99,6 @@ private:
virtual char* addr_to_mem(reg_t paddr) override;
virtual bool mmio_load(reg_t paddr, size_t len, uint8_t* bytes) override;
virtual bool mmio_store(reg_t paddr, size_t len, const uint8_t* bytes) override;
void make_dtb(const char* dtb_file);
void set_rom();
virtual const char* get_symbol(uint64_t paddr) override;

61
spike_main/spike.cc

@ -50,12 +50,7 @@ static void help(int exit_code = 1)
fprintf(stderr, " --l2=<S>:<W>:<B> B both powers of 2).\n");
fprintf(stderr, " --big-endian Use a big-endian memory system.\n");
fprintf(stderr, " --misaligned Support misaligned memory accesses\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, " --device=<name> Attach MMIO plugin device from an --extlib library\n");
fprintf(stderr, " --log-cache-miss Generate a log of cache miss\n");
fprintf(stderr, " --log-commits Generate a log of commits info\n");
fprintf(stderr, " --extension=<name> Specify RoCC Extension\n");
@ -336,7 +331,7 @@ int main(int argc, char** argv)
bool dtb_enabled = true;
const char* kernel = NULL;
reg_t kernel_offset, kernel_size;
std::vector<std::pair<reg_t, abstract_device_t*>> plugin_devices;
std::vector<const device_factory_t*> plugin_device_factories;
std::unique_ptr<icache_sim_t> ic;
std::unique_ptr<dcache_sim_t> dc;
std::unique_ptr<cache_sim_t> l2;
@ -376,47 +371,12 @@ int main(int argc, char** argv)
/*default_real_time_clint=*/false,
/*default_trigger_count=*/4);
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));
auto const device_parser = [&plugin_device_factories](const char *s) {
const std::string name(s);
if (name.empty()) throw std::runtime_error("Plugin name is empty.");
auto it = mmio_device_map().find(name);
if (it == mmio_device_map().end()) throw std::runtime_error("Plugin \"" + name + "\" not found in loaded extlibs.");
plugin_device_factories.push_back(it->second);
};
option_parser_t parser;
@ -564,7 +524,7 @@ int main(int argc, char** argv)
}
sim_t s(&cfg, halted,
mems, plugin_devices, htif_args, dm_config, log_path, dtb_enabled, dtb_file,
mems, plugin_device_factories, htif_args, dm_config, log_path, dtb_enabled, dtb_file,
socket,
cmd_file);
std::unique_ptr<remote_bitbang_t> remote_bitbang((remote_bitbang_t *) NULL);
@ -602,8 +562,5 @@ int main(int argc, char** argv)
for (auto& mem : mems)
delete mem.second;
for (auto& plugin_device : plugin_devices)
delete plugin_device.second;
return return_code;
}

Loading…
Cancel
Save