diff options
Diffstat (limited to 'riscv')
-rw-r--r-- | riscv/abstract_device.h | 1 | ||||
-rw-r--r-- | riscv/cfg.h | 2 | ||||
-rw-r--r-- | riscv/clint.cc | 10 | ||||
-rw-r--r-- | riscv/csr_init.cc | 60 | ||||
-rw-r--r-- | riscv/csrs.cc | 126 | ||||
-rw-r--r-- | riscv/csrs.h | 12 | ||||
-rw-r--r-- | riscv/debug_module.cc | 8 | ||||
-rw-r--r-- | riscv/debug_module.h | 5 | ||||
-rw-r--r-- | riscv/decode.h | 6 | ||||
-rw-r--r-- | riscv/decode_macros.h | 8 | ||||
-rw-r--r-- | riscv/devices.cc | 125 | ||||
-rw-r--r-- | riscv/devices.h | 36 | ||||
-rw-r--r-- | riscv/dts.cc | 1 | ||||
-rw-r--r-- | riscv/execute.cc | 15 | ||||
-rw-r--r-- | riscv/extension.cc | 14 | ||||
-rw-r--r-- | riscv/extension.h | 23 | ||||
-rw-r--r-- | riscv/interactive.cc | 27 | ||||
-rw-r--r-- | riscv/isa_parser.h | 2 | ||||
-rw-r--r-- | riscv/mmu.cc | 7 | ||||
-rw-r--r-- | riscv/mmu.h | 16 | ||||
-rw-r--r-- | riscv/ns16550.cc | 4 | ||||
-rw-r--r-- | riscv/plic.cc | 10 | ||||
-rw-r--r-- | riscv/processor.cc | 100 | ||||
-rw-r--r-- | riscv/processor.h | 6 | ||||
-rw-r--r-- | riscv/rocc.cc | 28 | ||||
-rw-r--r-- | riscv/rocc.h | 12 | ||||
-rw-r--r-- | riscv/sim.cc | 8 | ||||
-rw-r--r-- | riscv/sim.h | 7 | ||||
-rw-r--r-- | riscv/triggers.cc | 9 | ||||
-rw-r--r-- | riscv/triggers.h | 2 | ||||
-rw-r--r-- | riscv/v_ext_macros.h | 58 |
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; \ } \ |