update core wrapper: remove virtual memory support

This commit is contained in:
Stanislaw Kaushanski 2020-09-07 13:29:45 +02:00
parent 6f3963a473
commit 293c396a0d
2 changed files with 2 additions and 174 deletions

View File

@ -8,7 +8,7 @@ This library provide the infrastructure to build RISC-V ISS. Currently part of t
* RV32I (TGF01) * RV32I (TGF01)
* RV32MIC (TGF02) * 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. Last but not least an SystemC wrapper is provided which allows easy integration into SystemC based virtual platforms.

View File

@ -231,14 +231,6 @@ enum {
ISA_U = 1 << 20 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 { class trap_load_access_fault : public trap_access {
public: public:
trap_load_access_fault(uint64_t badaddr) trap_load_access_fault(uint64_t badaddr)
@ -249,28 +241,12 @@ public:
illegal_instruction_fault(uint64_t badaddr) illegal_instruction_fault(uint64_t badaddr)
: trap_access(2 << 16, 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 } // namespace
template <typename BASE> class riscv_hart_m_p : public BASE { template <typename BASE> class riscv_hart_m_p : public BASE {
public: public:
using super = BASE; using super = BASE;
using this_class = riscv_hart_m_p<BASE>; using this_class = riscv_hart_m_p<BASE>;
using virt_addr_t = typename super::virt_addr_t;
using phys_addr_t = typename super::phys_addr_t; using phys_addr_t = typename super::phys_addr_t;
using reg_t = typename super::reg_t; using reg_t = typename super::reg_t;
using addr_t = typename super::addr_t; using addr_t = typename super::addr_t;
@ -337,13 +313,7 @@ public:
static constexpr uint32_t get_mask() { static constexpr uint32_t get_mask() {
return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011 // only machine mode is supported 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() { constexpr reg_t get_irq_mask() {
return 0b101110111011; // only machine mode is supported return 0b101110111011; // only machine mode is supported
@ -356,8 +326,6 @@ public:
std::pair<uint64_t, bool> load_file(std::string name, int type = -1) override; std::pair<uint64_t, bool> 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, 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; 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, 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) 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 enter_trap(uint64_t flags, uint64_t addr) override;
virtual uint64_t leave_trap(uint64_t flags) 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; } const reg_t& get_mhartid() const { return mhartid_reg; }
void set_mhartid(reg_t mhartid) { mhartid_reg = mhartid; }; void set_mhartid(reg_t mhartid) { mhartid_reg = mhartid; };
@ -412,7 +379,6 @@ protected:
hart_state<reg_t> state; hart_state<reg_t> state;
uint64_t cycle_offset; uint64_t cycle_offset;
reg_t fault_data; reg_t fault_data;
std::array<vm_info, 2> vm;
uint64_t tohost = tohost_dflt; uint64_t tohost = tohost_dflt;
uint64_t fromhost = fromhost_dflt; uint64_t fromhost = fromhost_dflt;
unsigned to_host_wr_cnt = 0; unsigned to_host_wr_cnt = 0;
@ -423,7 +389,6 @@ protected:
using csr_page_type = typename csr_type::page_type; using csr_page_type = typename csr_type::page_type;
mem_type mem; mem_type mem;
csr_type csr; csr_type csr;
void update_vm_info();
std::stringstream uart_buf; std::stringstream uart_buf;
std::unordered_map<reg_t, uint64_t> ptw; std::unordered_map<reg_t, uint64_t> ptw;
std::unordered_map<uint64_t, uint8_t> atomic_reservation; std::unordered_map<uint64_t, uint8_t> atomic_reservation;
@ -439,10 +404,6 @@ private:
iss::status write_ie(unsigned addr, reg_t val); iss::status write_ie(unsigned addr, reg_t val);
iss::status read_ip(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 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); iss::status read_hartid(unsigned addr, reg_t &val);
reg_t mhartid_reg{0xF}; reg_t mhartid_reg{0xF};
@ -546,17 +507,6 @@ iss::status riscv_hart_m_p<BASE>::read(const address_type type, const access_typ
return iss::Err; return iss::Err;
} }
try { try {
if (unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary
vm_info vm = hart_state<reg_t>::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? auto res = type==iss::address_type::PHYSICAL?
read_mem( BASE::v2p(phys_addr_t{access, space, addr}), length, data): 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); read_mem( BASE::v2p(iss::addr_t{access, type, space, addr}), length, data);
@ -629,17 +579,6 @@ iss::status riscv_hart_m_p<BASE>::write(const address_type type, const access_ty
return iss::Err; return iss::Err;
} }
try { try {
if (unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary
vm_info vm = hart_state<reg_t>::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? auto res = type==iss::address_type::PHYSICAL?
write_mem(phys_addr_t{access, space, addr}, length, data): write_mem(phys_addr_t{access, space, addr}, length, data):
write_mem(BASE::v2p(iss::addr_t{access, type, space, addr}), length, data); write_mem(BASE::v2p(iss::addr_t{access, type, space, addr}), length, data);
@ -772,7 +711,6 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_status(unsigned
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_status(unsigned addr, reg_t val) { template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_status(unsigned addr, reg_t val) {
state.write_mstatus(val); state.write_mstatus(val);
check_interrupt(); check_interrupt();
update_vm_info();
return iss::Ok; return iss::Ok;
} }
@ -914,30 +852,15 @@ iss::status riscv_hart_m_p<BASE>::write_mem(phys_addr_t paddr, unsigned length,
template <typename BASE> inline void riscv_hart_m_p<BASE>::reset(uint64_t address) { template <typename BASE> inline void riscv_hart_m_p<BASE>::reset(uint64_t address) {
BASE::reset(address); BASE::reset(address);
state.mstatus = hart_state<reg_t>::mstatus_reset_val; state.mstatus = hart_state<reg_t>::mstatus_reset_val;
update_vm_info();
}
template <typename BASE> inline void riscv_hart_m_p<BASE>::update_vm_info() {
vm[1] = hart_state<reg_t>::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<reg_t>::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 <typename BASE> void riscv_hart_m_p<BASE>::check_interrupt() { template <typename BASE> void riscv_hart_m_p<BASE>::check_interrupt() {
auto status = state.mstatus;
auto ip = csr[mip];
auto ie = csr[mie];
auto ideleg = csr[mideleg]; auto ideleg = csr[mideleg];
// Multiple simultaneous interrupts and traps at the same privilege level are // Multiple simultaneous interrupts and traps at the same privilege level are
// handled in the following decreasing priority order: // handled in the following decreasing priority order:
// external interrupts, software interrupts, timer interrupts, then finally // external interrupts, software interrupts, timer interrupts, then finally
// any synchronous traps. // any synchronous traps.
auto ena_irq = ip & ie; auto ena_irq = csr[mip] & csr[mie];
bool mie = state.mstatus.MIE; bool mie = state.mstatus.MIE;
auto m_enabled = this->reg.machine_state < PRIV_M || (this->reg.machine_state == PRIV_M && mie); auto m_enabled = this->reg.machine_state < PRIV_M || (this->reg.machine_state == PRIV_M && mie);
@ -950,98 +873,6 @@ template <typename BASE> void riscv_hart_m_p<BASE>::check_interrupt() {
} }
} }
template <typename BASE>
typename riscv_hart_m_p<BASE>::phys_addr_t riscv_hart_m_p<BASE>::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<uint16_t>(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<BASE>::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<BASE>::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 <typename BASE> uint64_t riscv_hart_m_p<BASE>::enter_trap(uint64_t flags, uint64_t addr) { template <typename BASE> uint64_t riscv_hart_m_p<BASE>::enter_trap(uint64_t flags, uint64_t addr) {
// flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0] // flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0]
// calculate and write mcause val // calculate and write mcause val
@ -1085,7 +916,6 @@ template <typename BASE> uint64_t riscv_hart_m_p<BASE>::enter_trap(uint64_t flag
CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '" CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '"
<< (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" << cause << ")" << (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" << cause << ")"
<< " at address " << buffer.data() << " occurred"; << " at address " << buffer.data() << " occurred";
update_vm_info();
return this->reg.NEXT_PC; return this->reg.NEXT_PC;
} }
@ -1107,11 +937,9 @@ template <typename BASE> uint64_t riscv_hart_m_p<BASE>::leave_trap(uint64_t flag
// sets the pc to the value stored in the x epc register. // sets the pc to the value stored in the x epc register.
this->reg.NEXT_PC = csr[mepc]; this->reg.NEXT_PC = csr[mepc];
CLOG(INFO, disass) << "Executing xRET"; CLOG(INFO, disass) << "Executing xRET";
update_vm_info();
return this->reg.NEXT_PC; return this->reg.NEXT_PC;
} }
template <typename BASE> void riscv_hart_m_p<BASE>::wait_until(uint64_t flags) {}
} // namespace arch } // namespace arch
} // namespace iss } // namespace iss