update core wrapper: remove virtual memory support
This commit is contained in:
parent
6f3963a473
commit
293c396a0d
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue