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
3 changes: 3 additions & 0 deletions riscv/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
7 changes: 7 additions & 0 deletions riscv/src/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down
64 changes: 64 additions & 0 deletions riscv/src/register/siselect.rs
Original file line number Diff line number Diff line change
@@ -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);
});
}
}
69 changes: 69 additions & 0 deletions riscv/src/register/stopi.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
64 changes: 64 additions & 0 deletions riscv/src/register/vsiselect.rs
Original file line number Diff line number Diff line change
@@ -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);
});
}
}
Loading