diff --git a/README.md b/README.md index 12dfc0c..f8ea3af 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This library provide the infrastructure to build RISC-V ISS. Currently part of t * RV32I (TGF01) * RV32MIC (TGF02) -All pass the respective compliance tests. Along with those ISA implementations there is a wrapper implementing the M/S/U modes including virtual memory management and CSRs as of privileged spec 1.10. The main.cpp in src allows to build a stand-alone ISS when integrated into a top-level project. For further information please have a look at [https://git.minres.com/VP/RISCV-VP](https://git.minres.com/VP/RISCV-VP). +All pass the respective compliance tests. Along with those ISA implementations there is a wrapper (riscv_hart_m_p.h) implementing the Machine privileged mode as of privileged spec 1.10. The main.cpp in src allows to build a stand-alone ISS when integrated into a top-level project. For further information please have a look at [https://git.minres.com/VP/RISCV-VP](https://git.minres.com/VP/RISCV-VP). Last but not least an SystemC wrapper is provided which allows easy integration into SystemC based virtual platforms. diff --git a/incl/iss/arch/riscv_hart_m_p.h b/incl/iss/arch/riscv_hart_m_p.h index 7077506..ceb0058 100644 --- a/incl/iss/arch/riscv_hart_m_p.h +++ b/incl/iss/arch/riscv_hart_m_p.h @@ -231,14 +231,6 @@ enum { ISA_U = 1 << 20 }; -struct vm_info { - int levels; - int idxbits; - int ptesize; - uint64_t ptbase; - bool is_active() { return levels; } -}; - class trap_load_access_fault : public trap_access { public: trap_load_access_fault(uint64_t badaddr) @@ -249,28 +241,12 @@ public: illegal_instruction_fault(uint64_t badaddr) : trap_access(2 << 16, badaddr) {} }; -class trap_instruction_page_fault : public trap_access { -public: - trap_instruction_page_fault(uint64_t badaddr) - : trap_access(12 << 16, badaddr) {} -}; -class trap_load_page_fault : public trap_access { -public: - trap_load_page_fault(uint64_t badaddr) - : trap_access(13 << 16, badaddr) {} -}; -class trap_store_page_fault : public trap_access { -public: - trap_store_page_fault(uint64_t badaddr) - : trap_access(15 << 16, badaddr) {} -}; } // namespace template class riscv_hart_m_p : public BASE { public: using super = BASE; using this_class = riscv_hart_m_p; - using virt_addr_t = typename super::virt_addr_t; using phys_addr_t = typename super::phys_addr_t; using reg_t = typename super::reg_t; using addr_t = typename super::addr_t; @@ -337,13 +313,7 @@ public: static constexpr uint32_t get_mask() { return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011 // only machine mode is supported } - - static inline vm_info decode_vm_info() { - return {0, 0, 0, 0}; - } }; - const typename super::reg_t PGSIZE = 1 << PGSHIFT; - const typename super::reg_t PGMASK = PGSIZE - 1; constexpr reg_t get_irq_mask() { return 0b101110111011; // only machine mode is supported @@ -356,8 +326,6 @@ public: std::pair load_file(std::string name, int type = -1) override; - virtual phys_addr_t virt2phys(const iss::addr_t &addr) override; - iss::status read(const address_type type, const access_type access, const uint32_t space, const uint64_t addr, const unsigned length, uint8_t *const data) override; iss::status write(const address_type type, const access_type access, const uint32_t space, @@ -366,7 +334,6 @@ public: virtual uint64_t enter_trap(uint64_t flags) override { return riscv_hart_m_p::enter_trap(flags, fault_data); } virtual uint64_t enter_trap(uint64_t flags, uint64_t addr) override; virtual uint64_t leave_trap(uint64_t flags) override; - void wait_until(uint64_t flags) override; const reg_t& get_mhartid() const { return mhartid_reg; } void set_mhartid(reg_t mhartid) { mhartid_reg = mhartid; }; @@ -412,7 +379,6 @@ protected: hart_state state; uint64_t cycle_offset; reg_t fault_data; - std::array vm; uint64_t tohost = tohost_dflt; uint64_t fromhost = fromhost_dflt; unsigned to_host_wr_cnt = 0; @@ -423,7 +389,6 @@ protected: using csr_page_type = typename csr_type::page_type; mem_type mem; csr_type csr; - void update_vm_info(); std::stringstream uart_buf; std::unordered_map ptw; std::unordered_map atomic_reservation; @@ -439,10 +404,6 @@ private: iss::status write_ie(unsigned addr, reg_t val); iss::status read_ip(unsigned addr, reg_t &val); iss::status write_ip(unsigned addr, reg_t val); - iss::status read_satp(unsigned addr, reg_t &val); - iss::status write_satp(unsigned addr, reg_t val); - iss::status read_fcsr(unsigned addr, reg_t &val); - iss::status write_fcsr(unsigned addr, reg_t val); iss::status read_hartid(unsigned addr, reg_t &val); reg_t mhartid_reg{0xF}; @@ -546,17 +507,6 @@ iss::status riscv_hart_m_p::read(const address_type type, const access_typ return iss::Err; } try { - if (unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary - vm_info vm = hart_state::decode_vm_info(); - if (vm.levels != 0) { // VM is active - auto split_addr = (addr + length) & ~PGMASK; - auto len1 = split_addr - addr; - auto res = read(type, access, space, addr, len1, data); - if (res == iss::Ok) - res = read(type, access, space, split_addr, length - len1, data + len1); - return res; - } - } auto res = type==iss::address_type::PHYSICAL? read_mem( BASE::v2p(phys_addr_t{access, space, addr}), length, data): read_mem( BASE::v2p(iss::addr_t{access, type, space, addr}), length, data); @@ -629,17 +579,6 @@ iss::status riscv_hart_m_p::write(const address_type type, const access_ty return iss::Err; } try { - if (unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary - vm_info vm = hart_state::decode_vm_info(); - if (vm.levels != 0) { // VM is active - auto split_addr = (addr + length) & ~PGMASK; - auto len1 = split_addr - addr; - auto res = write(type, access, space, addr, len1, data); - if (res == iss::Ok) - res = write(type, access, space, split_addr, length - len1, data + len1); - return res; - } - } auto res = type==iss::address_type::PHYSICAL? write_mem(phys_addr_t{access, space, addr}, length, data): write_mem(BASE::v2p(iss::addr_t{access, type, space, addr}), length, data); @@ -772,7 +711,6 @@ template iss::status riscv_hart_m_p::read_status(unsigned template iss::status riscv_hart_m_p::write_status(unsigned addr, reg_t val) { state.write_mstatus(val); check_interrupt(); - update_vm_info(); return iss::Ok; } @@ -914,30 +852,15 @@ iss::status riscv_hart_m_p::write_mem(phys_addr_t paddr, unsigned length, template inline void riscv_hart_m_p::reset(uint64_t address) { BASE::reset(address); state.mstatus = hart_state::mstatus_reset_val; - update_vm_info(); -} - -template inline void riscv_hart_m_p::update_vm_info() { - vm[1] = hart_state::decode_vm_info(); - BASE::addr_mode[3]=BASE::addr_mode[2] = vm[1].is_active()? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL; - if (state.mstatus.MPRV) - vm[0] = hart_state::decode_vm_info(); - else - vm[0] = vm[1]; - BASE::addr_mode[1] = BASE::addr_mode[0]=vm[0].is_active() ? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL; - ptw.clear(); } template void riscv_hart_m_p::check_interrupt() { - auto status = state.mstatus; - auto ip = csr[mip]; - auto ie = csr[mie]; auto ideleg = csr[mideleg]; // Multiple simultaneous interrupts and traps at the same privilege level are // handled in the following decreasing priority order: // external interrupts, software interrupts, timer interrupts, then finally // any synchronous traps. - auto ena_irq = ip & ie; + auto ena_irq = csr[mip] & csr[mie]; bool mie = state.mstatus.MIE; auto m_enabled = this->reg.machine_state < PRIV_M || (this->reg.machine_state == PRIV_M && mie); @@ -950,98 +873,6 @@ template void riscv_hart_m_p::check_interrupt() { } } -template -typename riscv_hart_m_p::phys_addr_t riscv_hart_m_p::virt2phys(const iss::addr_t &addr) { - const auto type = addr.access & iss::access_type::FUNC; - auto it = ptw.find(addr.val >> PGSHIFT); - if (it != ptw.end()) { - const reg_t pte = it->second; - const reg_t ad = PTE_A | (type == iss::access_type::WRITE) * PTE_D; -#ifdef RISCV_ENABLE_DIRTY - // set accessed and possibly dirty bits. - *(uint32_t *)ppte |= ad; - return {addr.getAccessType(), addr.space, (pte & (~PGMASK)) | (addr.val & PGMASK)}; -#else - // take exception if access or possibly dirty bit is not set. - if ((pte & ad) == ad) - return {addr.access, addr.space, (pte & (~PGMASK)) | (addr.val & PGMASK)}; - else - ptw.erase(it); // throw an exception -#endif - } else { - uint32_t mode = type != iss::access_type::FETCH && state.mstatus.MPRV ? // MPRV - state.mstatus.MPP : - this->reg.machine_state; - - const vm_info &vm = this->vm[static_cast(type) / 2]; - - const bool sum = state.mstatus.SUM; - const bool mxr = state.mstatus.MXR; - - // verify bits xlen-1:va_bits-1 are all equal - const int va_bits = PGSHIFT + vm.levels * vm.idxbits; - const reg_t mask = (reg_t(1) << (traits::XLEN > -(va_bits - 1))) - 1; - const reg_t masked_msbs = (addr.val >> (va_bits - 1)) & mask; - const int levels = (masked_msbs != 0 && masked_msbs != mask) ? 0 : vm.levels; - - reg_t base = vm.ptbase; - for (int i = levels - 1; i >= 0; i--) { - const int ptshift = i * vm.idxbits; - const reg_t idx = (addr.val >> (PGSHIFT + ptshift)) & ((1 << vm.idxbits) - 1); - - // check that physical address of PTE is legal - reg_t pte = 0; - const uint8_t res = this->read(iss::address_type::PHYSICAL, addr.access, - traits::MEM, base + idx * vm.ptesize, vm.ptesize, (uint8_t *)&pte); - if (res != 0) throw trap_load_access_fault(addr.val); - const reg_t ppn = pte >> PTE_PPN_SHIFT; - - if (PTE_TABLE(pte)) { // next level of page table - base = ppn << PGSHIFT; - } else if (!(pte & PTE_U)) { - break; - } else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) { - break; - } else if (type == iss::access_type::FETCH - ? !(pte & PTE_X) - : type == iss::access_type::READ ? !(pte & PTE_R) && !(mxr && (pte & PTE_X)) - : !((pte & PTE_R) && (pte & PTE_W))) { - break; - } else if ((ppn & ((reg_t(1) << ptshift) - 1)) != 0) { - break; - } else { - const reg_t ad = PTE_A | ((type == iss::access_type::WRITE) * PTE_D); -#ifdef RISCV_ENABLE_DIRTY - // set accessed and possibly dirty bits. - *(uint32_t *)ppte |= ad; -#else - // take exception if access or possibly dirty bit is not set. - if ((pte & ad) != ad) break; -#endif - // for superpage mappings, make a fake leaf PTE for the TLB's benefit. - const reg_t vpn = addr.val >> PGSHIFT; - const reg_t value = (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT; - const reg_t offset = addr.val & PGMASK; - ptw[vpn] = value | (pte & 0xff); - return {addr.access, addr.space, value | offset}; - } - } - } - switch (type) { - case access_type::FETCH: - this->fault_data = addr.val; - throw trap_instruction_page_fault(addr.val); - case access_type::READ: - this->fault_data = addr.val; - throw trap_load_page_fault(addr.val); - case access_type::WRITE: - this->fault_data = addr.val; - throw trap_store_page_fault(addr.val); - default: - abort(); - } -} - template uint64_t riscv_hart_m_p::enter_trap(uint64_t flags, uint64_t addr) { // flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0] // calculate and write mcause val @@ -1085,7 +916,6 @@ template uint64_t riscv_hart_m_p::enter_trap(uint64_t flag CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '" << (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" << cause << ")" << " at address " << buffer.data() << " occurred"; - update_vm_info(); return this->reg.NEXT_PC; } @@ -1107,11 +937,9 @@ template uint64_t riscv_hart_m_p::leave_trap(uint64_t flag // sets the pc to the value stored in the x epc register. this->reg.NEXT_PC = csr[mepc]; CLOG(INFO, disass) << "Executing xRET"; - update_vm_info(); return this->reg.NEXT_PC; } -template void riscv_hart_m_p::wait_until(uint64_t flags) {} } // namespace arch } // namespace iss