From 1d50ad39847ecad975722f1814e71d50ab887e64 Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Tue, 24 Feb 2026 14:08:27 -0800 Subject: [PATCH 1/5] Make minstretcfg/mcyclecfg privilege bits read-only zero as appropriate --- riscv/csr_init.cc | 6 ++---- riscv/csrs.cc | 17 ++++++++++++++--- riscv/csrs.h | 4 ++-- riscv/processor.h | 2 ++ 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/riscv/csr_init.cc b/riscv/csr_init.cc index ffb11ecf..b5c91578 100644 --- a/riscv/csr_init.cc +++ b/riscv/csr_init.cc @@ -51,10 +51,8 @@ void state_t::csr_init(processor_t* const proc, reg_t max_isa) add_csr(CSR_MTVEC, mtvec = std::make_shared(proc, CSR_MTVEC)); add_csr(CSR_MCAUSE, mcause = std::make_shared(proc, CSR_MCAUSE)); - const reg_t minstretcfg_mask = !proc->extension_enabled_const(EXT_SMCNTRPMF) ? 0 : - MHPMEVENT_MINH | MHPMEVENT_SINH | MHPMEVENT_UINH | MHPMEVENT_VSINH | MHPMEVENT_VUINH; - auto minstretcfg = std::make_shared(proc, CSR_MINSTRETCFG, minstretcfg_mask, 0); - auto mcyclecfg = std::make_shared(proc, CSR_MCYCLECFG, minstretcfg_mask, 0); + minstretcfg = std::make_shared(proc, CSR_MINSTRETCFG); + mcyclecfg = std::make_shared(proc, CSR_MCYCLECFG); minstret = std::make_shared(proc, CSR_MINSTRET, minstretcfg); mcycle = std::make_shared(proc, CSR_MCYCLE, mcyclecfg); diff --git a/riscv/csrs.cc b/riscv/csrs.cc index bca7faac..39456ef7 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -755,6 +755,8 @@ bool misa_csr_t::unlogged_write(const reg_t val) noexcept { proc->set_extension_enable(EXT_ZBB, (new_misa & (1L << ('B' - 'A'))) || !proc->get_isa().extension_enabled('B')); proc->set_extension_enable(EXT_ZBS, (new_misa & (1L << ('B' - 'A'))) || !proc->get_isa().extension_enabled('B')); + basic_csr_t::unlogged_write(new_misa); + // update the hypervisor-only bits in MEDELEG and other CSRs if (!new_h && prev_h) { reg_t hypervisor_exceptions = 0 @@ -777,12 +779,14 @@ bool misa_csr_t::unlogged_write(const reg_t val) noexcept { const reg_t new_mevent = state->mevent[i]->read() & ~(MHPMEVENT_VUINH | MHPMEVENT_VSINH); state->mevent[i]->write(new_mevent); } + state->mcyclecfg->write(state->mcyclecfg->read()); + state->minstretcfg->write(state->minstretcfg->read()); } proc->get_mmu()->flush_tlb(); proc->build_opcode_map(); - return basic_csr_t::unlogged_write(new_misa); + return true; } bool misa_csr_t::extension_enabled_const(unsigned char ext) const noexcept { @@ -1970,7 +1974,7 @@ void sscsrind_reg_csr_t::add_ireg_proxy(const reg_t iselect_value, csr_t_p csr) ireg_proxy[iselect_value] = csr; } -smcntrpmf_csr_t::smcntrpmf_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) { +smcntrpmf_csr_t::smcntrpmf_csr_t(processor_t* const proc, const reg_t addr) : basic_csr_t(proc, addr, 0) { } reg_t smcntrpmf_csr_t::read_prev() const noexcept { @@ -1984,7 +1988,14 @@ void smcntrpmf_csr_t::reset_prev() noexcept { bool smcntrpmf_csr_t::unlogged_write(const reg_t val) noexcept { prev_val = read(); - return masked_csr_t::unlogged_write(val); + + const reg_t mask = !proc->extension_enabled_const(EXT_SMCNTRPMF) ? 0 : + MHPMEVENT_MINH | + (proc->extension_enabled_const('S') ? MHPMEVENT_SINH : 0) | + (proc->extension_enabled_const('U') ? MHPMEVENT_UINH : 0) | + (proc->extension_enabled('H') ? MHPMEVENT_VSINH | MHPMEVENT_VUINH : 0); + + return basic_csr_t::unlogged_write(val & mask); } srmcfg_csr_t::srmcfg_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init): diff --git a/riscv/csrs.h b/riscv/csrs.h index b1d5a3ba..b2fe6304 100644 --- a/riscv/csrs.h +++ b/riscv/csrs.h @@ -879,9 +879,9 @@ class sscsrind_reg_csr_t : public csr_t { // smcntrpmf_csr_t caches the previous state of the CSR in case a CSRW instruction // modifies the state that should not be immediately visible to bump() -class smcntrpmf_csr_t : public masked_csr_t { +class smcntrpmf_csr_t : public basic_csr_t { public: - smcntrpmf_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init); + smcntrpmf_csr_t(processor_t* const proc, const reg_t addr); reg_t read_prev() const noexcept; void reset_prev() noexcept; protected: diff --git a/riscv/processor.h b/riscv/processor.h index cb05210c..bf234d17 100644 --- a/riscv/processor.h +++ b/riscv/processor.h @@ -100,6 +100,8 @@ struct state_t csr_t_p mtval; csr_t_p mtvec; csr_t_p mcause; + smcntrpmf_csr_t_p minstretcfg; + smcntrpmf_csr_t_p mcyclecfg; wide_counter_csr_t_p minstret; wide_counter_csr_t_p mcycle; mie_csr_t_p mie; From cdca6411ad1e91b70b1c2243e3356a0cbc1b12f8 Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Tue, 24 Feb 2026 14:34:01 -0800 Subject: [PATCH 2/5] Simplify masking of mhpmevent bits when H is toggled --- riscv/csrs.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/riscv/csrs.cc b/riscv/csrs.cc index 39456ef7..9dfc24e3 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -776,8 +776,7 @@ bool misa_csr_t::unlogged_write(const reg_t val) noexcept { state->mip->write_with_mask(MIP_HS_MASK, 0); // also takes care of hip, sip, hvip state->hstatus->write(0); for (reg_t i = 0; i < N_HPMCOUNTERS; ++i) { - const reg_t new_mevent = state->mevent[i]->read() & ~(MHPMEVENT_VUINH | MHPMEVENT_VSINH); - state->mevent[i]->write(new_mevent); + state->mevent[i]->write(state->mevent[i]->read()); } state->mcyclecfg->write(state->mcyclecfg->read()); state->minstretcfg->write(state->minstretcfg->read()); @@ -1305,7 +1304,7 @@ bool mevent_csr_t::unlogged_write(const reg_t val) noexcept { | (proc->extension_enabled_const('U') ? MHPMEVENT_UINH : 0) | (proc->extension_enabled_const('S') ? MHPMEVENT_SINH : 0) | (proc->extension_enabled('H') ? MHPMEVENT_VUINH | MHPMEVENT_VSINH : 0) : 0; - return basic_csr_t::unlogged_write((read() & ~mask) | (val & mask)); + return basic_csr_t::unlogged_write(val & mask); } hypervisor_csr_t::hypervisor_csr_t(processor_t* const proc, const reg_t addr): From 6aa146d209517937ce31debb96bb2611bbd87eaf Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Tue, 24 Feb 2026 14:37:55 -0800 Subject: [PATCH 3/5] Simplify masking of medeleg bits when H is toggled --- riscv/csrs.cc | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/riscv/csrs.cc b/riscv/csrs.cc index 9dfc24e3..46bde799 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -759,15 +759,7 @@ bool misa_csr_t::unlogged_write(const reg_t val) noexcept { // update the hypervisor-only bits in MEDELEG and other CSRs if (!new_h && prev_h) { - reg_t hypervisor_exceptions = 0 - | (1 << CAUSE_VIRTUAL_SUPERVISOR_ECALL) - | (1 << CAUSE_FETCH_GUEST_PAGE_FAULT) - | (1 << CAUSE_LOAD_GUEST_PAGE_FAULT) - | (1 << CAUSE_VIRTUAL_INSTRUCTION) - | (1 << CAUSE_STORE_GUEST_PAGE_FAULT) - ; - - state->medeleg->write(state->medeleg->read() & ~hypervisor_exceptions); + state->medeleg->write(state->medeleg->read()); if (state->mnstatus) state->mnstatus->write(state->mnstatus->read() & ~MNSTATUS_MNPV); const reg_t new_mstatus = state->mstatus->read() & ~(MSTATUS_GVA | MSTATUS_MPV); state->mstatus->write(new_mstatus); @@ -1014,7 +1006,7 @@ bool medeleg_csr_t::unlogged_write(const reg_t val) noexcept { | (1 << CAUSE_SOFTWARE_CHECK_FAULT) | (1 << CAUSE_HARDWARE_ERROR_FAULT) ; - return basic_csr_t::unlogged_write((read() & ~mask) | (val & mask)); + return basic_csr_t::unlogged_write(val & mask); } sip_csr_t::sip_csr_t(processor_t* const proc, const reg_t addr, generic_int_accessor_t_p accr): From 0714f0f58825ce5cf3ad18f517c72d397ce3bffd Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Tue, 24 Feb 2026 14:43:11 -0800 Subject: [PATCH 4/5] Simplify masking of mnstatus bits when H is toggled --- riscv/csrs.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/riscv/csrs.cc b/riscv/csrs.cc index 46bde799..c631e4e3 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -595,15 +595,15 @@ mnstatus_csr_t::mnstatus_csr_t(processor_t* const proc, const reg_t addr): } 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) + const reg_t mask = 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)); const reg_t adjusted_val = set_field(val, MNSTATUS_MNPP, requested_mnpp); - const reg_t new_mnstatus = (read() & ~mask) | (adjusted_val & mask); + // NMIE can be set but not cleared + const reg_t new_mnstatus = (read() & MNSTATUS_NMIE) | (adjusted_val & mask); return basic_csr_t::unlogged_write(new_mnstatus); } @@ -760,7 +760,7 @@ bool misa_csr_t::unlogged_write(const reg_t val) noexcept { // update the hypervisor-only bits in MEDELEG and other CSRs if (!new_h && prev_h) { state->medeleg->write(state->medeleg->read()); - if (state->mnstatus) state->mnstatus->write(state->mnstatus->read() & ~MNSTATUS_MNPV); + if (state->mnstatus) state->mnstatus->write(state->mnstatus->read()); const reg_t new_mstatus = state->mstatus->read() & ~(MSTATUS_GVA | MSTATUS_MPV); state->mstatus->write(new_mstatus); if (state->mstatush) state->mstatush->write(new_mstatus >> 32); // log mstatush change From 114be91bbe7e907f1070d807ae56ce50993304f6 Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Tue, 24 Feb 2026 14:55:19 -0800 Subject: [PATCH 5/5] Remove incorrect use of static variable It prevents modeling mixed RV32/RV64 systems. --- riscv/csrs.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/riscv/csrs.cc b/riscv/csrs.cc index c631e4e3..cc12e1d2 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -455,12 +455,11 @@ reg_t base_status_csr_t::compute_sstatus_write_mask() const noexcept { } reg_t base_status_csr_t::adjust_sd(const reg_t val) const noexcept { - // This uses get_const_xlen() instead of get_xlen() not only because - // the variable is static, so it's only called once, but also + // This uses get_const_xlen() instead of get_xlen() // because the SD bit moves when XLEN changes, which means we would // need to call adjust_sd() on every read, instead of on every // write. - static const reg_t sd_bit = proc->get_const_xlen() == 64 ? SSTATUS64_SD : SSTATUS32_SD; + const reg_t sd_bit = proc->get_const_xlen() == 64 ? SSTATUS64_SD : SSTATUS32_SD; if (((val & SSTATUS_FS) == SSTATUS_FS) || ((val & SSTATUS_VS) == SSTATUS_VS) || ((val & SSTATUS_XS) == SSTATUS_XS)) {