aboutsummaryrefslogtreecommitdiff
path: root/riscv
diff options
context:
space:
mode:
Diffstat (limited to 'riscv')
-rw-r--r--riscv/abstract_device.h1
-rw-r--r--riscv/cfg.h2
-rw-r--r--riscv/clint.cc10
-rw-r--r--riscv/csr_init.cc60
-rw-r--r--riscv/csrs.cc126
-rw-r--r--riscv/csrs.h12
-rw-r--r--riscv/debug_module.cc8
-rw-r--r--riscv/debug_module.h5
-rw-r--r--riscv/decode.h6
-rw-r--r--riscv/decode_macros.h8
-rw-r--r--riscv/devices.cc125
-rw-r--r--riscv/devices.h36
-rw-r--r--riscv/dts.cc1
-rw-r--r--riscv/execute.cc15
-rw-r--r--riscv/extension.cc14
-rw-r--r--riscv/extension.h23
-rw-r--r--riscv/interactive.cc27
-rw-r--r--riscv/isa_parser.h2
-rw-r--r--riscv/mmu.cc7
-rw-r--r--riscv/mmu.h16
-rw-r--r--riscv/ns16550.cc4
-rw-r--r--riscv/plic.cc10
-rw-r--r--riscv/processor.cc100
-rw-r--r--riscv/processor.h6
-rw-r--r--riscv/rocc.cc28
-rw-r--r--riscv/rocc.h12
-rw-r--r--riscv/sim.cc8
-rw-r--r--riscv/sim.h7
-rw-r--r--riscv/triggers.cc9
-rw-r--r--riscv/triggers.h2
-rw-r--r--riscv/v_ext_macros.h58
31 files changed, 491 insertions, 257 deletions
diff --git a/riscv/abstract_device.h b/riscv/abstract_device.h
index 0726cd7..d8ddbab 100644
--- a/riscv/abstract_device.h
+++ b/riscv/abstract_device.h
@@ -16,6 +16,7 @@ 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 reg_t size() = 0;
virtual ~abstract_device_t() {}
virtual void tick(reg_t UNUSED rtc_ticks) {}
};
diff --git a/riscv/cfg.h b/riscv/cfg.h
index c972f03..388030b 100644
--- a/riscv/cfg.h
+++ b/riscv/cfg.h
@@ -6,6 +6,7 @@
#include <vector>
#include "decode.h"
#include <cassert>
+class abstract_sim_if_t;
typedef enum {
endianness_little,
@@ -77,6 +78,7 @@ public:
bool explicit_hartids;
bool real_time_clint;
reg_t trigger_count;
+ std::optional<abstract_sim_if_t*> external_simulator;
size_t nprocs() const { return hartids.size(); }
size_t max_hartid() const { return hartids.back(); }
diff --git a/riscv/clint.cc b/riscv/clint.cc
index 208ea0e..3d5c984 100644
--- a/riscv/clint.cc
+++ b/riscv/clint.cc
@@ -39,7 +39,8 @@ bool clint_t::load(reg_t addr, size_t len, uint8_t* bytes)
tick(0);
- if (addr >= MSIP_BASE && addr < MTIMECMP_BASE) {
+ static_assert(MSIP_BASE == 0);
+ if (/* addr >= MSIP_BASE && */ addr < MTIMECMP_BASE) {
if (len == 8) {
// Implement double-word loads as a pair of word loads
return load(addr, 4, bytes) && load(addr + 4, 4, bytes + 4);
@@ -68,7 +69,8 @@ bool clint_t::store(reg_t addr, size_t len, const uint8_t* bytes)
if (len > 8)
return false;
- if (addr >= MSIP_BASE && addr < MTIMECMP_BASE) {
+ static_assert(MSIP_BASE == 0);
+ if (/* addr >= MSIP_BASE && */ addr < MTIMECMP_BASE) {
if (len == 8) {
// Implement double-word stores as a pair of word stores
return store(addr, 4, bytes) && store(addr + 4, 4, bytes + 4);
@@ -117,7 +119,7 @@ void clint_t::tick(reg_t rtc_ticks)
}
clint_t* clint_parse_from_fdt(const void* fdt, const sim_t* sim, reg_t* base,
- const std::vector<std::string>& UNUSED sargs) {
+ const std::vector<std::string>& sargs UNUSED) {
if (fdt_parse_clint(fdt, base, "riscv,clint0") == 0 || fdt_parse_clint(fdt, base, "sifive,clint0") == 0)
return new clint_t(sim,
sim->CPU_HZ / sim->INSNS_PER_RTC_TICK,
@@ -126,7 +128,7 @@ clint_t* clint_parse_from_fdt(const void* fdt, const sim_t* sim, reg_t* base,
return nullptr;
}
-std::string clint_generate_dts(const sim_t* sim, const std::vector<std::string>& UNUSED sargs) {
+std::string clint_generate_dts(const sim_t* sim, const std::vector<std::string>& sargs UNUSED) {
std::stringstream s;
s << std::hex
<< " clint@" << CLINT_BASE << " {\n"
diff --git a/riscv/csr_init.cc b/riscv/csr_init.cc
index a03d188..cabb7c2 100644
--- a/riscv/csr_init.cc
+++ b/riscv/csr_init.cc
@@ -250,7 +250,8 @@ void state_t::csr_init(processor_t* const proc, reg_t max_isa)
(proc->extension_enabled(EXT_SSTC) ? MENVCFG_STCE : 0) |
(proc->extension_enabled(EXT_ZICFILP) ? MENVCFG_LPE : 0) |
(proc->extension_enabled(EXT_ZICFISS) ? MENVCFG_SSE : 0) |
- (proc->extension_enabled(EXT_SSDBLTRP) ? MENVCFG_DTE : 0);
+ (proc->extension_enabled(EXT_SSDBLTRP) ? MENVCFG_DTE : 0)|
+ (proc->extension_enabled(EXT_SMCSRIND) ? MENVCFG_CDE : 0);
menvcfg = std::make_shared<envcfg_csr_t>(proc, CSR_MENVCFG, menvcfg_mask, 0);
if (xlen == 32) {
add_user_csr(CSR_MENVCFG, std::make_shared<rv32_low_csr_t>(proc, CSR_MENVCFG, menvcfg));
@@ -337,6 +338,10 @@ void state_t::csr_init(processor_t* const proc, reg_t max_isa)
const reg_t ssp_mask = -reg_t(xlen / 8);
add_ext_csr(EXT_ZICFISS, CSR_SSP, ssp = std::make_shared<ssp_csr_t>(proc, CSR_SSP, ssp_mask, 0));
+ // Smcdeleg
+ if (proc->extension_enabled_const(EXT_SMCDELEG) || proc->extension_enabled_const(EXT_SSCCFG)) {
+ add_supervisor_csr(CSR_SCOUNTINHIBIT, scountinhibit = std::make_shared<scntinhibit_csr_t>(proc, CSR_SCOUNTINHIBIT, mcountinhibit));
+ }
// Smcsrind / Sscsrind
if (proc->extension_enabled_const(EXT_SMCSRIND)) {
@@ -363,6 +368,59 @@ void state_t::csr_init(processor_t* const proc, reg_t max_isa)
auto sireg = std::make_shared<sscsrind_reg_csr_t>(proc, sireg_csrs[i], siselect);
add_supervisor_csr(sireg_csrs[i], std::make_shared<virtualized_indirect_csr_t>(proc, sireg, vsireg));
+
+ // Smcdeleg
+ if (proc->extension_enabled(EXT_SSCCFG) || proc->extension_enabled(EXT_SMCDELEG)) {
+ switch (sireg_csrs[i]) {
+ case CSR_SIREG:
+ if (proc->extension_enabled_const(EXT_ZICNTR)) {
+ sireg->add_ireg_proxy(SISELECT_SMCDELEG_START, mcycle);
+ sireg->add_ireg_proxy(SISELECT_SMCDELEG_INSTRET, minstret);
+ }
+ if (proc->extension_enabled_const(EXT_ZIHPM)) {
+ for (size_t j = 0; j < (SISELECT_SMCDELEG_END - SISELECT_SMCDELEG_HPMEVENT_3 + 1); j++)
+ sireg->add_ireg_proxy(SISELECT_SMCDELEG_HPMCOUNTER_3 + j, csrmap[CSR_HPMCOUNTER3 + j]);
+ }
+ break;
+ case CSR_SIREG4:
+ if (xlen == 32) {
+ if (proc->extension_enabled_const(EXT_ZICNTR)) {
+ sireg->add_ireg_proxy(SISELECT_SMCDELEG_START, csrmap[CSR_CYCLEH]);
+ sireg->add_ireg_proxy(SISELECT_SMCDELEG_INSTRET, csrmap[CSR_INSTRETH]);
+ }
+ if (proc->extension_enabled_const(EXT_ZIHPM)) {
+ for (size_t j = 0; j < (SISELECT_SMCDELEG_END - SISELECT_SMCDELEG_HPMEVENT_3 + 1); j++)
+ sireg->add_ireg_proxy(SISELECT_SMCDELEG_HPMCOUNTER_3 + j, csrmap[CSR_HPMCOUNTER3H + j]);
+ }
+ }
+ break;
+ case CSR_SIREG2:
+ if (proc->extension_enabled_const(EXT_ZICNTR)) {
+ sireg->add_ireg_proxy(SISELECT_SMCDELEG_START, mcyclecfg);
+ sireg->add_ireg_proxy(SISELECT_SMCDELEG_INSTRETCFG, minstretcfg);
+ }
+ if (proc->extension_enabled_const(EXT_ZIHPM)) {
+ for (size_t j = 0; j < (SISELECT_SMCDELEG_END - SISELECT_SMCDELEG_HPMEVENT_3 + 1); j++)
+ sireg->add_ireg_proxy(SISELECT_SMCDELEG_HPMEVENT_3 + j, csrmap[CSR_MHPMEVENT3H + j]);
+ }
+ break;
+ case CSR_SIREG5:
+ if (xlen == 32) {
+ if (proc->extension_enabled_const(EXT_ZICNTR)) {
+ sireg->add_ireg_proxy(SISELECT_SMCDELEG_START, mcycle);
+ sireg->add_ireg_proxy(SISELECT_SMCDELEG_INSTRET, minstret);
+ }
+ if (proc->extension_enabled_const(EXT_ZIHPM)) {
+ for (size_t j = 0; j < (SISELECT_SMCDELEG_END - SISELECT_SMCDELEG_HPMEVENT_3); j++)
+ sireg->add_ireg_proxy(SISELECT_SMCDELEG_HPMCOUNTER_3 + j, csrmap[CSR_HPMCOUNTER3 + j]);
+ }
+ }
+ case CSR_SIREG3:
+ case CSR_SIREG6:
+ default:
+ break;
+ }
+ }
}
}
diff --git a/riscv/csrs.cc b/riscv/csrs.cc
index 3dbac7b..1873f7e 100644
--- a/riscv/csrs.cc
+++ b/riscv/csrs.cc
@@ -1075,6 +1075,7 @@ bool virtualized_satp_csr_t::unlogged_write(const reg_t val) noexcept {
wide_counter_csr_t::wide_counter_csr_t(processor_t* const proc, const reg_t addr, smcntrpmf_csr_t_p config_csr):
csr_t(proc, addr),
val(0),
+ written(false),
config_csr(config_csr) {
}
@@ -1083,7 +1084,15 @@ reg_t wide_counter_csr_t::read() const noexcept {
}
void wide_counter_csr_t::bump(const reg_t howmuch) noexcept {
- if (is_counting_enabled()) {
+ if (written) {
+ // Because writing a CSR serializes the simulator, howmuch should
+ // reflect exactly one instruction: the explicit CSR write.
+ // If counting is disabled, though, howmuch will be zero.
+ assert(howmuch <= 1);
+ // The ISA mandates that explicit writes to instret take precedence
+ // over the instret, so simply skip the increment.
+ written = false;
+ } else if (is_counting_enabled()) {
val += howmuch; // to keep log reasonable size, don't log every bump
}
// Clear cached value
@@ -1091,23 +1100,15 @@ void wide_counter_csr_t::bump(const reg_t howmuch) noexcept {
}
bool wide_counter_csr_t::unlogged_write(const reg_t val) noexcept {
+ // Because writing a CSR serializes the simulator and is followed by a
+ // bump, back-to-back writes with no intervening bump should never occur.
+ assert(!written);
+ written = true;
+
this->val = val;
- // The ISA mandates that if an instruction writes instret, the write
- // takes precedence over the increment to instret. However, Spike
- // unconditionally increments instret after executing an instruction.
- // Correct for this artifact by decrementing instret here.
- // Ensure that Smctrpmf hasn't disabled counting.
- if (is_counting_enabled()) {
- this->val--;
- }
return true;
}
-reg_t wide_counter_csr_t::written_value() const noexcept {
- // Re-adjust for upcoming bump()
- return this->val + 1;
-}
-
// Returns true if counting is not inhibited by Smcntrpmf.
// Note that minstretcfg / mcyclecfg / mhpmevent* share the same inhibit bits.
bool wide_counter_csr_t::is_counting_enabled() const noexcept {
@@ -1230,7 +1231,7 @@ hideleg_csr_t::hideleg_csr_t(processor_t* const proc, const reg_t addr, csr_t_p
reg_t hideleg_csr_t::read() const noexcept {
return masked_csr_t::read() & mideleg->read();
-};
+}
hgatp_csr_t::hgatp_csr_t(processor_t* const proc, const reg_t addr):
basic_csr_t(proc, addr, 0) {
@@ -1676,6 +1677,14 @@ scountovf_csr_t::scountovf_csr_t(processor_t* const proc, const reg_t addr):
void scountovf_csr_t::verify_permissions(insn_t insn, bool write) const {
if (!proc->extension_enabled(EXT_SSCOFPMF))
throw trap_illegal_instruction(insn.bits());
+
+ if (proc->extension_enabled('H') &&
+ (proc->extension_enabled_const(EXT_SMCDELEG) || proc->extension_enabled(EXT_SSCCFG))
+ ) {
+ if (state->v && (state->menvcfg->read() & MENVCFG_CDE)) {
+ throw trap_virtual_instruction(insn.bits());
+ }
+ }
csr_t::verify_permissions(insn, write);
}
@@ -1749,6 +1758,68 @@ void sscsrind_reg_csr_t::verify_permissions(insn_t insn, bool write) const {
if (insn.csr() == address)
csr_t::verify_permissions(insn, write);
+ if (proc->extension_enabled(EXT_SMSTATEEN)) {
+ if ((state->prv < PRV_M) && !(state->mstateen[0]->read() & MSTATEEN0_CSRIND))
+ throw trap_illegal_instruction(insn.bits());
+
+ if (state->v && !(state->hstateen[0]->read() & HSTATEEN0_CSRIND))
+ throw trap_virtual_instruction(insn.bits());
+ }
+
+ if (proc->extension_enabled(EXT_SMCDELEG)) {
+ if (insn.csr() >= CSR_VSIREG && insn.csr() <= CSR_VSIREG6) {
+ if (!state->v) {
+ // An attempt to access any vsireg* from M or S mode raises an illegal instruction exception.
+ throw trap_illegal_instruction(insn.bits());
+ } else {
+ if (state->prv == PRV_S) {
+ // An attempt from VS-mode to access any vsireg raises an illegal instruction
+ // exception if menvcfg.CDE = 0, or a virtual instruction exception if menvcfg.CDE = 1
+ if ((state->menvcfg->read() & MENVCFG_CDE) != MENVCFG_CDE) {
+ throw trap_illegal_instruction(insn.bits());
+ } else {
+ throw trap_virtual_instruction(insn.bits());
+ }
+ } else {
+ throw trap_virtual_instruction(insn.bits());
+ }
+ }
+ }
+ if (insn.csr() >= CSR_SIREG && insn.csr() <= CSR_SIREG6) {
+ // attempts to access any sireg* when menvcfg.CDE = 0;
+ if ((state->menvcfg->read() & MENVCFG_CDE) != MENVCFG_CDE) {
+ if (!state->v) {
+ throw trap_illegal_instruction(insn.bits());
+ } else {
+ if (state->prv == PRV_S) {
+ // An attempt from VS-mode to access any sireg* causes illegal instruction exception if menvcfg.CDE = 0
+ throw trap_illegal_instruction(insn.bits());
+ } else {
+ throw trap_virtual_instruction(insn.bits());
+ }
+ }
+ } else {
+ // menvcfg.CDE = 1;
+ if (state->v) {
+ // An attempt from VS-mode to access any sireg* causes a virtual instruction exception if menvcfg.CDE = 1
+ throw trap_virtual_instruction(insn.bits());
+ }
+ // counter selected by siselect is not delegated to S-mode (the corresponding bit in mcounteren = 0).
+ auto iselect_addr = iselect->read();
+ if (iselect_addr >= SISELECT_SMCDELEG_START && iselect_addr <= SISELECT_SMCDELEG_END) {
+ reg_t counter_id_offset = iselect_addr - SISELECT_SMCDELEG_START;
+ if (!(state->mcounteren->read() & (1U << counter_id_offset))) {
+ if (!state->v) {
+ throw trap_illegal_instruction(insn.bits());
+ } else {
+ throw trap_virtual_instruction(insn.bits());
+ }
+ }
+ }
+ }
+ }
+ }
+
csr_t_p proxy_csr = get_reg();
if (proxy_csr == nullptr) {
if (!state->v) {
@@ -1810,7 +1881,7 @@ srmcfg_csr_t::srmcfg_csr_t(processor_t* const proc, const reg_t addr, const reg_
masked_csr_t(proc, addr, mask, init) {
}
-void srmcfg_csr_t::verify_permissions(insn_t insn, bool write) const {
+void srmcfg_csr_t::verify_permissions(insn_t insn, bool write UNUSED) const {
if (!proc->extension_enabled(EXT_SSQOSID))
throw trap_illegal_instruction(insn.bits());
@@ -1879,3 +1950,26 @@ bool hstatus_csr_t::unlogged_write(const reg_t val) noexcept {
proc->get_mmu()->flush_tlb();
return basic_csr_t::unlogged_write(new_hstatus);
}
+
+scntinhibit_csr_t::scntinhibit_csr_t(processor_t* const proc, const reg_t addr, csr_t_p mcountinhibit):
+ basic_csr_t(proc, addr, mcountinhibit->read()) {
+}
+
+void scntinhibit_csr_t::verify_permissions(insn_t insn, bool write) const {
+ if (insn.csr() == address) {
+ csr_t::verify_permissions(insn, write);
+ }
+
+ if ((state->menvcfg->read() & MENVCFG_CDE) != MENVCFG_CDE) {
+ throw trap_illegal_instruction(insn.bits());
+ }
+}
+
+bool scntinhibit_csr_t::unlogged_write(const reg_t val) noexcept {
+ state->mcountinhibit->write(state->mcounteren->read() & val);
+ return true;
+}
+
+reg_t scntinhibit_csr_t::read() const noexcept {
+ return state->mcounteren->read() & state->mcountinhibit->read();
+}
diff --git a/riscv/csrs.h b/riscv/csrs.h
index 278bdb3..33ac33e 100644
--- a/riscv/csrs.h
+++ b/riscv/csrs.h
@@ -542,10 +542,10 @@ class wide_counter_csr_t: public csr_t {
void bump(const reg_t howmuch) noexcept;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
- virtual reg_t written_value() const noexcept override;
private:
bool is_counting_enabled() const noexcept;
reg_t val;
+ bool written;
smcntrpmf_csr_t_p config_csr;
};
@@ -899,4 +899,14 @@ class hstatus_csr_t final: public basic_csr_t {
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
};
+
+class scntinhibit_csr_t: public basic_csr_t {
+ public:
+ scntinhibit_csr_t(processor_t* const proc, const reg_t addr, csr_t_p mcountinhibit);
+ reg_t read() const noexcept override;
+ virtual void verify_permissions(insn_t insn, bool write) const override;
+ protected:
+ virtual bool unlogged_write(const reg_t val) noexcept override;
+};
+
#endif
diff --git a/riscv/debug_module.cc b/riscv/debug_module.cc
index 7c59744..a89a4ff 100644
--- a/riscv/debug_module.cc
+++ b/riscv/debug_module.cc
@@ -13,7 +13,7 @@
#if 0
# define D(x) x
#else
-# define D(x)
+# define D(x) (void) 0
#endif
// Return the number of bits wide that a field has to be to encode up to n
@@ -249,6 +249,11 @@ bool debug_module_t::store(reg_t addr, size_t len, const uint8_t* bytes)
return false;
}
+reg_t debug_module_t::size()
+{
+ return PGSIZE;
+}
+
void debug_module_t::write32(uint8_t *memory, unsigned int index, uint32_t value)
{
uint8_t* base = memory + index * 4;
@@ -445,7 +450,6 @@ bool debug_module_t::dmi_read(unsigned address, uint32_t *value)
} else {
dmstatus.allresumeack = false;
}
- auto hart = sim->get_harts().at(hart_id);
if (!hart_available(hart_id)) {
dmstatus.allrunning = false;
dmstatus.allhalted = false;
diff --git a/riscv/debug_module.h b/riscv/debug_module.h
index 3771489..904f03e 100644
--- a/riscv/debug_module.h
+++ b/riscv/debug_module.h
@@ -114,8 +114,9 @@ class debug_module_t : public abstract_device_t
debug_module_t(simif_t *sim, const debug_module_config_t &config);
~debug_module_t();
- 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;
+ reg_t size() override;
// Debug Module Interface that the debugger (in our case through JTAG DTM)
// uses to access the DM.
diff --git a/riscv/decode.h b/riscv/decode.h
index f36c04e..d17cb6b 100644
--- a/riscv/decode.h
+++ b/riscv/decode.h
@@ -170,23 +170,29 @@ public:
switch (rvc_rlist()) {
case 15:
stack_adj_base += 16;
+ [[fallthrough]];
case 14:
if (xlen == 64)
stack_adj_base += 16;
+ [[fallthrough]];
case 13:
case 12:
stack_adj_base += 16;
+ [[fallthrough]];
case 11:
case 10:
if (xlen == 64)
stack_adj_base += 16;
+ [[fallthrough]];
case 9:
case 8:
stack_adj_base += 16;
+ [[fallthrough]];
case 7:
case 6:
if (xlen == 64)
stack_adj_base += 16;
+ [[fallthrough]];
case 5:
case 4:
stack_adj_base += 16;
diff --git a/riscv/decode_macros.h b/riscv/decode_macros.h
index 807ad98..892515f 100644
--- a/riscv/decode_macros.h
+++ b/riscv/decode_macros.h
@@ -337,10 +337,10 @@ inline long double to_f(float128_t f) { long double r; memcpy(&r, &f, sizeof(r))
#define DEBUG_RVV_FMA_VF \
printf("vfma(%lu) vd=%f vs1=%f vs2=%f vd_old=%f\n", i, to_f(vd), to_f(rs1), to_f(vs2), to_f(vd_old));
#else
-#define DEBUG_RVV_FP_VV 0
-#define DEBUG_RVV_FP_VF 0
-#define DEBUG_RVV_FMA_VV 0
-#define DEBUG_RVV_FMA_VF 0
+#define DEBUG_RVV_FP_VV (void) 0
+#define DEBUG_RVV_FP_VF (void) 0
+#define DEBUG_RVV_FMA_VV (void) 0
+#define DEBUG_RVV_FMA_VF (void) 0
#endif
#define DECLARE_XENVCFG_VARS(field) \
diff --git a/riscv/devices.cc b/riscv/devices.cc
index 2c06f78..fb5bb5a 100644
--- a/riscv/devices.cc
+++ b/riscv/devices.cc
@@ -8,53 +8,92 @@ mmio_device_map_t& mmio_device_map()
return device_map;
}
+static auto empty_device = rom_device_t(std::vector<char>());
+
+bus_t::bus_t()
+ : bus_t(&empty_device)
+{
+}
+
+bus_t::bus_t(abstract_device_t* fallback)
+ : fallback(fallback)
+{
+}
+
void bus_t::add_device(reg_t addr, abstract_device_t* dev)
{
- // Searching devices via lower_bound/upper_bound
- // implicitly relies on the underlying std::map
- // container to sort the keys and provide ordered
- // iteration over this sort, which it does. (python's
- // SortedDict is a good analogy)
+ // Allow empty devices by omitting them
+ auto size = dev->size();
+ if (size == 0)
+ return;
+
+ // Reject devices that overflow address size
+ if (addr + size - 1 < addr) {
+ fprintf(stderr, "device at [%" PRIx64 ", %" PRIx64 ") overflows address size\n",
+ addr, addr + size);
+ abort();
+ }
+
+ // Reject devices that overlap other devices
+ if (auto it = devices.upper_bound(addr);
+ (it != devices.end() && addr + size - 1 >= it->first) ||
+ (it != devices.begin() && (it--, it->first + it->second->size() - 1 >= addr))) {
+ fprintf(stderr, "devices at [%" PRIx64 ", %" PRIx64 ") and [%" PRIx64 ", %" PRIx64 ") overlap\n",
+ it->first, it->first + it->second->size(), addr, addr + size);
+ abort();
+ }
+
devices[addr] = dev;
}
bool bus_t::load(reg_t addr, size_t len, uint8_t* bytes)
{
- // Find the device with the base address closest to but
- // less than addr (price-is-right search)
- auto it = devices.upper_bound(addr);
- if (devices.empty() || it == devices.begin()) {
- // Either the bus is empty, or there weren't
- // any items with a base address <= addr
- return false;
- }
- // Found at least one item with base address <= addr
- // The iterator points to the device after this, so
- // go back by one item.
- it--;
- return it->second->load(addr - it->first, len, bytes);
+ if (auto [base, dev] = find_device(addr, len); dev)
+ return dev->load(addr - base, len, bytes);
+ return false;
}
bool bus_t::store(reg_t addr, size_t len, const uint8_t* bytes)
{
- // See comments in bus_t::load
- auto it = devices.upper_bound(addr);
- if (devices.empty() || it == devices.begin()) {
- return false;
- }
- it--;
- return it->second->store(addr - it->first, len, bytes);
+ if (auto [base, dev] = find_device(addr, len); dev)
+ return dev->store(addr - base, len, bytes);
+ return false;
}
-std::pair<reg_t, abstract_device_t*> bus_t::find_device(reg_t addr)
+reg_t bus_t::size()
{
- // See comments in bus_t::load
- auto it = devices.upper_bound(addr);
- if (devices.empty() || it == devices.begin()) {
- return std::make_pair((reg_t)0, (abstract_device_t*)NULL);
+ if (auto last = devices.rbegin(); last != devices.rend())
+ return last->first + last->second->size();
+ return 0;
+}
+
+std::pair<reg_t, abstract_device_t*> bus_t::find_device(reg_t addr, size_t len)
+{
+ if (unlikely(!len || addr + len - 1 < addr))
+ return std::make_pair(0, nullptr);
+
+ // Obtain iterator to device immediately after the one that might match
+ auto it_after = devices.upper_bound(addr);
+ reg_t base, size;
+ if (likely(it_after != devices.begin())) {
+ // Obtain iterator to device that might match
+ auto it = std::prev(it_after);
+ base = it->first;
+ size = it->second->size();
+ if (likely(addr - base + len - 1 < size)) {
+ // it fully contains [addr, addr + len)
+ return std::make_pair(it->first, it->second);
+ }
}
- it--;
- return std::make_pair(it->first, it->second);
+
+ if (unlikely((it_after != devices.end() && addr + len - 1 >= it_after->first)
+ || (it_after != devices.begin() && addr - base < size))) {
+ // it_after or it contains part of, but not all of, [addr, add + len)
+ return std::make_pair(0, nullptr);
+ }
+
+ // No matching device
+ return std::make_pair(0, fallback);
}
mem_t::mem_t(reg_t size)
@@ -116,3 +155,25 @@ void mem_t::dump(std::ostream& o) {
}
}
}
+
+external_sim_device_t::external_sim_device_t(void* sim)
+ : external_simulator(sim) {}
+
+void external_sim_device_t::set_simulator(void* sim) {
+ external_simulator = sim;
+}
+
+bool external_sim_device_t::load(reg_t addr, size_t len, uint8_t* bytes) {
+ if (unlikely(external_simulator == nullptr)) return false;
+ return static_cast<abstract_sim_if_t*>(external_simulator)->load(addr, len, bytes);
+}
+
+bool external_sim_device_t::store(reg_t addr, size_t len, const uint8_t* bytes) {
+ if (unlikely(external_simulator == nullptr)) return false;
+ return static_cast<abstract_sim_if_t*>(external_simulator)->store(addr, len, bytes);
+}
+
+reg_t external_sim_device_t::size() {
+ if (unlikely(external_simulator == nullptr)) return 0;
+ return PGSIZE; // TODO: proper size
+}
diff --git a/riscv/devices.h b/riscv/devices.h
index 6ef32e9..e7b80ad 100644
--- a/riscv/devices.h
+++ b/riscv/devices.h
@@ -16,14 +16,21 @@ class simif_t;
class bus_t : public abstract_device_t {
public:
+ bus_t();
+
+ // the fallback device owns all addresses not owned by other devices
+ bus_t(abstract_device_t* fallback);
+
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;
+ reg_t size() override;
void add_device(reg_t addr, abstract_device_t* dev);
- std::pair<reg_t, abstract_device_t*> find_device(reg_t addr);
+ std::pair<reg_t, abstract_device_t*> find_device(reg_t addr, size_t len);
private:
std::map<reg_t, abstract_device_t*> devices;
+ abstract_device_t* fallback;
};
class rom_device_t : public abstract_device_t {
@@ -31,6 +38,7 @@ class rom_device_t : public abstract_device_t {
rom_device_t(std::vector<char> data);
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;
+ reg_t size() override { return data.size(); }
const std::vector<char>& contents() { return data; }
private:
std::vector<char> data;
@@ -41,7 +49,6 @@ class abstract_mem_t : public abstract_device_t {
virtual ~abstract_mem_t() = default;
virtual char* contents(reg_t addr) = 0;
- virtual reg_t size() = 0;
virtual void dump(std::ostream& o) = 0;
};
@@ -64,12 +71,31 @@ class mem_t : public abstract_mem_t {
reg_t sz;
};
+class abstract_sim_if_t {
+public:
+ virtual ~abstract_sim_if_t() = default;
+ 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;
+};
+
+class external_sim_device_t : public abstract_device_t {
+public:
+ external_sim_device_t(void* sim);
+ void set_simulator(void* sim);
+ 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;
+ reg_t size() override;
+
+private:
+ void* external_simulator;
+};
+
class clint_t : public abstract_device_t {
public:
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; }
+ reg_t size() override { return CLINT_SIZE; }
void tick(reg_t rtc_ticks) override;
uint64_t get_mtimecmp(reg_t hartid) { return mtimecmp[hartid]; }
uint64_t get_mtime() { return mtime; }
@@ -109,7 +135,7 @@ class plic_t : public abstract_device_t, public abstract_interrupt_controller_t
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; }
+ reg_t size() override { return PLIC_SIZE; }
private:
std::vector<plic_context_t> contexts;
uint32_t num_ids;
@@ -140,7 +166,7 @@ class ns16550_t : public abstract_device_t {
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; }
+ reg_t size() override { return NS16550_SIZE; }
private:
abstract_interrupt_controller_t *intctrl;
uint32_t interrupt_id;
diff --git a/riscv/dts.cc b/riscv/dts.cc
index 9751ffe..5be9d57 100644
--- a/riscv/dts.cc
+++ b/riscv/dts.cc
@@ -424,7 +424,6 @@ int fdt_parse_isa(const void *fdt, int cpu_offset, const char **isa)
int fdt_parse_hartid(const void *fdt, int cpu_offset, uint32_t *hartid)
{
int len, rc;
- const void *prop;
const fdt32_t *val;
if ((rc = check_cpu_node(fdt, cpu_offset)) < 0)
diff --git a/riscv/execute.cc b/riscv/execute.cc
index 1fa6111..c29eb2d 100644
--- a/riscv/execute.cc
+++ b/riscv/execute.cc
@@ -40,13 +40,12 @@ static void commit_log_print_value(FILE *log_file, int width, const void *data)
fprintf(log_file, "0x%016" PRIx64, *(const uint64_t *)data);
break;
default:
- // max lengh of vector
- if (((width - 1) & width) == 0) {
- const uint64_t *arr = (const uint64_t *)data;
+ if (width % 8 == 0) {
+ const uint8_t *arr = (const uint8_t *)data;
fprintf(log_file, "0x");
- for (int idx = width / 64 - 1; idx >= 0; --idx) {
- fprintf(log_file, "%016" PRIx64, arr[idx]);
+ for (int idx = width / 8 - 1; idx >= 0; --idx) {
+ fprintf(log_file, "%02" PRIx8, arr[idx]);
}
} else {
abort();
@@ -365,12 +364,10 @@ void processor_t::step(size_t n)
in_wfi = true;
}
- if (!(state.mcountinhibit->read() & MCOUNTINHIBIT_IR))
- state.minstret->bump(instret);
+ state.minstret->bump((state.mcountinhibit->read() & MCOUNTINHIBIT_IR) ? 0 : instret);
// Model a hart whose CPI is 1.
- if (!(state.mcountinhibit->read() & MCOUNTINHIBIT_CY))
- state.mcycle->bump(instret);
+ state.mcycle->bump((state.mcountinhibit->read() & MCOUNTINHIBIT_CY) ? 0 : instret);
n -= instret;
}
diff --git a/riscv/extension.cc b/riscv/extension.cc
index 520c2ed..195eea1 100644
--- a/riscv/extension.cc
+++ b/riscv/extension.cc
@@ -3,21 +3,15 @@
#include "extension.h"
#include "trap.h"
-extension_t::~extension_t()
-{
-}
-
-void extension_t::illegal_instruction()
+void extension_t::illegal_instruction([[maybe_unused]] processor_t &proc)
{
throw trap_illegal_instruction(0);
}
-void extension_t::raise_interrupt()
+void extension_t::raise_interrupt([[maybe_unused]] processor_t &proc)
{
- p->take_interrupt((reg_t)1 << IRQ_COP); // must not return
+ proc.take_interrupt((reg_t)1 << IRQ_COP); // must not return
throw std::logic_error("a COP exception was posted, but interrupts are disabled!");
}
-void extension_t::clear_interrupt()
-{
-}
+void extension_t::clear_interrupt([[maybe_unused]] processor_t &proc) {}
diff --git a/riscv/extension.h b/riscv/extension.h
index 991da7e..411e65b 100644
--- a/riscv/extension.h
+++ b/riscv/extension.h
@@ -11,21 +11,18 @@
class extension_t
{
public:
- virtual std::vector<insn_desc_t> get_instructions() = 0;
- virtual std::vector<disasm_insn_t*> get_disasms() = 0;
- virtual std::vector<csr_t_p> get_csrs ([[maybe_unused]] processor_t &proc) const { return {}; };
- virtual const char* name() = 0;
- virtual void reset() {};
- virtual void set_debug(bool UNUSED value) {}
- virtual ~extension_t();
+ virtual std::vector<insn_desc_t> get_instructions(const processor_t &proc) = 0;
+ virtual std::vector<disasm_insn_t*> get_disasms(const processor_t *proc = nullptr) = 0;
+ virtual std::vector<csr_t_p> get_csrs(processor_t &) const { return {}; };
+ virtual const char* name() const = 0;
+ virtual void reset(processor_t &) {};
+ virtual void set_debug(bool UNUSED value, const processor_t &) {}
+ virtual ~extension_t() = default;
- void set_processor(processor_t* _p) { p = _p; }
protected:
- processor_t* p;
-
- void illegal_instruction();
- void raise_interrupt();
- void clear_interrupt();
+ void illegal_instruction(processor_t &proc);
+ void raise_interrupt(processor_t &proc);
+ void clear_interrupt(processor_t &proc);
};
std::function<extension_t*()> find_extension(const char* name);
diff --git a/riscv/interactive.cc b/riscv/interactive.cc
index 2701f49..9afc718 100644
--- a/riscv/interactive.cc
+++ b/riscv/interactive.cc
@@ -83,8 +83,7 @@ static void clear_str(bool noncanonical, int fd, std::string target_str)
clear_motion += ' ';
}
clear_motion += '\r';
- if (write(fd, clear_motion.c_str(), clear_motion.size() + 1))
- ; // shut up gcc
+ (void) write(fd, clear_motion.c_str(), clear_motion.size() + 1);
}
}
@@ -97,8 +96,7 @@ static void send_key(bool noncanonical, int fd, keybuffer_t key_code, const int
{
key_motion += (char) ((key_code >> (i * BITS_PER_CHAR)) & 0xff);
}
- if (write(fd, key_motion.c_str(), len) != len)
- ; // shut up gcc
+ (void) write(fd, key_motion.c_str(), len);
}
}
@@ -145,8 +143,8 @@ static std::string readline(int fd)
clear_str(noncanonical, fd, s);
cursor_pos--;
s.erase(cursor_pos, 1);
- if (noncanonical && write(fd, s.c_str(), s.size() + 1) != 1)
- ; // shut up gcc
+ if (noncanonical)
+ (void) write(fd, s.c_str(), s.size() + 1);
// move cursor by left arrow key
for (unsigned i = 0; i < s.size() - cursor_pos; i++) {
send_key(noncanonical, fd, KEYCODE_LEFT, 3);
@@ -177,8 +175,8 @@ static std::string readline(int fd)
clear_str(noncanonical, fd, s);
history_index = std::min(history_commands.size(), history_index + 1);
s = history_commands[history_commands.size() - history_index];
- if (noncanonical && write(fd, s.c_str(), s.size() + 1))
- ; // shut up gcc
+ if (noncanonical)
+ (void) write(fd, s.c_str(), s.size() + 1);
cursor_pos = s.size();
}
key_buffer = 0;
@@ -193,8 +191,8 @@ static std::string readline(int fd)
} else {
s = history_commands[history_commands.size() - history_index];
}
- if (noncanonical && write(fd, s.c_str(), s.size() + 1))
- ; // shut up gcc
+ if (noncanonical)
+ (void) write(fd, s.c_str(), s.size() + 1);
cursor_pos = s.size();
}
key_buffer = 0;
@@ -222,14 +220,13 @@ static std::string readline(int fd)
key_buffer = 0;
break;
case KEYCODE_ENTER:
- if (noncanonical && write(fd, &ch, 1) != 1)
- ; // shut up gcc
+ if (noncanonical)
+ (void) write(fd, &ch, 1);
if (s.size() > initial_s_len && (history_commands.size() == 0 || s != history_commands[history_commands.size() - 1])) {
history_commands.push_back(s);
}
return s.substr(initial_s_len);
default:
- DEFAULT_KEY:
// unknown buffered key, do nothing
if (key_buffer != 0) {
key_buffer = 0;
@@ -238,8 +235,8 @@ static std::string readline(int fd)
clear_str(noncanonical, fd, s);
s.insert(cursor_pos, 1, ch);
cursor_pos++;
- if (noncanonical && write(fd, s.c_str(), s.size() + 1) != 1)
- ; // shut up gcc
+ if (noncanonical)
+ (void) write(fd, s.c_str(), s.size() + 1);
// send left arrow key to move cursor
for (unsigned i = 0; i < s.size() - cursor_pos; i++) {
send_key(noncanonical, fd, KEYCODE_LEFT, 3);
diff --git a/riscv/isa_parser.h b/riscv/isa_parser.h
index 61ba5a8..ea64660 100644
--- a/riscv/isa_parser.h
+++ b/riscv/isa_parser.h
@@ -76,6 +76,8 @@ typedef enum {
EXT_INTERNAL_ZFH_MOVE,
EXT_SMCSRIND,
EXT_SSCSRIND,
+ EXT_SMCDELEG,
+ EXT_SSCCFG,
EXT_SMCNTRPMF,
EXT_ZIMOP,
EXT_ZCMOP,
diff --git a/riscv/mmu.cc b/riscv/mmu.cc
index d950146..616cacc 100644
--- a/riscv/mmu.cc
+++ b/riscv/mmu.cc
@@ -123,7 +123,8 @@ reg_t reg_from_bytes(size_t len, const uint8_t* bytes)
bool mmu_t::mmio_ok(reg_t paddr, access_type UNUSED type)
{
// Disallow access to debug region when not in debug mode
- if (paddr >= DEBUG_START && paddr <= DEBUG_END && proc && !proc->state.debug_mode)
+ static_assert(DEBUG_START == 0);
+ if (/* paddr >= DEBUG_START && */ paddr <= DEBUG_END && proc && !proc->state.debug_mode)
return false;
return true;
@@ -618,12 +619,14 @@ void mmu_t::register_memtracer(memtracer_t* t)
}
reg_t mmu_t::get_pmlen(bool effective_virt, reg_t effective_priv, xlate_flags_t flags) const {
- if (!proc || proc->get_xlen() != 64 || ((proc->state.sstatus->readvirt(false) | proc->state.sstatus->readvirt(effective_virt)) & MSTATUS_MXR) || flags.hlvx)
+ if (!proc || proc->get_xlen() != 64 || flags.hlvx)
return 0;
reg_t pmm = 0;
if (effective_priv == PRV_M)
pmm = get_field(proc->state.mseccfg->read(), MSECCFG_PMM);
+ else if ((proc->state.sstatus->readvirt(false) | proc->state.sstatus->readvirt(effective_virt)) & MSTATUS_MXR)
+ pmm = 0;
else if (!effective_virt && (effective_priv == PRV_S || (!proc->extension_enabled('S') && effective_priv == PRV_U)))
pmm = get_field(proc->state.menvcfg->read(), MENVCFG_PMM);
else if (effective_virt && effective_priv == PRV_S)
diff --git a/riscv/mmu.h b/riscv/mmu.h
index 3a12355..6695383 100644
--- a/riscv/mmu.h
+++ b/riscv/mmu.h
@@ -214,7 +214,10 @@ public:
throw trap_load_address_misaligned((proc) ? proc->state.v : false, addr, 0, 0);
}
- return (float128_t){load<uint64_t>(addr), load<uint64_t>(addr + 8)};
+ float128_t res;
+ res.v[0] = load<uint64_t>(addr);
+ res.v[1] = load<uint64_t>(addr + 8);
+ return res;
}
void cbo_zero(reg_t addr) {
@@ -287,8 +290,15 @@ public:
template<typename T>
T ALWAYS_INLINE fetch_jump_table(reg_t addr) {
- auto tlb_entry = translate_insn_addr(addr);
- return from_target(*(target_endian<T>*)(tlb_entry.host_offset + addr));
+ typedef std::remove_const<std::remove_pointer<decltype(translate_insn_addr_to_host(addr))>::type>::type U;
+ U parcels[sizeof(T) / sizeof(U)];
+
+ for (size_t i = 0; i < std::size(parcels); i++)
+ parcels[i] = *translate_insn_addr_to_host(addr + i * sizeof(U));
+
+ target_endian<T> res;
+ memcpy(&res, parcels, sizeof(T));
+ return from_target(res);
}
inline icache_entry_t* refill_icache(reg_t addr, icache_entry_t* entry)
diff --git a/riscv/ns16550.cc b/riscv/ns16550.cc
index 2805fd8..15e0873 100644
--- a/riscv/ns16550.cc
+++ b/riscv/ns16550.cc
@@ -328,7 +328,7 @@ void ns16550_t::tick(reg_t UNUSED rtc_ticks)
update_interrupt();
}
-std::string ns16550_generate_dts(const sim_t* sim, const std::vector<std::string>& UNUSED sargs)
+std::string ns16550_generate_dts(const sim_t* sim, const std::vector<std::string>& sargs UNUSED)
{
std::stringstream s;
s << std::hex
@@ -348,7 +348,7 @@ std::string ns16550_generate_dts(const sim_t* sim, const std::vector<std::string
return s.str();
}
-ns16550_t* ns16550_parse_from_fdt(const void* fdt, const sim_t* sim, reg_t* base, const std::vector<std::string>& UNUSED sargs)
+ns16550_t* ns16550_parse_from_fdt(const void* fdt, const sim_t* sim, reg_t* base, const std::vector<std::string>& sargs UNUSED)
{
uint32_t ns16550_shift, ns16550_io_width, ns16550_int_id;
if (fdt_parse_ns16550(fdt, base,
diff --git a/riscv/plic.cc b/riscv/plic.cc
index 14de6df..b6d204b 100644
--- a/riscv/plic.cc
+++ b/riscv/plic.cc
@@ -343,7 +343,8 @@ bool plic_t::load(reg_t addr, size_t len, uint8_t* bytes)
return false;
}
- if (PRIORITY_BASE <= addr && addr < PENDING_BASE) {
+ static_assert(PRIORITY_BASE == 0);
+ if (/* PRIORITY_BASE <= addr && */ addr < PENDING_BASE) {
ret = priority_read(addr, &val);
} else if (PENDING_BASE <= addr && addr < ENABLE_BASE) {
ret = pending_read(addr - PENDING_BASE, &val);
@@ -384,7 +385,8 @@ bool plic_t::store(reg_t addr, size_t len, const uint8_t* bytes)
write_little_endian_reg(&val, addr, len, bytes);
- if (PRIORITY_BASE <= addr && addr < ENABLE_BASE) {
+ static_assert(PRIORITY_BASE == 0);
+ if (/* PRIORITY_BASE <= addr && */ addr < ENABLE_BASE) {
ret = priority_write(addr, val);
} else if (ENABLE_BASE <= addr && addr < CONTEXT_BASE) {
uint32_t cntx = (addr - ENABLE_BASE) / ENABLE_PER_HART;
@@ -401,7 +403,7 @@ 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, const std::vector<std::string>& UNUSED sargs)
+std::string plic_generate_dts(const sim_t* sim, const std::vector<std::string>& sargs UNUSED)
{
std::stringstream s;
s << std::hex
@@ -424,7 +426,7 @@ std::string plic_generate_dts(const sim_t* sim, const std::vector<std::string>&
return s.str();
}
-plic_t* plic_parse_from_fdt(const void* fdt, const sim_t* sim, reg_t* base, const std::vector<std::string>& UNUSED sargs)
+plic_t* plic_parse_from_fdt(const void* fdt, const sim_t* sim, reg_t* base, const std::vector<std::string>& sargs UNUSED)
{
uint32_t plic_ndev;
if (fdt_parse_plic(fdt, base, &plic_ndev, "riscv,plic0") == 0 ||
diff --git a/riscv/processor.cc b/riscv/processor.cc
index 2917153..cc868e8 100644
--- a/riscv/processor.cc
+++ b/riscv/processor.cc
@@ -99,42 +99,6 @@ processor_t::~processor_t()
delete disassembler;
}
-static void bad_option_string(const char *option, const char *value,
- const char *msg)
-{
- fprintf(stderr, "error: bad %s option '%s'. %s\n", option, value, msg);
- abort();
-}
-
-static std::string get_string_token(std::string str, const char delimiter, size_t& pos)
-{
- size_t _pos = pos;
- while (pos < str.length() && str[pos] != delimiter) ++pos;
- return str.substr(_pos, pos - _pos);
-}
-
-static bool check_pow2(int val)
-{
- return ((val & (val - 1))) == 0;
-}
-
-static std::string strtolower(const char* str)
-{
- std::string res;
- for (const char *r = str; *r; r++)
- res += std::tolower(*r);
- return res;
-}
-
-static int xlen_to_uxl(int xlen)
-{
- if (xlen == 32)
- return 1;
- if (xlen == 64)
- return 2;
- abort();
-}
-
void state_t::reset(processor_t* const proc, reg_t max_isa)
{
pc = DEFAULT_RSTVEC;
@@ -169,7 +133,7 @@ void processor_t::set_debug(bool value)
debug = value;
for (auto e : custom_extensions)
- e.second->set_debug(value);
+ e.second->set_debug(value, *this);
}
void processor_t::set_histogram(bool value)
@@ -200,7 +164,7 @@ void processor_t::reset()
for (auto e : custom_extensions) { // reset any extensions
for (auto &csr: e.second->get_csrs(*this))
state.add_csr(csr->address, csr);
- e.second->reset();
+ e.second->reset(*this);
}
if (sim)
@@ -258,10 +222,10 @@ void processor_t::set_mmu_capability(int cap)
break;
case IMPL_MMU_SV57:
set_impl(IMPL_MMU_SV57, true);
- // Fall through
+ [[fallthrough]];
case IMPL_MMU_SV48:
set_impl(IMPL_MMU_SV48, true);
- // Fall through
+ [[fallthrough]];
case IMPL_MMU_SV39:
set_impl(IMPL_MMU_SV39, true);
set_impl(IMPL_MMU, true);
@@ -447,7 +411,10 @@ void processor_t::take_trap(trap_t& t, reg_t epc)
// An unexpected trap - a trap when SDT is 1 - traps to M-mode
if ((state.prv <= PRV_S && bit < max_xlen) &&
(((vsdeleg >> bit) & 1) || ((hsdeleg >> bit) & 1))) {
- reg_t s = state.sstatus->read();
+ // Trap is handled in VS-mode or HS-mode. Read the sstatus of the
+ // mode that will handle the trap based on the delegation control
+ reg_t s = (((vsdeleg >> bit) & 1)) ? state.sstatus->read() :
+ state.nonvirtual_sstatus->read();
supv_double_trap = get_field(s, MSTATUS_SDT);
if (supv_double_trap)
vsdeleg = hsdeleg = 0;
@@ -703,18 +670,17 @@ void processor_t::build_opcode_map()
}
void processor_t::register_extension(extension_t *x) {
- for (auto insn : x->get_instructions())
+ for (auto insn : x->get_instructions(*this))
register_custom_insn(insn);
build_opcode_map();
- for (auto disasm_insn : x->get_disasms())
+ for (auto disasm_insn : x->get_disasms(this))
disassembler->add_insn(disasm_insn);
if (!custom_extensions.insert(std::make_pair(x->name(), x)).second) {
fprintf(stderr, "extensions must have unique names (got two named \"%s\"!)\n", x->name());
abort();
}
- x->set_processor(this);
}
void processor_t::register_base_instructions()
@@ -739,21 +705,27 @@ void processor_t::register_base_instructions()
#include "insn_list.h"
#undef DEFINE_INSN
+ #define DEFINE_INSN_UNCOND(name) { \
+ insn_desc_t insn = { \
+ name##_match, \
+ name##_mask, \
+ fast_rv32i_##name, \
+ fast_rv64i_##name, \
+ fast_rv32e_##name, \
+ fast_rv64e_##name, \
+ logged_rv32i_##name, \
+ logged_rv64i_##name, \
+ logged_rv32e_##name, \
+ logged_rv64e_##name \
+ }; \
+ register_base_insn(insn); \
+ }
+
// add overlapping instructions first, in order
#define DECLARE_OVERLAP_INSN(name, ext) \
name##_overlapping = true; \
if (isa.extension_enabled(ext)) \
- register_base_insn((insn_desc_t) { \
- name##_match, \
- name##_mask, \
- fast_rv32i_##name, \
- fast_rv64i_##name, \
- fast_rv32e_##name, \
- fast_rv64e_##name, \
- logged_rv32i_##name, \
- logged_rv64i_##name, \
- logged_rv32e_##name, \
- logged_rv64e_##name});
+ DEFINE_INSN_UNCOND(name);
#include "overlap_list.h"
#undef DECLARE_OVERLAP_INSN
@@ -762,19 +734,10 @@ void processor_t::register_base_instructions()
// appear earlier to improve search time on opcode_cache misses.
#define DEFINE_INSN(name) \
if (!name##_overlapping) \
- register_base_insn((insn_desc_t) { \
- name##_match, \
- name##_mask, \
- fast_rv32i_##name, \
- fast_rv64i_##name, \
- fast_rv32e_##name, \
- fast_rv64e_##name, \
- logged_rv32i_##name, \
- logged_rv64i_##name, \
- logged_rv32e_##name, \
- logged_rv64e_##name});
+ DEFINE_INSN_UNCOND(name);
#include "insn_list.h"
#undef DEFINE_INSN
+ #undef DEFINE_INSN_UNCOND
// terminate instruction list with a catch-all
register_base_insn(insn_desc_t::illegal_instruction);
@@ -813,6 +776,11 @@ bool processor_t::store(reg_t addr, size_t len, const uint8_t* bytes)
return false;
}
+reg_t processor_t::size()
+{
+ return PGSIZE;
+}
+
void processor_t::trigger_updated(const std::vector<triggers::trigger_t *> &triggers)
{
mmu->flush_tlb();
diff --git a/riscv/processor.h b/riscv/processor.h
index 4f22cbd..6b611d7 100644
--- a/riscv/processor.h
+++ b/riscv/processor.h
@@ -109,6 +109,7 @@ struct state_t
csr_t_p stvec;
virtualized_csr_t_p satp;
csr_t_p scause;
+ csr_t_p scountinhibit;
// When taking a trap into HS-mode, we must access the nonvirtualized HS-mode CSRs directly:
csr_t_p nonvirtual_stvec;
@@ -341,8 +342,9 @@ public:
void register_extension(extension_t*);
// MMIO slave interface
- 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;
+ reg_t size() override;
// When true, display disassembly of each instruction that's executed.
bool debug;
diff --git a/riscv/rocc.cc b/riscv/rocc.cc
index 53ee051..9ba4fc1 100644
--- a/riscv/rocc.cc
+++ b/riscv/rocc.cc
@@ -14,15 +14,15 @@
u.i = insn; \
reg_t xs1 = u.r.xs1 ? RS1 : -1; \
reg_t xs2 = u.r.xs2 ? RS2 : -1; \
- reg_t xd = rocc->custom##n(u.r, xs1, xs2); \
+ reg_t xd = rocc->custom##n(p, u.r, xs1, xs2); \
if (u.r.xd) \
WRITE_RD(xd); \
return pc+4; \
} \
\
- reg_t rocc_t::custom##n(rocc_insn_t UNUSED insn, reg_t UNUSED xs1, reg_t UNUSED xs2) \
+ reg_t rocc_t::custom##n(processor_t *p, rocc_insn_t UNUSED insn, reg_t UNUSED xs1, reg_t UNUSED xs2) \
{ \
- illegal_instruction(); \
+ illegal_instruction(*p); \
return 0; \
}
@@ -31,25 +31,17 @@ customX(1)
customX(2)
customX(3)
-std::vector<insn_desc_t> rocc_t::get_instructions()
+std::vector<insn_desc_t> rocc_t::get_instructions(const processor_t &)
{
- std::vector<insn_desc_t> insns;
- insns.push_back((insn_desc_t){0x0b, 0x7f,
- &::illegal_instruction, c0, &::illegal_instruction, c0,
- &::illegal_instruction, c0, &::illegal_instruction, c0});
- insns.push_back((insn_desc_t){0x2b, 0x7f,
- &::illegal_instruction, c1, &::illegal_instruction, c1,
- &::illegal_instruction, c1, &::illegal_instruction, c1});
- insns.push_back((insn_desc_t){0x5b, 0x7f,
- &::illegal_instruction, c2, &::illegal_instruction, c2,
- &::illegal_instruction, c2, &::illegal_instruction, c2});
- insns.push_back((insn_desc_t){0x7b, 0x7f,
- &::illegal_instruction, c3, &::illegal_instruction, c3,
- &::illegal_instruction, c0, &::illegal_instruction, c3});
+ std::vector<insn_desc_t> insns = {
+ {0x0b, 0x7f, &::illegal_instruction, c0, &::illegal_instruction, c0, &::illegal_instruction, c0, &::illegal_instruction, c0},
+ {0x2b, 0x7f, &::illegal_instruction, c1, &::illegal_instruction, c1, &::illegal_instruction, c1, &::illegal_instruction, c1},
+ {0x5b, 0x7f, &::illegal_instruction, c2, &::illegal_instruction, c2, &::illegal_instruction, c2, &::illegal_instruction, c2},
+ {0x7b, 0x7f, &::illegal_instruction, c3, &::illegal_instruction, c3, &::illegal_instruction, c0, &::illegal_instruction, c3}};
return insns;
}
-std::vector<disasm_insn_t*> rocc_t::get_disasms()
+std::vector<disasm_insn_t *> rocc_t::get_disasms(const processor_t *)
{
std::vector<disasm_insn_t*> insns;
return insns;
diff --git a/riscv/rocc.h b/riscv/rocc.h
index d65ec97..d7fee26 100644
--- a/riscv/rocc.h
+++ b/riscv/rocc.h
@@ -24,12 +24,12 @@ union rocc_insn_union_t
class rocc_t : public extension_t
{
public:
- virtual reg_t custom0(rocc_insn_t insn, reg_t xs1, reg_t xs2);
- virtual reg_t custom1(rocc_insn_t insn, reg_t xs1, reg_t xs2);
- virtual reg_t custom2(rocc_insn_t insn, reg_t xs1, reg_t xs2);
- virtual reg_t custom3(rocc_insn_t insn, reg_t xs1, reg_t xs2);
- std::vector<insn_desc_t> get_instructions();
- std::vector<disasm_insn_t*> get_disasms();
+ virtual reg_t custom0(processor_t *, rocc_insn_t insn, reg_t xs1, reg_t xs2);
+ virtual reg_t custom1(processor_t *, rocc_insn_t insn, reg_t xs1, reg_t xs2);
+ virtual reg_t custom2(processor_t *, rocc_insn_t insn, reg_t xs1, reg_t xs2);
+ virtual reg_t custom3(processor_t *, rocc_insn_t insn, reg_t xs1, reg_t xs2);
+ std::vector<insn_desc_t> get_instructions(const processor_t &proc) override;
+ std::vector<disasm_insn_t *> get_disasms(const processor_t *proc = nullptr) override;
};
#define define_custom_func(type_name, ext_name_str, func_name, method_name) \
diff --git a/riscv/sim.cc b/riscv/sim.cc
index 81acb1c..fd1c6fb 100644
--- a/riscv/sim.cc
+++ b/riscv/sim.cc
@@ -137,7 +137,6 @@ sim_t::sim_t(const cfg_t *cfg, bool halted,
dtb = strstream.str();
dts = dtb_to_dts(dtb);
} else {
- std::pair<reg_t, reg_t> initrd_bounds = cfg->initrd_bounds;
std::string device_nodes;
for (const device_factory_sargs_t& factory_sargs: device_factories) {
const device_factory_t* factory = factory_sargs.first;
@@ -238,6 +237,8 @@ sim_t::sim_t(const cfg_t *cfg, bool halted,
procs[cpu_idx]->set_mmu_capability(IMPL_MMU_SBARE);
}
+ procs[cpu_idx]->reset();
+
cpu_idx++;
}
@@ -404,10 +405,9 @@ void sim_t::set_rom()
char* sim_t::addr_to_mem(reg_t paddr) {
if (!paddr_ok(paddr))
return NULL;
- auto desc = bus.find_device(paddr);
+ auto desc = bus.find_device(paddr >> PGSHIFT << PGSHIFT, PGSIZE);
if (auto mem = dynamic_cast<abstract_mem_t*>(desc.second))
- if (paddr - desc.first < mem->size())
- return mem->contents(paddr - desc.first);
+ return mem->contents(paddr - desc.first);
return NULL;
}
diff --git a/riscv/sim.h b/riscv/sim.h
index ce5dc6f..da04a88 100644
--- a/riscv/sim.h
+++ b/riscv/sim.h
@@ -100,8 +100,13 @@ private:
remote_bitbang_t* remote_bitbang;
std::optional<std::function<void()>> next_interactive_action;
- // memory-mapped I/O routines
+ // If padd corresponds to memory (as opposed to an I/O device), return a
+ // host pointer corresponding to paddr.
+ // For these purposes, only memories that include the entire base page
+ // surrounding paddr are considered; smaller memories are treated as I/O.
virtual char* addr_to_mem(reg_t paddr) override;
+
+ // memory-mapped I/O routines
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 set_rom();
diff --git a/riscv/triggers.cc b/riscv/triggers.cc
index e130a87..9c21330 100644
--- a/riscv/triggers.cc
+++ b/riscv/triggers.cc
@@ -52,7 +52,7 @@ void trigger_t::tdata3_write(processor_t * const proc, const reg_t val) noexcept
mhselect = get_field(val, CSR_TEXTRA_MHSELECT(xlen));
sbytemask = get_field(val, CSR_TEXTRA_SBYTEMASK(xlen));
svalue = proc->extension_enabled_const('S') ? get_field(val, CSR_TEXTRA_SVALUE(xlen)) : 0;
- sselect = (sselect_t)((proc->extension_enabled_const('S') && get_field(val, CSR_TEXTRA_SSELECT(xlen)) <= SSELECT_MAXVAL) ? get_field(val, CSR_TEXTRA_SSELECT(xlen)) : SSELECT_IGNORE);
+ sselect = (sselect_t)((proc->extension_enabled_const('S') && get_field(val, CSR_TEXTRA_SSELECT(xlen)) <= SSELECT_MAXVAL) ? get_field(val, CSR_TEXTRA_SSELECT(xlen)) : (reg_t)SSELECT_IGNORE);
}
static reg_t tcontrol_value(const state_t * state) {
@@ -274,7 +274,10 @@ std::optional<match_result_t> mcontrol_common_t::detect_memory_access_match(proc
mcontrol_common_t::match_t mcontrol_common_t::legalize_match(reg_t val, reg_t maskmax) noexcept
{
switch (val) {
- case MATCH_NAPOT: if (maskmax == 0) return MATCH_EQUAL;
+ case MATCH_NAPOT:
+ if (maskmax == 0)
+ return MATCH_EQUAL;
+ [[fallthrough]];
case MATCH_EQUAL:
case MATCH_GE:
case MATCH_LT:
@@ -675,4 +678,4 @@ reg_t module_t::tinfo_read(unsigned UNUSED index) const noexcept
(CSR_TINFO_VERSION_1 << CSR_TINFO_VERSION_OFFSET);
}
-};
+}
diff --git a/riscv/triggers.h b/riscv/triggers.h
index 3f1e86f..60ee5ca 100644
--- a/riscv/triggers.h
+++ b/riscv/triggers.h
@@ -301,6 +301,6 @@ private:
std::vector<trigger_t *> triggers;
};
-};
+}
#endif
diff --git a/riscv/v_ext_macros.h b/riscv/v_ext_macros.h
index 3e7dc45..5b4d9bd 100644
--- a/riscv/v_ext_macros.h
+++ b/riscv/v_ext_macros.h
@@ -454,7 +454,7 @@ static inline bool is_overlapped_widen(const int astart, int asize,
#define VFP_VF_CMP_PARAMS(width) \
float##width##_t rs1 = f##width(READ_FREG(rs1_num)); \
- float##width##_t vs2 = P.VU.elt<float##width##_t>(rs2_num, i);
+ float##width##_t UNUSED vs2 = P.VU.elt<float##width##_t>(rs2_num, i);
#define VFP_VF_PARAMS(width) \
float##width##_t &vd = P.VU.elt<float##width##_t>(rd_num, i, true); \
@@ -1181,25 +1181,6 @@ VI_VX_ULOOP({ \
#define VI_STRIP(inx) \
reg_t vreg_inx = inx;
-#define VI_DUPLICATE_VREG(reg_num, idx_sew) \
-reg_t index[P.VU.vlmax]; \
- for (reg_t i = 0; i < P.VU.vlmax && P.VU.vl->read() != 0; ++i) { \
- switch (idx_sew) { \
- case e8: \
- index[i] = P.VU.elt<uint8_t>(reg_num, i); \
- break; \
- case e16: \
- index[i] = P.VU.elt<uint16_t>(reg_num, i); \
- break; \
- case e32: \
- index[i] = P.VU.elt<uint32_t>(reg_num, i); \
- break; \
- case e64: \
- index[i] = P.VU.elt<uint64_t>(reg_num, i); \
- break; \
- } \
-}
-
#define VI_LD(stride, offset, elt_width, is_mask_ldst) \
const reg_t nf = insn.v_nf() + 1; \
VI_CHECK_LOAD(elt_width, is_mask_ldst); \
@@ -1218,6 +1199,23 @@ reg_t index[P.VU.vlmax]; \
} \
P.VU.vstart->write(0);
+#define VI_LDST_GET_INDEX(elt_width) \
+ reg_t index; \
+ switch (elt_width) { \
+ case e8: \
+ index = P.VU.elt<uint8_t>(insn.rs2(), i); \
+ break; \
+ case e16: \
+ index = P.VU.elt<uint16_t>(insn.rs2(), i); \
+ break; \
+ case e32: \
+ index = P.VU.elt<uint32_t>(insn.rs2(), i); \
+ break; \
+ case e64: \
+ index = P.VU.elt<uint64_t>(insn.rs2(), i); \
+ break; \
+ } \
+
#define VI_LD_INDEX(elt_width, is_seg) \
const reg_t nf = insn.v_nf() + 1; \
VI_CHECK_LD_INDEX(elt_width); \
@@ -1226,8 +1224,8 @@ reg_t index[P.VU.vlmax]; \
const reg_t vd = insn.rd(); \
if (!is_seg) \
require(nf == 1); \
- VI_DUPLICATE_VREG(insn.rs2(), elt_width); \
for (reg_t i = 0; i < vl; ++i) { \
+ VI_LDST_GET_INDEX(elt_width); \
VI_ELEMENT_SKIP; \
VI_STRIP(i); \
P.VU.vstart->write(i); \
@@ -1235,19 +1233,19 @@ reg_t index[P.VU.vlmax]; \
switch (P.VU.vsew) { \
case e8: \
P.VU.elt<uint8_t>(vd + fn * flmul, vreg_inx, true) = \
- MMU.load<uint8_t>(baseAddr + index[i] + fn * 1); \
+ MMU.load<uint8_t>(baseAddr + index + fn * 1); \
break; \
case e16: \
P.VU.elt<uint16_t>(vd + fn * flmul, vreg_inx, true) = \
- MMU.load<uint16_t>(baseAddr + index[i] + fn * 2); \
+ MMU.load<uint16_t>(baseAddr + index + fn * 2); \
break; \
case e32: \
P.VU.elt<uint32_t>(vd + fn * flmul, vreg_inx, true) = \
- MMU.load<uint32_t>(baseAddr + index[i] + fn * 4); \
+ MMU.load<uint32_t>(baseAddr + index + fn * 4); \
break; \
default: \
P.VU.elt<uint64_t>(vd + fn * flmul, vreg_inx, true) = \
- MMU.load<uint64_t>(baseAddr + index[i] + fn * 8); \
+ MMU.load<uint64_t>(baseAddr + index + fn * 8); \
break; \
} \
} \
@@ -1280,27 +1278,27 @@ reg_t index[P.VU.vlmax]; \
const reg_t vs3 = insn.rd(); \
if (!is_seg) \
require(nf == 1); \
- VI_DUPLICATE_VREG(insn.rs2(), elt_width); \
for (reg_t i = 0; i < vl; ++i) { \
+ VI_LDST_GET_INDEX(elt_width); \
VI_STRIP(i) \
VI_ELEMENT_SKIP; \
P.VU.vstart->write(i); \
for (reg_t fn = 0; fn < nf; ++fn) { \
switch (P.VU.vsew) { \
case e8: \
- MMU.store<uint8_t>(baseAddr + index[i] + fn * 1, \
+ MMU.store<uint8_t>(baseAddr + index + fn * 1, \
P.VU.elt<uint8_t>(vs3 + fn * flmul, vreg_inx)); \
break; \
case e16: \
- MMU.store<uint16_t>(baseAddr + index[i] + fn * 2, \
+ MMU.store<uint16_t>(baseAddr + index + fn * 2, \
P.VU.elt<uint16_t>(vs3 + fn * flmul, vreg_inx)); \
break; \
case e32: \
- MMU.store<uint32_t>(baseAddr + index[i] + fn * 4, \
+ MMU.store<uint32_t>(baseAddr + index + fn * 4, \
P.VU.elt<uint32_t>(vs3 + fn * flmul, vreg_inx)); \
break; \
default: \
- MMU.store<uint64_t>(baseAddr + index[i] + fn * 8, \
+ MMU.store<uint64_t>(baseAddr + index + fn * 8, \
P.VU.elt<uint64_t>(vs3 + fn * flmul, vreg_inx)); \
break; \
} \