use super::CoreCtx;
use crate::arch::Hart;
use crate::isa::csr;
use crate::isa::csr::CsrAddr;
use crate::isa::privileged::{PagingMode, Trap};
use crate::soc::uncore::Uncore;
pub(super) fn read_for_update(hart: &Hart, uncore: &Uncore, addr: CsrAddr) -> u64 {
let value = read(hart, uncore, addr);
if addr.as_u32() != csr::MIP.as_u32() {
return value;
}
let software_seip = if hart.sw_seip { csr::MIP_SEIP } else { 0 };
(value & !csr::MIP_SEIP) | software_seip
}
pub(super) fn read(hart: &Hart, uncore: &Uncore, addr: CsrAddr) -> u64 {
let raw = addr.as_u32();
match raw {
x if x == csr::FFLAGS.as_u32() => hart.csrs.fflags & 0x1F,
x if x == csr::FRM.as_u32() => hart.csrs.frm & 0x7,
x if x == csr::FCSR.as_u32() => ((hart.csrs.frm & 0x7) << 5) | (hart.csrs.fflags & 0x1F),
x if x == csr::MVENDORID.as_u32()
|| x == csr::MARCHID.as_u32()
|| x == csr::MIMPID.as_u32() =>
{
0
}
x if x == csr::MHARTID.as_u32() => u64::from(hart.hart_id.val()),
x if x == csr::MSTATUS.as_u32() => crate::arch::csr::with_state_dirty(hart.csrs.mstatus),
x if x == csr::MEDELEG.as_u32() => hart.csrs.medeleg,
x if x == csr::MIDELEG.as_u32() => hart.csrs.mideleg,
x if x == csr::MIE.as_u32() => hart.csrs.mie,
x if x == csr::MTVEC.as_u32() => hart.csrs.mtvec,
x if x == csr::MISA.as_u32() => hart.csrs.misa,
x if x == csr::MSCRATCH.as_u32() => hart.csrs.mscratch,
x if x == csr::MEPC.as_u32() => hart.csrs.mepc,
x if x == csr::MCAUSE.as_u32() => hart.csrs.mcause,
x if x == csr::MTVAL.as_u32() => hart.csrs.mtval,
x if x == csr::MIP.as_u32() => hart.csrs.mip,
x if x == csr::SSTATUS.as_u32() => crate::arch::csr::with_state_dirty(hart.csrs.sstatus()),
x if x == csr::SIE.as_u32() => hart.csrs.mie & hart.csrs.mideleg,
x if x == csr::STVEC.as_u32() => hart.csrs.stvec,
x if x == csr::SSCRATCH.as_u32() => hart.csrs.sscratch,
x if x == csr::SEPC.as_u32() => hart.csrs.sepc,
x if x == csr::SCAUSE.as_u32() => hart.csrs.scause,
x if x == csr::STVAL.as_u32() => hart.csrs.stval,
x if x == csr::SIP.as_u32() => hart.csrs.mip & hart.csrs.mideleg,
x if x == csr::STIMECMP.as_u32() => hart.csrs.stimecmp,
x if x == csr::SATP.as_u32() => hart.csrs.satp,
x if x == csr::MCOUNTEREN.as_u32() => hart.csrs.mcounteren,
x if x == csr::SCOUNTEREN.as_u32() => hart.csrs.scounteren,
x if x == csr::MENVCFG.as_u32() => hart.csrs.menvcfg,
x if x == csr::SENVCFG.as_u32() => hart.csrs.senvcfg,
x if x == csr::CYCLE.as_u32() || x == csr::MCYCLE.as_u32() => hart.csrs.mcycle,
x if x == csr::TIME.as_u32() => uncore.bus.mtime(),
x if x == csr::INSTRET.as_u32() || x == csr::MINSTRET.as_u32() => hart.csrs.minstret,
x if x == csr::MCOUNTINHIBIT.as_u32() => hart.csrs.mcountinhibit,
x if x == csr::PMPCFG0.as_u32() => {
hart.pmp.get_cfg(0) as u64
| ((hart.pmp.get_cfg(1) as u64) << 8)
| ((hart.pmp.get_cfg(2) as u64) << 16)
| ((hart.pmp.get_cfg(3) as u64) << 24)
| ((hart.pmp.get_cfg(4) as u64) << 32)
| ((hart.pmp.get_cfg(5) as u64) << 40)
| ((hart.pmp.get_cfg(6) as u64) << 48)
| ((hart.pmp.get_cfg(7) as u64) << 56)
}
x if x == csr::PMPCFG2.as_u32() => {
hart.pmp.get_cfg(8) as u64
| ((hart.pmp.get_cfg(9) as u64) << 8)
| ((hart.pmp.get_cfg(10) as u64) << 16)
| ((hart.pmp.get_cfg(11) as u64) << 24)
| ((hart.pmp.get_cfg(12) as u64) << 32)
| ((hart.pmp.get_cfg(13) as u64) << 40)
| ((hart.pmp.get_cfg(14) as u64) << 48)
| ((hart.pmp.get_cfg(15) as u64) << 56)
}
x if x >= csr::PMPADDR0.as_u32() && x <= csr::PMPADDR15.as_u32() => {
hart.pmp.get_addr((raw - csr::PMPADDR0.as_u32()) as usize)
}
x if x == csr::VSTART.as_u32() => hart.csrs.vstart,
x if x == csr::VXSAT.as_u32() => hart.csrs.vxsat & 0x1,
x if x == csr::VXRM.as_u32() => hart.csrs.vxrm & 0x3,
x if x == csr::VCSR.as_u32() => (hart.csrs.vxsat & 0x1) | ((hart.csrs.vxrm & 0x3) << 1),
x if x == csr::VL.as_u32() => hart.csrs.vl,
x if x == csr::VTYPE.as_u32() => hart.csrs.vtype,
x if x == csr::VLENB.as_u32() => hart.csrs.vlenb,
x if x == csr::TSELECT.as_u32() => hart.csrs.tselect,
x if x == csr::TDATA1.as_u32() => {
let i = hart.csrs.tselect as usize;
hart.csrs.tdata1[i]
}
x if x == csr::TDATA2.as_u32() => {
let i = hart.csrs.tselect as usize;
hart.csrs.tdata2[i]
}
x if x == csr::TDATA3.as_u32() => 0, x if x == csr::TINFO.as_u32() => 1 << 2, x if x == csr::TCONTROL.as_u32() => hart.csrs.tcontrol & 0x88, _ => 0,
}
}
impl CoreCtx<'_> {
#[inline]
pub const fn is_valid_csr(&self, addr: CsrAddr) -> bool {
self.hart.is_valid_csr(addr)
}
pub fn csr_read(&self, addr: CsrAddr) -> u64 {
read(self.hart, self.uncore, addr)
}
pub fn csr_write(&mut self, addr: CsrAddr, val: u64) {
let raw = addr.as_u32();
match raw {
x if x == csr::FFLAGS.as_u32() => {
self.hart.csrs.fflags = val & 0x1F;
self.hart.csrs.mstatus =
(self.hart.csrs.mstatus & !csr::MSTATUS_FS) | csr::MSTATUS_FS_DIRTY;
}
x if x == csr::FRM.as_u32() => {
self.hart.csrs.frm = val & 0x7;
self.hart.csrs.mstatus =
(self.hart.csrs.mstatus & !csr::MSTATUS_FS) | csr::MSTATUS_FS_DIRTY;
}
x if x == csr::FCSR.as_u32() => {
self.hart.csrs.fflags = val & 0x1F;
self.hart.csrs.frm = (val >> 5) & 0x7;
self.hart.csrs.mstatus =
(self.hart.csrs.mstatus & !csr::MSTATUS_FS) | csr::MSTATUS_FS_DIRTY;
}
x if x == csr::CSR_SIM_PANIC.as_u32() => {
self.trap(&Trap::RequestedTrap(val), self.hart.pc);
}
x if x == csr::MSTATUS.as_u32() => {
const MSTATUS_WRITABLE: u64 = csr::MSTATUS_SIE
| csr::MSTATUS_MIE
| csr::MSTATUS_SPIE
| csr::MSTATUS_MPIE
| csr::MSTATUS_SPP
| csr::MSTATUS_MPP
| csr::MSTATUS_VS
| csr::MSTATUS_FS
| csr::MSTATUS_MPRV
| csr::MSTATUS_SUM
| csr::MSTATUS_MXR
| csr::MSTATUS_TVM
| csr::MSTATUS_TW
| csr::MSTATUS_TSR;
let preserved = self.hart.csrs.mstatus & (csr::MSTATUS_UXL | csr::MSTATUS_SXL);
self.hart.csrs.mstatus = (val & MSTATUS_WRITABLE) | preserved;
let mpp =
(self.hart.csrs.mstatus >> csr::MSTATUS_MPP_SHIFT) & csr::MSTATUS_MPP_MASK;
if mpp == 2 {
self.hart.csrs.mstatus &= !csr::MSTATUS_MPP;
}
}
x if x == csr::MEDELEG.as_u32() => {
self.hart.csrs.medeleg = val & !(1 << 11);
}
x if x == csr::MIDELEG.as_u32() => {
let mask = csr::MIP_SSIP | csr::MIP_STIP | csr::MIP_SEIP;
self.hart.csrs.mideleg = val & mask;
}
x if x == csr::MIE.as_u32() => {
let mask = csr::MIE_SSIP
| csr::MIE_MSIP
| csr::MIE_STIE
| csr::MIE_MTIE
| csr::MIE_SEIP
| csr::MIE_MEIP;
self.hart.csrs.mie = val & mask;
}
x if x == csr::MTVEC.as_u32() => {
let mode = val & 3;
self.hart.csrs.mtvec = if mode >= 2 { val & !3 } else { val };
}
x if x == csr::MISA.as_u32() => {
}
x if x == csr::MSCRATCH.as_u32() => self.hart.csrs.mscratch = val,
x if x == csr::MEPC.as_u32() => {
self.hart.csrs.mepc = val & !crate::arch::csr::ialign_low_bits(self.hart.csrs.misa);
}
x if x == csr::MCAUSE.as_u32() => self.hart.csrs.mcause = val,
x if x == csr::MTVAL.as_u32() => self.hart.csrs.mtval = val,
x if x == csr::MIP.as_u32() => {
let mask = csr::MIP_SSIP | csr::MIP_STIP | csr::MIP_SEIP;
self.hart.csrs.mip = (self.hart.csrs.mip & !mask) | (val & mask);
self.hart.sw_seip = (val & csr::MIP_SEIP) != 0;
}
x if x == csr::SSTATUS.as_u32() => {
self.hart.csrs.mstatus = (self.hart.csrs.mstatus & !csr::SSTATUS_WRITABLE)
| (val & csr::SSTATUS_WRITABLE);
}
x if x == csr::SIE.as_u32() => {
let mask = self.hart.csrs.mideleg;
self.hart.csrs.mie = (self.hart.csrs.mie & !mask) | (val & mask);
}
x if x == csr::STVEC.as_u32() => {
let mode = val & 3;
self.hart.csrs.stvec = if mode >= 2 { val & !3 } else { val };
}
x if x == csr::SSCRATCH.as_u32() => self.hart.csrs.sscratch = val,
x if x == csr::SEPC.as_u32() => {
self.hart.csrs.sepc = val & !crate::arch::csr::ialign_low_bits(self.hart.csrs.misa);
}
x if x == csr::SCAUSE.as_u32() => self.hart.csrs.scause = val,
x if x == csr::STVAL.as_u32() => self.hart.csrs.stval = val,
x if x == csr::SIP.as_u32() => {
let mask = self.hart.csrs.mideleg & (csr::MIP_SSIP);
self.hart.csrs.mip = (self.hart.csrs.mip & !mask) | (val & mask);
}
x if x == csr::MCOUNTEREN.as_u32() => {
self.hart.csrs.mcounteren = val & 0x7;
}
x if x == csr::SCOUNTEREN.as_u32() => {
self.hart.csrs.scounteren = val & 0x7;
}
x if x == csr::MENVCFG.as_u32() => {
let svadu = if self.config.isa.svadu { csr::MENVCFG_ADUE } else { 0 };
let writable = csr::MENVCFG_WRITABLE | svadu;
self.hart.csrs.menvcfg = crate::arch::csr::legalize_envcfg(val, writable);
}
x if x == csr::SENVCFG.as_u32() => {
self.hart.csrs.senvcfg =
crate::arch::csr::legalize_envcfg(val, csr::SENVCFG_WRITABLE);
}
x if x == csr::MCYCLE.as_u32() => self.hart.csrs.mcycle = val,
x if x == csr::MINSTRET.as_u32() => self.hart.csrs.minstret = val,
x if x == csr::MCOUNTINHIBIT.as_u32() => {
self.hart.csrs.mcountinhibit = val & csr::MCOUNTINHIBIT_WRITABLE;
}
x if x == csr::PMPCFG0.as_u32() => {
for i in 0..8 {
self.hart.pmp.set_cfg(i, ((val >> (i * 8)) & 0xFF) as u8);
}
}
x if x == csr::PMPCFG2.as_u32() => {
for i in 0..8 {
self.hart.pmp.set_cfg(8 + i, ((val >> (i * 8)) & 0xFF) as u8);
}
}
x if x >= csr::PMPADDR0.as_u32() && x <= csr::PMPADDR15.as_u32() => {
self.hart.pmp.set_addr((raw - csr::PMPADDR0.as_u32()) as usize, val);
}
x if x == csr::STIMECMP.as_u32() => {
self.hart.csrs.stimecmp = val;
self.hart.csrs.mip &= !csr::MIP_STIP;
}
x if x == csr::SATP.as_u32() => {
let mode = (val >> csr::SATP_MODE_SHIFT) & csr::SATP_MODE_MASK;
let allowed = PagingMode::from_satp_mode(mode)
.is_some_and(|m| m.is_at_most(self.core.mmu.paging_mode_max));
let new_val = if allowed {
val
} else {
val & !(csr::SATP_MODE_MASK << csr::SATP_MODE_SHIFT)
};
self.hart.csrs.satp = new_val;
}
x if x == csr::VSTART.as_u32() => self.hart.csrs.vstart = val,
x if x == csr::VXSAT.as_u32() => self.hart.csrs.vxsat = val & 0x1,
x if x == csr::VXRM.as_u32() => self.hart.csrs.vxrm = val & 0x3,
x if x == csr::VCSR.as_u32() => {
self.hart.csrs.vxsat = val & 0x1;
self.hart.csrs.vxrm = (val >> 1) & 0x3;
}
x if x == csr::TSELECT.as_u32() => {
self.hart.csrs.tselect = val.min(1); }
x if x == csr::TDATA1.as_u32() => {
let i = self.hart.csrs.tselect as usize;
let ttype = (val >> 60) & 0xF;
if ttype == 2 {
const MCONTROL_MASK: u64 = (0xFu64 << 60) | (1 << 13) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 7); self.hart.csrs.tdata1[i] = val & MCONTROL_MASK;
} else {
self.hart.csrs.tdata1[i] = 0;
}
}
x if x == csr::TDATA2.as_u32() => {
let i = self.hart.csrs.tselect as usize;
self.hart.csrs.tdata2[i] = val;
}
x if x == csr::TDATA3.as_u32() => {} x if x == csr::TINFO.as_u32() => {} x if x == csr::TCONTROL.as_u32() => {
self.hart.csrs.tcontrol = val & 0x88; }
_ => {}
}
}
}
#[cfg(test)]
mod tests {
use crate::config::Config;
use crate::isa::csr;
#[test]
fn test_cpu_csr_read_write_mstatus() {
let config = Config::default();
let mut sys = crate::system::SystemState::build(&config, "");
let mut state = sys.core_ctx(0);
state.csr_write(csr::MSTATUS, 0xFFFF_FFFF_FFFF_FFFF);
let mstatus = state.csr_read(csr::MSTATUS);
assert_ne!(mstatus, 0xFFFF_FFFF_FFFF_FFFF);
let sstatus = state.csr_read(csr::SSTATUS);
assert_eq!(sstatus, mstatus & (csr::MSTATUS_SD | csr::SSTATUS_VISIBLE));
}
#[test]
fn mstatus_reads_sd_when_only_the_vector_state_is_dirty() {
let config = Config::default();
let mut sys = crate::system::SystemState::build(&config, "");
let mut state = sys.core_ctx(0);
state.csr_write(csr::MSTATUS, csr::MSTATUS_VS_DIRTY);
assert_ne!(state.csr_read(csr::MSTATUS) & csr::MSTATUS_SD, 0);
}
#[test]
fn test_cpu_csr_read_write_fcsr() {
let config = Config::default();
let mut sys = crate::system::SystemState::build(&config, "");
let mut state = sys.core_ctx(0);
state.csr_write(csr::FCSR, 0xFF);
assert_eq!(state.csr_read(csr::FCSR), 0xFF);
assert_eq!(state.csr_read(csr::FFLAGS), 0x1F);
assert_eq!(state.csr_read(csr::FRM), 0x7);
}
}