Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions riscv/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
116 changes: 100 additions & 16 deletions riscv/src/register/mstatus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! {
Expand Down Expand Up @@ -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],
}
Expand Down Expand Up @@ -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,
}

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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::*;
Expand All @@ -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);
Expand All @@ -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);
});
}
}
}
82 changes: 70 additions & 12 deletions riscv/src/register/sstatus.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -37,13 +37,26 @@ 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
spp,
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
Expand Down Expand Up @@ -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,
}

Expand All @@ -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);
Expand All @@ -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::*;
Expand All @@ -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| {
Expand Down Expand Up @@ -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());
}
}
Loading