diff --git a/riscv/CHANGELOG.md b/riscv/CHANGELOG.md index 3598eb96..e1bfe669 100644 --- a/riscv/CHANGELOG.md +++ b/riscv/CHANGELOG.md @@ -9,11 +9,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Add `mstatus` and `sstatus` CSRs helpers for UBE, VS, UXL and SXL, and the corresponding tests - Add `pmpaddr16` ~ `pmpaddr63` CSRs - Add `siselect` CSR - Add `vsiselect` CSR - Add `stopi` CSR +### Changed +- Fix the CSR masks to preserve all legal status bits +- Modify XS and SD bits to read-only summary fields instead of writable fields + ## v0.16.0 - 2025-12-19 ### Added diff --git a/riscv/src/register/mstatus.rs b/riscv/src/register/mstatus.rs index ff1a3c67..458103fb 100644 --- a/riscv/src/register/mstatus.rs +++ b/riscv/src/register/mstatus.rs @@ -8,14 +8,14 @@ use crate::bits::{bf_extract, bf_insert}; read_write_csr! { /// mstatus register Mstatus: 0x300, - mask: 0x8000_0000_007f_fffe, + mask: 0x8000_003f_007f_ffea, } #[cfg(target_arch = "riscv32")] read_write_csr! { /// mstatus register Mstatus: 0x300, - mask: 0x807f_fffe, + mask: 0x807f_ffea, } csr_field_enum! { @@ -153,12 +153,15 @@ read_write_csr_field! { FS: [13:14], } -read_write_csr_field! { +read_only_csr_field! { Mstatus, /// Additional extension state /// - /// Encodes the status of additional user-mode extensions and associated - /// state. + /// Encodes the status of additional user-mode extensions and associated state. + /// + /// This is a read-only field summarizing the status of all + /// user-mode extensions; it is set indirectly by writing individual + /// extension status CSRs. xs, XS: [15:16], } @@ -218,16 +221,20 @@ read_write_csr_field! { } #[cfg(target_arch = "riscv32")] -read_write_csr_field! { +read_only_csr_field! { Mstatus, - /// Whether either the FS field or XS field signals the presence of some dirty state + /// Whether either the FS field or XS field signals the presence of some dirty state. + /// + /// This is a read-only bit computed by hardware as `(FS == Dirty) || (XS == Dirty) || (VS == Dirty)`. sd: 31, } #[cfg(not(target_arch = "riscv32"))] -read_write_csr_field! { +read_only_csr_field! { Mstatus, - /// Whether either the FS field or XS field signals the presence of some dirty state + /// Whether either the FS field or XS field signals the presence of some dirty state. + /// + /// This is a read-only bit computed by hardware as `(FS == Dirty) || (XS == Dirty) || (VS == Dirty)`. sd: 63, } @@ -349,7 +356,11 @@ set!(0x300); clear!(0x300); set_clear_csr!( - /// User Interrupt Enable + /// User Interrupt Enable. + /// + /// This helper is kept for API compatibility with legacy code that still + /// targets the deprecated N-extension encoding. On systems that do not + /// implement these bits, writes may be ignored by hardware. , set_uie, clear_uie, 1 << 0); set_clear_csr!( /// Supervisor Interrupt Enable @@ -358,7 +369,11 @@ set_clear_csr!( /// Machine Interrupt Enable , set_mie, clear_mie, 1 << 3); set_csr!( - /// User Previous Interrupt Enable + /// User Previous Interrupt Enable. + /// + /// This helper is kept for API compatibility with legacy code that still + /// targets the deprecated N-extension encoding. On systems that do not + /// implement these bits, writes may be ignored by hardware. , set_upie, 1 << 4); set_csr!( /// Supervisor Previous Interrupt Enable @@ -466,6 +481,32 @@ pub unsafe fn set_mbe(endianness: Endianness) { } } +/// Set effective xlen in U-mode (i.e., `UXLEN`). +/// +/// # Note +/// +/// In RISCV-32, `UXL` does not exist. +#[inline] +#[cfg(not(target_arch = "riscv32"))] +pub unsafe fn set_uxl(uxl: XLEN) { + let mut value = _read(); + value = bf_insert(value, 32, 2, uxl as usize); + _write(value); +} + +/// Set effective xlen in S-mode (i.e., `SXLEN`). +/// +/// # Note +/// +/// In RISCV-32, `SXL` does not exist. +#[inline] +#[cfg(not(target_arch = "riscv32"))] +pub unsafe fn set_sxl(sxl: XLEN) { + let mut value = _read(); + value = bf_insert(value, 34, 2, sxl as usize); + _write(value); +} + #[cfg(test)] mod test { use super::*; @@ -491,10 +532,19 @@ mod test { test_csr_field!(mstatus, vs: VS::Clean); test_csr_field!(mstatus, vs: VS::Dirty); - test_csr_field!(mstatus, xs: XS::AllOff); - test_csr_field!(mstatus, xs: XS::NoneDirtyOrClean); - test_csr_field!(mstatus, xs: XS::NoneDirtySomeClean); - test_csr_field!(mstatus, xs: XS::SomeDirty); + // XS is read-only: it summarizes all user-mode extension states. + [ + XS::AllOff, + XS::NoneDirtyOrClean, + XS::NoneDirtySomeClean, + XS::SomeDirty, + ] + .into_iter() + .for_each(|xs| { + let m = Mstatus::from_bits(xs.into_usize() << 15); + assert_eq!(m.xs(), xs); + assert_eq!(m.try_xs(), Ok(xs)); + }); test_csr_field!(mstatus, sie); test_csr_field!(mstatus, mie); @@ -507,6 +557,40 @@ mod test { test_csr_field!(mstatus, tvm); test_csr_field!(mstatus, tw); test_csr_field!(mstatus, tsr); - test_csr_field!(mstatus, sd); + + // SD is read-only: hardware sets it whenever FS, VS, or XS == Dirty. + assert!(!Mstatus::from_bits(0).sd()); + assert!(Mstatus::from_bits(usize::MAX).sd()); + + #[cfg(not(target_arch = "riscv32"))] + { + [XLEN::XLEN32, XLEN::XLEN64, XLEN::XLEN128] + .into_iter() + .for_each(|xlen| { + let mut m = Mstatus::from_bits(0); + m.set_uxl(xlen); + assert_eq!(m.uxl(), xlen); + }); + + [XLEN::XLEN32, XLEN::XLEN64, XLEN::XLEN128] + .into_iter() + .for_each(|xlen| { + let mut m = Mstatus::from_bits(0); + m.set_sxl(xlen); + assert_eq!(m.sxl(), xlen); + }); + + [Endianness::LittleEndian, Endianness::BigEndian] + .into_iter() + .for_each(|endianness| { + let mut m = Mstatus::from_bits(0); + m.set_sbe(endianness); + assert_eq!(m.sbe(), endianness); + + let mut m = Mstatus::from_bits(0); + m.set_mbe(endianness); + assert_eq!(m.mbe(), endianness); + }); + } } } diff --git a/riscv/src/register/sstatus.rs b/riscv/src/register/sstatus.rs index 883004d7..70b7adb3 100644 --- a/riscv/src/register/sstatus.rs +++ b/riscv/src/register/sstatus.rs @@ -1,12 +1,12 @@ //! sstatus register pub use super::misa::XLEN; -pub use super::mstatus::{FS, XS}; +pub use super::mstatus::{FS, VS, XS}; #[cfg(target_arch = "riscv32")] -const MASK: usize = 0x800d_e122; +const MASK: usize = 0x800d_e762; #[cfg(not(target_arch = "riscv32"))] -const MASK: usize = 0x8000_0003_000d_e122; +const MASK: usize = 0x8000_0003_000d_e762; read_write_csr! { /// Supervisor Status Register @@ -37,6 +37,12 @@ read_write_csr_field! { spie: 5, } +read_write_csr_field! { + Sstatus, + /// U-mode non-instruction-fetch memory endianness + ube: 6, +} + read_write_csr_field! { Sstatus, /// Supervisor Previous Privilege Mode @@ -44,6 +50,13 @@ read_write_csr_field! { SPP: [8:8], } +read_write_csr_field! { + Sstatus, + /// Vector extension state + vs, + VS: [9:10], +} + read_write_csr_field! { Sstatus, /// The status of the floating-point unit @@ -80,18 +93,20 @@ read_write_csr_field! { } #[cfg(target_arch = "riscv32")] -read_write_csr_field! { +read_only_csr_field! { Sstatus, - /// Whether either the FS field or XS field - /// signals the presence of some dirty state + /// Whether either the FS field or XS field signals the presence of some dirty state. + /// + /// This is a read-only bit computed by hardware as `(FS == Dirty) || (XS == Dirty) || (VS == Dirty)`. sd: 31, } #[cfg(not(target_arch = "riscv32"))] -read_write_csr_field! { +read_only_csr_field! { Sstatus, - /// Whether either the FS field or XS field - /// signals the presence of some dirty state + /// Whether either the FS field or XS field signals the presence of some dirty state. + /// + /// This is a read-only bit computed by hardware as `(FS == Dirty) || (XS == Dirty) || (VS == Dirty)`. sd: 63, } @@ -110,17 +125,28 @@ set!(0x100); clear!(0x100); set_clear_csr!( - /// User Interrupt Enable + /// User Interrupt Enable. + /// + /// This helper is kept for API compatibility with legacy code that still + /// targets the deprecated N-extension encoding. On systems that do not + /// implement these bits, writes may be ignored by hardware. , set_uie, clear_uie, 1 << 0); set_clear_csr!( /// Supervisor Interrupt Enable , set_sie, clear_sie, 1 << 1); set_csr!( - /// User Previous Interrupt Enable + /// User Previous Interrupt Enable. + /// + /// This helper is kept for API compatibility with legacy code that still + /// targets the deprecated N-extension encoding. On systems that do not + /// implement these bits, writes may be ignored by hardware. , set_upie, 1 << 4); set_csr!( /// Supervisor Previous Interrupt Enable , set_spie, 1 << 5); +set_clear_csr!( + /// U-mode non-instruction-fetch memory endianness + , set_ube, clear_ube, 1 << 6); set_clear_csr!( /// Permit Supervisor User Memory access , set_sum, clear_sum, 1 << 18); @@ -146,6 +172,29 @@ pub unsafe fn set_fs(fs: FS) { _write(value); } +/// Vector extension state +#[inline] +pub unsafe fn set_vs(vs: VS) { + let mut value = _read(); + value &= !(0x3 << 9); // clear previous value + value |= (vs as usize) << 9; + _write(value); +} + +/// Effective xlen in U-mode (i.e., `UXLEN`) +/// +/// # Note +/// +/// In RISCV-32, `UXL` does not exist. +#[inline] +#[cfg(not(target_arch = "riscv32"))] +pub unsafe fn set_uxl(uxl: XLEN) { + let mut value = _read(); + value &= !(0x3 << 32); // clear previous value + value |= (uxl as usize) << 32; + _write(value); +} + #[cfg(test)] mod tests { use super::*; @@ -156,11 +205,18 @@ mod tests { test_csr_field!(sstatus, sie); test_csr_field!(sstatus, spie); + test_csr_field!(sstatus, ube); [SPP::User, SPP::Supervisor].into_iter().for_each(|spp| { test_csr_field!(sstatus, spp: spp); }); + [VS::Off, VS::Initial, VS::Clean, VS::Dirty] + .into_iter() + .for_each(|vs| { + test_csr_field!(sstatus, vs: vs); + }); + [FS::Off, FS::Initial, FS::Clean, FS::Dirty] .into_iter() .for_each(|fs| { @@ -189,6 +245,8 @@ mod tests { test_csr_field!(sstatus, uxl: xlen); }); - test_csr_field!(sstatus, sd); + // SD is read-only: hardware sets it whenever FS, VS, or XS == Dirty. + assert!(!Sstatus::from_bits(0).sd()); + assert!(Sstatus::from_bits(usize::MAX).sd()); } }