Refactored hart vm implementation to use more structured description
This commit is contained in:
parent
d59dfa93f6
commit
768df67646
|
@ -45,6 +45,7 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <util/ities.h>
|
#include <util/ities.h>
|
||||||
#include <util/sparse_array.h>
|
#include <util/sparse_array.h>
|
||||||
|
#include <util/bit_field.h>
|
||||||
|
|
||||||
namespace iss {
|
namespace iss {
|
||||||
namespace arch {
|
namespace arch {
|
||||||
|
@ -245,166 +246,8 @@ struct trap_store_page_fault : public trap_access {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
using mstatus32_t = union {
|
template <typename BASE> class riscv_hart_msu_vp : public BASE {
|
||||||
uint32_t val;
|
public:
|
||||||
struct /*mstatus*/ {
|
|
||||||
uint32_t SD : 1, // SD bit is read-only and is set when either the FS or XS
|
|
||||||
// bits encode a Dirty state (i.e., SD=((FS==11) OR
|
|
||||||
// (XS==11)))
|
|
||||||
_WPRI3 : 8, // unused
|
|
||||||
TSR : 1, // Trap SRET
|
|
||||||
TW : 1, // Timeout Wait
|
|
||||||
TVM : 1, // Trap Virtual Memory
|
|
||||||
MXR : 1, // Make eXecutable Readable
|
|
||||||
SUM : 1, // permit Supervisor User Memory access
|
|
||||||
MPRV : 1, // Modify PRiVilege
|
|
||||||
XS : 2, // status of additional user-mode extensions and associated
|
|
||||||
// state, All off/None dirty or clean, some on/None dirty, some
|
|
||||||
// clean/Some dirty
|
|
||||||
FS : 2, // floating-point unit status Off/Initial/Clean/Dirty
|
|
||||||
MPP : 2, // machine previous privilege
|
|
||||||
_WPRI2 : 2, // unused
|
|
||||||
SPP : 1, // supervisor previous privilege
|
|
||||||
MPIE : 1, // previous machine interrupt-enable
|
|
||||||
_WPRI1 : 1, // unused
|
|
||||||
SPIE : 1, // previous supervisor interrupt-enable
|
|
||||||
UPIE : 1, // previous user interrupt-enable
|
|
||||||
MIE : 1, // machine interrupt-enable
|
|
||||||
_WPRI0 : 1, // unused
|
|
||||||
SIE : 1, // supervisor interrupt-enable
|
|
||||||
UIE : 1; // user interrupt-enable
|
|
||||||
} m;
|
|
||||||
struct /*sstatus*/ {
|
|
||||||
uint32_t SD : 1, _WPRI4 : 11, MXR : 1, SUM : 1, _WPRI3 : 1, XS : 2, FS : 2, _WPRI2 : 4, SPP : 1, _WPRI1 : 2,
|
|
||||||
SPIE : 1, UPIE : 1, _WPRI0 : 2, SIE : 1, UIE : 1;
|
|
||||||
} s;
|
|
||||||
struct /*ustatus*/ {
|
|
||||||
uint32_t SD : 1, _WPRI4 : 11, MXR : 1, SUM : 1, _WPRI3 : 1, XS : 2, FS : 2, _WPRI2 : 8, UPIE : 1, _WPRI0 : 3,
|
|
||||||
UIE : 1;
|
|
||||||
} u;
|
|
||||||
};
|
|
||||||
|
|
||||||
using mstatus64_t = union {
|
|
||||||
uint64_t val;
|
|
||||||
struct /*mstatus*/ {
|
|
||||||
uint64_t SD : 1, // SD bit is read-only and is set when either the FS or XS
|
|
||||||
// bits encode a Dirty state (i.e., SD=((FS==11) OR
|
|
||||||
// (XS==11)))
|
|
||||||
_WPRI4 : 27, // unused
|
|
||||||
SXL : 2, // value of XLEN for S-mode
|
|
||||||
UXL : 2, // value of XLEN for U-mode
|
|
||||||
_WPRI3 : 9, // unused
|
|
||||||
TSR : 1, // Trap SRET
|
|
||||||
TW : 1, // Timeout Wait
|
|
||||||
TVM : 1, // Trap Virtual Memory
|
|
||||||
MXR : 1, // Make eXecutable Readable
|
|
||||||
SUM : 1, // permit Supervisor User Memory access
|
|
||||||
MPRV : 1, // Modify PRiVilege
|
|
||||||
XS : 2, // status of additional user-mode extensions and associated
|
|
||||||
// state, All off/None dirty or clean, some on/None dirty, some
|
|
||||||
// clean/Some dirty
|
|
||||||
FS : 2, // floating-point unit status Off/Initial/Clean/Dirty
|
|
||||||
MPP : 2, // machine previous privilege
|
|
||||||
_WPRI2 : 2, // unused
|
|
||||||
SPP : 1, // supervisor previous privilege
|
|
||||||
MPIE : 1, // previous machine interrupt-enable
|
|
||||||
_WPRI1 : 1, // unused
|
|
||||||
SPIE : 1, // previous supervisor interrupt-enable
|
|
||||||
UPIE : 1, // previous user interrupt-enable
|
|
||||||
MIE : 1, // machine interrupt-enable
|
|
||||||
_WPRI0 : 1, // unused
|
|
||||||
SIE : 1, // supervisor interrupt-enable
|
|
||||||
UIE : 1; // ‚user interrupt-enable
|
|
||||||
} m;
|
|
||||||
struct /*sstatus*/ {
|
|
||||||
uint64_t SD : 1,
|
|
||||||
_WPRI5 : 29, // unused
|
|
||||||
UXL : 2, // value of XLEN for U-mode
|
|
||||||
_WPRI4 : 12, MXR : 1, SUM : 1, _WPRI3 : 1, XS : 2, FS : 2, _WPRI2 : 4, SPP : 1, _WPRI1 : 2, SPIE : 1,
|
|
||||||
UPIE : 1, _WPRI0 : 2, SIE : 1, UIE : 1;
|
|
||||||
} s;
|
|
||||||
struct /*ustatus*/ {
|
|
||||||
uint32_t SD : 1,
|
|
||||||
_WPRI4 : 29, // unused
|
|
||||||
UXL : 2, // value of XLEN for U-mode
|
|
||||||
_WPRI3 : 12, MXR : 1, SUM : 1, _WPRI2 : 1, XS : 2, FS : 2, _WPRI1 : 8, UPIE : 1, _WPRI0 : 3, UIE : 1;
|
|
||||||
} u;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <unsigned L> inline vm_info decode_vm_info(uint32_t state, uint64_t sptbr);
|
|
||||||
|
|
||||||
template <> inline vm_info decode_vm_info<32u>(uint32_t state, uint64_t sptbr) {
|
|
||||||
if (state == PRIV_M) {
|
|
||||||
return {0, 0, 0, 0};
|
|
||||||
} else if (state <= PRIV_S) {
|
|
||||||
switch (bit_sub<31, 1>(sptbr)) {
|
|
||||||
case 0: // off
|
|
||||||
return {0, 0, 0, 0};
|
|
||||||
case 1: // SV32
|
|
||||||
return {2, 10, 4, bit_sub<0, 22>(sptbr) << PGSHIFT};
|
|
||||||
default:
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
return {0, 0, 0, 0}; // dummy
|
|
||||||
}
|
|
||||||
|
|
||||||
template <> inline vm_info decode_vm_info<64u>(uint32_t state, uint64_t sptbr) {
|
|
||||||
if (state == PRIV_M) {
|
|
||||||
return {0, 0, 0, 0};
|
|
||||||
} else if (state <= PRIV_S) {
|
|
||||||
switch (bit_sub<60, 4>(sptbr)) {
|
|
||||||
case 0: // off
|
|
||||||
return {0, 0, 0, 0};
|
|
||||||
case 8: // SV39
|
|
||||||
return {3, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT};
|
|
||||||
case 9: // SV48
|
|
||||||
return {4, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT};
|
|
||||||
case 10: // SV57
|
|
||||||
return {5, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT};
|
|
||||||
case 11: // SV64
|
|
||||||
return {6, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT};
|
|
||||||
default:
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
return {0, 0, 0, 0}; // dummy
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr uint32_t get_mask(unsigned priv_lvl, uint32_t mask) {
|
|
||||||
switch (priv_lvl) {
|
|
||||||
case PRIV_U:
|
|
||||||
return mask & 0x80000011UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001
|
|
||||||
case PRIV_S:
|
|
||||||
return mask & 0x800de133UL; // 0b1000 0000 0000 1101 1110 0001 0011 0011
|
|
||||||
default:
|
|
||||||
return mask & 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr uint64_t get_mask(unsigned priv_lvl, uint64_t mask) {
|
|
||||||
switch (priv_lvl) {
|
|
||||||
case PRIV_U:
|
|
||||||
return mask & 0x8000000000000011ULL; // 0b1...0 1111 0000 0000 0111 1111
|
|
||||||
// 1111 1001 1011 1011
|
|
||||||
case PRIV_S:
|
|
||||||
return mask & 0x80000003000de133ULL; // 0b1...0 0011 0000 0000 0000 1101
|
|
||||||
// 1110 0001 0011 0011
|
|
||||||
default:
|
|
||||||
return mask & 0x8000000f007ff9ddULL; // 0b1...0 1111 0000 0000 0111 1111
|
|
||||||
// 1111 1001 1011 1011
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr uint32_t get_misa(uint32_t mask) { return (1UL << 30) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M; }
|
|
||||||
|
|
||||||
constexpr uint64_t get_misa(uint64_t mask) { return (2ULL << 62) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M; }
|
|
||||||
|
|
||||||
template <typename BASE> struct riscv_hart_msu_vp : public BASE {
|
|
||||||
using super = BASE;
|
using super = BASE;
|
||||||
using this_class = riscv_hart_msu_vp<BASE>;
|
using this_class = riscv_hart_msu_vp<BASE>;
|
||||||
using virt_addr_t = typename super::virt_addr_t;
|
using virt_addr_t = typename super::virt_addr_t;
|
||||||
|
@ -415,6 +258,148 @@ template <typename BASE> struct riscv_hart_msu_vp : public BASE {
|
||||||
using rd_csr_f = iss::status (this_class::*)(unsigned addr, reg_t &);
|
using rd_csr_f = iss::status (this_class::*)(unsigned addr, reg_t &);
|
||||||
using wr_csr_f = iss::status (this_class::*)(unsigned addr, reg_t);
|
using wr_csr_f = iss::status (this_class::*)(unsigned addr, reg_t);
|
||||||
|
|
||||||
|
// primary template
|
||||||
|
template<class T, class Enable = void> struct hart_state { };
|
||||||
|
// specialization 32bit
|
||||||
|
template <typename T>
|
||||||
|
struct hart_state<T, typename std::enable_if<std::is_same<T, uint32_t>::value>::type> {
|
||||||
|
BEGIN_BF_DECL(mstatus_t, T);
|
||||||
|
// SD bit is read-only and is set when either the FS or XS bits encode a Dirty state (i.e., SD=((FS==11) OR XS==11)))
|
||||||
|
BF_FIELD(SD, 31, 1);
|
||||||
|
// Trap SRET
|
||||||
|
BF_FIELD(TSR, 22, 1);
|
||||||
|
// Timeout Wait
|
||||||
|
BF_FIELD(TW, 21, 1);
|
||||||
|
// Trap Virtual Memory
|
||||||
|
BF_FIELD(TVM, 20, 1);
|
||||||
|
// Make eXecutable Readable
|
||||||
|
BF_FIELD(MXR, 19, 1);
|
||||||
|
// permit Supervisor User Memory access
|
||||||
|
BF_FIELD(SUM, 18, 1);
|
||||||
|
// Modify PRiVilege
|
||||||
|
BF_FIELD(MPRV, 17, 1);
|
||||||
|
// status of additional user-mode extensions and associated state, All off/None dirty or clean, some on/None dirty, some clean/Some dirty
|
||||||
|
BF_FIELD(XS, 15, 2);
|
||||||
|
// floating-point unit status Off/Initial/Clean/Dirty
|
||||||
|
BF_FIELD(FS, 13, 2);
|
||||||
|
// machine previous privilege
|
||||||
|
BF_FIELD(MPP, 11, 2);
|
||||||
|
// supervisor previous privilege
|
||||||
|
BF_FIELD(SPP, 8, 1);
|
||||||
|
// previous machine interrupt-enable
|
||||||
|
BF_FIELD(MPIE, 7, 1);
|
||||||
|
// previous supervisor interrupt-enable
|
||||||
|
BF_FIELD(SPIE, 5, 1);
|
||||||
|
// previous user interrupt-enable
|
||||||
|
BF_FIELD(UPIE, 4, 1);
|
||||||
|
// machine interrupt-enable
|
||||||
|
BF_FIELD(MIE, 3, 1);
|
||||||
|
// supervisor interrupt-enable
|
||||||
|
BF_FIELD(SIE, 1, 1);
|
||||||
|
// user interrupt-enable
|
||||||
|
BF_FIELD(UIE, 0, 1);
|
||||||
|
END_BF_DECL();
|
||||||
|
|
||||||
|
mstatus_t mstatus;
|
||||||
|
|
||||||
|
T satp;
|
||||||
|
|
||||||
|
static constexpr T get_misa() { return (1UL << 30) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M; }
|
||||||
|
|
||||||
|
static constexpr uint32_t get_mask(unsigned priv_lvl) {
|
||||||
|
switch (priv_lvl) {
|
||||||
|
case PRIV_U: return 0x80000011UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001
|
||||||
|
case PRIV_S: return 0x800de133UL; // 0b1000 0000 0000 1101 1110 0001 0011 0011
|
||||||
|
default: return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline vm_info decode_vm_info(uint32_t state, 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
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// specialization 64bit
|
||||||
|
template <typename T>
|
||||||
|
struct hart_state<T, typename std::enable_if<std::is_same<T, uint64_t>::value>::type> {
|
||||||
|
BEGIN_BF_DECL(mstatus_t, T);
|
||||||
|
// SD bit is read-only and is set when either the FS or XS bits encode a Dirty state (i.e., SD=((FS==11) OR XS==11)))
|
||||||
|
BF_FIELD(SD, 63, 1);
|
||||||
|
// value of XLEN for S-mode
|
||||||
|
BF_FIELD(SXL, 34, 2);
|
||||||
|
// value of XLEN for U-mode
|
||||||
|
BF_FIELD(UXL, 32, 2);
|
||||||
|
// Trap SRET
|
||||||
|
BF_FIELD(TSR, 22, 1);
|
||||||
|
// Timeout Wait
|
||||||
|
BF_FIELD(TW, 21, 1);
|
||||||
|
// Trap Virtual Memory
|
||||||
|
BF_FIELD(TVM, 20, 1);
|
||||||
|
// Make eXecutable Readable
|
||||||
|
BF_FIELD(MXR, 19, 1);
|
||||||
|
// permit Supervisor User Memory access
|
||||||
|
BF_FIELD(SUM, 18, 1);
|
||||||
|
// Modify PRiVilege
|
||||||
|
BF_FIELD(MPRV, 17, 1);
|
||||||
|
// status of additional user-mode extensions and associated state, All off/None dirty or clean, some on/None dirty, some clean/Some dirty
|
||||||
|
BF_FIELD(XS, 15, 2);
|
||||||
|
// floating-point unit status Off/Initial/Clean/Dirty
|
||||||
|
BF_FIELD(FS, 13, 2);
|
||||||
|
// machine previous privilege
|
||||||
|
BF_FIELD(MPP, 11, 2);
|
||||||
|
// supervisor previous privilege
|
||||||
|
BF_FIELD(SPP, 8, 1);
|
||||||
|
// previous machine interrupt-enable
|
||||||
|
BF_FIELD(MPIE, 7, 1);
|
||||||
|
// previous supervisor interrupt-enable
|
||||||
|
BF_FIELD(SPIE, 5, 1);
|
||||||
|
// previous user interrupt-enable
|
||||||
|
BF_FIELD(UPIE, 4, 1);
|
||||||
|
// machine interrupt-enable
|
||||||
|
BF_FIELD(MIE, 3, 1);
|
||||||
|
// supervisor interrupt-enable
|
||||||
|
BF_FIELD(SIE, 1, 1);
|
||||||
|
// user interrupt-enable
|
||||||
|
BF_FIELD(UIE, 0, 1);
|
||||||
|
END_BF_DECL();
|
||||||
|
|
||||||
|
mstatus_t mstatus;
|
||||||
|
|
||||||
|
T satp;
|
||||||
|
|
||||||
|
static constexpr T get_misa() { return (2ULL << 62) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M; }
|
||||||
|
|
||||||
|
static constexpr T get_mask(unsigned priv_lvl) {
|
||||||
|
switch (priv_lvl) {
|
||||||
|
case PRIV_U: return 0x8000000000000011ULL; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011
|
||||||
|
case PRIV_S: return 0x80000003000de133ULL; // 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline vm_info decode_vm_info(uint32_t state, 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 super::reg_t PGSIZE = 1 << PGSHIFT;
|
const typename super::reg_t PGSIZE = 1 << PGSHIFT;
|
||||||
const typename super::reg_t PGMASK = PGSIZE - 1;
|
const typename super::reg_t PGMASK = PGSIZE - 1;
|
||||||
|
|
||||||
|
@ -446,7 +431,7 @@ template <typename BASE> struct riscv_hart_msu_vp : public BASE {
|
||||||
virtual std::string get_additional_disass_info() {
|
virtual std::string get_additional_disass_info() {
|
||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
s << "[p:" << lvl[this->reg.machine_state] << ";s:0x" << std::hex << std::setfill('0')
|
s << "[p:" << lvl[this->reg.machine_state] << ";s:0x" << std::hex << std::setfill('0')
|
||||||
<< std::setw(sizeof(reg_t) * 2) << mstatus_r << std::dec << ";c:" << this->reg.icount << "]";
|
<< std::setw(sizeof(reg_t) * 2) << (reg_t)state.mstatus << std::dec << ";c:" << this->reg.icount << "]";
|
||||||
return s.str();
|
return s.str();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -466,8 +451,7 @@ 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;
|
||||||
reg_t &mstatus_r;
|
hart_state<reg_t> state;
|
||||||
reg_t &satp_r;
|
|
||||||
unsigned to_host_wr_cnt = 0;
|
unsigned to_host_wr_cnt = 0;
|
||||||
std::stringstream uart_buf;
|
std::stringstream uart_buf;
|
||||||
std::unordered_map<reg_t, uint64_t> ptw;
|
std::unordered_map<reg_t, uint64_t> ptw;
|
||||||
|
@ -490,9 +474,8 @@ private:
|
||||||
|
|
||||||
template <typename BASE>
|
template <typename BASE>
|
||||||
riscv_hart_msu_vp<BASE>::riscv_hart_msu_vp()
|
riscv_hart_msu_vp<BASE>::riscv_hart_msu_vp()
|
||||||
: mstatus_r(csr[mstatus])
|
: state() {
|
||||||
, satp_r(csr[satp]) {
|
csr[misa] = hart_state<reg_t>::get_misa();
|
||||||
csr[misa] = traits<BASE>::XLEN == 32 ? 1ULL << (traits<BASE>::XLEN - 2) : 2ULL << (traits<BASE>::XLEN - 2);
|
|
||||||
uart_buf.str("");
|
uart_buf.str("");
|
||||||
// read-only registers
|
// read-only registers
|
||||||
csr_wr_cb[misa] = nullptr;
|
csr_wr_cb[misa] = nullptr;
|
||||||
|
@ -539,13 +522,10 @@ template <typename BASE> void riscv_hart_msu_vp<BASE>::load_file(std::string nam
|
||||||
// Load ELF data
|
// Load ELF data
|
||||||
if (!reader.load(name)) throw std::runtime_error("could not process elf file");
|
if (!reader.load(name)) throw std::runtime_error("could not process elf file");
|
||||||
// check elf properties
|
// check elf properties
|
||||||
// TODO: fix ELFCLASS like:
|
if ( reader.get_class() != ELFCLASS32 )
|
||||||
// if ( reader.get_class() != ELFCLASS32 ) throw std::runtime_error("wrong
|
if(sizeof(reg_t) == 4) throw std::runtime_error("wrong elf class in file");
|
||||||
// elf class in file");
|
|
||||||
if (reader.get_type() != ET_EXEC) throw std::runtime_error("wrong elf type in file");
|
if (reader.get_type() != ET_EXEC) throw std::runtime_error("wrong elf type in file");
|
||||||
// TODO: fix machine type like:
|
if ( reader.get_machine() != EM_RISCV ) throw std::runtime_error("wrong elf machine in file");
|
||||||
// if ( reader.get_machine() != EM_RISCV ) throw std::runtime_error("wrong
|
|
||||||
// elf machine in file");
|
|
||||||
for (const auto pseg : reader.segments) {
|
for (const auto pseg : reader.segments) {
|
||||||
const auto fsize = pseg->get_file_size(); // 0x42c/0x0
|
const auto fsize = pseg->get_file_size(); // 0x42c/0x0
|
||||||
const auto seg_data = pseg->get_data();
|
const auto seg_data = pseg->get_data();
|
||||||
|
@ -588,7 +568,7 @@ iss::status riscv_hart_msu_vp<BASE>::read(const iss::addr_t &addr, unsigned leng
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if ((addr.val & ~PGMASK) != ((addr.val + length - 1) & ~PGMASK)) { // we may cross a page boundary
|
if ((addr.val & ~PGMASK) != ((addr.val + length - 1) & ~PGMASK)) { // we may cross a page boundary
|
||||||
vm_info vm = decode_vm_info<traits<BASE>::XLEN>(this->reg.machine_state, csr[satp]);
|
vm_info vm = hart_state<reg_t>::decode_vm_info(this->reg.machine_state, state.satp);
|
||||||
if (vm.levels != 0) { // VM is active
|
if (vm.levels != 0) { // VM is active
|
||||||
auto split_addr = (addr.val + length) & ~PGMASK;
|
auto split_addr = (addr.val + length) & ~PGMASK;
|
||||||
auto len1 = split_addr - addr.val;
|
auto len1 = split_addr - addr.val;
|
||||||
|
@ -616,8 +596,7 @@ iss::status riscv_hart_msu_vp<BASE>::read(const iss::addr_t &addr, unsigned leng
|
||||||
switch (addr.val) {
|
switch (addr.val) {
|
||||||
case 2: // SFENCE:VMA lower
|
case 2: // SFENCE:VMA lower
|
||||||
case 3: { // SFENCE:VMA upper
|
case 3: { // SFENCE:VMA upper
|
||||||
auto status = csr[mstatus];
|
auto tvm = state.mstatus.TVM;
|
||||||
auto tvm = status & (1 << 20);
|
|
||||||
if (this->reg.machine_state == PRIV_S & tvm != 0) {
|
if (this->reg.machine_state == PRIV_S & tvm != 0) {
|
||||||
this->reg.trap_state = (1 << 31) | (2 << 16);
|
this->reg.trap_state = (1 << 31) | (2 << 16);
|
||||||
this->fault_data = this->reg.PC;
|
this->fault_data = this->reg.PC;
|
||||||
|
@ -677,7 +656,7 @@ iss::status riscv_hart_msu_vp<BASE>::write(const iss::addr_t &addr, unsigned len
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if ((addr.val & ~PGMASK) != ((addr.val + length - 1) & ~PGMASK)) { // we may cross a page boundary
|
if ((addr.val & ~PGMASK) != ((addr.val + length - 1) & ~PGMASK)) { // we may cross a page boundary
|
||||||
vm_info vm = decode_vm_info<traits<BASE>::XLEN>(this->reg.machine_state, csr[satp]);
|
vm_info vm = hart_state<reg_t>::decode_vm_info(this->reg.machine_state, state.satp);
|
||||||
if (vm.levels != 0) { // VM is active
|
if (vm.levels != 0) { // VM is active
|
||||||
auto split_addr = (addr.val + length) & ~PGMASK;
|
auto split_addr = (addr.val + length) & ~PGMASK;
|
||||||
auto len1 = split_addr - addr.val;
|
auto len1 = split_addr - addr.val;
|
||||||
|
@ -738,8 +717,7 @@ iss::status riscv_hart_msu_vp<BASE>::write(const iss::addr_t &addr, unsigned len
|
||||||
case 2:
|
case 2:
|
||||||
case 3: {
|
case 3: {
|
||||||
ptw.clear();
|
ptw.clear();
|
||||||
auto status = csr[mstatus];
|
auto tvm = state.mstatus.TVM;
|
||||||
auto tvm = status & (1 << 20);
|
|
||||||
if (this->reg.machine_state == PRIV_S & tvm != 0) {
|
if (this->reg.machine_state == PRIV_S & tvm != 0) {
|
||||||
this->reg.trap_state = (1 << 31) | (2 << 16);
|
this->reg.trap_state = (1 << 31) | (2 << 16);
|
||||||
this->fault_data = this->reg.PC;
|
this->fault_data = this->reg.PC;
|
||||||
|
@ -799,18 +777,17 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_cycle(unsigne
|
||||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_status(unsigned addr, reg_t &val) {
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_status(unsigned addr, reg_t &val) {
|
||||||
auto req_priv_lvl = addr >> 8;
|
auto req_priv_lvl = addr >> 8;
|
||||||
if (this->reg.machine_state < req_priv_lvl) throw illegal_instruction_fault(this->fault_data);
|
if (this->reg.machine_state < req_priv_lvl) throw illegal_instruction_fault(this->fault_data);
|
||||||
auto mask = get_mask(req_priv_lvl, (reg_t)(std::numeric_limits<reg_t>::max()));
|
val = state.mstatus & hart_state<reg_t>::get_mask(req_priv_lvl);
|
||||||
val = csr[mstatus] & mask;
|
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_status(unsigned addr, reg_t val) {
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_status(unsigned addr, reg_t val) {
|
||||||
auto req_priv_lvl = addr >> 8;
|
auto req_priv_lvl = addr >> 8;
|
||||||
if (this->reg.machine_state < req_priv_lvl) throw illegal_instruction_fault(this->fault_data);
|
if (this->reg.machine_state < req_priv_lvl) throw illegal_instruction_fault(this->fault_data);
|
||||||
auto mask = get_mask(req_priv_lvl, (reg_t)std::numeric_limits<reg_t>::max());
|
auto mask = hart_state<reg_t>::get_mask(req_priv_lvl);
|
||||||
auto old_val = csr[mstatus];
|
auto old_val = state.mstatus;
|
||||||
auto new_val = (old_val & ~mask) | (val & mask);
|
auto new_val = (old_val & ~mask) | (val & mask);
|
||||||
csr[mstatus] = new_val;
|
state.mstatus = new_val;
|
||||||
check_interrupt();
|
check_interrupt();
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
@ -836,9 +813,9 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_ie(unsigned
|
||||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_ip(unsigned addr, reg_t &val) {
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_ip(unsigned addr, reg_t &val) {
|
||||||
auto req_priv_lvl = addr >> 8;
|
auto req_priv_lvl = addr >> 8;
|
||||||
if (this->reg.machine_state < req_priv_lvl) throw illegal_instruction_fault(this->fault_data);
|
if (this->reg.machine_state < req_priv_lvl) throw illegal_instruction_fault(this->fault_data);
|
||||||
val = csr[mie];
|
val = csr[mip];
|
||||||
if (addr < mie) val &= csr[mideleg];
|
if (addr < mip) val &= csr[mideleg];
|
||||||
if (addr < sie) val &= csr[sideleg];
|
if (addr < sip) val &= csr[sideleg];
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -852,26 +829,24 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_ip(unsigned
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_satp(unsigned addr, reg_t &val) {
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_satp(unsigned addr, reg_t &val) {
|
||||||
auto status = csr[mstatus];
|
reg_t tvm = state.mstatus.TVM;
|
||||||
auto tvm = status & (1 << 20);
|
|
||||||
if (this->reg.machine_state == PRIV_S & tvm != 0) {
|
if (this->reg.machine_state == PRIV_S & tvm != 0) {
|
||||||
this->reg.trap_state = (1 << 31) | (2 << 16);
|
this->reg.trap_state = (1 << 31) | (2 << 16);
|
||||||
this->fault_data = this->reg.PC;
|
this->fault_data = this->reg.PC;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
val = csr[satp];
|
val = state.satp;
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_satp(unsigned addr, reg_t val) {
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_satp(unsigned addr, reg_t val) {
|
||||||
auto status = csr[mstatus];
|
reg_t tvm = state.mstatus.TVM;
|
||||||
auto tvm = status & (1 << 20);
|
|
||||||
if (this->reg.machine_state == PRIV_S & tvm != 0) {
|
if (this->reg.machine_state == PRIV_S & tvm != 0) {
|
||||||
this->reg.trap_state = (1 << 31) | (2 << 16);
|
this->reg.trap_state = (1 << 31) | (2 << 16);
|
||||||
this->fault_data = this->reg.PC;
|
this->fault_data = this->reg.PC;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
csr[satp] = val;
|
state.satp = val;
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -973,7 +948,7 @@ iss::status riscv_hart_msu_vp<BASE>::write_mem(phys_addr_t paddr, unsigned lengt
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE> void riscv_hart_msu_vp<BASE>::check_interrupt() {
|
template <typename BASE> void riscv_hart_msu_vp<BASE>::check_interrupt() {
|
||||||
auto status = csr[mstatus];
|
auto status = state.mstatus;
|
||||||
auto ip = csr[mip];
|
auto ip = csr[mip];
|
||||||
auto ie = csr[mie];
|
auto ie = csr[mie];
|
||||||
auto ideleg = csr[mideleg];
|
auto ideleg = csr[mideleg];
|
||||||
|
@ -983,19 +958,19 @@ template <typename BASE> void riscv_hart_msu_vp<BASE>::check_interrupt() {
|
||||||
// any synchronous traps.
|
// any synchronous traps.
|
||||||
auto ena_irq = ip & ie;
|
auto ena_irq = ip & ie;
|
||||||
|
|
||||||
auto mie = (csr[mstatus] >> 3) & 1;
|
auto 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);
|
||||||
auto enabled_interrupts = m_enabled ? ena_irq & ~ideleg : 0;
|
auto enabled_interrupts = m_enabled ? ena_irq & ~ideleg : 0;
|
||||||
|
|
||||||
if (enabled_interrupts == 0) {
|
if (enabled_interrupts == 0) {
|
||||||
auto sie = (csr[mstatus] >> 1) & 1;
|
auto sie = state.mstatus.SIE;
|
||||||
auto s_enabled = this->reg.machine_state < PRIV_S || (this->reg.machine_state == PRIV_S && sie);
|
auto s_enabled = this->reg.machine_state < PRIV_S || (this->reg.machine_state == PRIV_S && sie);
|
||||||
enabled_interrupts = s_enabled ? ena_irq & ideleg : 0;
|
enabled_interrupts = s_enabled ? ena_irq & ideleg : 0;
|
||||||
}
|
}
|
||||||
if (enabled_interrupts != 0) {
|
if (enabled_interrupts != 0) {
|
||||||
int res = 0;
|
int res = 0;
|
||||||
while ((enabled_interrupts & 1) == 0) enabled_interrupts >>= 1, res++;
|
while ((enabled_interrupts & 1) == 0) enabled_interrupts >>= 1, res++;
|
||||||
this->reg.pending_trap = res << 16 | 1;
|
this->reg.pending_trap = res << 16 | 1; // 0x80 << 24 | (cause << 16) | trap_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1011,12 +986,11 @@ typename riscv_hart_msu_vp<BASE>::phys_addr_t riscv_hart_msu_vp<BASE>::v2p(const
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto type = (access_type)(addr.getAccessType() & ~iss::DEBUG);
|
const auto type = (access_type)(addr.getAccessType() & ~iss::DEBUG);
|
||||||
uint32_t mode = type != iss::FETCH && bit_sub<17, 1>(mstatus_r) ? // MPRV
|
uint32_t mode = type != iss::FETCH && state.mstatus.MPRV ? // MPRV
|
||||||
mode = bit_sub<11, 2>(mstatus_r)
|
mode = state.mstatus.MPP:
|
||||||
: // MPV
|
|
||||||
this->reg.machine_state;
|
this->reg.machine_state;
|
||||||
|
|
||||||
const vm_info vm = decode_vm_info<traits<BASE>::XLEN>(mode, satp_r);
|
const vm_info vm = hart_state<reg_t>::decode_vm_info(mode, state.satp);
|
||||||
|
|
||||||
if (vm.levels == 0) {
|
if (vm.levels == 0) {
|
||||||
phys_addr_t ret(addr);
|
phys_addr_t ret(addr);
|
||||||
|
@ -1025,8 +999,8 @@ typename riscv_hart_msu_vp<BASE>::phys_addr_t riscv_hart_msu_vp<BASE>::v2p(const
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool s_mode = mode == PRIV_S;
|
const bool s_mode = mode == PRIV_S;
|
||||||
const bool sum = bit_sub<18, 1>(mstatus_r); // MSTATUS_SUM);
|
const bool sum = state.mstatus.SUM;
|
||||||
const bool mxr = bit_sub<19, 1>(mstatus_r); // MSTATUS_MXR);
|
const bool mxr = state.mstatus.MXR;
|
||||||
|
|
||||||
auto it = ptw.find(addr.val >> PGSHIFT);
|
auto it = ptw.find(addr.val >> PGSHIFT);
|
||||||
if (it != ptw.end()) {
|
if (it != ptw.end()) {
|
||||||
|
@ -1110,9 +1084,10 @@ typename riscv_hart_msu_vp<BASE>::phys_addr_t riscv_hart_msu_vp<BASE>::v2p(const
|
||||||
|
|
||||||
template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t flags, uint64_t addr) {
|
template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t flags, uint64_t addr) {
|
||||||
auto cur_priv = this->reg.machine_state;
|
auto cur_priv = this->reg.machine_state;
|
||||||
|
// flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0]
|
||||||
// calculate and write mcause val
|
// calculate and write mcause val
|
||||||
auto trap_id = flags & 0xffff;
|
auto trap_id = bit_sub<0, 16>(flags);
|
||||||
auto cause = (flags >> 16) & 0x7fff;
|
auto cause = bit_sub<16, 15>(flags);
|
||||||
if (trap_id == 0 && cause == 11) cause = 0x8 + cur_priv; // adjust environment call cause
|
if (trap_id == 0 && cause == 11) cause = 0x8 + cur_priv; // adjust environment call cause
|
||||||
// calculate effective privilege level
|
// calculate effective privilege level
|
||||||
auto new_priv = PRIV_M;
|
auto new_priv = PRIV_M;
|
||||||
|
@ -1143,26 +1118,23 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
|
||||||
// is written with the value of the active interrupt-enable bit at the time of
|
// is written with the value of the active interrupt-enable bit at the time of
|
||||||
// the trap; and the x IE field of mstatus
|
// the trap; and the x IE field of mstatus
|
||||||
// is cleared
|
// is cleared
|
||||||
auto status = csr[mstatus];
|
// store the actual privilege level in yPP and store interrupt enable flags
|
||||||
auto xie = (status >> cur_priv) & 1;
|
|
||||||
// store the actual privilege level in yPP
|
|
||||||
switch (new_priv) {
|
switch (new_priv) {
|
||||||
case PRIV_M:
|
case PRIV_M:
|
||||||
status &= ~(3 << 11);
|
state.mstatus.MPP=cur_priv;
|
||||||
status |= (cur_priv & 0x3) << 11;
|
state.mstatus.MPIE=state.mstatus.MIE;
|
||||||
break;
|
break;
|
||||||
case PRIV_S:
|
case PRIV_S:
|
||||||
status &= ~(1 << 8);
|
state.mstatus.SPP = cur_priv;
|
||||||
status |= (cur_priv & 0x1) << 8;
|
state.mstatus.SPIE=state.mstatus.SIE;
|
||||||
|
break;
|
||||||
|
case PRIV_U:
|
||||||
|
state.mstatus.UPIE=state.mstatus.UIE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// store interrupt enable flags
|
|
||||||
status &= ~(1 << (new_priv + 4) | 1 << cur_priv); // clear respective xPIE and yIE
|
|
||||||
status |= (xie << (new_priv + 4)); // store yIE
|
|
||||||
|
|
||||||
csr[mstatus] = status;
|
|
||||||
// get trap vector
|
// get trap vector
|
||||||
auto ivec = csr[utvec | (new_priv << 8)];
|
auto ivec = csr[utvec | (new_priv << 8)];
|
||||||
// calculate addr// set NEXT_PC to trap addressess to jump to based on MODE
|
// calculate addr// set NEXT_PC to trap addressess to jump to based on MODE
|
||||||
|
@ -1174,7 +1146,7 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
|
||||||
this->reg.trap_state = 0;
|
this->reg.trap_state = 0;
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
sprintf(buffer, "0x%016lx", addr);
|
sprintf(buffer, "0x%016lx", addr);
|
||||||
CLOG(INFO, disass) << (trap_id ? "Interrupt " : "Trap ") << trap_id << " with cause '" << irq_str[cause]
|
CLOG(INFO, disass) << (trap_id ? "Interrupt " : "Trap ") << " with cause '" << (trap_id ? irq_str[cause] : trap_str[cause])
|
||||||
<< "' at address " << buffer << " occurred, changing privilege level from " << lvl[cur_priv]
|
<< "' at address " << buffer << " occurred, changing privilege level from " << lvl[cur_priv]
|
||||||
<< " to " << lvl[new_priv];
|
<< " to " << lvl[new_priv];
|
||||||
return this->reg.NEXT_PC;
|
return this->reg.NEXT_PC;
|
||||||
|
@ -1183,10 +1155,9 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
|
||||||
template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::leave_trap(uint64_t flags) {
|
template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::leave_trap(uint64_t flags) {
|
||||||
auto cur_priv = this->reg.machine_state;
|
auto cur_priv = this->reg.machine_state;
|
||||||
auto inst_priv = flags & 0x3;
|
auto inst_priv = flags & 0x3;
|
||||||
auto status = csr[mstatus];
|
auto status = state.mstatus;
|
||||||
auto ppl = inst_priv; // previous privilege level
|
|
||||||
|
|
||||||
auto tsr = status & (1 << 22);
|
auto tsr = state.mstatus.TSR;
|
||||||
if (cur_priv == PRIV_S && inst_priv == PRIV_S && tsr != 0) {
|
if (cur_priv == PRIV_S && inst_priv == PRIV_S && tsr != 0) {
|
||||||
this->reg.trap_state = (1 << 31) | (2 << 16);
|
this->reg.trap_state = (1 << 31) | (2 << 16);
|
||||||
this->fault_data = this->reg.PC;
|
this->fault_data = this->reg.PC;
|
||||||
|
@ -1194,33 +1165,32 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::leave_trap(uint64_t f
|
||||||
}
|
}
|
||||||
|
|
||||||
// pop the relevant lower-privilege interrupt enable and privilege mode stack
|
// pop the relevant lower-privilege interrupt enable and privilege mode stack
|
||||||
|
// clear respective yIE
|
||||||
switch (inst_priv) {
|
switch (inst_priv) {
|
||||||
case PRIV_M:
|
case PRIV_M:
|
||||||
ppl = (status >> 11) & 0x3;
|
this->reg.machine_state = state.mstatus.MPP;
|
||||||
status &= ~(0x3 << 11); // clear mpp to U mode
|
state.mstatus.MPP=0; // clear mpp to U mode
|
||||||
|
state.mstatus.MIE=state.mstatus.MPIE;
|
||||||
break;
|
break;
|
||||||
case PRIV_S:
|
case PRIV_S:
|
||||||
ppl = (status >> 8) & 1;
|
this->reg.machine_state = state.mstatus.SPP;
|
||||||
status &= ~(1 << 8); // clear spp to U mode
|
state.mstatus.SPP= 0; // clear spp to U mode
|
||||||
|
state.mstatus.SIE=state.mstatus.SPIE;
|
||||||
break;
|
break;
|
||||||
case PRIV_U:
|
case PRIV_U:
|
||||||
ppl = 0;
|
this->reg.machine_state = 0;
|
||||||
|
state.mstatus.UIE=state.mstatus.UPIE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// 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[uepc | inst_priv << 8];
|
this->reg.NEXT_PC = csr[uepc | inst_priv << 8];
|
||||||
status &= ~(1 << ppl); // clear respective yIE
|
CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << lvl[cur_priv] << " to " << lvl[this->reg.machine_state];
|
||||||
auto pie = (status >> (inst_priv + 4)) & 0x1; // previous interrupt enable
|
|
||||||
status |= pie << inst_priv; // and set the pie
|
|
||||||
csr[mstatus] = status;
|
|
||||||
this->reg.machine_state = ppl;
|
|
||||||
CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << lvl[cur_priv] << " to " << lvl[ppl];
|
|
||||||
return this->reg.NEXT_PC;
|
return this->reg.NEXT_PC;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE> void riscv_hart_msu_vp<BASE>::wait_until(uint64_t flags) {
|
template <typename BASE> void riscv_hart_msu_vp<BASE>::wait_until(uint64_t flags) {
|
||||||
auto status = csr[mstatus];
|
auto status = state.mstatus;
|
||||||
auto tw = status & (1 << 21);
|
auto tw = status.TW;
|
||||||
if (this->reg.machine_state == PRIV_S && tw != 0) {
|
if (this->reg.machine_state == PRIV_S && tw != 0) {
|
||||||
this->reg.trap_state = (1 << 31) | (2 << 16);
|
this->reg.trap_state = (1 << 31) | (2 << 16);
|
||||||
this->fault_data = this->reg.PC;
|
this->fault_data = this->reg.PC;
|
||||||
|
|
Loading…
Reference in New Issue