From 8f9761fe30af98be85a7f243c9362fdfd3e5b103 Mon Sep 17 00:00:00 2001 From: cong-or Date: Tue, 7 Apr 2026 11:37:23 +0100 Subject: [PATCH 1/2] Add AIA CSRs: siselect, vsiselect, stopi Add supervisor and virtual supervisor indirect access select registers (siselect, vsiselect) mirroring the existing miselect implementation, and supervisor top priority interrupt register (stopi) mirroring mtopi/vstopi. Ref: #226 --- riscv/src/register.rs | 7 ++++ riscv/src/register/siselect.rs | 64 ++++++++++++++++++++++++++++++ riscv/src/register/stopi.rs | 69 +++++++++++++++++++++++++++++++++ riscv/src/register/vsiselect.rs | 64 ++++++++++++++++++++++++++++++ 4 files changed, 204 insertions(+) create mode 100644 riscv/src/register/siselect.rs create mode 100644 riscv/src/register/stopi.rs create mode 100644 riscv/src/register/vsiselect.rs diff --git a/riscv/src/register.rs b/riscv/src/register.rs index 8235cb7b..cdf83f83 100644 --- a/riscv/src/register.rs +++ b/riscv/src/register.rs @@ -62,6 +62,7 @@ pub mod senvcfg; pub mod sepc; pub mod sip; pub mod sscratch; +pub mod stopi; pub mod stval; pub mod vstopi; @@ -125,6 +126,12 @@ pub mod mseccfgh; // Machine indirect access pub mod miselect; +// Supervisor indirect access +pub mod siselect; + +// Virtual supervisor indirect access +pub mod vsiselect; + #[cfg(test)] mod tests; diff --git a/riscv/src/register/siselect.rs b/riscv/src/register/siselect.rs new file mode 100644 index 00000000..c8714f02 --- /dev/null +++ b/riscv/src/register/siselect.rs @@ -0,0 +1,64 @@ +//! `siselect` register. + +const MASK: usize = usize::MAX; + +read_write_csr! { + /// `siselect` register. + Siselect: 0x150, + mask: MASK, +} + +#[cfg(target_arch = "riscv32")] +read_write_csr_field! { + Siselect, + /// Returns whether `siselect` is for custom use of indirect CSRs. + is_custom: 31, +} + +#[cfg(not(target_arch = "riscv32"))] +read_write_csr_field! { + Siselect, + /// Returns whether `siselect` is for custom use of indirect CSRs. + is_custom: 63, +} + +#[cfg(target_arch = "riscv32")] +read_write_csr_field! { + Siselect, + /// Gets the value stored in the `siselect` CSR. + /// + /// # Note + /// + /// The semantics of the value depend on the extension for the referenced CSR, + /// and the relevant `sireg*` value. + value: [0:30], +} + +#[cfg(not(target_arch = "riscv32"))] +read_write_csr_field! { + Siselect, + /// Gets the value stored in the `siselect` CSR. + /// + /// # Note + /// + /// The semantics of the value depend on the extension for the referenced CSR, + /// and the relevant `sireg*` value. + value: [0:62], +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test() { + (0..=usize::BITS) + .map(|r| ((1u128 << r) - 1) as usize) + .for_each(|bits| { + let mut siselect = Siselect::from_bits(bits); + + test_csr_field!(siselect, is_custom); + test_csr_field!(siselect, value: [0, usize::BITS - 2], 0); + }); + } +} diff --git a/riscv/src/register/stopi.rs b/riscv/src/register/stopi.rs new file mode 100644 index 00000000..613be38c --- /dev/null +++ b/riscv/src/register/stopi.rs @@ -0,0 +1,69 @@ +//! `stopi` register — Supervisor Top Priority Interrupt (0xDC0) + +read_only_csr! { + /// Supervisor Top Priority Interrupt Register + Stopi: 0xDC0, + mask: 0x0FFF_00FF, +} + +read_only_csr_field! { + Stopi, + /// Interrupt ID (bits 16..27) + /// + /// Identifies the specific interrupt source. A value of 0 indicates no interrupt is pending. + /// Non-zero values correspond to specific interrupt sources as defined by the interrupt controller. + iid: [16:27], +} + +read_only_csr_field! { + Stopi, + /// Interrupt Priority ID (bits 0..7) + /// + /// Represents the priority level of the pending interrupt. + /// Lower numerical values indicate higher priority interrupts. + iprio: [0:7], +} + +impl Stopi { + /// Returns true if there is a valid interrupt pending + /// + /// When this returns true, both `iid()` and `iprio()` will return meaningful values. + #[inline] + pub fn is_interrupt_pending(&self) -> bool { + self.iid() != 0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_stopi_fields() { + let stopi = Stopi::from_bits(0); + test_ro_csr_field!(stopi, iid: [16, 27], 0x0); + test_ro_csr_field!(stopi, iprio: [0, 7], 0x0); + + let stopi = Stopi::from_bits((0xB << 16) | 5); + test_ro_csr_field!(stopi, iid: [16, 27], 0xB); + test_ro_csr_field!(stopi, iprio: [0, 7], 0x5); + + let stopi = Stopi::from_bits((0xFFF << 16) | 0xFF); + test_ro_csr_field!(stopi, iid: [16, 27], 0xFFF); + test_ro_csr_field!(stopi, iprio: [0, 7], 0xFF); + + let stopi = Stopi::from_bits(1 << 16); + test_ro_csr_field!(stopi, iid: [16, 27], 0x1); + test_ro_csr_field!(stopi, iprio: [0, 7], 0x0); + + let stopi = Stopi::from_bits(1); + test_ro_csr_field!(stopi, iid: [16, 27], 0x0); + test_ro_csr_field!(stopi, iprio: [0, 7], 0x1); + } + + #[test] + fn test_stopi_bitmask() { + let stopi = Stopi::from_bits(usize::MAX); + assert_eq!(stopi.bits(), 0x0FFF_00FFusize); + } +} diff --git a/riscv/src/register/vsiselect.rs b/riscv/src/register/vsiselect.rs new file mode 100644 index 00000000..19afff74 --- /dev/null +++ b/riscv/src/register/vsiselect.rs @@ -0,0 +1,64 @@ +//! `vsiselect` register. + +const MASK: usize = usize::MAX; + +read_write_csr! { + /// `vsiselect` register. + Vsiselect: 0x250, + mask: MASK, +} + +#[cfg(target_arch = "riscv32")] +read_write_csr_field! { + Vsiselect, + /// Returns whether `vsiselect` is for custom use of indirect CSRs. + is_custom: 31, +} + +#[cfg(not(target_arch = "riscv32"))] +read_write_csr_field! { + Vsiselect, + /// Returns whether `vsiselect` is for custom use of indirect CSRs. + is_custom: 63, +} + +#[cfg(target_arch = "riscv32")] +read_write_csr_field! { + Vsiselect, + /// Gets the value stored in the `vsiselect` CSR. + /// + /// # Note + /// + /// The semantics of the value depend on the extension for the referenced CSR, + /// and the relevant `vsireg*` value. + value: [0:30], +} + +#[cfg(not(target_arch = "riscv32"))] +read_write_csr_field! { + Vsiselect, + /// Gets the value stored in the `vsiselect` CSR. + /// + /// # Note + /// + /// The semantics of the value depend on the extension for the referenced CSR, + /// and the relevant `vsireg*` value. + value: [0:62], +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test() { + (0..=usize::BITS) + .map(|r| ((1u128 << r) - 1) as usize) + .for_each(|bits| { + let mut vsiselect = Vsiselect::from_bits(bits); + + test_csr_field!(vsiselect, is_custom); + test_csr_field!(vsiselect, value: [0, usize::BITS - 2], 0); + }); + } +} From cd3d548931c11f452a8556c8b5f9c44f1b1ecbeb Mon Sep 17 00:00:00 2001 From: cong-or Date: Fri, 10 Apr 2026 21:26:29 +0100 Subject: [PATCH 2/2] Add CHANGELOG entry for siselect, vsiselect, stopi CSRs --- riscv/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/riscv/CHANGELOG.md b/riscv/CHANGELOG.md index 47356d34..3598eb96 100644 --- a/riscv/CHANGELOG.md +++ b/riscv/CHANGELOG.md @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - Add `pmpaddr16` ~ `pmpaddr63` CSRs +- Add `siselect` CSR +- Add `vsiselect` CSR +- Add `stopi` CSR ## v0.16.0 - 2025-12-19