aboutsummaryrefslogtreecommitdiff
path: root/riscv/csrs.cc
diff options
context:
space:
mode:
Diffstat (limited to 'riscv/csrs.cc')
-rw-r--r--riscv/csrs.cc454
1 files changed, 409 insertions, 45 deletions
diff --git a/riscv/csrs.cc b/riscv/csrs.cc
index 6fdd6a3..3a32712 100644
--- a/riscv/csrs.cc
+++ b/riscv/csrs.cc
@@ -15,6 +15,8 @@
#include "insn_macros.h"
// For CSR_DCSR_V:
#include "debug_defines.h"
+// For ctz:
+#include "arith.h"
// STATE macro used by require_privilege() macro:
#undef STATE
@@ -313,31 +315,31 @@ bool mseccfg_csr_t::get_sseed() const noexcept {
}
bool mseccfg_csr_t::unlogged_write(const reg_t val) noexcept {
- if (proc->n_pmp == 0)
- return false;
-
- // pmpcfg.L is 1 in any rule or entry (including disabled entries)
- const bool pmplock_recorded = std::any_of(state->pmpaddr, state->pmpaddr + proc->n_pmp,
- [](const pmpaddr_csr_t_p & c) { return c->is_locked(); } );
reg_t new_val = read();
- // When RLB is 0 and pmplock_recorded, RLB is locked to 0.
- // Otherwise set the RLB bit according val
- if (!(pmplock_recorded && (read() & MSECCFG_RLB) == 0)) {
- new_val &= ~MSECCFG_RLB;
- new_val |= (val & MSECCFG_RLB);
- }
+ if (proc->n_pmp != 0) {
+ // pmpcfg.L is 1 in any rule or entry (including disabled entries)
+ const bool pmplock_recorded = std::any_of(state->pmpaddr, state->pmpaddr + proc->n_pmp,
+ [](const pmpaddr_csr_t_p & c) { return c->is_locked(); } );
- new_val |= (val & MSECCFG_MMWP); //MMWP is sticky
- new_val |= (val & MSECCFG_MML); //MML is sticky
+ // When RLB is 0 and pmplock_recorded, RLB is locked to 0.
+ // Otherwise set the RLB bit according val
+ if (!(pmplock_recorded && (read() & MSECCFG_RLB) == 0)) {
+ new_val &= ~MSECCFG_RLB;
+ new_val |= (val & MSECCFG_RLB);
+ }
+
+ new_val |= (val & MSECCFG_MMWP); //MMWP is sticky
+ new_val |= (val & MSECCFG_MML); //MML is sticky
+
+ proc->get_mmu()->flush_tlb();
+ }
if (proc->extension_enabled(EXT_ZKR)) {
uint64_t mask = MSECCFG_USEED | MSECCFG_SSEED;
new_val = (new_val & ~mask) | (val & mask);
}
- proc->get_mmu()->flush_tlb();
-
if (proc->extension_enabled(EXT_ZICFILP)) {
new_val &= ~MSECCFG_MLPE;
new_val |= (val & MSECCFG_MLPE);
@@ -548,13 +550,16 @@ bool mstatus_csr_t::unlogged_write(const reg_t val) noexcept {
| (has_page ? MSTATUS_TVM : 0)
| (has_gva ? MSTATUS_GVA : 0)
| (has_mpv ? MSTATUS_MPV : 0)
+ | (proc->extension_enabled(EXT_SMDBLTRP) ? MSTATUS_MDT : 0)
| (proc->extension_enabled(EXT_ZICFILP) ? (MSTATUS_SPELP | MSTATUS_MPELP) : 0)
| (proc->extension_enabled(EXT_SSDBLTRP) ? SSTATUS_SDT : 0)
;
const reg_t requested_mpp = proc->legalize_privilege(get_field(val, MSTATUS_MPP));
const reg_t adjusted_val = set_field(val, MSTATUS_MPP, requested_mpp);
- const reg_t new_mstatus = (read() & ~mask) | (adjusted_val & mask);
+ reg_t new_mstatus = (read() & ~mask) | (adjusted_val & mask);
+ new_mstatus = (new_mstatus & MSTATUS_MDT) ? (new_mstatus & ~MSTATUS_MIE) : new_mstatus;
+ new_mstatus = (new_mstatus & MSTATUS_SDT) ? (new_mstatus & ~MSTATUS_SIE) : new_mstatus;
maybe_flush_tlb(new_mstatus);
this->val = adjust_sd(new_mstatus);
return true;
@@ -569,6 +574,7 @@ reg_t mstatus_csr_t::compute_mstatus_initial_value() const noexcept {
| (proc->extension_enabled_const('U') && (proc->get_const_xlen() != 32) ? set_field((reg_t)0, MSTATUS_UXL, xlen_to_uxl(proc->get_const_xlen())) : 0)
| (proc->extension_enabled_const('S') && (proc->get_const_xlen() != 32) ? set_field((reg_t)0, MSTATUS_SXL, xlen_to_uxl(proc->get_const_xlen())) : 0)
| (proc->get_mmu()->is_target_big_endian() ? big_endian_bits : 0)
+ | (proc->extension_enabled(EXT_SMDBLTRP) ? MSTATUS_MDT : 0)
| 0; // initial value for mstatus
}
@@ -581,6 +587,7 @@ bool mnstatus_csr_t::unlogged_write(const reg_t val) noexcept {
// NMIE can be set but not cleared
const reg_t mask = (~read() & MNSTATUS_NMIE)
| (proc->extension_enabled('H') ? MNSTATUS_MNPV : 0)
+ | (proc->extension_enabled(EXT_ZICFILP) ? MNSTATUS_MNPELP : 0)
| MNSTATUS_MNPP;
const reg_t requested_mnpp = proc->legalize_privilege(get_field(val, MNSTATUS_MNPP));
@@ -634,6 +641,22 @@ reg_t rv32_high_csr_t::written_value() const noexcept {
return (orig->written_value() >> 32) & 0xffffffffU;
}
+aia_rv32_high_csr_t::aia_rv32_high_csr_t(processor_t* const proc, const reg_t addr, csr_t_p orig):
+ rv32_high_csr_t(proc, addr, orig) {
+}
+
+void aia_rv32_high_csr_t::verify_permissions(insn_t insn, bool write) const {
+ if (proc->extension_enabled(EXT_SMSTATEEN)) {
+ if ((state->prv < PRV_M) && !(state->mstateen[0]->read() & MSTATEEN0_AIA))
+ throw trap_illegal_instruction(insn.bits());
+
+ if (state->v && !(state->hstateen[0]->read() & HSTATEEN0_AIA))
+ throw trap_virtual_instruction(insn.bits());
+ }
+
+ rv32_high_csr_t::verify_permissions(insn, write);
+}
+
// implement class sstatus_csr_t
sstatus_csr_t::sstatus_csr_t(processor_t* const proc, sstatus_proxy_csr_t_p orig, vsstatus_csr_t_p virt):
virtualized_csr_t(proc, orig, virt),
@@ -776,8 +799,14 @@ mip_csr_t::mip_csr_t(processor_t* const proc, const reg_t addr):
mip_or_mie_csr_t(proc, addr) {
}
+void mip_csr_t::write_with_mask(const reg_t mask, const reg_t val) noexcept {
+ if (!(state->mvien->read() & MIP_SEIP) && (mask & MIP_SEIP))
+ state->mvip->write_with_mask(MIP_SEIP, val); // mvip.SEIP is an alias of mip.SEIP when mvien.SEIP=0
+ mip_or_mie_csr_t::write_with_mask(mask & ~MIP_SEIP, val);
+}
+
reg_t mip_csr_t::read() const noexcept {
- return val | state->hvip->basic_csr_t::read();
+ return val | state->hvip->basic_csr_t::read() | ((state->mvien->read() & MIP_SEIP) ? 0 : (state->mvip->basic_csr_t::read() & MIP_SEIP));
}
void mip_csr_t::backdoor_write_with_mask(const reg_t mask, const reg_t val) noexcept {
@@ -859,6 +888,15 @@ mip_proxy_csr_t::mip_proxy_csr_t(processor_t* const proc, const reg_t addr, gene
accr(accr) {
}
+void mip_proxy_csr_t::verify_permissions(insn_t insn, bool write) const {
+ csr_t::verify_permissions(insn, write);
+ if (proc->extension_enabled_const(EXT_SSAIA) && proc->extension_enabled('H')) {
+ if ((state->csrmap[CSR_HVICTL]->read() & HVICTL_VTI) &&
+ proc->extension_enabled('S') && state->v)
+ throw trap_virtual_instruction(insn.bits()); // VS-mode attempts to access sip when hvictl.VTI=1
+ }
+}
+
reg_t mip_proxy_csr_t::read() const noexcept {
return accr->ip_read();
}
@@ -874,6 +912,15 @@ mie_proxy_csr_t::mie_proxy_csr_t(processor_t* const proc, const reg_t addr, gene
accr(accr) {
}
+void mie_proxy_csr_t::verify_permissions(insn_t insn, bool write) const {
+ csr_t::verify_permissions(insn, write);
+ if (proc->extension_enabled_const(EXT_SSAIA) && proc->extension_enabled('H')) {
+ if ((state->csrmap[CSR_HVICTL]->read() & HVICTL_VTI) &&
+ proc->extension_enabled('S') && state->v)
+ throw trap_virtual_instruction(insn.bits()); // VS-mode attempts to access sie when hvictl.VTI=1
+ }
+}
+
reg_t mie_proxy_csr_t::read() const noexcept {
return accr->ie_read();
}
@@ -951,6 +998,38 @@ bool medeleg_csr_t::unlogged_write(const reg_t val) noexcept {
return basic_csr_t::unlogged_write((read() & ~mask) | (val & mask));
}
+sip_csr_t::sip_csr_t(processor_t* const proc, const reg_t addr, generic_int_accessor_t_p accr):
+ mip_proxy_csr_t(proc, addr, accr) {
+}
+
+reg_t sip_csr_t::read() const noexcept {
+ const reg_t mask = ~state->mideleg->read() & state->mvien->read();
+ return (mip_proxy_csr_t::read() & ~mask) | (state->mvip->read() & mask);
+}
+
+bool sip_csr_t::unlogged_write(const reg_t val) noexcept {
+ const reg_t mask = ~state->mideleg->read() & state->mvien->read();
+ state->mvip->write_with_mask(mask & accr->get_ip_write_mask(), val);
+ return mip_proxy_csr_t::unlogged_write(val & ~mask);
+}
+
+sie_csr_t::sie_csr_t(processor_t* const proc, const reg_t addr, generic_int_accessor_t_p accr):
+ mie_proxy_csr_t(proc, addr, accr),
+ val(0) {
+}
+
+reg_t sie_csr_t::read() const noexcept {
+ const reg_t mask = ~state->mideleg->read() & state->mvien->read();
+ return (mie_proxy_csr_t::read() & ~mask) | (val & mask);
+}
+
+bool sie_csr_t::unlogged_write(const reg_t val) noexcept {
+ const reg_t mask = ~state->mideleg->read() & state->mvien->read();
+ this->val = (this->val & ~mask) | (val & mask);
+ mie_proxy_csr_t::unlogged_write(val & ~mask);
+ return true;
+}
+
// implement class masked_csr_t
masked_csr_t::masked_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init):
basic_csr_t(proc, addr, init),
@@ -1070,6 +1149,7 @@ bool virtualized_satp_csr_t::unlogged_write(const reg_t val) noexcept {
wide_counter_csr_t::wide_counter_csr_t(processor_t* const proc, const reg_t addr, smcntrpmf_csr_t_p config_csr):
csr_t(proc, addr),
val(0),
+ written(false),
config_csr(config_csr) {
}
@@ -1078,7 +1158,15 @@ reg_t wide_counter_csr_t::read() const noexcept {
}
void wide_counter_csr_t::bump(const reg_t howmuch) noexcept {
- if (is_counting_enabled()) {
+ if (written) {
+ // Because writing a CSR serializes the simulator, howmuch should
+ // reflect exactly one instruction: the explicit CSR write.
+ // If counting is disabled, though, howmuch will be zero.
+ assert(howmuch <= 1);
+ // The ISA mandates that explicit writes to instret take precedence
+ // over the instret, so simply skip the increment.
+ written = false;
+ } else if (is_counting_enabled()) {
val += howmuch; // to keep log reasonable size, don't log every bump
}
// Clear cached value
@@ -1086,23 +1174,15 @@ void wide_counter_csr_t::bump(const reg_t howmuch) noexcept {
}
bool wide_counter_csr_t::unlogged_write(const reg_t val) noexcept {
+ // Because writing a CSR serializes the simulator and is followed by a
+ // bump, back-to-back writes with no intervening bump should never occur.
+ assert(!written);
+ written = true;
+
this->val = val;
- // The ISA mandates that if an instruction writes instret, the write
- // takes precedence over the increment to instret. However, Spike
- // unconditionally increments instret after executing an instruction.
- // Correct for this artifact by decrementing instret here.
- // Ensure that Smctrpmf hasn't disabled counting.
- if (is_counting_enabled()) {
- this->val--;
- }
return true;
}
-reg_t wide_counter_csr_t::written_value() const noexcept {
- // Re-adjust for upcoming bump()
- return this->val + 1;
-}
-
// Returns true if counting is not inhibited by Smcntrpmf.
// Note that minstretcfg / mcyclecfg / mhpmevent* share the same inhibit bits.
bool wide_counter_csr_t::is_counting_enabled() const noexcept {
@@ -1225,7 +1305,7 @@ hideleg_csr_t::hideleg_csr_t(processor_t* const proc, const reg_t addr, csr_t_p
reg_t hideleg_csr_t::read() const noexcept {
return masked_csr_t::read() & mideleg->read();
-};
+}
hgatp_csr_t::hgatp_csr_t(processor_t* const proc, const reg_t addr):
basic_csr_t(proc, addr, 0) {
@@ -1340,9 +1420,11 @@ dcsr_csr_t::dcsr_csr_t(processor_t* const proc, const reg_t addr):
ebreaku(false),
ebreakvs(false),
ebreakvu(false),
- halt(false),
v(false),
+ mprven(false),
cause(0),
+ ext_cause(0),
+ cetrig(0),
pelp(elp_t::NO_LP_EXPECTED) {
}
@@ -1354,7 +1436,7 @@ void dcsr_csr_t::verify_permissions(insn_t insn, bool write) const {
reg_t dcsr_csr_t::read() const noexcept {
reg_t result = 0;
- result = set_field(result, DCSR_XDEBUGVER, 1);
+ result = set_field(result, DCSR_XDEBUGVER, 4);
result = set_field(result, DCSR_EBREAKM, ebreakm);
result = set_field(result, DCSR_EBREAKS, ebreaks);
result = set_field(result, DCSR_EBREAKU, ebreaku);
@@ -1363,9 +1445,13 @@ reg_t dcsr_csr_t::read() const noexcept {
result = set_field(result, DCSR_STOPCOUNT, 0);
result = set_field(result, DCSR_STOPTIME, 0);
result = set_field(result, DCSR_CAUSE, cause);
+ result = set_field(result, DCSR_EXTCAUSE, ext_cause);
+ if (proc->extension_enabled(EXT_SMDBLTRP))
+ result = set_field(result, DCSR_CETRIG, cetrig);
result = set_field(result, DCSR_STEP, step);
result = set_field(result, DCSR_PRV, prv);
result = set_field(result, CSR_DCSR_V, v);
+ result = set_field(result, DCSR_MPRVEN, mprven);
result = set_field(result, DCSR_PELP, pelp);
return result;
}
@@ -1380,14 +1466,17 @@ bool dcsr_csr_t::unlogged_write(const reg_t val) noexcept {
ebreakvs = proc->extension_enabled('H') ? get_field(val, CSR_DCSR_EBREAKVS) : false;
ebreakvu = proc->extension_enabled('H') ? get_field(val, CSR_DCSR_EBREAKVU) : false;
v = proc->extension_enabled('H') ? get_field(val, CSR_DCSR_V) : false;
+ mprven = get_field(val, CSR_DCSR_MPRVEN);
pelp = proc->extension_enabled(EXT_ZICFILP) ?
static_cast<elp_t>(get_field(val, DCSR_PELP)) : elp_t::NO_LP_EXPECTED;
+ cetrig = proc->extension_enabled(EXT_SMDBLTRP) ? get_field(val, DCSR_CETRIG) : false;
return true;
}
-void dcsr_csr_t::update_fields(const uint8_t cause, const reg_t prv,
+void dcsr_csr_t::update_fields(const uint8_t cause, uint8_t ext_cause, const reg_t prv,
const bool v, const elp_t pelp) noexcept {
this->cause = cause;
+ this->ext_cause = ext_cause;
this->prv = prv;
this->v = v;
this->pelp = pelp;
@@ -1400,8 +1489,9 @@ float_csr_t::float_csr_t(processor_t* const proc, const reg_t addr, const reg_t
void float_csr_t::verify_permissions(insn_t insn, bool write) const {
masked_csr_t::verify_permissions(insn, write);
- require_fs;
- if (!proc->extension_enabled('F') && !proc->extension_enabled(EXT_ZFINX))
+
+ if (!((proc->extension_enabled('F') && STATE.sstatus->enabled(SSTATUS_FS))
+ || proc->extension_enabled(EXT_ZFINX)))
throw trap_illegal_instruction(insn.bits());
if (proc->extension_enabled(EXT_SMSTATEEN) && proc->extension_enabled(EXT_ZFINX)) {
@@ -1421,7 +1511,8 @@ void float_csr_t::verify_permissions(insn_t insn, bool write) const {
}
bool float_csr_t::unlogged_write(const reg_t val) noexcept {
- dirty_fp_state;
+ if (!proc->extension_enabled(EXT_ZFINX))
+ dirty_fp_state;
return masked_csr_t::unlogged_write(val);
}
@@ -1631,10 +1722,6 @@ bool stimecmp_csr_t::unlogged_write(const reg_t val) noexcept {
return basic_csr_t::unlogged_write(val);
}
-virtualized_stimecmp_csr_t::virtualized_stimecmp_csr_t(processor_t* const proc, csr_t_p orig, csr_t_p virt):
- virtualized_csr_t(proc, orig, virt) {
-}
-
void stimecmp_csr_t::verify_permissions(insn_t insn, bool write) const {
if (!(state->menvcfg->read() & MENVCFG_STCE)) {
// access to (v)stimecmp with MENVCFG.STCE = 0
@@ -1650,9 +1737,18 @@ void stimecmp_csr_t::verify_permissions(insn_t insn, bool write) const {
}
basic_csr_t::verify_permissions(insn, write);
+
+ if (proc->extension_enabled_const(EXT_SSAIA) && proc->extension_enabled('H')) {
+ if ((state->csrmap[CSR_HVICTL]->read() & HVICTL_VTI) && state->v && write)
+ throw trap_virtual_instruction(insn.bits());
+ }
+}
+
+virtualized_with_special_permission_csr_t::virtualized_with_special_permission_csr_t(processor_t* const proc, csr_t_p orig, csr_t_p virt):
+ virtualized_csr_t(proc, orig, virt) {
}
-void virtualized_stimecmp_csr_t::verify_permissions(insn_t insn, bool write) const {
+void virtualized_with_special_permission_csr_t::verify_permissions(insn_t insn, bool write) const {
orig_csr->verify_permissions(insn, write);
}
@@ -1663,6 +1759,14 @@ scountovf_csr_t::scountovf_csr_t(processor_t* const proc, const reg_t addr):
void scountovf_csr_t::verify_permissions(insn_t insn, bool write) const {
if (!proc->extension_enabled(EXT_SSCOFPMF))
throw trap_illegal_instruction(insn.bits());
+
+ if (proc->extension_enabled('H') &&
+ (proc->extension_enabled_const(EXT_SMCDELEG) || proc->extension_enabled(EXT_SSCCFG))
+ ) {
+ if (state->v && (state->menvcfg->read() & MENVCFG_CDE)) {
+ throw trap_virtual_instruction(insn.bits());
+ }
+ }
csr_t::verify_permissions(insn, write);
}
@@ -1732,10 +1836,74 @@ sscsrind_reg_csr_t::sscsrind_reg_csr_t(processor_t* const proc, const reg_t addr
}
void sscsrind_reg_csr_t::verify_permissions(insn_t insn, bool write) const {
+ if (proc->extension_enabled(EXT_SMSTATEEN)) {
+ if ((state->prv < PRV_M) && !(state->mstateen[0]->read() & MSTATEEN0_CSRIND))
+ throw trap_illegal_instruction(insn.bits());
+ }
+
// Don't call base verify_permission for VS registers remapped to S-mode
if (insn.csr() == address)
csr_t::verify_permissions(insn, write);
+ if (proc->extension_enabled(EXT_SMSTATEEN)) {
+ if (state->v && !(state->hstateen[0]->read() & HSTATEEN0_CSRIND))
+ throw trap_virtual_instruction(insn.bits());
+ }
+
+ if (proc->extension_enabled(EXT_SMCDELEG)) {
+ if (insn.csr() >= CSR_VSIREG && insn.csr() <= CSR_VSIREG6) {
+ if (!state->v) {
+ // An attempt to access any vsireg* from M or S mode raises an illegal instruction exception.
+ throw trap_illegal_instruction(insn.bits());
+ } else {
+ if (state->prv == PRV_S) {
+ // An attempt from VS-mode to access any vsireg raises an illegal instruction
+ // exception if menvcfg.CDE = 0, or a virtual instruction exception if menvcfg.CDE = 1
+ if ((state->menvcfg->read() & MENVCFG_CDE) != MENVCFG_CDE) {
+ throw trap_illegal_instruction(insn.bits());
+ } else {
+ throw trap_virtual_instruction(insn.bits());
+ }
+ } else {
+ throw trap_virtual_instruction(insn.bits());
+ }
+ }
+ }
+ if (insn.csr() >= CSR_SIREG && insn.csr() <= CSR_SIREG6) {
+ // attempts to access any sireg* when menvcfg.CDE = 0;
+ if ((state->menvcfg->read() & MENVCFG_CDE) != MENVCFG_CDE) {
+ if (!state->v) {
+ throw trap_illegal_instruction(insn.bits());
+ } else {
+ if (state->prv == PRV_S) {
+ // An attempt from VS-mode to access any sireg* causes illegal instruction exception if menvcfg.CDE = 0
+ throw trap_illegal_instruction(insn.bits());
+ } else {
+ throw trap_virtual_instruction(insn.bits());
+ }
+ }
+ } else {
+ // menvcfg.CDE = 1;
+ if (state->v) {
+ // An attempt from VS-mode to access any sireg* causes a virtual instruction exception if menvcfg.CDE = 1
+ throw trap_virtual_instruction(insn.bits());
+ }
+ // counter selected by siselect is not delegated to S-mode (the corresponding bit in mcounteren = 0).
+ auto iselect_addr = iselect->read();
+ if (iselect_addr >= SISELECT_SMCDELEG_START && iselect_addr <= SISELECT_SMCDELEG_END) {
+ reg_t counter_id_offset = iselect_addr - SISELECT_SMCDELEG_START;
+ if (!(state->mcounteren->read() & (1U << counter_id_offset))) {
+ if (!state->v) {
+ throw trap_illegal_instruction(insn.bits());
+ } else {
+ throw trap_virtual_instruction(insn.bits());
+ }
+ }
+ }
+ }
+ }
+ }
+
csr_t_p proxy_csr = get_reg();
if (proxy_csr == nullptr) {
if (!state->v) {
@@ -1797,7 +1965,7 @@ srmcfg_csr_t::srmcfg_csr_t(processor_t* const proc, const reg_t addr, const reg_
masked_csr_t(proc, addr, mask, init) {
}
-void srmcfg_csr_t::verify_permissions(insn_t insn, bool write) const {
+void srmcfg_csr_t::verify_permissions(insn_t insn, bool write UNUSED) const {
if (!proc->extension_enabled(EXT_SSQOSID))
throw trap_illegal_instruction(insn.bits());
@@ -1866,3 +2034,199 @@ bool hstatus_csr_t::unlogged_write(const reg_t val) noexcept {
proc->get_mmu()->flush_tlb();
return basic_csr_t::unlogged_write(new_hstatus);
}
+
+scntinhibit_csr_t::scntinhibit_csr_t(processor_t* const proc, const reg_t addr, csr_t_p mcountinhibit):
+ basic_csr_t(proc, addr, mcountinhibit->read()) {
+}
+
+void scntinhibit_csr_t::verify_permissions(insn_t insn, bool write) const {
+ if (insn.csr() == address) {
+ csr_t::verify_permissions(insn, write);
+ }
+
+ if ((state->menvcfg->read() & MENVCFG_CDE) != MENVCFG_CDE) {
+ throw trap_illegal_instruction(insn.bits());
+ }
+}
+
+bool scntinhibit_csr_t::unlogged_write(const reg_t val) noexcept {
+ state->mcountinhibit->write(state->mcounteren->read() & val);
+ return true;
+}
+
+reg_t scntinhibit_csr_t::read() const noexcept {
+ return state->mcounteren->read() & state->mcountinhibit->read();
+}
+
+mtopi_csr_t::mtopi_csr_t(processor_t* const proc, const reg_t addr):
+ csr_t(proc, addr) {
+}
+
+reg_t mtopi_csr_t::read() const noexcept {
+ reg_t enabled_interrupts = state->mip->read() & state->mie->read() & ~state->mideleg->read();
+ if (!enabled_interrupts)
+ return 0; // no enabled pending interrupt to M-mode
+
+ reg_t selected_interrupt = proc->select_an_interrupt_with_default_priority(enabled_interrupts);
+ reg_t identity = ctz(selected_interrupt);
+ return set_field((reg_t)1, MTOPI_IID, identity); // IPRIO always 1 if iprio array is RO0
+}
+
+bool mtopi_csr_t::unlogged_write(const reg_t UNUSED val) noexcept {
+ return false;
+}
+
+mvip_csr_t::mvip_csr_t(processor_t* const proc, const reg_t addr, const reg_t init):
+ basic_csr_t(proc, addr, init) {
+}
+
+reg_t mvip_csr_t::read() const noexcept {
+ const reg_t val = basic_csr_t::read();
+ const reg_t mvien = state->mvien->read();
+ const reg_t mip = state->mip->read();
+ const reg_t menvcfg = state->menvcfg->read();
+ return 0
+ | (val & MIP_SEIP)
+ | ((menvcfg & MENVCFG_STCE) ? 0 : (mip & MIP_STIP))
+ | (((mvien & MIP_SSIP) ? val : mip) & MIP_SSIP)
+ ;
+}
+
+bool mvip_csr_t::unlogged_write(const reg_t val) noexcept {
+ if (!(state->menvcfg->read() & MENVCFG_STCE))
+ state->mip->write_with_mask(MIP_STIP, val); // mvip.STIP is an alias of mip.STIP when mip.STIP is writable
+ if (!(state->mvien->read() & MIP_SSIP))
+ state->mip->write_with_mask(MIP_SSIP, val); // mvip.SSIP is an alias of mip.SSIP when mvien.SSIP=0
+
+ const reg_t new_val = (val & MIP_SEIP) | (((state->mvien->read() & MIP_SSIP) ? val : basic_csr_t::read()) & MIP_SSIP);
+ return basic_csr_t::unlogged_write(new_val);
+}
+
+void mvip_csr_t::write_with_mask(const reg_t mask, const reg_t val) noexcept {
+ basic_csr_t::unlogged_write((basic_csr_t::read() & ~mask) | (val & mask));
+ log_write();
+}
+
+nonvirtual_stopi_csr_t::nonvirtual_stopi_csr_t(processor_t* const proc, const reg_t addr):
+ csr_t(proc, addr) {
+}
+
+void nonvirtual_stopi_csr_t::verify_permissions(insn_t insn, bool write) const {
+ if (proc->extension_enabled(EXT_SMSTATEEN)) {
+ if ((state->prv < PRV_M) && !(state->mstateen[0]->read() & MSTATEEN0_AIA))
+ throw trap_illegal_instruction(insn.bits());
+
+ if (state->v && !(state->hstateen[0]->read() & HSTATEEN0_AIA))
+ throw trap_virtual_instruction(insn.bits());
+ }
+
+ csr_t::verify_permissions(insn, write);
+}
+
+reg_t nonvirtual_stopi_csr_t::read() const noexcept {
+ reg_t enabled_interrupts = state->nonvirtual_sip->read() & state->nonvirtual_sie->read() & ~state->hideleg->read();
+ if (!enabled_interrupts)
+ return 0; // no enabled pending interrupt to S-mode
+
+ reg_t selected_interrupt = proc->select_an_interrupt_with_default_priority(enabled_interrupts);
+ reg_t identity = ctz(selected_interrupt);
+ return set_field((reg_t)1, MTOPI_IID, identity); // IPRIO always 1 if iprio array is RO0
+}
+
+bool nonvirtual_stopi_csr_t::unlogged_write(const reg_t UNUSED val) noexcept {
+ return false;
+}
+
+inaccessible_csr_t::inaccessible_csr_t(processor_t* const proc, const reg_t addr):
+ csr_t(proc, addr) {
+}
+
+void inaccessible_csr_t::verify_permissions(insn_t insn, bool write) const {
+ if (state->v)
+ throw trap_virtual_instruction(insn.bits());
+ else
+ throw trap_illegal_instruction(insn.bits());
+}
+
+vstopi_csr_t::vstopi_csr_t(processor_t* const proc, const reg_t addr):
+ csr_t(proc, addr) {
+}
+
+void vstopi_csr_t::verify_permissions(insn_t insn, bool write) const {
+ if (proc->extension_enabled(EXT_SMSTATEEN)) {
+ if ((state->prv < PRV_M) && !(state->mstateen[0]->read() & MSTATEEN0_AIA))
+ throw trap_illegal_instruction(insn.bits());
+
+ if (state->v && !(state->hstateen[0]->read() & HSTATEEN0_AIA))
+ throw trap_virtual_instruction(insn.bits());
+ }
+
+ csr_t::verify_permissions(insn, write);
+}
+
+reg_t vstopi_csr_t::read() const noexcept {
+ reg_t hvictl = state->hvictl->read();
+ bool vti = hvictl & HVICTL_VTI;
+ reg_t iid = get_field(hvictl, HVICTL_IID);
+ bool dpr = hvictl & HVICTL_DPR;
+ bool ipriom = hvictl & HVICTL_IPRIOM;
+ reg_t iprio = get_field(hvictl, HVICTL_IPRIO);
+
+ reg_t enabled_interrupts = state->mip->read() & state->mie->read() & state->hideleg->read();
+ enabled_interrupts >>= 1; // VSSIP -> SSIP, etc
+ reg_t vgein = get_field(state->hstatus->read(), HSTATUS_VGEIN);
+ reg_t virtual_sei_priority = (vgein == 0 && iid == IRQ_S_EXT && iprio != 0) ? iprio : 255; // vstopi.IPRIO is 255 for priority number 256
+
+ reg_t identity, priority;
+ if (vti) {
+ if (!(enabled_interrupts & MIP_SEIP) && iid == IRQ_S_EXT)
+ return 0;
+
+ identity = ((enabled_interrupts & MIP_SEIP) && (iid == IRQ_S_EXT || dpr)) ? IRQ_S_EXT : iid;
+ priority = (identity == IRQ_S_EXT) ? virtual_sei_priority : ((iprio != 0 || !dpr) ? iprio : 255);
+ } else {
+ if (!enabled_interrupts)
+ return 0; // no enabled pending interrupt to VS-mode
+
+ reg_t selected_interrupt = proc->select_an_interrupt_with_default_priority(enabled_interrupts);
+ identity = ctz(selected_interrupt);
+ priority = (identity == IRQ_S_EXT) ? virtual_sei_priority : 255; // vstopi.IPRIO is 255 for interrupt with default priority lower than VSEI
+ }
+ return set_field((reg_t)(ipriom ? priority : 1), MTOPI_IID, identity);
+}
+
+bool vstopi_csr_t::unlogged_write(const reg_t UNUSED val) noexcept {
+ return false;
+}
+
+siselect_csr_t::siselect_csr_t(processor_t* const proc, const reg_t addr, const reg_t init):
+ basic_csr_t(proc, addr, init) {
+}
+
+void siselect_csr_t::verify_permissions(insn_t insn, bool write) const {
+ if (proc->extension_enabled(EXT_SMSTATEEN)) {
+ if ((state->prv < PRV_M) && !(state->mstateen[0]->read() & MSTATEEN0_CSRIND))
+ throw trap_illegal_instruction(insn.bits());
+
+ if (state->v && !(state->hstateen[0]->read() & HSTATEEN0_CSRIND))
+ throw trap_virtual_instruction(insn.bits());
+ }
+
+ basic_csr_t::verify_permissions(insn, write);
+}
+
+aia_csr_t::aia_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init):
+ masked_csr_t(proc, addr, mask, init) {
+}
+
+void aia_csr_t::verify_permissions(insn_t insn, bool write) const {
+ if (proc->extension_enabled(EXT_SMSTATEEN)) {
+ if ((state->prv < PRV_M) && !(state->mstateen[0]->read() & MSTATEEN0_AIA))
+ throw trap_illegal_instruction(insn.bits());
+
+ if (state->v && !(state->hstateen[0]->read() & HSTATEEN0_AIA))
+ throw trap_virtual_instruction(insn.bits());
+ }
+
+ basic_csr_t::verify_permissions(insn, write);
+}