mod access;
use crate::isa::csr::{
CBIE_FLUSH, CBIE_ILLEGAL, CBIE_INVAL, CBIE_RESERVED, CYCLE, CsrAddr, ENVCFG_CBIE, FCSR, FFLAGS,
FRM, INSTRET, MCAUSE, MCOUNTEREN, MCOUNTINHIBIT, MCOUNTINHIBIT_CY, MCOUNTINHIBIT_IR,
MCOUNTINHIBIT_WRITABLE, MCYCLE, MEDELEG, MENVCFG, MENVCFG_CBCFE, MENVCFG_CBIE_MASK,
MENVCFG_CBIE_SHIFT, MENVCFG_CBZE, MEPC, MIDELEG, MIE, MINSTRET, MIP, MISA, MISA_EXT_C,
MSCRATCH, MSTATUS, MSTATUS_FS, MSTATUS_FS_DIRTY, MSTATUS_SD, MSTATUS_VS, MSTATUS_VS_DIRTY,
MTVAL, MTVEC, SATP, SATP_MODE_BARE, SATP_MODE_MASK, SATP_MODE_SHIFT, SCAUSE, SCOUNTEREN,
SENVCFG, SENVCFG_CBCFE, SENVCFG_CBZE, SEPC, SIE, SIP, SSCRATCH, SSTATUS, SSTATUS_VISIBLE,
SSTATUS_WRITABLE, STVAL, STVEC, VCSR, VL, VLENB, VSTART, VTYPE, VXRM, VXSAT,
};
use crate::isa::privileged::PagingMode;
#[must_use]
pub const fn legalize_envcfg(val: u64, writable: u64) -> u64 {
let val = val & writable;
if (val >> MENVCFG_CBIE_SHIFT) & MENVCFG_CBIE_MASK == CBIE_RESERVED {
return val & !ENVCFG_CBIE;
}
val
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CboInvalAction {
Illegal,
Flush,
Invalidate,
}
pub const fn cboz_allowed(
menvcfg: u64,
senvcfg: u64,
privilege: crate::isa::privileged::PrivilegeMode,
) -> bool {
use crate::isa::privileged::PrivilegeMode;
match privilege {
PrivilegeMode::Machine => true,
PrivilegeMode::Supervisor => (menvcfg & MENVCFG_CBZE) != 0,
PrivilegeMode::User => (menvcfg & MENVCFG_CBZE) != 0 && (senvcfg & SENVCFG_CBZE) != 0,
}
}
pub const fn cbocf_allowed(
menvcfg: u64,
senvcfg: u64,
privilege: crate::isa::privileged::PrivilegeMode,
) -> bool {
use crate::isa::privileged::PrivilegeMode;
match privilege {
PrivilegeMode::Machine => true,
PrivilegeMode::Supervisor => (menvcfg & MENVCFG_CBCFE) != 0,
PrivilegeMode::User => (menvcfg & MENVCFG_CBCFE) != 0 && (senvcfg & SENVCFG_CBCFE) != 0,
}
}
pub const fn cbo_inval_action(
menvcfg: u64,
senvcfg: u64,
privilege: crate::isa::privileged::PrivilegeMode,
) -> CboInvalAction {
use crate::isa::privileged::PrivilegeMode;
let m_field = (menvcfg >> MENVCFG_CBIE_SHIFT) & MENVCFG_CBIE_MASK;
let s_field = (senvcfg >> MENVCFG_CBIE_SHIFT) & MENVCFG_CBIE_MASK;
let effective = match privilege {
PrivilegeMode::Machine => CBIE_INVAL,
PrivilegeMode::Supervisor => m_field,
PrivilegeMode::User => cbie_intersect(m_field, s_field),
};
match effective {
CBIE_FLUSH => CboInvalAction::Flush,
CBIE_INVAL => CboInvalAction::Invalidate,
_ => CboInvalAction::Illegal,
}
}
const fn cbie_intersect(a: u64, b: u64) -> u64 {
if a == CBIE_ILLEGAL || b == CBIE_ILLEGAL {
CBIE_ILLEGAL
} else if a == CBIE_FLUSH || b == CBIE_FLUSH {
CBIE_FLUSH
} else if a == CBIE_INVAL && b == CBIE_INVAL {
CBIE_INVAL
} else {
CBIE_ILLEGAL
}
}
#[must_use]
pub const fn with_state_dirty(status: u64) -> u64 {
let val = status & !MSTATUS_SD;
let fs_dirty = val & MSTATUS_FS == MSTATUS_FS_DIRTY;
let vs_dirty = val & MSTATUS_VS == MSTATUS_VS_DIRTY;
if fs_dirty || vs_dirty { val | MSTATUS_SD } else { val }
}
#[must_use]
pub const fn ialign_low_bits(misa: u64) -> u64 {
if misa & MISA_EXT_C != 0 { 0b01 } else { 0b11 }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CsrSerializationType {
FenceRequired,
Serializing,
Relaxed,
}
pub const fn csr_serialization_type(addr: CsrAddr) -> CsrSerializationType {
match addr.as_u32() {
x if x == SATP.as_u32() => CsrSerializationType::FenceRequired,
x if x == MSTATUS.as_u32()
|| x == SSTATUS.as_u32()
|| x == MTVEC.as_u32()
|| x == STVEC.as_u32()
|| x == MEDELEG.as_u32()
|| x == MIDELEG.as_u32()
|| x == VSTART.as_u32()
|| x == VXRM.as_u32() =>
{
CsrSerializationType::Serializing
}
_ => CsrSerializationType::Relaxed,
}
}
#[derive(Clone, Default, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Csrs {
pub mstatus: u64,
pub misa: u64,
pub medeleg: u64,
pub mideleg: u64,
pub mie: u64,
pub mtvec: u64,
pub mscratch: u64,
pub mepc: u64,
pub mcause: u64,
pub mtval: u64,
pub mip: u64,
pub sie: u64,
pub stvec: u64,
pub sscratch: u64,
pub sepc: u64,
pub scause: u64,
pub stval: u64,
pub sip: u64,
pub satp: u64,
pub mcycle: u64,
pub minstret: u64,
pub mcountinhibit: u64,
pub stimecmp: u64,
pub fflags: u64,
pub frm: u64,
pub mcounteren: u64,
pub scounteren: u64,
pub menvcfg: u64,
pub senvcfg: u64,
pub vstart: u64,
pub vxsat: u64,
pub vxrm: u64,
pub vl: u64,
pub vtype: u64,
pub vlenb: u64,
pub tselect: u64,
pub tdata1: [u64; 2],
pub tdata2: [u64; 2],
pub tcontrol: u64,
}
impl Csrs {
#[must_use]
pub const fn vector_config(&self) -> crate::isa::rvv::VectorConfig {
crate::isa::rvv::VectorConfig { vtype: self.vtype, vl: self.vl, vstart: self.vstart }
}
pub const fn count_cycle(&mut self) {
if self.mcountinhibit & MCOUNTINHIBIT_CY == 0 {
self.mcycle = self.mcycle.wrapping_add(1);
}
}
pub const fn count_cycles(&mut self, cycles: u64) {
if self.mcountinhibit & MCOUNTINHIBIT_CY == 0 {
self.mcycle = self.mcycle.wrapping_add(cycles);
}
}
pub const fn count_retired(&mut self) {
if self.mcountinhibit & MCOUNTINHIBIT_IR == 0 {
self.minstret = self.minstret.wrapping_add(1);
}
}
#[must_use]
pub const fn sstatus(&self) -> u64 {
self.mstatus & SSTATUS_VISIBLE
}
pub const fn read(&self, addr: CsrAddr) -> u64 {
match addr.as_u32() {
x if x == FFLAGS.as_u32() => self.fflags & 0x1F,
x if x == FRM.as_u32() => self.frm & 0x7,
x if x == FCSR.as_u32() => ((self.frm & 0x7) << 5) | (self.fflags & 0x1F),
x if x == MSTATUS.as_u32() => with_state_dirty(self.mstatus),
x if x == MISA.as_u32() => self.misa,
x if x == MEDELEG.as_u32() => self.medeleg,
x if x == MIDELEG.as_u32() => self.mideleg,
x if x == MIE.as_u32() => self.mie,
x if x == MTVEC.as_u32() => self.mtvec,
x if x == MSCRATCH.as_u32() => self.mscratch,
x if x == MEPC.as_u32() => self.mepc,
x if x == MCAUSE.as_u32() => self.mcause,
x if x == MTVAL.as_u32() => self.mtval,
x if x == MIP.as_u32() => self.mip,
x if x == SSTATUS.as_u32() => with_state_dirty(self.sstatus()),
x if x == SIE.as_u32() => self.sie,
x if x == STVEC.as_u32() => self.stvec,
x if x == SSCRATCH.as_u32() => self.sscratch,
x if x == SEPC.as_u32() => self.sepc,
x if x == SCAUSE.as_u32() => self.scause,
x if x == STVAL.as_u32() => self.stval,
x if x == SIP.as_u32() => self.sip,
x if x == SATP.as_u32() => self.satp,
x if x == CYCLE.as_u32() || x == MCYCLE.as_u32() => self.mcycle,
x if x == INSTRET.as_u32() || x == MINSTRET.as_u32() => self.minstret,
x if x == MCOUNTINHIBIT.as_u32() => self.mcountinhibit,
x if x == MCOUNTEREN.as_u32() => self.mcounteren,
x if x == SCOUNTEREN.as_u32() => self.scounteren,
x if x == MENVCFG.as_u32() => self.menvcfg,
x if x == SENVCFG.as_u32() => self.senvcfg,
x if x == VSTART.as_u32() => self.vstart,
x if x == VXSAT.as_u32() => self.vxsat & 0x1,
x if x == VXRM.as_u32() => self.vxrm & 0x3,
x if x == VCSR.as_u32() => (self.vxsat & 0x1) | ((self.vxrm & 0x3) << 1),
x if x == VL.as_u32() => self.vl,
x if x == VTYPE.as_u32() => self.vtype,
x if x == VLENB.as_u32() => self.vlenb,
_ => 0,
}
}
pub const fn write(&mut self, addr: CsrAddr, val: u64) {
match addr.as_u32() {
x if x == FFLAGS.as_u32() => self.fflags = val & 0x1F,
x if x == FRM.as_u32() => self.frm = val & 0x7,
x if x == FCSR.as_u32() => {
self.fflags = val & 0x1F;
self.frm = (val >> 5) & 0x7;
}
x if x == MSTATUS.as_u32() => self.mstatus = val,
x if x == MISA.as_u32() => self.misa = val,
x if x == MEDELEG.as_u32() => self.medeleg = val,
x if x == MIDELEG.as_u32() => self.mideleg = val,
x if x == MIE.as_u32() => self.mie = val,
x if x == MTVEC.as_u32() => self.mtvec = val,
x if x == MSCRATCH.as_u32() => self.mscratch = val,
x if x == MEPC.as_u32() => self.mepc = val,
x if x == MCAUSE.as_u32() => self.mcause = val,
x if x == MTVAL.as_u32() => self.mtval = val,
x if x == MIP.as_u32() => self.mip = val,
x if x == SSTATUS.as_u32() => {
self.mstatus = (self.mstatus & !SSTATUS_WRITABLE) | (val & SSTATUS_WRITABLE);
}
x if x == SIE.as_u32() => self.sie = val,
x if x == STVEC.as_u32() => self.stvec = val,
x if x == SSCRATCH.as_u32() => self.sscratch = val,
x if x == SEPC.as_u32() => self.sepc = val,
x if x == SCAUSE.as_u32() => self.scause = val,
x if x == STVAL.as_u32() => self.stval = val,
x if x == SIP.as_u32() => self.sip = val,
x if x == SATP.as_u32() => {
let mode = (val >> SATP_MODE_SHIFT) & SATP_MODE_MASK;
let new_mode =
if PagingMode::from_satp_mode(mode).is_some() { mode } else { SATP_MODE_BARE };
let mask = !(SATP_MODE_MASK << SATP_MODE_SHIFT);
self.satp = (val & mask) | (new_mode << SATP_MODE_SHIFT);
}
x if x == MCYCLE.as_u32() => self.mcycle = val,
x if x == MINSTRET.as_u32() => self.minstret = val,
x if x == MCOUNTINHIBIT.as_u32() => self.mcountinhibit = val & MCOUNTINHIBIT_WRITABLE,
x if x == MCOUNTEREN.as_u32() => self.mcounteren = val,
x if x == SCOUNTEREN.as_u32() => self.scounteren = val,
x if x == MENVCFG.as_u32() => self.menvcfg = val,
x if x == SENVCFG.as_u32() => self.senvcfg = val,
x if x == VSTART.as_u32() => self.vstart = val,
x if x == VXSAT.as_u32() => self.vxsat = val & 0x1,
x if x == VXRM.as_u32() => self.vxrm = val & 0x3,
x if x == VCSR.as_u32() => {
self.vxsat = val & 0x1;
self.vxrm = (val >> 1) & 0x3;
}
_ => {}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::isa::csr::{
MSTATUS_MIE, MSTATUS_MPP, MSTATUS_MXR, MSTATUS_SIE, MSTATUS_SUM, SATP_MODE_SV39,
};
#[test]
fn an_sstatus_write_lands_in_mstatus() {
let mut csrs = Csrs::default();
csrs.write(SSTATUS, MSTATUS_SIE | MSTATUS_SUM | MSTATUS_MIE);
assert_eq!(
csrs.read(MSTATUS) & (MSTATUS_SIE | MSTATUS_SUM | MSTATUS_MIE),
MSTATUS_SIE | MSTATUS_SUM
);
}
#[test]
fn an_mstatus_write_shows_through_sstatus() {
let mut csrs = Csrs::default();
csrs.write(MSTATUS, MSTATUS_MXR | MSTATUS_MPP);
assert_eq!(csrs.read(SSTATUS), MSTATUS_MXR);
}
#[test]
fn test_csr_serialization_type() {
assert_eq!(csr_serialization_type(SATP), CsrSerializationType::FenceRequired);
assert_eq!(csr_serialization_type(MSTATUS), CsrSerializationType::Serializing);
assert_eq!(csr_serialization_type(SSTATUS), CsrSerializationType::Serializing);
assert_eq!(csr_serialization_type(MTVEC), CsrSerializationType::Serializing);
assert_eq!(csr_serialization_type(STVEC), CsrSerializationType::Serializing);
assert_eq!(csr_serialization_type(MEDELEG), CsrSerializationType::Serializing);
assert_eq!(csr_serialization_type(MIDELEG), CsrSerializationType::Serializing);
assert_eq!(csr_serialization_type(MCAUSE), CsrSerializationType::Relaxed);
assert_eq!(csr_serialization_type(CsrAddr::from_u32(0)), CsrSerializationType::Relaxed);
}
#[test]
fn test_csrs_read_write() {
let mut csrs = Csrs::default();
csrs.write(MSTATUS, 0x1234);
assert_eq!(csrs.read(MSTATUS), 0x1234);
csrs.write(MISA, 0x5678);
assert_eq!(csrs.read(MISA), 0x5678);
csrs.write(SATP, SATP_MODE_SV39 << SATP_MODE_SHIFT | 0xabc);
assert_eq!(csrs.read(SATP), SATP_MODE_SV39 << SATP_MODE_SHIFT | 0xabc);
csrs.write(SATP, 0xF << SATP_MODE_SHIFT | 0xdef);
assert_eq!(csrs.read(SATP), SATP_MODE_BARE << SATP_MODE_SHIFT | 0xdef);
csrs.write(FFLAGS, 0x1F);
assert_eq!(csrs.read(FFLAGS), 0x1F);
csrs.write(FRM, 0x7);
assert_eq!(csrs.read(FRM), 0x7);
assert_eq!(csrs.read(FCSR), (0x7 << 5) | 0x1F);
csrs.write(FCSR, (0x3 << 5) | 0xA);
assert_eq!(csrs.read(FRM), 0x3);
assert_eq!(csrs.read(FFLAGS), 0xA);
csrs.write(CsrAddr::from_u32(9999), 0x1);
assert_eq!(csrs.read(CsrAddr::from_u32(9999)), 0x0);
}
}