moves mmu related code into mmu unit

This commit is contained in:
Eyck Jentzsch 2025-03-15 09:34:19 +01:00
parent e238369e18
commit 54233b448d
19 changed files with 683 additions and 598 deletions

View File

@ -18,7 +18,7 @@ add_subdirectory(softfloat)
set(LIB_SOURCES
src/iss/plugin/instruction_count.cpp
src/iss/arch/tgc5c.cpp
src/iss/mmio/memory_if.cpp
src/iss/mem/memory_if.cpp
src/vm/interp/vm_tgc5c.cpp
src/vm/fp_functions.cpp
src/iss/debugger/csr_names.cpp

View File

@ -37,7 +37,6 @@
#include "iss/arch/traits.h"
#include "iss/log_categories.h"
#include "iss/mmio/memory_if.h"
#include "iss/vm_types.h"
#include "mstatus.h"
#include "util/delegate.h"
@ -54,6 +53,7 @@
#include <unordered_map>
#include <util/logging.h>
#include <util/sparse_array.h>
#include "../mem/memory_if.h"
#if defined(__GNUC__)
#define likely(x) ::__builtin_expect(!!(x), 1)
@ -198,23 +198,6 @@ enum riscv_csr {
dscratch1 = 0x7B3
};
enum {
PGSHIFT = 12,
PTE_PPN_SHIFT = 10,
// page table entry (PTE) fields
PTE_V = 0x001, // Valid
PTE_R = 0x002, // Read
PTE_W = 0x004, // Write
PTE_X = 0x008, // Execute
PTE_U = 0x010, // User
PTE_G = 0x020, // Global
PTE_A = 0x040, // Accessed
PTE_D = 0x080, // Dirty
PTE_SOFT = 0x300 // Reserved for Software
};
template <typename T> inline bool PTE_TABLE(T PTE) { return (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V); }
enum { PRIV_U = 0, PRIV_S = 1, PRIV_M = 3, PRIV_D = 4 };
enum {
@ -233,21 +216,6 @@ enum {
ISA_U = 1 << 20
};
struct vm_info {
int levels;
int idxbits;
int ptesize;
uint64_t ptbase;
bool is_active() { return levels; }
};
struct feature_config {
uint64_t tcm_base{0x10000000};
uint64_t tcm_size{0x8000};
uint64_t io_address{0xf0000000};
uint64_t io_addr_mask{0xf0000000};
};
class trap_load_access_fault : public trap_access {
public:
trap_load_access_fault(uint64_t badaddr)
@ -281,15 +249,18 @@ template <typename WORD_TYPE> struct priv_if {
std::function<iss::status(unsigned, WORD_TYPE&)> read_csr;
std::function<iss::status(unsigned, WORD_TYPE)> write_csr;
std::function<iss::status(uint8_t const*)> exec_htif;
std::function<void(uint16_t,uint16_t,WORD_TYPE)> raise_trap; // trap_id, cause, fault_data
std::unordered_map<unsigned, rd_csr_f>& csr_rd_cb;
std::unordered_map<unsigned, wr_csr_f>& csr_wr_cb;
hart_state<WORD_TYPE>& mstatus;
hart_state<WORD_TYPE>& state;
uint8_t& PRIV;
WORD_TYPE& PC;
uint64_t& tohost;
uint64_t& fromhost;
unsigned& mcause_max_irq;
unsigned& max_irq;
};
template <typename BASE, typename LOGCAT = logging::disass> struct riscv_hart_common : public BASE, public mmio::memory_elem {
template <typename BASE, typename LOGCAT = logging::disass> struct riscv_hart_common : public BASE, public mem::memory_elem {
const std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
const std::array<const char*, 16> trap_str = {{""
"Instruction address misaligned", // 0
@ -718,12 +689,19 @@ template <typename BASE, typename LOGCAT = logging::disass> struct riscv_hart_co
return priv_if<reg_t>{.read_csr = [this](unsigned addr, reg_t& val) -> iss::status { return read_csr(addr, val); },
.write_csr = [this](unsigned addr, reg_t val) -> iss::status { return write_csr(addr, val); },
.exec_htif = [this](uint8_t const* data) -> iss::status { return execute_htif(data); },
.raise_trap = [this](uint16_t trap_id, uint16_t cause, reg_t fault_data){
this->reg.trap_state = 0x80ULL << 24 | (cause << 16) | trap_id;
this->fault_data = fault_data;
},
.csr_rd_cb{this->csr_rd_cb},
.csr_wr_cb{csr_wr_cb},
.mstatus{this->state},
.state{this->state},
.PRIV{this->reg.PRIV},
.PC{this->reg.PC},
.tohost{this->tohost},
.fromhost{this->fromhost},
.mcause_max_irq{mcause_max_irq}};
.max_irq{mcause_max_irq}};
}
iss::status execute_htif(uint8_t const* data) {
@ -763,14 +741,14 @@ template <typename BASE, typename LOGCAT = logging::disass> struct riscv_hart_co
}
}
mmio::memory_hierarchy memories;
mem::memory_hierarchy memories;
virtual mmio::memory_if get_mem_if() override {
virtual mem::memory_if get_mem_if() override {
assert(false || "This function should nevver be called");
return mmio::memory_if{};
return mem::memory_if{};
}
virtual void set_next(mmio::memory_if mem_if) { memory = mem_if; };
virtual void set_next(mem::memory_if mem_if) { memory = mem_if; };
void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); }
@ -789,7 +767,7 @@ protected:
}
}
mmio::memory_if memory;
mem::memory_if memory;
struct riscv_instrumentation_if : public iss::instrumentation_if {
riscv_instrumentation_if(riscv_hart_common<BASE, LOGCAT>& arch)
@ -846,7 +824,6 @@ protected:
int64_t cycle_offset{0};
int64_t instret_offset{0};
semihosting_cb_t<reg_t> semihosting_cb;
std::array<vm_info, 2> vm;
unsigned mcause_max_irq{16U};
};

View File

@ -48,7 +48,7 @@
#include <array>
#include <elfio/elfio.hpp>
#include <fmt/format.h>
#include <iss/mmio/memory_with_htif.h>
#include "../mem/memory_with_htif.h"
#include <unordered_map>
namespace iss {
@ -111,7 +111,7 @@ public:
return 0b100010001000; // only machine mode is supported
}
riscv_hart_m_p(feature_config cfg = feature_config{});
riscv_hart_m_p();
virtual ~riscv_hart_m_p() = default;
@ -128,15 +128,12 @@ public:
void set_csr(unsigned addr, reg_t val) { this->csr[addr & this->csr.page_addr_mask] = val; }
void set_num_of_irq(unsigned i) { this->mcause_max_irq = 1 << util::ilog2(i); }
protected:
using mem_read_f = iss::status(phys_addr_t addr, unsigned, uint8_t* const);
using mem_write_f = iss::status(phys_addr_t addr, unsigned, uint8_t const* const);
hart_state<reg_t> state;
std::unordered_map<reg_t, uint64_t> ptw;
std::unordered_map<uint64_t, uint8_t> atomic_reservation;
iss::status read_status(unsigned addr, reg_t& val);
@ -154,18 +151,20 @@ protected:
iss::status write_dpc(unsigned addr, reg_t val);
void check_interrupt();
feature_config cfg;
mmio::memory_with_htif<reg_t> default_mem;
mem::memory_with_htif<reg_t> default_mem;
};
template <typename BASE, features_e FEAT, typename LOGCAT>
riscv_hart_m_p<BASE, FEAT, LOGCAT>::riscv_hart_m_p(feature_config cfg)
: cfg(cfg)
, default_mem(base::get_priv_if()) {
const std::array<unsigned, 4> rwaddrs{{mepc, mtvec, mscratch, mtval}};
riscv_hart_m_p<BASE, FEAT, LOGCAT>::riscv_hart_m_p()
: default_mem(base::get_priv_if()) {
const std::array<unsigned, 4> rwaddrs{{
mepc,
mtvec,
mscratch,
mtval
}};
for(auto addr : rwaddrs) {
this->csr_rd_cb[addr] = MK_CSR_RD_CB(read_plain);
// MK_CSR_RD_CB(read_plain(a,r);};
this->csr_wr_cb[addr] = MK_CSR_WR_CB(write_plain);
}
this->csr_rd_cb[mstatus] = MK_CSR_RD_CB(read_status);
@ -195,7 +194,7 @@ riscv_hart_m_p<BASE, FEAT, LOGCAT>::riscv_hart_m_p(feature_config cfg)
}
this->rd_func = util::delegate<arch_if::rd_func_sig>::from<this_class, &this_class::read>(this);
this->wr_func = util::delegate<arch_if::wr_func_sig>::from<this_class, &this_class::write>(this);
this->memories.prepend(*this);
this->memories.root(*this);
this->memories.append(default_mem);
}
@ -300,7 +299,7 @@ iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::write(const address_type type, c
try {
switch(space) {
case traits<BASE>::MEM: {
if(unlikely((access && iss::access_type::FETCH) && (addr & 0x1) == 1)) {
if(unlikely(is_fetch(access) && (addr & 0x1) == 1)) {
this->fault_data = addr;
if(access && iss::access_type::DEBUG)
throw trap_access(0, addr);
@ -403,15 +402,13 @@ template <typename BASE, features_e FEAT, typename LOGCAT> inline void riscv_har
}
template <typename BASE, features_e FEAT, typename LOGCAT> void riscv_hart_m_p<BASE, FEAT, LOGCAT>::check_interrupt() {
// TODO: Implement CLIC functionality
// 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 = this->csr[mip] & this->csr[mie];
bool mstatus_mie = this->state.mstatus.MIE;
bool mstatus_mie = state.mstatus.MIE;
auto m_enabled = this->reg.PRIV < PRIV_M || mstatus_mie;
auto enabled_interrupts = m_enabled ? ena_irq : 0;
@ -525,9 +522,6 @@ uint64_t riscv_hart_m_p<BASE, FEAT, LOGCAT>::enter_trap(uint64_t flags, uint64_t
if((xtvec & 0x1) == 1 && trap_id != 0)
this->reg.NEXT_PC += 4 * cause;
}
// reset trap state
this->reg.PRIV = new_priv;
this->reg.trap_state = 0;
std::array<char, 32> buffer;
#if defined(_MSC_VER)
sprintf(buffer.data(), "0x%016llx", addr);
@ -538,6 +532,9 @@ uint64_t riscv_hart_m_p<BASE, FEAT, LOGCAT>::enter_trap(uint64_t flags, uint64_t
NSCLOG(INFO, LOGCAT) << (trap_id ? "Interrupt" : "Trap") << " with cause '"
<< (trap_id ? this->irq_str[cause] : this->trap_str[cause]) << "' (" << cause << ")"
<< " at address " << buffer.data() << " occurred";
// reset trap state
this->reg.PRIV = new_priv;
this->reg.trap_state = 0;
return this->reg.NEXT_PC;
}

View File

@ -41,22 +41,23 @@
#include "util/logging.h"
#include <algorithm>
#include <cstdint>
#include <array>
#include <elfio/elf_types.hpp>
#include <elfio/elfio.hpp>
#include <limits>
#ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY
#endif
#include <array>
#include <elfio/elfio.hpp>
#include <fmt/format.h>
#include <type_traits>
#include <unordered_map>
#include <util/bit_field.h>
#include "../mem/mmu.h"
#include "../mem/memory_with_htif.h"
namespace iss {
namespace arch {
template <typename BASE> class riscv_hart_msu_vp : public riscv_hart_common<BASE> {
template <typename BASE, features_e FEAT = FEAT_NONE, typename LOGCAT = logging::disass> class riscv_hart_msu_vp : public riscv_hart_common<BASE> {
public:
using core = BASE;
using base = riscv_hart_common<BASE>;
@ -65,10 +66,6 @@ public:
using reg_t = typename core::reg_t;
using addr_t = typename core::addr_t;
static constexpr reg_t get_misa() {
return (sizeof(reg_t) == 4 ? (1UL << 30) : (2ULL << 62)) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M;
}
static constexpr reg_t get_mstatus_mask(unsigned priv_lvl) {
if(sizeof(reg_t) == 4) {
#if __cplusplus < 201402L
@ -76,21 +73,21 @@ public:
#else
switch(priv_lvl) {
case PRIV_U:
return 0x80000011UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001
return 0x80000000UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001
case PRIV_S:
return 0x800de133UL; // 0b1000 0000 0000 1101 1110 0001 0011 0011
return 0x800de122UL; // 0b1000 0000 0000 1101 1110 0001 0011 0011
default:
return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011
return 0x807ff9aaUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011
}
#endif
} else if(sizeof(reg_t) == 8) {
switch(priv_lvl) {
case PRIV_U:
return 0x8000000f00000011ULL; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011
return 0x8000000f00000000ULL; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011
case PRIV_S:
return 0x8000000f000de133ULL; // 0b1...0 0011 0000 0000 0000 1101 1110 0001 0011 0011
return 0x8000000f000de122ULL; // 0b1...0 0011 0000 0000 0000 1101 1110 0001 0011 0011
default:
return 0x8000000f007ff9ddULL; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011
return 0x8000000f007ff9aaULL; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011
}
} else
assert(false && "Unsupported XLEN value");
@ -117,48 +114,6 @@ public:
}
this->state.mstatus = new_val;
}
reg_t satp;
static inline vm_info decode_vm_info(uint32_t state, uint32_t sptbr) {
if(state == PRIV_M)
return {0, 0, 0, 0};
if(state <= PRIV_S)
switch(bit_sub<31, 1>(sptbr)) {
case 0:
return {0, 0, 0, 0}; // off
case 1:
return {2, 10, 4, bit_sub<0, 22>(sptbr) << PGSHIFT}; // SV32
default:
abort();
}
abort();
return {0, 0, 0, 0}; // dummy
}
static inline vm_info decode_vm_info(uint32_t state, uint64_t sptbr) {
if(state == PRIV_M)
return {0, 0, 0, 0};
if(state <= PRIV_S)
switch(bit_sub<60, 4>(sptbr)) {
case 0:
return {0, 0, 0, 0}; // off
case 8:
return {3, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV39
case 9:
return {4, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV48
case 10:
return {5, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV57
case 11:
return {6, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV64
default:
abort();
}
abort();
return {0, 0, 0, 0}; // dummy
}
const typename core::reg_t PGSIZE = 1 << PGSHIFT;
const typename core::reg_t PGMASK = PGSIZE - 1;
constexpr reg_t get_irq_mask(size_t mode) {
std::array<const reg_t, 4> m = {{
@ -176,8 +131,6 @@ public:
void reset(uint64_t address) override;
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);
iss::status write(const address_type type, const access_type access, const uint32_t space, const uint64_t addr, const unsigned length,
@ -190,22 +143,14 @@ public:
void set_csr(unsigned addr, reg_t val) { this->csr[addr & this->csr.page_addr_mask] = val; }
void set_irq_num(unsigned i) { this->mcause_max_irq = 1 << util::ilog2(i); }
protected:
virtual iss::status read_mem(phys_addr_t addr, unsigned length, uint8_t* const data);
virtual iss::status write_mem(phys_addr_t addr, unsigned length, const uint8_t* const data);
using mem_read_f = iss::status(phys_addr_t addr, unsigned, uint8_t* const);
using mem_write_f = iss::status(phys_addr_t addr, unsigned, uint8_t const* const);
hart_state<reg_t> state;
using mem_type = util::sparse_array<uint8_t, 1ULL << 32>;
mem_type mem;
void update_vm_info();
std::unordered_map<reg_t, uint64_t> ptw;
std::unordered_map<uint64_t, uint8_t> atomic_reservation;
std::vector<uint8_t> tcm;
iss::status read_status(unsigned addr, reg_t& val);
iss::status write_status(unsigned addr, reg_t val);
iss::status write_cause(unsigned addr, reg_t val);
@ -214,29 +159,37 @@ protected:
iss::status read_ip(unsigned addr, reg_t& val);
iss::status write_ideleg(unsigned addr, reg_t val);
iss::status write_edeleg(unsigned addr, reg_t val);
iss::status read_satp(unsigned addr, reg_t& val);
iss::status write_satp(unsigned addr, reg_t val);
virtual iss::status read_custom_csr_reg(unsigned addr, reg_t& val) { return iss::status::Err; };
virtual iss::status write_custom_csr_reg(unsigned addr, reg_t val) { return iss::status::Err; };
void register_custom_csr_rd(unsigned addr) { this->csr_rd_cb[addr] = MK_CSR_RD_CB(read_custom_csr_reg); }
void register_custom_csr_wr(unsigned addr) { this->csr_wr_cb[addr] = MK_CSR_WR_CB(write_custom_csr_reg); }
reg_t mhartid_reg{0x0};
void check_interrupt();
mem::mmu<reg_t> mmu;
mem::memory_with_htif<reg_t> default_mem;
};
template <typename BASE>
riscv_hart_msu_vp<BASE>::riscv_hart_msu_vp()
: state() {
this->mmu = true;
this->rd_func = util::delegate<arch_if::rd_func_sig>::from<this_class, &this_class::read>(this);
this->rd_func = util::delegate<arch_if::wr_func_sig>::from<this_class, &this_class::write>(this);
template <typename BASE, features_e FEAT, typename LOGCAT>
riscv_hart_msu_vp<BASE, FEAT, LOGCAT>::riscv_hart_msu_vp()
: state()
, mmu(base::get_priv_if())
, default_mem(base::get_priv_if()) {
// common regs
const std::array<unsigned, 17> rwaddrs{{mepc, mtvec, mscratch, mtval, mscratch, sepc, stvec, sscratch, scause, stval, sscratch, uepc,
utvec, uscratch, ucause, utval, uscratch}};
const std::array<unsigned, 17> rwaddrs{{
mepc,
mtvec,
mscratch,
mtval,
mscratch,
sepc,
stvec,
sscratch,
scause,
stval,
sscratch,
uepc,
utvec,
uscratch,
ucause,
utval,
uscratch
}};
for(auto addr : rwaddrs) {
this->csr_rd_cb[addr] = MK_CSR_RD_CB(read_plain);
this->csr_wr_cb[addr] = MK_CSR_WR_CB(write_plain);
@ -272,17 +225,32 @@ riscv_hart_msu_vp<BASE>::riscv_hart_msu_vp()
this->csr_wr_cb[mvendorid] = MK_CSR_WR_CB(write_null);
this->csr_wr_cb[marchid] = MK_CSR_WR_CB(write_null);
this->csr_wr_cb[mimpid] = MK_CSR_WR_CB(write_null);
this->csr_rd_cb[satp] = MK_CSR_RD_CB(read_satp);
this->csr_wr_cb[satp] = MK_CSR_WR_CB(write_satp);
this->rd_func = util::delegate<arch_if::rd_func_sig>::from<this_class, &this_class::read>(this);
this->wr_func = util::delegate<arch_if::wr_func_sig>::from<this_class, &this_class::write>(this);
if(FEAT & FEAT_DEBUG) {
this->csr_wr_cb[dscratch0] = MK_CSR_WR_CB(write_dscratch);
this->csr_rd_cb[dscratch0] = MK_CSR_RD_CB(read_debug);
this->csr_wr_cb[dscratch1] = MK_CSR_WR_CB(write_dscratch);
this->csr_rd_cb[dscratch1] = MK_CSR_RD_CB(read_debug);
this->csr_wr_cb[dpc] = MK_CSR_WR_CB(write_dpc);
this->csr_rd_cb[dpc] = MK_CSR_RD_CB(read_dpc);
this->csr_wr_cb[dcsr] = MK_CSR_WR_CB(write_dcsr);
this->csr_rd_cb[dcsr] = MK_CSR_RD_CB(read_debug);
}
this->rd_func = util::delegate<arch_if::rd_func_sig>::from<this_class, &this_class::read>(this);
this->wr_func = util::delegate<arch_if::wr_func_sig>::from<this_class, &this_class::write>(this);
this->set_next(mmu.get_mem_if());
this->memories.root(mmu);
this->memories.append(default_mem);
}
template <typename BASE>
iss::status riscv_hart_msu_vp<BASE>::read(const address_type type, const access_type access, const uint32_t space, const uint64_t addr,
const unsigned length, uint8_t* const data) {
template <typename BASE, features_e FEAT, typename LOGCAT>
iss::status riscv_hart_msu_vp<BASE, FEAT, LOGCAT>::read(const address_type type, const access_type access, const uint32_t space,
const uint64_t addr, const unsigned length, uint8_t* const data) {
#ifndef NDEBUG
if(access && iss::access_type::DEBUG) {
CPPLOG(TRACEALL) << "debug read of " << length << " bytes @addr 0x" << std::hex << addr;
} else if(access && iss::access_type::FETCH) {
} else if(is_fetch(access)) {
CPPLOG(TRACEALL) << "fetch of " << length << " bytes @addr 0x" << std::hex << addr;
} else {
CPPLOG(TRACE) << "read of " << length << " bytes @addr 0x" << std::hex << addr;
@ -294,31 +262,20 @@ iss::status riscv_hart_msu_vp<BASE>::read(const address_type type, const access_
auto alignment = is_fetch(access) ? (this->has_compressed() ? 2 : 4) : std::min<unsigned>(length, sizeof(reg_t));
if(unlikely(is_fetch(access) && (addr & (alignment - 1)))) {
this->fault_data = addr;
if(access && iss::access_type::DEBUG)
if(is_debug(access))
throw trap_access(0, addr);
this->reg.trap_state = (1 << 31); // issue trap 0
this->reg.trap_state = (1UL<< 31); // issue trap 0
return iss::Err;
}
try {
if(!is_debug(access) && (addr & (alignment - 1))) {
this->reg.trap_state = 1 << 31 | 4 << 16;
this->reg.trap_state = (1UL << 31) | 4 << 16;
this->fault_data = addr;
return iss::Err;
}
if(unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary
vm_info vm = decode_vm_info(this->reg.PRIV, satp);
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 = read_mem(virt2phys(iss::addr_t{access, type, space, addr}), length, data);
auto res = this->memory.rd_mem(access, addr, length, data);
if(unlikely(res != iss::Ok && (access & access_type::DEBUG) == 0)) {
this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault
this->reg.trap_state = (1UL << 31) | (5 << 16); // issue trap 5 (load access fault
this->fault_data = addr;
}
return res;
@ -336,8 +293,6 @@ iss::status riscv_hart_msu_vp<BASE>::read(const address_type type, const access_
return this->read_csr(addr, *reinterpret_cast<reg_t* const>(data));
} break;
case traits<BASE>::FENCE: {
if((addr + length) > mem.size())
return iss::Err;
switch(addr) {
case 2: // SFENCE:VMA lower
case 3: { // SFENCE:VMA upper
@ -372,8 +327,8 @@ iss::status riscv_hart_msu_vp<BASE>::read(const address_type type, const access_
}
}
template <typename BASE>
iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access_type access, const uint32_t space, const uint64_t addr,
template <typename BASE, features_e FEAT, typename LOGCAT>
iss::status riscv_hart_msu_vp<BASE, FEAT, LOGCAT>::write(const address_type type, const access_type access, const uint32_t space, const uint64_t addr,
const unsigned length, const uint8_t* const data) {
#ifndef NDEBUG
const char* prefix = (access && iss::access_type::DEBUG) ? "debug " : "";
@ -401,29 +356,22 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
try {
switch(space) {
case traits<BASE>::MEM: {
if(unlikely((access && iss::access_type::FETCH) && (addr & 0x1) == 1)) {
if(unlikely(is_fetch(access) && (addr & 0x1) == 1)) {
this->fault_data = addr;
if(access && iss::access_type::DEBUG)
throw trap_access(0, addr);
this->reg.trap_state = (1 << 31); // issue trap 0
this->reg.trap_state = (1UL << 31); // issue trap 0
return iss::Err;
}
phys_addr_t paddr = virt2phys(iss::addr_t{access, type, space, addr});
try {
// TODO: There is no check for alignment
if(unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary
vm_info vm = decode_vm_info(this->reg.PRIV, satp);
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 alignment = std::min<unsigned>(length, sizeof(reg_t));
if(length > 1 && (addr & (alignment - 1)) && !is_debug(access)) {
this->reg.trap_state = (1UL << 31) | 6 << 16;
this->fault_data = addr;
return iss::Err;
}
auto res = write_mem(paddr, length, data);
if(unlikely(res != iss::Ok && (access & access_type::DEBUG) == 0)) {
auto res = this->memory.wr_mem(access, addr, length, data);
if(unlikely(res != iss::Ok && !is_debug(access))) {
this->reg.trap_state = (1UL << 31) | (7UL << 16); // issue trap 7 (Store/AMO access fault)
this->fault_data = addr;
}
@ -440,12 +388,9 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
return this->write_csr(addr, *reinterpret_cast<const reg_t*>(data));
} break;
case traits<BASE>::FENCE: {
if((addr + length) > mem.size())
return iss::Err;
switch(addr) {
case 2:
case 3: {
ptw.clear();
auto tvm = state.mstatus.TVM;
if(this->reg.PRIV == PRIV_S & tvm != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
@ -472,26 +417,25 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
}
}
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_status(unsigned addr, reg_t& val) {
template <typename BASE, features_e FEAT, typename LOGCAT> iss::status riscv_hart_msu_vp<BASE, FEAT, LOGCAT>::read_status(unsigned addr, reg_t& val) {
auto req_priv_lvl = (addr >> 8) & 0x3;
val = state.mstatus & get_mstatus_mask(req_priv_lvl);
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_status(unsigned addr, reg_t val) {
template <typename BASE, features_e FEAT, typename LOGCAT> iss::status riscv_hart_msu_vp<BASE, FEAT, LOGCAT>::write_status(unsigned addr, reg_t val) {
auto req_priv_lvl = (addr >> 8) & 0x3;
write_mstatus(val, req_priv_lvl);
check_interrupt();
update_vm_info();
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_cause(unsigned addr, reg_t val) {
template <typename BASE, features_e FEAT, typename LOGCAT> iss::status riscv_hart_msu_vp<BASE, FEAT, LOGCAT>::write_cause(unsigned addr, reg_t val) {
this->csr[addr] = val & ((1UL << (traits<BASE>::XLEN - 1)) | 0xf); // TODO: make exception code size configurable
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_ie(unsigned addr, reg_t& val) {
template <typename BASE, features_e FEAT, typename LOGCAT> iss::status riscv_hart_msu_vp<BASE, FEAT, LOGCAT>::read_ie(unsigned addr, reg_t& val) {
val = this->csr[mie];
if(addr < mie)
val &= this->csr[mideleg];
@ -500,7 +444,7 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_ie(unsigned a
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_ie(unsigned addr, reg_t val) {
template <typename BASE, features_e FEAT, typename LOGCAT> iss::status riscv_hart_msu_vp<BASE, FEAT, LOGCAT>::write_ie(unsigned addr, reg_t val) {
auto req_priv_lvl = (addr >> 8) & 0x3;
auto mask = get_irq_mask(req_priv_lvl);
this->csr[mie] = (this->csr[mie] & ~mask) | (val & mask);
@ -508,7 +452,7 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_ie(unsigned
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_ip(unsigned addr, reg_t& val) {
template <typename BASE, features_e FEAT, typename LOGCAT> iss::status riscv_hart_msu_vp<BASE, FEAT, LOGCAT>::read_ip(unsigned addr, reg_t& val) {
val = this->csr[mip];
if(addr < mip)
val &= this->csr[mideleg];
@ -517,111 +461,12 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_ip(unsigned a
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_satp(unsigned addr, reg_t& val) {
reg_t tvm = state.mstatus.TVM;
if(this->reg.PRIV == PRIV_S & tvm != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
this->fault_data = this->reg.PC;
return iss::Err;
}
val = satp;
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_satp(unsigned addr, reg_t val) {
reg_t tvm = state.mstatus.TVM;
if(this->reg.PRIV == PRIV_S & tvm != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
this->fault_data = this->reg.PC;
return iss::Err;
}
satp = val;
update_vm_info();
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_mem(phys_addr_t paddr, unsigned length, uint8_t* const data) {
switch(paddr.val) {
default: {
for(auto offs = 0U; offs < length; ++offs) {
*(data + offs) = mem[(paddr.val + offs) % mem.size()];
}
}
}
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t* const data) {
mem_type::page_type& p = mem(paddr.val / mem.page_size);
std::copy(data, data + length, p.data() + (paddr.val & mem.page_addr_mask));
// tohost handling in case of riscv-test
// according to https://github.com/riscv-software-src/riscv-isa-sim/issues/364#issuecomment-607657754:
if(paddr.access && iss::access_type::FUNC) {
if(paddr.val == this->tohost) {
reg_t cur_data = *reinterpret_cast<const reg_t*>(data);
// Extract Device (bits 63:56)
uint8_t device = traits<BASE>::XLEN == 32
? *reinterpret_cast<uint32_t*>(p.data() + ((this->tohost + 4) & mem.page_addr_mask)) >> 24
: (cur_data >> 56) & 0xFF;
// Extract Command (bits 55:48)
uint8_t command = traits<BASE>::XLEN == 32
? *reinterpret_cast<uint32_t*>(p.data() + ((this->tohost + 4) & mem.page_addr_mask)) >> 16
: (cur_data >> 48) & 0xFF;
// Extract payload (bits 47:0)
uint64_t payload_addr = cur_data & 0xFFFFFFFFFFFFULL;
if(payload_addr & 1) {
CPPLOG(FATAL) << "tohost value is 0x" << std::hex << payload_addr << std::dec << " (" << payload_addr
<< "), stopping simulation";
this->reg.trap_state = std::numeric_limits<uint32_t>::max();
this->interrupt_sim = payload_addr;
return iss::Ok;
} else if(device == 0 && command == 0) {
std::array<uint64_t, 8> loaded_payload;
if(read(address_type::PHYSICAL, access_type::DEBUG_READ, traits<BASE>::MEM, payload_addr, 8 * sizeof(uint64_t),
reinterpret_cast<uint8_t*>(loaded_payload.data())) == iss::Err)
CPPLOG(ERR) << "Syscall read went wrong";
uint64_t syscall_num = loaded_payload.at(0);
if(syscall_num == 64) { // SYS_WRITE
return this->execute_sys_write(this, loaded_payload, traits<BASE>::MEM);
} else {
CPPLOG(ERR) << "tohost syscall with number 0x" << std::hex << syscall_num << std::dec << " (" << syscall_num
<< ") not implemented";
this->reg.trap_state = std::numeric_limits<uint32_t>::max();
this->interrupt_sim = payload_addr;
return iss::Ok;
}
} else {
CPPLOG(ERR) << "tohost functionality not implemented for device " << device << " and command " << command;
this->reg.trap_state = std::numeric_limits<uint32_t>::max();
this->interrupt_sim = payload_addr;
return iss::Ok;
}
}
if((traits<BASE>::XLEN == 32 && paddr.val == this->fromhost + 4) || (traits<BASE>::XLEN == 64 && paddr.val == this->fromhost)) {
uint64_t fhostvar = *reinterpret_cast<uint64_t*>(p.data() + (this->fromhost & mem.page_addr_mask));
*reinterpret_cast<uint64_t*>(p.data() + (this->tohost & mem.page_addr_mask)) = fhostvar;
}
}
return iss::Ok;
}
template <typename BASE> inline void riscv_hart_msu_vp<BASE>::reset(uint64_t address) {
template <typename BASE, features_e FEAT, typename LOGCAT> inline void riscv_hart_msu_vp<BASE, FEAT, LOGCAT>::reset(uint64_t address) {
BASE::reset(address);
state.mstatus = hart_state<reg_t>::mstatus_reset_val;
update_vm_info();
}
template <typename BASE> inline void riscv_hart_msu_vp<BASE>::update_vm_info() {
this->vm[1] = decode_vm_info(this->reg.PRIV, satp);
BASE::addr_mode[3] = BASE::addr_mode[2] = this->vm[1].is_active() ? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL;
if(state.mstatus.MPRV)
this->vm[0] = decode_vm_info(state.mstatus.MPP, satp);
else
this->vm[0] = this->vm[1];
BASE::addr_mode[1] = BASE::addr_mode[0] = this->vm[0].is_active() ? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL;
ptw.clear();
}
template <typename BASE> void riscv_hart_msu_vp<BASE>::check_interrupt() {
template <typename BASE, features_e FEAT, typename LOGCAT> void riscv_hart_msu_vp<BASE, FEAT, LOGCAT>::check_interrupt() {
auto status = state.mstatus;
auto ip = this->csr[mip];
auto ie = this->csr[mie];
@ -649,101 +494,8 @@ template <typename BASE> void riscv_hart_msu_vp<BASE>::check_interrupt() {
}
}
template <typename BASE> typename riscv_hart_msu_vp<BASE>::phys_addr_t riscv_hart_msu_vp<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.PRIV;
const vm_info& vm = this->vm[static_cast<uint16_t>(type) / 2];
const bool s_mode = mode == PRIV_S;
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) ? s_mode && (type == iss::access_type::FETCH || !sum) : !s_mode) {
break;
} else if(!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
break;
} else if(type == (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_msu_vp<BASE>::enter_trap(uint64_t flags, uint64_t addr, uint64_t instr) {
auto cur_priv = this->reg.PRIV;
template <typename BASE, features_e FEAT, typename LOGCAT> uint64_t riscv_hart_msu_vp<BASE, FEAT, LOGCAT>::enter_trap(uint64_t flags, uint64_t addr, uint64_t instr) {
// flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0]
// calculate and write mcause val
if(flags == std::numeric_limits<uint64_t>::max())
@ -751,11 +503,11 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
auto trap_id = bit_sub<0, 16>(flags);
auto cause = bit_sub<16, 15>(flags);
if(trap_id == 0 && cause == 11)
cause = 0x8 + cur_priv; // adjust environment call cause
cause = 0x8 + this->reg.PRIV; // adjust environment call cause
// calculate effective privilege level
auto new_priv = PRIV_M;
unsigned new_priv = PRIV_M;
if(trap_id == 0) { // exception
if(cur_priv != PRIV_M && ((this->csr[medeleg] >> cause) & 0x1) != 0)
if(this->reg.PRIV != PRIV_M && ((this->csr[medeleg] >> cause) & 0x1) != 0)
new_priv = (this->csr[sedeleg] >> cause) & 0x1 ? PRIV_U : PRIV_S;
// store ret addr in xepc register
this->csr[uepc | (new_priv << 8)] = static_cast<reg_t>(addr); // store actual address instruction of exception
@ -771,21 +523,23 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
this->csr[utval | (new_priv << 8)] = static_cast<reg_t>(addr);
break;
case 2:
this->csr[utval | (new_priv << 8)] = (instr & 0x3) == 3 ? instr : instr & 0xffff;
this->csr[utval | (new_priv << 8)] = (!this->has_compressed() || (instr & 0x3) == 3) ? instr : instr & 0xffff;
break;
case 3:
// TODO: implement debug mode behavior
// csr[dpc] = addr;
// csr[dcsr] = (csr[dcsr] & ~0x1c3) | (1<<6) | PRIV_M; //FIXME: cause should not be 4 (stepi)
this->csr[utval | (new_priv << 8)] = addr;
if((FEAT & FEAT_DEBUG) && (this->csr[dcsr] & 0x8000)) {
this->reg.DPC = addr;
this->csr[dcsr] = (this->csr[dcsr] & ~0x1c3) | (1 << 6) | PRIV_M; // FIXME: cause should not be 4 (stepi)
new_priv = this->reg.PRIV | PRIV_D;
} else {
this->csr[utval | (new_priv << 8)] = addr;
}
if(this->semihosting_cb) {
// Check for semihosting call
phys_addr_t p_addr(access_type::DEBUG_READ, traits<BASE>::MEM, addr - 4);
std::array<uint8_t, 8> data;
// check for SLLI_X0_X0_0X1F and SRAI_X0_X0_0X07
this->read_mem(p_addr, 4, data.data());
p_addr.val += 8;
this->read_mem(p_addr, 4, data.data() + 4);
this->memory.rd_mem(iss::access_type::DEBUG_READ, addr - 4, 4, data.data());
addr += 8;
this->memory.rd_mem(iss::access_type::DEBUG_READ, addr - 4, 4, data.data() + 4);
const std::array<uint8_t, 8> ref_data = {0x13, 0x10, 0xf0, 0x01, 0x13, 0x50, 0x70, 0x40};
if(data == ref_data) {
@ -797,7 +551,7 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
#else
sprintf(buffer.data(), "0x%016lx", addr);
#endif
CLOG(INFO, disass) << "Semihosting call at address " << buffer.data() << " occurred ";
NSCLOG(INFO, LOGCAT) << "Semihosting call at address " << buffer.data() << " occurred ";
this->semihosting_cb(this, &(this->reg.X10) /*a0*/, &(this->reg.X11) /*a1*/);
return this->reg.NEXT_PC;
@ -814,7 +568,7 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
}
this->fault_data = 0;
} else {
if(cur_priv != PRIV_M && ((this->csr[mideleg] >> cause) & 0x1) != 0)
if(this->reg.PRIV != PRIV_M && ((this->csr[mideleg] >> cause) & 0x1) != 0)
new_priv = (this->csr[sideleg] >> cause) & 0x1 ? PRIV_U : PRIV_S;
this->csr[uepc | (new_priv << 8)] = this->reg.NEXT_PC; // store next address if interrupt
this->reg.pending_trap = 0;
@ -830,12 +584,12 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
// store the actual privilege level in yPP and store interrupt enable flags
switch(new_priv) {
case PRIV_M:
state.mstatus.MPP = cur_priv;
state.mstatus.MPP = this->reg.PRIV;
state.mstatus.MPIE = state.mstatus.MIE;
state.mstatus.MIE = false;
break;
case PRIV_S:
state.mstatus.SPP = cur_priv;
state.mstatus.SPP = this->reg.PRIV;
state.mstatus.SPIE = state.mstatus.SIE;
state.mstatus.SIE = false;
break;
@ -848,71 +602,82 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
}
// get trap vector
auto ivec = this->csr[utvec | (new_priv << 8)];
auto xtvec = this->csr[utvec | (new_priv << 8)];
// calculate addr// set NEXT_PC to trap addressess to jump to based on MODE
// bits in mtvec
this->reg.NEXT_PC = ivec & ~0x3UL;
if((ivec & 0x1) == 1 && trap_id != 0)
this->reg.NEXT_PC += 4 * cause;
this->reg.NEXT_PC = xtvec & ~0x3UL;
if(trap_id != 0) {
if((xtvec & 0x3UL) == 3UL) {
reg_t data;
auto ret = read(address_type::LOGICAL, access_type::READ, 0, this->csr[mtvt], sizeof(reg_t), reinterpret_cast<uint8_t*>(&data));
if(ret == iss::Err)
return this->reg.PC;
this->reg.NEXT_PC = data;
} else if((xtvec & 0x3UL) == 1UL)
this->reg.NEXT_PC += 4 * cause;
}
std::array<char, 32> buffer;
#if defined(_MSC_VER)
sprintf(buffer.data(), "0x%016llx", addr);
#else
sprintf(buffer.data(), "0x%016lx", addr);
#endif
if((flags & 0xffffffff) != 0xffffffff)
CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '"
NSCLOG(INFO, LOGCAT) << (trap_id ? "Interrupt" : "Trap") << " with cause '"
<< (trap_id ? this->irq_str[cause] : this->trap_str[cause]) << "' (" << cause << ")"
<< " at address " << buffer.data() << " occurred, changing privilege level from " << this->lvl[cur_priv]
<< " at address " << buffer.data() << " occurred, changing privilege level from " << this->lvl[this->reg.PRIV]
<< " to " << this->lvl[new_priv];
// reset trap state
this->reg.PRIV = new_priv;
this->reg.trap_state = 0;
update_vm_info();
return this->reg.NEXT_PC;
}
template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::leave_trap(uint64_t flags) {
template <typename BASE, features_e FEAT, typename LOGCAT> uint64_t riscv_hart_msu_vp<BASE, FEAT, LOGCAT>::leave_trap(uint64_t flags) {
auto cur_priv = this->reg.PRIV;
auto inst_priv = flags & 0x3;
auto status = state.mstatus;
auto tsr = state.mstatus.TSR;
if(cur_priv == PRIV_S && inst_priv == PRIV_S && tsr != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
this->fault_data = this->reg.PC;
return this->reg.PC;
this->reg.trap_state = 0x80ULL << 24 | (2 << 16); // illegal instruction
this->reg.NEXT_PC = std::numeric_limits<uint32_t>::max();
} else {
auto status = state.mstatus;
// pop the relevant lower-privilege interrupt enable and privilege mode stack
// clear respective yIE
switch(inst_priv) {
case PRIV_M:
this->reg.PRIV = state.mstatus.MPP;
state.mstatus.MPP = 0; // clear mpp to U mode
state.mstatus.MIE = state.mstatus.MPIE;
state.mstatus.MPIE = 1;
break;
case PRIV_S:
this->reg.PRIV = state.mstatus.SPP;
state.mstatus.SPP = 0; // clear spp to U mode
state.mstatus.SIE = state.mstatus.SPIE;
state.mstatus.SPIE = 1;
break;
case PRIV_U:
this->reg.PRIV = 0;
state.mstatus.UIE = state.mstatus.UPIE;
state.mstatus.UPIE = 1;
break;
}
// sets the pc to the value stored in the x epc register.
this->reg.NEXT_PC = this->csr[uepc | inst_priv << 8];
NSCLOG(INFO, LOGCAT) << "Executing xRET , changing privilege level from " << this->lvl[cur_priv] << " to "
<< this->lvl[this->reg.PRIV];
check_interrupt();
}
// pop the relevant lower-privilege interrupt enable and privilege mode stack
// clear respective yIE
switch(inst_priv) {
case PRIV_M:
this->reg.PRIV = state.mstatus.MPP;
state.mstatus.MPP = 0; // clear mpp to U mode
state.mstatus.MIE = state.mstatus.MPIE;
state.mstatus.MPIE = 1;
break;
case PRIV_S:
this->reg.PRIV = state.mstatus.SPP;
state.mstatus.SPP = 0; // clear spp to U mode
state.mstatus.SIE = state.mstatus.SPIE;
state.mstatus.SPIE = 1;
break;
case PRIV_U:
this->reg.PRIV = 0;
state.mstatus.UIE = state.mstatus.UPIE;
state.mstatus.UPIE = 1;
break;
}
// sets the pc to the value stored in the x epc register.
this->reg.NEXT_PC = this->csr[uepc | inst_priv << 8];
CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << this->lvl[cur_priv] << " to " << this->lvl[this->reg.PRIV];
update_vm_info();
check_interrupt();
this->reg.trap_state = this->reg.pending_trap;
return this->reg.NEXT_PC;
}
template <typename BASE> void riscv_hart_msu_vp<BASE>::wait_until(uint64_t flags) {
template <typename BASE, features_e FEAT, typename LOGCAT> void riscv_hart_msu_vp<BASE, FEAT, LOGCAT>::wait_until(uint64_t flags) {
auto status = state.mstatus;
auto tw = status.TW;
if(this->reg.PRIV == PRIV_S && tw != 0) {
if(this->reg.PRIV < PRIV_M && tw != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
this->fault_data = this->reg.PC;
}

View File

@ -41,14 +41,14 @@
#include "util/logging.h"
#include <algorithm>
#include <cstdint>
#include <array>
#include <elfio/elf_types.hpp>
#include <elfio/elfio.hpp>
#ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY
#endif
#include <array>
#include <elfio/elfio.hpp>
#include <fmt/format.h>
#include <iss/mmio/memory_with_htif.h>
#include "../mem/memory_with_htif.h"
#include <unordered_map>
namespace iss {
@ -71,7 +71,7 @@ public:
#else
switch(priv_lvl) {
case PRIV_U:
return 0x00000011UL; // 0b1...0 0001 0001
return FEAT & features_e::FEAT_EXT_N? 0x00000011UL : 0UL; // 0b1...0 0001 0001
default:
// +-SD
// | +-TSR
@ -88,7 +88,7 @@ public:
// | |||||| | | | || +-UPIE
// | ||||||/|/|/| || |+-MIE
// | ||||||/|/|/| || || +-UIE
return 0b00000000000000000001100010011001;
return 0b10000000001000000001100010011001;
}
#endif
} else if(sizeof(reg_t) == 8) {
@ -97,24 +97,23 @@ public:
#else
switch(priv_lvl) {
case PRIV_U:
return 0x00000011UL; // 0b1...0 0001 0001
return FEAT & features_e::FEAT_EXT_N? 0x8000000000000011ULL : 0ULL; // 0b1...0 0001 0001
default:
// +-SD
// | +-TSR
// | |+-TW
// | ||+-TVM
// | |||+-MXR
// | ||||+-SUM
// | |||||+-MPRV
// | |||||| +-XS
// | |||||| | +-FS
// | |||||| | | +-MPP
// | |||||| | | | +-SPP
// | |||||| | | | |+-MPIE
// | |||||| | | | || +-UPIE
// | ||||||/|/|/| || |+-MIE
// | ||||||/|/|/| || || +-UIE
return 0b00000000000000000001100010011001;
// +-TSR
// |+-TW
// ||+-TVM
// |||+-MXR
// ||||+-SUM
// |||||+-MPRV
// |||||| +-XS
// |||||| | +-FS
// |||||| | | +-MPP
// |||||| | | | +-SPP
// |||||| | | | |+-MPIE
// |||||| | | | || +-UPIE
// ||||||/|/|/| || |+-MIE
// ||||||/|/|/| || || +-UIE
return 0b00000000001000000001100010011001 | 0x8000000000000000ULL;
}
#endif
} else
@ -137,7 +136,7 @@ public:
return m[mode];
}
riscv_hart_mu_p(feature_config cfg = feature_config{});
riscv_hart_mu_p();
virtual ~riscv_hart_mu_p() = default;
@ -151,6 +150,7 @@ public:
uint64_t enter_trap(uint64_t flags) override { return riscv_hart_mu_p::enter_trap(flags, this->fault_data, this->fault_data); }
uint64_t enter_trap(uint64_t flags, uint64_t addr, uint64_t instr) override;
uint64_t leave_trap(uint64_t flags) override;
void wait_until(uint64_t flags) override;
void set_csr(unsigned addr, reg_t val) { this->csr[addr & this->csr.page_addr_mask] = val; }
@ -160,7 +160,6 @@ protected:
hart_state<reg_t> state;
std::unordered_map<reg_t, uint64_t> ptw;
std::unordered_map<uint64_t, uint8_t> atomic_reservation;
iss::status read_status(unsigned addr, reg_t& val);
@ -180,14 +179,12 @@ protected:
iss::status write_edeleg(unsigned addr, reg_t val);
void check_interrupt();
feature_config cfg;
mmio::memory_with_htif<reg_t> default_mem;
mem::memory_with_htif<reg_t> default_mem;
};
template <typename BASE, features_e FEAT, typename LOGCAT>
riscv_hart_mu_p<BASE, FEAT, LOGCAT>::riscv_hart_mu_p(feature_config cfg)
riscv_hart_mu_p<BASE, FEAT, LOGCAT>::riscv_hart_mu_p()
: state()
, cfg(cfg)
, default_mem(base::get_priv_if()) {
const std::array<unsigned, 8> rwaddrs{{
mepc,
@ -248,13 +245,13 @@ riscv_hart_mu_p<BASE, FEAT, LOGCAT>::riscv_hart_mu_p(feature_config cfg)
}
this->rd_func = util::delegate<arch_if::rd_func_sig>::from<this_class, &this_class::read>(this);
this->wr_func = util::delegate<arch_if::wr_func_sig>::from<this_class, &this_class::write>(this);
this->memories.prepend(*this);
this->memories.root(*this);
this->memories.append(default_mem);
}
template <typename BASE, features_e FEAT, typename LOGCAT>
iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::read(const address_type type, const access_type access, const uint32_t space,
const uint64_t addr, const unsigned length, uint8_t* const data) {
const uint64_t addr,const unsigned length, uint8_t* const data) {
#ifndef NDEBUG
if(access && iss::access_type::DEBUG) {
CPPLOG(TRACEALL) << "debug read of " << length << " bytes @addr 0x" << std::hex << addr;
@ -384,16 +381,8 @@ iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::write(const address_type type,
return iss::Err;
return this->write_csr(addr, *reinterpret_cast<const reg_t*>(data));
} break;
case traits<BASE>::FENCE: {
switch(addr) {
case 2:
case 3: {
ptw.clear();
auto tvm = state.mstatus.TVM;
return iss::Ok;
}
}
} break;
case traits<BASE>::FENCE:
break;
case traits<BASE>::RES: {
atomic_reservation[addr] = data[0];
} break;
@ -505,12 +494,12 @@ template <typename BASE, features_e FEAT, typename LOGCAT> void riscv_hart_mu_p<
}
template <typename BASE, features_e FEAT, typename LOGCAT>
uint64_t riscv_hart_mu_p<BASE, FEAT, LOGCAT>::enter_trap(uint64_t flags, uint64_t addr, uint64_t instr) {
uint64_t riscv_hart_mu_p<BASE, FEAT, LOGCAT>::enter_trap(uint64_t flags, uint64_t addr, uint64_t tval) {
// flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0]
// calculate and write mcause val
if(flags == std::numeric_limits<uint64_t>::max())
flags = this->reg.trap_state;
auto trap_id = bit_sub<0, 16>(flags);
auto const trap_id = bit_sub<0, 16>(flags);
auto cause = bit_sub<16, 15>(flags);
if(trap_id == 0 && cause == 11)
cause = 0x8 + this->reg.PRIV; // adjust environment call cause
@ -533,7 +522,7 @@ uint64_t riscv_hart_mu_p<BASE, FEAT, LOGCAT>::enter_trap(uint64_t flags, uint64_
this->csr[utval | (new_priv << 8)] = static_cast<reg_t>(addr);
break;
case 2:
this->csr[utval | (new_priv << 8)] = (!this->has_compressed() || (instr & 0x3) == 3) ? instr : instr & 0xffff;
this->csr[utval | (new_priv << 8)] = (!this->has_compressed() || (tval & 0x3) == 3) ? tval : tval & 0xffff;
break;
case 3:
if((FEAT & FEAT_DEBUG) && (this->csr[dcsr] & 0x8000)) {
@ -545,7 +534,6 @@ uint64_t riscv_hart_mu_p<BASE, FEAT, LOGCAT>::enter_trap(uint64_t flags, uint64_
}
if(this->semihosting_cb) {
// Check for semihosting call
phys_addr_t p_addr(access_type::DEBUG_READ, traits<BASE>::MEM, addr - 4);
std::array<uint8_t, 8> data;
// check for SLLI_X0_X0_0X1F and SRAI_X0_X0_0X07
this->memory.rd_mem(iss::access_type::DEBUG_READ, addr - 4, 4, data.data());
@ -562,7 +550,7 @@ uint64_t riscv_hart_mu_p<BASE, FEAT, LOGCAT>::enter_trap(uint64_t flags, uint64_
#else
sprintf(buffer.data(), "0x%016lx", addr);
#endif
CLOG(INFO, disass) << "Semihosting call at address " << buffer.data() << " occurred ";
NSCLOG(INFO, LOGCAT) << "Semihosting call at address " << buffer.data() << " occurred ";
this->semihosting_cb(this, &(this->reg.X10) /*a0*/, &(this->reg.X11) /*a1*/);
return this->reg.NEXT_PC;
@ -611,15 +599,15 @@ uint64_t riscv_hart_mu_p<BASE, FEAT, LOGCAT>::enter_trap(uint64_t flags, uint64_
auto xtvec = this->csr[utvec | (new_priv << 8)];
// calculate addr// set NEXT_PC to trap addressess to jump to based on MODE
// bits in mtvec
if(trap_id != 0 && (xtvec & 0x3UL) == 3UL) {
reg_t data;
auto ret = read(address_type::LOGICAL, access_type::READ, 0, this->csr[mtvt], sizeof(reg_t), reinterpret_cast<uint8_t*>(&data));
if(ret == iss::Err)
return this->reg.PC;
this->reg.NEXT_PC = data;
} else {
this->reg.NEXT_PC = xtvec & ~0x3UL;
if((xtvec & 0x1) == 1 && trap_id != 0)
this->reg.NEXT_PC = xtvec & ~0x3UL;
if(trap_id != 0) {
if((xtvec & 0x3UL) == 3UL) {
reg_t data;
auto ret = read(address_type::LOGICAL, access_type::READ, 0, this->csr[mtvt], sizeof(reg_t), reinterpret_cast<uint8_t*>(&data));
if(ret == iss::Err)
return this->reg.PC;
this->reg.NEXT_PC = data;
} else if((xtvec & 0x3UL) == 1UL)
this->reg.NEXT_PC += 4 * cause;
}
std::array<char, 32> buffer;
@ -629,7 +617,7 @@ uint64_t riscv_hart_mu_p<BASE, FEAT, LOGCAT>::enter_trap(uint64_t flags, uint64_
sprintf(buffer.data(), "0x%016lx", addr);
#endif
if((flags & 0xffffffff) != 0xffffffff)
CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '"
NSCLOG(INFO, LOGCAT) << (trap_id ? "Interrupt" : "Trap") << " with cause '"
<< (trap_id ? this->irq_str[cause] : this->trap_str[cause]) << "' (" << cause << ")"
<< " at address " << buffer.data() << " occurred, changing privilege level from " << this->lvl[this->reg.PRIV]
<< " to " << this->lvl[new_priv];
@ -643,8 +631,7 @@ template <typename BASE, features_e FEAT, typename LOGCAT> uint64_t riscv_hart_m
auto cur_priv = this->reg.PRIV;
auto inst_priv = (flags & 0x3) ? 3 : 0;
if(inst_priv > cur_priv) {
auto trap_val = 0x80ULL << 24 | (2 << 16); // illegal instruction
this->reg.trap_state = trap_val;
this->reg.trap_state = 0x80ULL << 24 | (2 << 16); // illegal instruction
this->reg.NEXT_PC = std::numeric_limits<uint32_t>::max();
} else {
auto status = state.mstatus;
@ -665,13 +652,22 @@ template <typename BASE, features_e FEAT, typename LOGCAT> uint64_t riscv_hart_m
}
// sets the pc to the value stored in the x epc register.
this->reg.NEXT_PC = this->csr[uepc | inst_priv << 8];
CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << this->lvl[cur_priv] << " to "
NSCLOG(INFO, LOGCAT) << "Executing xRET , changing privilege level from " << this->lvl[cur_priv] << " to "
<< this->lvl[this->reg.PRIV];
check_interrupt();
}
this->reg.trap_state = this->reg.pending_trap;
return this->reg.NEXT_PC;
}
template <typename BASE, features_e FEAT, typename LOGCAT> void riscv_hart_mu_p<BASE, FEAT, LOGCAT>::wait_until(uint64_t flags) {
auto status = state.mstatus;
auto tw = status.TW;
if(this->reg.PRIV == PRIV_S && tw != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
this->fault_data = this->reg.PC;
}
}
} // namespace arch
} // namespace iss

View File

@ -244,7 +244,6 @@ struct tgc5c: public arch_if {
uint32_t last_branch = 0;
} reg;
#pragma pack(pop)
std::array<address_type, 4> addr_mode;
uint64_t interrupt_sim=0;

View File

@ -23,35 +23,30 @@ using tgc5c_xrb_nn_plat_type = iss::arch::hwl<iss::arch::riscv_hart_m_p<iss::arc
#ifdef CORE_TGC5D
#include "riscv_hart_mu_p.h"
#include <iss/arch/tgc5d.h>
using tgc5d_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5d, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC |
iss::arch::FEAT_EXT_N)>;
using tgc5d_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5d, (iss::arch::features_e)(iss::arch::FEAT_EXT_N)>;
#endif
#ifdef CORE_TGC5D_XRB_MAC
#include "riscv_hart_mu_p.h"
#include <iss/arch/tgc5d_xrb_mac.h>
using tgc5d_xrb_mac_plat_type =
iss::arch::riscv_hart_mu_p<iss::arch::tgc5d_xrb_mac,
(iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>;
iss::arch::riscv_hart_mu_p<iss::arch::tgc5d_xrb_mac (iss::arch::features_e)(iss::arch::FEAT_EXT_N)>;
#endif
#ifdef CORE_TGC5D_XRB_NN
#include "hwl.h"
#include "riscv_hart_mu_p.h"
#include <iss/arch/tgc5d_xrb_nn.h>
using tgc5d_xrb_nn_plat_type =
iss::arch::hwl<iss::arch::riscv_hart_mu_p<iss::arch::tgc5d_xrb_nn,
(iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC | iss::arch::FEAT_EXT_N)>>;
iss::arch::hwl<iss::arch::riscv_hart_mu_p<iss::arch::tgc5d_xrb_nn, (iss::arch::features_e)(iss::arch::FEAT_EXT_N)>>;
#endif
#ifdef CORE_TGC5E
#include "riscv_hart_mu_p.h"
#include <iss/arch/tgc5e.h>
using tgc5e_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5e, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC |
iss::arch::FEAT_EXT_N)>;
using tgc5e_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5e, (iss::arch::features_e)(iss::arch::FEAT_EXT_N)>;
#endif
#ifdef CORE_TGC5X
#include "riscv_hart_mu_p.h"
#include <iss/arch/tgc5x.h>
using tgc5x_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5x, (iss::arch::features_e)(iss::arch::FEAT_PMP | iss::arch::FEAT_CLIC |
iss::arch::FEAT_EXT_N | iss::arch::FEAT_TCM)>;
using tgc5x_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc5x, (iss::arch::features_e)(iss::arch::FEAT_EXT_N)>;
#endif
#endif

View File

@ -1,11 +1,11 @@
#include "iss/arch/riscv_hart_common.h"
#include "iss/vm_types.h"
#include "memory_if.h"
#include <util/logging.h>
#include "../mem/memory_if.h"
namespace iss {
namespace mmio {
namespace mem {
struct clic_config {
uint64_t clic_base{0xc0000000};
unsigned clic_int_ctl_bits{4};
@ -199,12 +199,12 @@ template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::read_cause(unsigned a
switch(mode) {
case 0:
val |= clic_uprev_lvl << 16;
val |= hart_if.mstatus.UPIE << 27;
val |= hart_if.state.mstatus.UPIE << 27;
break;
default:
val |= clic_mprev_lvl << 16;
val |= hart_if.mstatus.MPIE << 27;
val |= hart_if.mstatus.MPP << 28;
val |= hart_if.state.mstatus.MPIE << 27;
val |= hart_if.state.mstatus.MPP << 28;
break;
}
} else
@ -220,12 +220,12 @@ template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::write_cause(unsigned
switch(mode) {
case 0:
clic_uprev_lvl = ((val >> 16) & 0xff) | (1 << (8 - cfg.clic_int_ctl_bits)) - 1;
hart_if.mstatus.UPIE = (val >> 27) & 0x1;
hart_if.state.mstatus.UPIE = (val >> 27) & 0x1;
break;
default:
clic_mprev_lvl = ((val >> 16) & 0xff) | (1 << (8 - cfg.clic_int_ctl_bits)) - 1;
hart_if.mstatus.MPIE = (val >> 27) & 0x1;
hart_if.mstatus.MPP = (val >> 28) & 0x3;
hart_if.state.mstatus.MPIE = (val >> 27) & 0x1;
hart_if.state.mstatus.MPP = (val >> 28) & 0x3;
break;
}
} else {
@ -248,5 +248,5 @@ template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::write_intthresh(unsig
return iss::Ok;
}
} // namespace mmio
} // namespace mem
} // namespace iss

67
src/iss/mem/memory_if.cpp Normal file
View File

@ -0,0 +1,67 @@
#include "memory_if.h"
#include <algorithm>
namespace iss {
namespace mem {
void memory_hierarchy::root(memory_elem& e) {
hierarchy.push_front(&e);
root_set=true;
update_chain();
}
void memory_hierarchy::prepend(memory_elem& e) {
if(root_set)
hierarchy.insert(hierarchy.begin()+1, &e);
else
hierarchy.push_front(&e);
update_chain();
}
void memory_hierarchy::append(memory_elem& e) {
hierarchy.push_back(&e);
update_chain();
}
void memory_hierarchy::insert_before(memory_elem&) {}
void memory_hierarchy::insert_after(memory_elem&) {}
void memory_hierarchy::replace_last(memory_elem& e) {
hierarchy.pop_back();
hierarchy.push_back(&e);
}
void memory_hierarchy::update_chain() {
bool tail = false;
for(size_t i = 0; i < hierarchy.size(); ++i) {
hierarchy[i]->register_csrs();
if(i)
hierarchy[i - 1]->set_next(hierarchy[i]->get_mem_if());
}
}
void memory_hierarchy::prepend(std::unique_ptr<memory_elem>&& p) {
prepend(*p);
owned_elems.push_back(std::move(p));
}
void memory_hierarchy::append(std::unique_ptr<memory_elem>&& p) {
append(*p);
owned_elems.push_back(std::move(p));
}
void memory_hierarchy::insert_before(std::unique_ptr<memory_elem>&& p) {
insert_before(*p);
owned_elems.push_back(std::move(p));
}
void memory_hierarchy::insert_after(std::unique_ptr<memory_elem>&& p) {
insert_after(*p);
owned_elems.push_back(std::move(p));
}
void memory_hierarchy::replace_last(std::unique_ptr<memory_elem>&& p) {
auto e = hierarchy.back();
replace_last(*p);
auto it = std::find_if(std::begin(owned_elems), std::end(owned_elems), [e](std::unique_ptr<memory_elem> const& p) {return p.get()==e;});
if(it!=std::end(owned_elems))
owned_elems.erase(it);
owned_elems.push_back(std::move(p));
}
} // namespace mem
} // namespace iss

View File

@ -37,12 +37,14 @@
#include "iss/vm_types.h"
#include <deque>
#include <vector>
#include <functional>
#include <limits>
#include <util/delegate.h>
#include <memory>
namespace iss {
namespace mmio {
namespace mem {
using rd_mem_func_sig = iss::status(iss::access_type, uint64_t, unsigned, uint8_t*);
using wr_mem_func_sig = iss::status(iss::access_type, uint64_t, unsigned, uint8_t const*);
@ -53,6 +55,7 @@ struct memory_if {
};
struct memory_elem {
virtual ~memory_elem() = default;
virtual memory_if get_mem_if() = 0;
virtual void set_next(memory_if) = 0;
virtual void register_csrs() {}
@ -60,17 +63,25 @@ struct memory_elem {
};
struct memory_hierarchy {
void root(memory_elem&);
void prepend(memory_elem&);
void append(memory_elem&);
void insert_before(memory_elem&);
void insert_after(memory_elem&);
void replace_last(memory_elem&);
void prepend(std::unique_ptr<memory_elem>&&);
void append(std::unique_ptr<memory_elem>&&);
void insert_before(std::unique_ptr<memory_elem>&&);
void insert_after(std::unique_ptr<memory_elem>&&);
void replace_last(std::unique_ptr<memory_elem>&&);
protected:
void update_chain();
std::deque<std::reference_wrapper<memory_elem>> hierarchy;
std::deque<memory_elem*> hierarchy;
std::vector<std::unique_ptr<memory_elem>> owned_elems;
bool root_set{false};
};
} // namespace mmio
} // namespace mem
} // namespace iss
#endif
#endif

View File

@ -3,12 +3,12 @@
#include "iss/arch/riscv_hart_common.h"
#include "iss/vm_types.h"
#include "memory_if.h"
#include <util/logging.h>
#include <util/sparse_array.h>
#include "../mem/memory_if.h"
namespace iss {
namespace mmio {
namespace mem {
template <typename WORD_TYPE> struct memory_with_htif : public memory_elem {
using this_class = memory_with_htif<WORD_TYPE>;
constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8;
@ -51,6 +51,6 @@ protected:
mem_type mem;
arch::priv_if<WORD_TYPE> hart_if;
};
} // namespace mmio
} // namespace mem
} // namespace iss
#endif // _MEMORY_WITH_HTIF_

325
src/iss/mem/mmu.h Normal file
View File

@ -0,0 +1,325 @@
#include "iss/arch/riscv_hart_common.h"
#include "iss/vm_types.h"
#include <util/logging.h>
#include "../mem/memory_if.h"
namespace iss {
namespace mem {
enum {
PGSHIFT = 12,
PTE_PPN_SHIFT = 10,
// page table entry (PTE) fields
PTE_V = 0x001, // Valid
PTE_R = 0x002, // Read
PTE_W = 0x004, // Write
PTE_X = 0x008, // Execute
PTE_U = 0x010, // User
PTE_G = 0x020, // Global
PTE_A = 0x040, // Accessed
PTE_D = 0x080, // Dirty
PTE_SOFT = 0x300 // Reserved for Software
};
template <typename T> inline bool PTE_TABLE(T PTE) { return (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V); }
struct vm_info {
int levels;
int idxbits;
int ptesize;
uint64_t ptbase;
bool is_active() { return levels; }
};
inline void read_reg_with_offset(uint32_t reg, uint8_t offs, uint8_t* const data, unsigned length) {
auto reg_ptr = reinterpret_cast<uint8_t*>(&reg);
switch(offs) {
default:
for(auto i = 0U; i < length; ++i)
*(data + i) = *(reg_ptr + i);
break;
case 1:
for(auto i = 0U; i < length; ++i)
*(data + i) = *(reg_ptr + 1 + i);
break;
case 2:
for(auto i = 0U; i < length; ++i)
*(data + i) = *(reg_ptr + 2 + i);
break;
case 3:
*data = *(reg_ptr + 3);
break;
}
}
inline void write_reg_with_offset(uint32_t& reg, uint8_t offs, const uint8_t* const data, unsigned length) {
auto reg_ptr = reinterpret_cast<uint8_t*>(&reg);
switch(offs) {
default:
for(auto i = 0U; i < length; ++i)
*(reg_ptr + i) = *(data + i);
break;
case 1:
for(auto i = 0U; i < length; ++i)
*(reg_ptr + 1 + i) = *(data + i);
break;
case 2:
for(auto i = 0U; i < length; ++i)
*(reg_ptr + 2 + i) = *(data + i);
break;
case 3:
*(reg_ptr + 3) = *data;
break;
}
}
//TODO: update vminfo on trap enter and leave as well as mstatus write, reset
template <typename WORD_TYPE> struct mmu : public memory_elem {
using this_class = mmu<WORD_TYPE>;
using reg_t = WORD_TYPE;
constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8;
constexpr static reg_t PGSIZE = 1 << PGSHIFT;
constexpr static reg_t PGMASK = PGSIZE - 1;
mmu(arch::priv_if<WORD_TYPE> hart_if)
: hart_if(hart_if) {
hart_if.csr_rd_cb[satp] = MK_CSR_RD_CB(read_satp);
hart_if.csr_wr_cb[satp] = MK_CSR_WR_CB(write_satp);
}
virtual ~mmu() = default;
memory_if get_mem_if() override {
return memory_if{.rd_mem{util::delegate<rd_mem_func_sig>::from<this_class, &this_class::read_mem>(this)},
.wr_mem{util::delegate<wr_mem_func_sig>::from<this_class, &this_class::write_mem>(this)}};
}
void set_next(memory_if mem) override { down_stream_mem = mem; }
private:
iss::status read_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t* data) {
if(unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary
vm_info vm = decode_vm_info(hart_if.PRIV, satp);
if(vm.levels != 0) { // VM is active
auto split_addr = (addr + length) & ~PGMASK;
auto len1 = split_addr - addr;
auto res = down_stream_mem.rd_mem(access, addr, len1, data);
if(res == iss::Ok)
res = down_stream_mem.rd_mem(access, split_addr, length - len1, data + len1);
return res;
}
}
return down_stream_mem.rd_mem(access, addr, length, data);
}
iss::status write_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t const* data) {
if(unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary
vm_info vm = decode_vm_info(hart_if.PRIV, satp);
if(vm.levels != 0) { // VM is active
auto split_addr = (addr + length) & ~PGMASK;
auto len1 = split_addr - addr;
auto res = down_stream_mem.wr_mem(access, addr, len1, data);
if(res == iss::Ok)
res = down_stream_mem.wr_mem(access, split_addr, length - len1, data + len1);
return res;
}
}
return down_stream_mem.wr_mem(access, virt2phys(access, addr), length, data);
}
void update_vm_info();
iss::status read_plain(unsigned addr, reg_t& val) {
val = hart_if.csr[addr];
return iss::Ok;
}
iss::status write_plain(unsigned addr, reg_t const& val) {
hart_if.csr[addr] = val;
return iss::Ok;
}
iss::status read_satp(unsigned addr, reg_t& val) {
auto tvm = bit_sub<20, 1>(hart_if.state.mstatus());
if(hart_if.PRIV == arch::PRIV_S & tvm != 0) {
hart_if.raise_trap(2, 0, hart_if.PC);
// hart_if.reg.trap_state = (1 << 31) | (2 << 16);
// hart_if.fault_data = hart_if.reg.PC;
return iss::Err;
}
val = satp;
return iss::Ok;
}
iss::status write_satp(unsigned addr, reg_t val) {
reg_t tvm = hart_if.state.mstatus.TVM;
if(hart_if.PRIV == arch::PRIV_S & tvm != 0) {
hart_if.raise_trap(2, 0, hart_if.PC);
// hart_if.reg.trap_state = (1 << 31) | (2 << 16);
// hart_if.fault_data = hart_if.reg.PC;
return iss::Err;
}
satp = val;
update_vm_info();
return iss::Ok;
}
uint64_t virt2phys(iss::access_type access, uint64_t addr);
static inline vm_info decode_vm_info(uint32_t state, uint32_t sptbr) {
if(state == arch::PRIV_M)
return {0, 0, 0, 0};
if(state <= arch::PRIV_S)
switch(bit_sub<31, 1>(sptbr)) {
case 0:
return {0, 0, 0, 0}; // off
case 1:
return {2, 10, 4, bit_sub<0, 22>(sptbr) << PGSHIFT}; // SV32
default:
abort();
}
abort();
return {0, 0, 0, 0}; // dummy
}
static inline vm_info decode_vm_info(uint32_t state, uint64_t sptbr) {
if(state == arch::PRIV_M)
return {0, 0, 0, 0};
if(state <= arch::PRIV_S)
switch(bit_sub<60, 4>(sptbr)) {
case 0:
return {0, 0, 0, 0}; // off
case 8:
return {3, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV39
case 9:
return {4, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV48
case 10:
return {5, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV57
case 11:
return {6, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV64
default:
abort();
}
abort();
return {0, 0, 0, 0}; // dummy
}
protected:
reg_t satp;
std::unordered_map<reg_t, uint64_t> ptw;
std::array<vm_info, 2> vmt;
std::array<address_type, 4> addr_mode;
arch::priv_if<WORD_TYPE> hart_if;
memory_if down_stream_mem;
};
template <typename WORD_TYPE> uint64_t mmu<WORD_TYPE>::virt2phys(iss::access_type access, uint64_t addr) {
const auto type = access & iss::access_type::FUNC;
auto it = ptw.find(addr >> 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 {(pte & (~PGMASK)) | (addr & PGMASK)};
else
ptw.erase(it); // throw an exception
#endif
} else {
uint32_t mode = type != iss::access_type::FETCH && hart_if.state.mstatus.MPRV ? // MPRV
hart_if.state.mstatus.MPP
: hart_if.PRIV;
const vm_info& vm = vmt[static_cast<uint16_t>(type) / 2];
const bool s_mode = mode == arch::PRIV_S;
const bool sum = hart_if.state.mstatus.SUM;
const bool mxr = hart_if.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) << (sizeof(reg_t)*8 - (va_bits - 1))) - 1;
const reg_t masked_msbs = (addr >> (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 >> (PGSHIFT + ptshift)) & ((1 << vm.idxbits) - 1);
// check that physical address of PTE is legal
reg_t pte = 0;
const uint8_t res = down_stream_mem.rd_mem(iss::access_type::READ, base + idx * vm.ptesize, vm.ptesize,
(uint8_t*)&pte);
if(res != 0)
throw arch::trap_load_access_fault(addr);
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) ? s_mode && (type == iss::access_type::FETCH || !sum) : !s_mode) {
break;
} else if(!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
break;
} else if(type == (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 >> PGSHIFT;
const reg_t value = (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT;
const reg_t offset = addr & PGMASK;
ptw[vpn] = value | (pte & 0xff);
return value | offset;
}
}
}
switch(type) {
case access_type::FETCH:
hart_if.raise_trap(12, 0, addr);
throw arch::trap_instruction_page_fault(addr);
case access_type::READ:
hart_if.raise_trap(13, 0, addr);
throw arch::trap_load_page_fault(addr);
case access_type::WRITE:
hart_if.raise_trap(15, 0, addr);
throw arch::trap_store_page_fault(addr);
default:
abort();
}
}
template <typename WORD_TYPE> inline void mmu<WORD_TYPE>::update_vm_info() {
vmt[1] = decode_vm_info(hart_if.PRIV, satp);
addr_mode[3] = addr_mode[2] = vmt[1].is_active() ? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL;
if(hart_if.state.mstatus.MPRV)
vmt[0] = decode_vm_info(hart_if.state.mstatus.MPP, satp);
else
vmt[0] = vmt[1];
addr_mode[1] = addr_mode[0] = vmt[0].is_active() ? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL;
ptw.clear();
}
} // namespace mem
} // namespace iss

View File

@ -1,11 +1,11 @@
#include "iss/arch/riscv_hart_common.h"
#include "iss/vm_types.h"
#include "memory_if.h"
#include <util/logging.h>
#include "../mem/memory_if.h"
namespace iss {
namespace mmio {
namespace mem {
struct clic_config {
uint64_t clic_base{0xc0000000};
unsigned clic_int_ctl_bits{4};
@ -61,9 +61,8 @@ template <typename WORD_TYPE> struct pmp : public memory_elem {
using reg_t = WORD_TYPE;
constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8;
pmp(arch::priv_if<WORD_TYPE> hart_if, clic_config cfg)
: hart_if(hart_if)
, cfg(cfg) {
pmp(arch::priv_if<WORD_TYPE> hart_if)
: hart_if(hart_if) {
for(size_t i = arch::pmpaddr0; i <= arch::pmpaddr15; ++i) {
hart_if.csr_rd_cb[i] = MK_CSR_RD_CB(read_plain);
hart_if.csr_wr_cb[i] = MK_CSR_WR_CB(write_plain);
@ -74,7 +73,7 @@ template <typename WORD_TYPE> struct pmp : public memory_elem {
}
}
~pmp() = default;
virtual ~pmp() = default;
memory_if get_mem_if() override {
return memory_if{.rd_mem{util::delegate<rd_mem_func_sig>::from<this_class, &this_class::read_mem>(this)},
@ -208,5 +207,5 @@ template <typename WORD_TYPE> bool pmp<WORD_TYPE>::pmp_check(const access_type t
return !any_active || hart_if.reg.PRIV == arch::PRIV_M;
}
} // namespace mmio
} // namespace mem
} // namespace iss

View File

@ -1,26 +0,0 @@
#include "memory_if.h"
namespace iss {
namespace mmio {
void memory_hierarchy::prepend(memory_elem& e) {
hierarchy.push_front(e);
update_chain();
}
void memory_hierarchy::append(memory_elem& e) {
hierarchy.push_back(e);
update_chain();
}
void memory_hierarchy::insert_before(memory_elem&) {}
void memory_hierarchy::insert_after(memory_elem&) {}
void memory_hierarchy::replace_last(memory_elem&) {}
void memory_hierarchy::update_chain() {
bool tail = false;
for(size_t i = 0; i < hierarchy.size(); ++i) {
hierarchy[i].get().register_csrs();
if(i)
hierarchy[i - 1].get().set_next(hierarchy[i].get().get_mem_if());
}
}
} // namespace mmio
} // namespace iss

View File

@ -146,10 +146,10 @@ public:
void wait_until(uint64_t flags) override {
SCCDEBUG(owner->hier_name()) << "Sleeping until interrupt";
PLAT::wait_until(flags);
while(this->reg.pending_trap == 0 && (this->csr[iss::arch::mip] & this->csr[iss::arch::mie]) == 0) {
sc_core::wait(wfi_evt);
}
PLAT::wait_until(flags);
}
void local_irq(short id, bool value) override {

View File

@ -4785,8 +4785,6 @@ continuation_e vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, jit_hold
code_word_t instr = 0;
phys_addr_t paddr(pc);
auto *const data = (uint8_t *)&instr;
if(this->core.has_mmu())
paddr = this->core.virt2phys(pc);
auto res = this->core.read(paddr, 4, data);
if (res != iss::Ok)
return ILLEGAL_FETCH;
@ -4926,4 +4924,4 @@ volatile std::array<bool, 2> dummy = {
};
}
}
// clang-format on
// clang-format on

View File

@ -251,22 +251,8 @@ private:
decoder instr_decoder;
iss::status fetch_ins(virt_addr_t pc, uint8_t * data){
if(this->core.has_mmu()) {
auto phys_pc = this->core.virt2phys(pc);
// if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary
// if (this->core.read(phys_pc, 2, data) != iss::Ok) return iss::Err;
// if ((data[0] & 0x3) == 0x3) // this is a 32bit instruction
// if (this->core.read(this->core.v2p(pc + 2), 2, data + 2) != iss::Ok)
// return iss::Err;
// } else {
if (this->core.read(phys_pc, 4, data) != iss::Ok)
return iss::Err;
// }
} else {
if (this->core.read(iss::address_type::PHYSICAL, pc.access, pc.space, pc.val, 4, data) != iss::Ok)
return iss::Err;
}
if (this->core.read(iss::address_type::PHYSICAL, pc.access, pc.space, pc.val, 4, data) != iss::Ok)
return iss::Err;
return iss::Ok;
}
};

View File

@ -4944,8 +4944,6 @@ vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, BasicBlock *this_block)
// const typename traits::addr_t upper_bits = ~traits::PGMASK;
phys_addr_t paddr(pc);
auto *const data = (uint8_t *)&instr;
if(this->core.has_mmu())
paddr = this->core.virt2phys(pc);
auto res = this->core.read(paddr, 4, data);
if (res != iss::Ok)
return std::make_tuple(ILLEGAL_FETCH, nullptr);

View File

@ -3743,8 +3743,6 @@ vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, tu_builder& tu) {
enum {TRAP_ID=1<<16};
code_word_t instr = 0;
phys_addr_t paddr(pc);
if(this->core.has_mmu())
paddr = this->core.virt2phys(pc);
auto res = this->core.read(paddr, 4, reinterpret_cast<uint8_t*>(&instr));
if (res != iss::Ok)
return ILLEGAL_FETCH;
@ -3833,4 +3831,4 @@ volatile std::array<bool, 2> dummy = {
};
}
}
// clang-format on
// clang-format on