aboutsummaryrefslogtreecommitdiff
path: root/riscv
diff options
context:
space:
mode:
authorAndrew Waterman <andrew@sifive.com>2020-05-10 01:43:04 -0700
committerAndrew Waterman <andrew@sifive.com>2020-05-10 01:43:04 -0700
commit9430a823731b068c16972f5d790c55661e183acf (patch)
treedc5a0d9c818cb4301bb4e754c76b37c4350be56c /riscv
parent90e67e05789a0b0de95c6e0910d86658847c06bb (diff)
parent0212b6d66f9f321732480b20b368ee0882415314 (diff)
downloadspike-9430a823731b068c16972f5d790c55661e183acf.zip
spike-9430a823731b068c16972f5d790c55661e183acf.tar.gz
spike-9430a823731b068c16972f5d790c55661e183acf.tar.bz2
Merge branch 'configurable_PMP'
Diffstat (limited to 'riscv')
-rw-r--r--riscv/dts.cc97
-rw-r--r--riscv/dts.h6
-rw-r--r--riscv/mmu.cc14
-rw-r--r--riscv/processor.cc67
-rw-r--r--riscv/processor.h14
-rw-r--r--riscv/riscv.mk.in1
-rw-r--r--riscv/sim.cc81
-rw-r--r--riscv/sim.h11
8 files changed, 252 insertions, 39 deletions
diff --git a/riscv/dts.cc b/riscv/dts.cc
index 5dbed86..c12ff06 100644
--- a/riscv/dts.cc
+++ b/riscv/dts.cc
@@ -1,6 +1,7 @@
// See LICENSE for license details.
#include "dts.h"
+#include "libfdt.h"
#include <iostream>
#include <sstream>
#include <signal.h>
@@ -43,6 +44,8 @@ std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz,
" compatible = \"riscv\";\n"
" riscv,isa = \"" << procs[i]->get_isa_string() << "\";\n"
" mmu-type = \"riscv," << (procs[i]->get_max_xlen() <= 32 ? "sv32" : "sv48") << "\";\n"
+ " riscv,pmpregions = <16>;\n"
+ " riscv,pmpgranularity = <4>;\n"
" clock-frequency = <" << cpu_hz << ">;\n"
" CPU" << i << "_intc: interrupt-controller {\n"
" #interrupt-cells = <1>;\n"
@@ -165,3 +168,97 @@ std::string dts_compile(const std::string& dts)
return dtb.str();
}
+
+
+static int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr,
+ unsigned long *size, const char *field)
+{
+ int parent, len, i;
+ int cell_addr, cell_size;
+ const fdt32_t *prop_addr, *prop_size;
+ uint64_t temp = 0;
+
+ parent = fdt_parent_offset(fdt, node);
+ if (parent < 0)
+ return parent;
+
+ cell_addr = fdt_address_cells(fdt, parent);
+ if (cell_addr < 1)
+ return -ENODEV;
+
+ cell_size = fdt_size_cells(fdt, parent);
+ if (cell_size < 0)
+ return -ENODEV;
+
+ if (!field)
+ return -ENODEV;
+
+ prop_addr = (fdt32_t *)fdt_getprop(fdt, node, field, &len);
+ if (!prop_addr)
+ return -ENODEV;
+ prop_size = prop_addr + cell_addr;
+
+ if (addr) {
+ for (i = 0; i < cell_addr; i++)
+ temp = (temp << 32) | fdt32_to_cpu(*prop_addr++);
+ *addr = temp;
+ }
+ temp = 0;
+
+ if (size) {
+ for (i = 0; i < cell_size; i++)
+ temp = (temp << 32) | fdt32_to_cpu(*prop_size++);
+ *size = temp;
+ }
+
+ return 0;
+}
+
+int fdt_parse_clint(void *fdt, unsigned long *clint_addr,
+ const char *compatible)
+{
+ int nodeoffset, rc;
+
+ nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible);
+ if (nodeoffset < 0)
+ return nodeoffset;
+
+ rc = fdt_get_node_addr_size(fdt, nodeoffset, clint_addr, NULL, "reg");
+ if (rc < 0 || !clint_addr)
+ return -ENODEV;
+
+ return 0;
+}
+
+int fdt_parse_pmp_num(void *fdt, unsigned long *pmp_num, const char *compatible)
+{
+ int nodeoffset, rc;
+
+ nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible);
+ if (nodeoffset < 0)
+ return nodeoffset;
+
+ rc = fdt_get_node_addr_size(fdt, nodeoffset, pmp_num, NULL,
+ "riscv,pmpregions");
+ if (rc < 0 || !pmp_num)
+ return -ENODEV;
+
+ return 0;
+}
+
+int fdt_parse_pmp_alignment(void *fdt, unsigned long *pmp_align,
+ const char *compatible)
+{
+ int nodeoffset, rc;
+
+ nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible);
+ if (nodeoffset < 0)
+ return nodeoffset;
+
+ rc = fdt_get_node_addr_size(fdt, nodeoffset, pmp_align, NULL,
+ "riscv,pmpgranularity");
+ if (rc < 0 || !pmp_align)
+ return -ENODEV;
+
+ return 0;
+}
diff --git a/riscv/dts.h b/riscv/dts.h
index b9ddb8e..08d8f9d 100644
--- a/riscv/dts.h
+++ b/riscv/dts.h
@@ -13,4 +13,10 @@ std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz,
std::string dts_compile(const std::string& dts);
+int fdt_parse_clint(void *fdt, unsigned long *clint_addr,
+ const char *compatible);
+int fdt_parse_pmp_num(void *fdt, unsigned long *pmp_num,
+ const char *compatible);
+int fdt_parse_pmp_alignment(void *fdt, unsigned long *pmp_align,
+ const char *compatible);
#endif
diff --git a/riscv/mmu.cc b/riscv/mmu.cc
index 1e8dd8b..b038f23 100644
--- a/riscv/mmu.cc
+++ b/riscv/mmu.cc
@@ -200,19 +200,19 @@ tlb_entry_t mmu_t::refill_tlb(reg_t vaddr, reg_t paddr, char* host_addr, access_
reg_t mmu_t::pmp_ok(reg_t addr, reg_t len, access_type type, reg_t mode)
{
- if (!proc)
+ if (!proc || proc->n_pmp == 0)
return true;
reg_t base = 0;
- for (size_t i = 0; i < proc->state.n_pmp; i++) {
- reg_t tor = proc->state.pmpaddr[i] << PMP_SHIFT;
+ for (size_t i = 0; i < proc->n_pmp; i++) {
+ reg_t tor = (proc->state.pmpaddr[i] & proc->pmp_tor_mask()) << PMP_SHIFT;
uint8_t cfg = proc->state.pmpcfg[i];
if (cfg & PMP_A) {
bool is_tor = (cfg & PMP_A) == PMP_TOR;
bool is_na4 = (cfg & PMP_A) == PMP_NA4;
- reg_t mask = (proc->state.pmpaddr[i] << 1) | (!is_na4);
+ reg_t mask = (proc->state.pmpaddr[i] << 1) | (!is_na4) | ~proc->pmp_tor_mask();
mask = ~(mask & ~(mask + 1)) << PMP_SHIFT;
// Check each 4-byte sector of the access
@@ -255,8 +255,8 @@ reg_t mmu_t::pmp_homogeneous(reg_t addr, reg_t len)
return true;
reg_t base = 0;
- for (size_t i = 0; i < proc->state.n_pmp; i++) {
- reg_t tor = proc->state.pmpaddr[i] << PMP_SHIFT;
+ for (size_t i = 0; i < proc->n_pmp; i++) {
+ reg_t tor = (proc->state.pmpaddr[i] & proc->pmp_tor_mask()) << PMP_SHIFT;
uint8_t cfg = proc->state.pmpcfg[i];
if (cfg & PMP_A) {
@@ -270,7 +270,7 @@ reg_t mmu_t::pmp_homogeneous(reg_t addr, reg_t len)
bool tor_homogeneous = ends_before_lower || begins_after_upper ||
(begins_after_lower && ends_before_upper);
- reg_t mask = (proc->state.pmpaddr[i] << 1) | (!is_na4);
+ reg_t mask = (proc->state.pmpaddr[i] << 1) | (!is_na4) | ~proc->pmp_tor_mask();
mask = ~(mask & ~(mask + 1)) << PMP_SHIFT;
bool mask_homogeneous = ~(mask << 1) & len;
bool napot_homogeneous = mask_homogeneous || ((addr ^ tor) / len) != 0;
diff --git a/riscv/processor.cc b/riscv/processor.cc
index bf3c40a..2dc930b 100644
--- a/riscv/processor.cc
+++ b/riscv/processor.cc
@@ -43,6 +43,8 @@ processor_t::processor_t(const char* isa, const char* priv, const char* varch,
for (auto disasm_insn : ext->get_disasms())
disassembler->add_insn(disasm_insn);
+ set_pmp_granularity(1 << PMP_SHIFT);
+ set_pmp_num(state.max_pmp);
reset();
}
@@ -421,10 +423,12 @@ void processor_t::reset()
set_csr(CSR_MSTATUS, state.mstatus);
VU.reset();
- // For backwards compatibility with software that is unaware of PMP,
- // initialize PMP to permit unprivileged access to all of memory.
- set_csr(CSR_PMPADDR0, ~reg_t(0));
- set_csr(CSR_PMPCFG0, PMP_R | PMP_W | PMP_X | PMP_NAPOT);
+ if (n_pmp > 0) {
+ // For backwards compatibility with software that is unaware of PMP,
+ // initialize PMP to permit unprivileged access to all of memory.
+ set_csr(CSR_PMPADDR0, ~reg_t(0));
+ set_csr(CSR_PMPCFG0, PMP_R | PMP_W | PMP_X | PMP_NAPOT);
+ }
if (ext)
ext->reset(); // reset the extension
@@ -443,6 +447,26 @@ static int ctz(reg_t val)
return res;
}
+void processor_t::set_pmp_num(reg_t n)
+{
+ // check the number of pmp is in a reasonable range
+ if (n > state.max_pmp) {
+ fprintf(stderr, "error: bad number of pmp regions: '%ld' from the dtb\n", (unsigned long)n);
+ abort();
+ }
+ n_pmp = n;
+}
+
+void processor_t::set_pmp_granularity(reg_t gran) {
+ // check the pmp granularity is set from dtb(!=0) and is power of 2
+ if (gran < (1 << PMP_SHIFT) || (gran & (gran - 1)) != 0) {
+ fprintf(stderr, "error: bad pmp granularity '%ld' from the dtb\n", (unsigned long)gran);
+ abort();
+ }
+
+ lg_pmp_granularity = ctz(gran);
+}
+
void processor_t::take_interrupt(reg_t pending_interrupts)
{
reg_t mie = get_field(state.mstatus, MSTATUS_MIE);
@@ -612,22 +636,32 @@ void processor_t::set_csr(int which, reg_t val)
reg_t delegable_ints = supervisor_ints | coprocessor_ints;
reg_t all_ints = delegable_ints | MIP_MSIP | MIP_MTIP;
- if (which >= CSR_PMPADDR0 && which < CSR_PMPADDR0 + state.n_pmp) {
+ if (which >= CSR_PMPADDR0 && which < CSR_PMPADDR0 + state.max_pmp) {
+ // If no PMPs are configured, disallow access to all. Otherwise, allow
+ // access to all, but unimplemented ones are hardwired to zero.
+ if (n_pmp == 0)
+ return;
+
size_t i = which - CSR_PMPADDR0;
bool locked = state.pmpcfg[i] & PMP_L;
- bool next_locked = i+1 < state.n_pmp && (state.pmpcfg[i+1] & PMP_L);
- bool next_tor = i+1 < state.n_pmp && (state.pmpcfg[i+1] & PMP_A) == PMP_TOR;
- if (!locked && !(next_locked && next_tor))
+ bool next_locked = i+1 < state.max_pmp && (state.pmpcfg[i+1] & PMP_L);
+ bool next_tor = i+1 < state.max_pmp && (state.pmpcfg[i+1] & PMP_A) == PMP_TOR;
+ if (i < n_pmp && !locked && !(next_locked && next_tor))
state.pmpaddr[i] = val & ((reg_t(1) << (MAX_PADDR_BITS - PMP_SHIFT)) - 1);
mmu->flush_tlb();
}
- if (which >= CSR_PMPCFG0 && which < CSR_PMPCFG0 + state.n_pmp / 4) {
+ if (which >= CSR_PMPCFG0 && which < CSR_PMPCFG0 + state.max_pmp / 4) {
+ if (n_pmp == 0)
+ return;
+
for (size_t i0 = (which - CSR_PMPCFG0) * 4, i = i0; i < i0 + xlen / 8; i++) {
- if (!(state.pmpcfg[i] & PMP_L)) {
+ if (i < n_pmp && !(state.pmpcfg[i] & PMP_L)) {
uint8_t cfg = (val >> (8 * (i - i0))) & (PMP_R | PMP_W | PMP_X | PMP_A | PMP_L);
cfg &= ~PMP_W | ((cfg & PMP_R) ? PMP_W : 0); // Disallow R=0 W=1
+ if (lg_pmp_granularity != PMP_SHIFT && (cfg & PMP_A) == PMP_NA4)
+ cfg |= PMP_NAPOT; // Disallow A=NA4 when granularity > 4
state.pmpcfg[i] = cfg;
}
}
@@ -890,14 +924,19 @@ reg_t processor_t::get_csr(int which)
if (which >= CSR_MHPMEVENT3 && which <= CSR_MHPMEVENT31)
return 0;
- if (which >= CSR_PMPADDR0 && which < CSR_PMPADDR0 + state.n_pmp)
- return state.pmpaddr[which - CSR_PMPADDR0];
+ if (which >= CSR_PMPADDR0 && which < CSR_PMPADDR0 + state.max_pmp) {
+ reg_t i = which - CSR_PMPADDR0;
+ if ((state.pmpcfg[i] & PMP_A) >= PMP_NAPOT)
+ return state.pmpaddr[i] | (~pmp_tor_mask() >> 1);
+ else
+ return state.pmpaddr[i] & pmp_tor_mask();
+ }
- if (which >= CSR_PMPCFG0 && which < CSR_PMPCFG0 + state.n_pmp / 4) {
+ if (which >= CSR_PMPCFG0 && which < CSR_PMPCFG0 + state.max_pmp / 4) {
require((which & ((xlen / 32) - 1)) == 0);
reg_t res = 0;
- for (size_t i0 = (which - CSR_PMPCFG0) * 4, i = i0; i < i0 + xlen / 8 && i < state.n_pmp; i++)
+ for (size_t i0 = (which - CSR_PMPCFG0) * 4, i = i0; i < i0 + xlen / 8 && i < state.max_pmp; i++)
res |= reg_t(state.pmpcfg[i]) << (8 * (i - i0));
return res;
}
diff --git a/riscv/processor.h b/riscv/processor.h
index f9ec1f1..7ff79ef 100644
--- a/riscv/processor.h
+++ b/riscv/processor.h
@@ -193,9 +193,9 @@ struct state_t
reg_t tdata2[num_triggers];
bool debug_mode;
- static const int n_pmp = 16;
- uint8_t pmpcfg[n_pmp];
- reg_t pmpaddr[n_pmp];
+ static const int max_pmp = 16;
+ uint8_t pmpcfg[max_pmp];
+ reg_t pmpaddr[max_pmp];
uint32_t fflags;
uint32_t frm;
@@ -389,6 +389,9 @@ public:
void trigger_updated();
+ void set_pmp_num(reg_t pmp_num);
+ void set_pmp_granularity(reg_t pmp_granularity);
+
private:
simif_t* sim;
mmu_t* mmu; // main memory is always accessed via the mmu
@@ -419,6 +422,8 @@ private:
void disasm(insn_t insn); // disassemble and print an instruction
int paddr_bits();
+ reg_t pmp_tor_mask() { return -(reg_t(1) << (lg_pmp_granularity - PMP_SHIFT)); }
+
void enter_debug_mode(uint8_t cause);
friend class mmu_t;
@@ -434,6 +439,9 @@ private:
// Track repeated executions for processor_t::disasm()
uint64_t last_pc, last_bits, executions;
+ reg_t n_pmp;
+ reg_t lg_pmp_granularity;
+
public:
class vectorUnit_t {
public:
diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in
index bbcba5e..fbe6786 100644
--- a/riscv/riscv.mk.in
+++ b/riscv/riscv.mk.in
@@ -2,6 +2,7 @@ get_insn_list = $(shell grep ^DECLARE_INSN $(1) | sed 's/DECLARE_INSN(\(.*\),.*,
get_opcode = $(shell grep ^DECLARE_INSN.*\\\<$(2)\\\> $(1) | sed 's/DECLARE_INSN(.*,\(.*\),.*)/\1/')
riscv_subproject_deps = \
+ fdt \
softfloat \
riscv_install_prog_srcs = \
diff --git a/riscv/sim.cc b/riscv/sim.cc
index 0b29720..b25d79e 100644
--- a/riscv/sim.cc
+++ b/riscv/sim.cc
@@ -5,6 +5,7 @@
#include "dts.h"
#include "remote_bitbang.h"
#include "byteorder.h"
+#include <fstream>
#include <map>
#include <iostream>
#include <sstream>
@@ -33,14 +34,25 @@ sim_t::sim_t(const char* isa, const char* priv, const char* varch,
const std::vector<std::string>& args,
std::vector<int> const hartids,
const debug_module_config_t &dm_config,
- const char *log_path)
- : htif_t(args), mems(mems), plugin_devices(plugin_devices),
+ const char *log_path,
+ bool dtb_enabled, const char *dtb_file)
+ : htif_t(args),
+ mems(mems),
+ plugin_devices(plugin_devices),
procs(std::max(nprocs, size_t(1))),
- initrd_start(initrd_start), initrd_end(initrd_end), start_pc(start_pc),
+ initrd_start(initrd_start),
+ initrd_end(initrd_end),
+ start_pc(start_pc),
+ dtb_file(dtb_file ? dtb_file : ""),
+ dtb_enabled(dtb_enabled),
log_file(log_path),
- current_step(0), current_proc(0), debug(false), histogram_enabled(false),
- log(false), dtb_enabled(true),
- remote_bitbang(NULL), debug_module(this, dm_config)
+ current_step(0),
+ current_proc(0),
+ debug(false),
+ histogram_enabled(false),
+ log(false),
+ remote_bitbang(NULL),
+ debug_module(this, dm_config)
{
signal(SIGINT, &handle_signal);
@@ -68,8 +80,24 @@ sim_t::sim_t(const char* isa, const char* priv, const char* varch,
log_file.get());
}
+ make_dtb();
+
clint.reset(new clint_t(procs, CPU_HZ / INSNS_PER_RTC_TICK, real_time_clint));
- bus.add_device(CLINT_BASE, clint.get());
+ reg_t clint_base;
+ if (fdt_parse_clint((void *)dtb.c_str(), &clint_base, "riscv,clint0")) {
+ bus.add_device(CLINT_BASE, clint.get());
+ } else {
+ bus.add_device(clint_base, clint.get());
+ }
+
+ for (size_t i = 0; i < nprocs; i++) {
+ reg_t pmp_num = 0, pmp_granularity = 0;
+ fdt_parse_pmp_num((void *)dtb.c_str(), &pmp_num, "riscv");
+ fdt_parse_pmp_alignment((void *)dtb.c_str(), &pmp_granularity, "riscv");
+
+ procs[i]->set_pmp_num(pmp_num);
+ procs[i]->set_pmp_granularity(pmp_granularity);
+ }
}
sim_t::~sim_t()
@@ -190,6 +218,25 @@ bool sim_t::mmio_store(reg_t addr, size_t len, const uint8_t* bytes)
void sim_t::make_dtb()
{
+ if (!dtb_file.empty()) {
+ std::ifstream fin(dtb_file.c_str(), 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 {
+ dts = make_dts(INSNS_PER_RTC_TICK, CPU_HZ, initrd_start, initrd_end, procs, mems);
+ dtb = dts_compile(dts);
+ }
+}
+
+void sim_t::set_rom()
+{
const int reset_vec_size = 8;
start_pc = start_pc == reg_t(-1) ? get_entry_point() : start_pc;
@@ -211,8 +258,22 @@ void sim_t::make_dtb()
std::vector<char> rom((char*)reset_vec, (char*)reset_vec + sizeof(reset_vec));
- dts = make_dts(INSNS_PER_RTC_TICK, CPU_HZ, initrd_start, initrd_end, procs, mems);
- std::string dtb = dts_compile(dts);
+ std::string dtb;
+ if (!dtb_file.empty()) {
+ std::ifstream fin(dtb_file.c_str(), 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 {
+ dts = make_dts(INSNS_PER_RTC_TICK, CPU_HZ, initrd_start, initrd_end, procs, mems);
+ dtb = dts_compile(dts);
+ }
rom.insert(rom.end(), dtb.begin(), dtb.end());
const int align = 0x1000;
@@ -237,7 +298,7 @@ char* sim_t::addr_to_mem(reg_t addr) {
void sim_t::reset()
{
if (dtb_enabled)
- make_dtb();
+ set_rom();
}
void sim_t::idle()
diff --git a/riscv/sim.h b/riscv/sim.h
index 91aedab..eee24e5 100644
--- a/riscv/sim.h
+++ b/riscv/sim.h
@@ -29,7 +29,8 @@ public:
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, const char *log_path);
+ const debug_module_config_t &dm_config, const char *log_path,
+ bool dtb_enabled, const char *dtb_file);
~sim_t();
// run the simulation to completion
@@ -46,9 +47,6 @@ public:
void configure_log(bool enable_log, bool enable_commitlog);
void set_procs_debug(bool value);
- void set_dtb_enabled(bool value) {
- this->dtb_enabled = value;
- }
void set_remote_bitbang(remote_bitbang_t* remote_bitbang) {
this->remote_bitbang = remote_bitbang;
}
@@ -68,6 +66,9 @@ private:
reg_t initrd_end;
reg_t start_pc;
std::string dts;
+ std::string dtb;
+ std::string dtb_file;
+ bool dtb_enabled;
std::unique_ptr<rom_device_t> boot_rom;
std::unique_ptr<clint_t> clint;
bus_t bus;
@@ -83,7 +84,6 @@ private:
bool debug;
bool histogram_enabled; // provide a histogram of PCs
bool log;
- bool dtb_enabled;
remote_bitbang_t* remote_bitbang;
// memory-mapped I/O routines
@@ -91,6 +91,7 @@ private:
bool mmio_load(reg_t addr, size_t len, uint8_t* bytes);
bool mmio_store(reg_t addr, size_t len, const uint8_t* bytes);
void make_dtb();
+ void set_rom();
// presents a prompt for introspection into the simulation
void interactive();