diff --git a/riscv/CHANGELOG.md b/riscv/CHANGELOG.md index 23756a28..d1393498 100644 --- a/riscv/CHANGELOG.md +++ b/riscv/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Added `mtopi` CSR support for the RISC-V Advanced Interrupt Architecture extension. - Added DCSR (Debug Control and Status Register) CSR support for the RISC-V - Add `miselect` CSR - Improved assembly macro handling in asm.rs diff --git a/riscv/src/register.rs b/riscv/src/register.rs index a20328bc..72903fc6 100644 --- a/riscv/src/register.rs +++ b/riscv/src/register.rs @@ -89,6 +89,7 @@ pub mod mepc; pub mod mip; pub mod mscratch; pub mod mtinst; +pub mod mtopi; pub mod mtval; pub mod mtval2; diff --git a/riscv/src/register/macros.rs b/riscv/src/register/macros.rs index b5c9ee8e..594037a0 100644 --- a/riscv/src/register/macros.rs +++ b/riscv/src/register/macros.rs @@ -1041,6 +1041,19 @@ macro_rules! test_csr_field { } }}; + // test a multi-bit bitfield for read-only CSR (must come before enum pattern) + ($reg:ident, $field:ident: [$start:literal, $end:literal]) => {{ + let bits = $reg.bits(); + + let shift = $end - $start + 1; + let mask = (1usize << shift) - 1; + + let exp_val = (bits >> $start) & mask; + + // Test field extraction matches expected value + assert_eq!($reg.$field(), exp_val); + }}; + // test an enum bit field ($reg:ident, $field:ident: $var:expr) => {{ $crate::paste! { diff --git a/riscv/src/register/mtopi.rs b/riscv/src/register/mtopi.rs new file mode 100644 index 00000000..a8e51137 --- /dev/null +++ b/riscv/src/register/mtopi.rs @@ -0,0 +1,102 @@ +//! mtopi register — Machine Top Priority Interrupt (0x7C0) +//! +//! Provides information about the highest-priority pending interrupt when AIA (Advanced Interrupt Architecture) is supported. +//! This CSR is part of the RISC-V Advanced Interrupt Architecture extension and allows software to quickly +//! identify the most important pending interrupt without scanning through multiple interrupt pending registers. +//! +//! # Usage +//! +//! ```no_run +//! use riscv::register::mtopi; +//! +//! // Read the machine top priority interrupt register +//! let mtopi_val = mtopi::read(); +//! +//! if mtopi_val.has_interrupt() { +//! let interrupt_id = mtopi_val.iid(); +//! let priority = mtopi_val.ipid(); +//! println!("Highest priority interrupt: ID={}, Priority={}", interrupt_id, priority); +//! } else { +//! println!("No interrupts pending"); +//! } +//! ``` + +read_only_csr! { + /// Machine Top Priority Interrupt Register + Mtopi: 0x7C0, + mask: 0x0FFF_FFFF, +} + +read_only_csr_field! { + Mtopi, + /// 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! { + Mtopi, + /// 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 Mtopi { + /// Returns true if there is a valid interrupt pending + /// + /// When this returns true, both `interrupt_id()` and `priority()` will return meaningful values. + #[inline] + pub fn has_interrupt(&self) -> bool { + self.iid() != 0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_mtopi_fields() { + // Test using helper macros as requested - follows mcounteren.rs pattern + let mut mtopi = Mtopi::from_bits(0); + + // Test iid field [16:27] - using test helper macro + test_csr_field!(mtopi, iid: [16, 27]); + // Test ipid field [0:7] - using test helper macro + test_csr_field!(mtopi, ipid: [0, 7]); + + // Test helper methods + assert!(!mtopi.has_interrupt()); + + // Test with some interrupt pending (IID = 11, IPID = 5) + mtopi = Mtopi::from_bits((11 << 16) | 5); + test_csr_field!(mtopi, iid: [16, 27]); + test_csr_field!(mtopi, ipid: [0, 7]); + assert!(mtopi.has_interrupt()); + + // Test maximum values for each field + mtopi = Mtopi::from_bits((0xFFF << 16) | 0xFF); + test_csr_field!(mtopi, iid: [16, 27]); + test_csr_field!(mtopi, ipid: [0, 7]); + assert!(mtopi.has_interrupt()); + + // Test field boundaries + mtopi = Mtopi::from_bits(1 << 16); + test_csr_field!(mtopi, iid: [16, 27]); + test_csr_field!(mtopi, ipid: [0, 7]); + + mtopi = Mtopi::from_bits(1); + test_csr_field!(mtopi, iid: [16, 27]); + test_csr_field!(mtopi, ipid: [0, 7]); + } + + #[test] + fn test_mtopi_bitmask() { + let mtopi = Mtopi::from_bits(usize::MAX); + assert_eq!(mtopi.bits(), usize::MAX); + } +}