From b2f88f8abb706d04dc7f6c52fbf82af0c02997ec Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Wed, 24 Sep 2025 17:19:39 +0530 Subject: [PATCH 1/6] feat: add mtopi CSR (Machine Top Priority Interrupt) register Add support for the mtopi CSR (0x7C0) from RISC-V Advanced Interrupt Architecture (AIA). Provides read-only access to highest-priority pending interrupt information with IID and IPID fields. --- riscv/CHANGELOG.md | 1 + riscv/src/register.rs | 1 + riscv/src/register/mtopi.rs | 111 ++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 riscv/src/register/mtopi.rs 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/mtopi.rs b/riscv/src/register/mtopi.rs new file mode 100644 index 00000000..af789f80 --- /dev/null +++ b/riscv/src/register/mtopi.rs @@ -0,0 +1,111 @@ +//! 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.interrupt_id(); +//! let priority = mtopi_val.priority(); +//! 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: usize::MAX, +} + +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. + /// Higher numerical values indicate higher priority interrupts. + ipid: [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 + } + + /// Returns the interrupt priority, with higher values indicating higher priority + /// + /// This value is only meaningful when `has_interrupt()` returns true. + #[inline] + pub fn priority(&self) -> usize { + self.ipid() + } + + /// Returns the interrupt identifier + /// + /// A value of 0 indicates no interrupt is pending. Non-zero values identify + /// specific interrupt sources as defined by the interrupt controller configuration. + #[inline] + pub fn interrupt_id(&self) -> usize { + self.iid() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_mtopi_fields() { + let mtopi = Mtopi::from_bits(0); + + assert_eq!(mtopi.iid(), 0); + assert_eq!(mtopi.ipid(), 0); + assert!(!mtopi.has_interrupt()); + assert_eq!(mtopi.priority(), 0); + assert_eq!(mtopi.interrupt_id(), 0); + + // Test with some interrupt pending (IID = 11, IPID = 5) + let mtopi = Mtopi::from_bits((11 << 16) | 5); + + assert_eq!(mtopi.iid(), 11); + assert_eq!(mtopi.ipid(), 5); + assert!(mtopi.has_interrupt()); + assert_eq!(mtopi.priority(), 5); + assert_eq!(mtopi.interrupt_id(), 11); + + // Test maximum values + let mtopi = Mtopi::from_bits((0xFFF << 16) | 0xFF); + + assert_eq!(mtopi.iid(), 0xFFF); + assert_eq!(mtopi.ipid(), 0xFF); + assert!(mtopi.has_interrupt()); + } + + #[test] + fn test_mtopi_bitmask() { + let mtopi = Mtopi::from_bits(usize::MAX); + assert_eq!(mtopi.bits(), usize::MAX); + } +} From 15f8a16bc503f25f640542bf7d2b6d8160f64c28 Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Wed, 24 Sep 2025 18:03:05 +0530 Subject: [PATCH 2/6] Use test helper macros for mtopi CSR field testing Replace manual assertions with test_ro_csr_field! macro that follows the same pattern as test_csr_field! but adapted for read-only CSRs. --- riscv/src/register/mtopi.rs | 38 +++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/riscv/src/register/mtopi.rs b/riscv/src/register/mtopi.rs index af789f80..f5d039b6 100644 --- a/riscv/src/register/mtopi.rs +++ b/riscv/src/register/mtopi.rs @@ -76,30 +76,44 @@ impl Mtopi { mod tests { use super::*; + macro_rules! test_ro_csr_field { + // test a multi-bit bitfield for read-only CSR + ($reg:ident, $field:ident: [$start:expr, $end:expr]) => {{ + let bits = $reg.bits(); + let shift = $end - $start + 1; + let mask = (1usize << shift) - 1; + let exp_val = (bits >> $start) & mask; + + // Test that field extraction matches expected value + assert_eq!($reg.$field(), exp_val); + }}; + } + #[test] fn test_mtopi_fields() { - let mtopi = Mtopi::from_bits(0); + let mut mtopi = Mtopi::from_bits(0); + + // Test iid field [16:27] with zero bits + test_ro_csr_field!(mtopi, iid: [16, 27]); + // Test ipid field [0:7] with zero bits + test_ro_csr_field!(mtopi, ipid: [0, 7]); - assert_eq!(mtopi.iid(), 0); - assert_eq!(mtopi.ipid(), 0); assert!(!mtopi.has_interrupt()); assert_eq!(mtopi.priority(), 0); assert_eq!(mtopi.interrupt_id(), 0); // Test with some interrupt pending (IID = 11, IPID = 5) - let mtopi = Mtopi::from_bits((11 << 16) | 5); - - assert_eq!(mtopi.iid(), 11); - assert_eq!(mtopi.ipid(), 5); + mtopi = Mtopi::from_bits((11 << 16) | 5); + test_ro_csr_field!(mtopi, iid: [16, 27]); + test_ro_csr_field!(mtopi, ipid: [0, 7]); assert!(mtopi.has_interrupt()); assert_eq!(mtopi.priority(), 5); assert_eq!(mtopi.interrupt_id(), 11); - // Test maximum values - let mtopi = Mtopi::from_bits((0xFFF << 16) | 0xFF); - - assert_eq!(mtopi.iid(), 0xFFF); - assert_eq!(mtopi.ipid(), 0xFF); + // Test maximum values for each field + mtopi = Mtopi::from_bits((0xFFF << 16) | 0xFF); + test_ro_csr_field!(mtopi, iid: [16, 27]); + test_ro_csr_field!(mtopi, ipid: [0, 7]); assert!(mtopi.has_interrupt()); } From 311f82b716a963db3373906eb343de9ecb8e07b5 Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Wed, 24 Sep 2025 18:24:31 +0530 Subject: [PATCH 3/6] Enhance mtopi test helper macro with comprehensive coverage - Add field boundary testing for both iid and ipid fields - Improve comments to clarify macro usage and pattern following - Ensure complete test coverage matching original test_csr_field! scope --- riscv/src/register/mtopi.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/riscv/src/register/mtopi.rs b/riscv/src/register/mtopi.rs index f5d039b6..3668a8f8 100644 --- a/riscv/src/register/mtopi.rs +++ b/riscv/src/register/mtopi.rs @@ -77,27 +77,29 @@ mod tests { use super::*; macro_rules! test_ro_csr_field { - // test a multi-bit bitfield for read-only CSR + // test a multi-bit bitfield for read-only CSR - matches test_csr_field! pattern ($reg:ident, $field:ident: [$start:expr, $end:expr]) => {{ let bits = $reg.bits(); let shift = $end - $start + 1; let mask = (1usize << shift) - 1; let exp_val = (bits >> $start) & mask; - // Test that field extraction matches expected value + // Test field extraction matches expected value (same as test_csr_field! macro) assert_eq!($reg.$field(), exp_val); }}; } #[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] with zero bits + // Test iid field [16:27] - using test helper macro test_ro_csr_field!(mtopi, iid: [16, 27]); - // Test ipid field [0:7] with zero bits + // Test ipid field [0:7] - using test helper macro test_ro_csr_field!(mtopi, ipid: [0, 7]); + // Test helper methods assert!(!mtopi.has_interrupt()); assert_eq!(mtopi.priority(), 0); assert_eq!(mtopi.interrupt_id(), 0); @@ -115,6 +117,15 @@ mod tests { test_ro_csr_field!(mtopi, iid: [16, 27]); test_ro_csr_field!(mtopi, ipid: [0, 7]); assert!(mtopi.has_interrupt()); + + // Test field boundaries + mtopi = Mtopi::from_bits(1 << 16); + test_ro_csr_field!(mtopi, iid: [16, 27]); + test_ro_csr_field!(mtopi, ipid: [0, 7]); + + mtopi = Mtopi::from_bits(1); + test_ro_csr_field!(mtopi, iid: [16, 27]); + test_ro_csr_field!(mtopi, ipid: [0, 7]); } #[test] From 3a5c598c5e1b37408ec9d683f7efa846f7e1f444 Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Wed, 24 Sep 2025 19:02:23 +0530 Subject: [PATCH 4/6] Add read-only CSR support to existing test_csr_field! macro Extend test_csr_field! macro with new arm for read-only multi-bit fields. Update mtopi tests to use standard macro instead of custom implementation. --- riscv/src/register/macros.rs | 13 +++++++++++++ riscv/src/register/mtopi.rs | 33 ++++++++++----------------------- 2 files changed, 23 insertions(+), 23 deletions(-) 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 index 3668a8f8..0a3c1487 100644 --- a/riscv/src/register/mtopi.rs +++ b/riscv/src/register/mtopi.rs @@ -76,28 +76,15 @@ impl Mtopi { mod tests { use super::*; - macro_rules! test_ro_csr_field { - // test a multi-bit bitfield for read-only CSR - matches test_csr_field! pattern - ($reg:ident, $field:ident: [$start:expr, $end:expr]) => {{ - 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 (same as test_csr_field! macro) - assert_eq!($reg.$field(), exp_val); - }}; - } - #[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_ro_csr_field!(mtopi, iid: [16, 27]); + test_csr_field!(mtopi, iid: [16, 27]); // Test ipid field [0:7] - using test helper macro - test_ro_csr_field!(mtopi, ipid: [0, 7]); + test_csr_field!(mtopi, ipid: [0, 7]); // Test helper methods assert!(!mtopi.has_interrupt()); @@ -106,26 +93,26 @@ mod tests { // Test with some interrupt pending (IID = 11, IPID = 5) mtopi = Mtopi::from_bits((11 << 16) | 5); - test_ro_csr_field!(mtopi, iid: [16, 27]); - test_ro_csr_field!(mtopi, ipid: [0, 7]); + test_csr_field!(mtopi, iid: [16, 27]); + test_csr_field!(mtopi, ipid: [0, 7]); assert!(mtopi.has_interrupt()); assert_eq!(mtopi.priority(), 5); assert_eq!(mtopi.interrupt_id(), 11); // Test maximum values for each field mtopi = Mtopi::from_bits((0xFFF << 16) | 0xFF); - test_ro_csr_field!(mtopi, iid: [16, 27]); - test_ro_csr_field!(mtopi, ipid: [0, 7]); + 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_ro_csr_field!(mtopi, iid: [16, 27]); - test_ro_csr_field!(mtopi, ipid: [0, 7]); + test_csr_field!(mtopi, iid: [16, 27]); + test_csr_field!(mtopi, ipid: [0, 7]); mtopi = Mtopi::from_bits(1); - test_ro_csr_field!(mtopi, iid: [16, 27]); - test_ro_csr_field!(mtopi, ipid: [0, 7]); + test_csr_field!(mtopi, iid: [16, 27]); + test_csr_field!(mtopi, ipid: [0, 7]); } #[test] From 71ae94a79aab4b61ffea73f7150661f3146be857 Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Wed, 24 Sep 2025 19:30:40 +0530 Subject: [PATCH 5/6] Remove convenience methods priority() and interrupt_id() from mtopi These methods were simple aliases for ipid() and iid() respectively, which doesn't follow the crate's pattern of only adding convenience methods that provide semantic value or logical operations. --- riscv/src/register/mtopi.rs | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/riscv/src/register/mtopi.rs b/riscv/src/register/mtopi.rs index 0a3c1487..ee5cde5f 100644 --- a/riscv/src/register/mtopi.rs +++ b/riscv/src/register/mtopi.rs @@ -13,8 +13,8 @@ //! let mtopi_val = mtopi::read(); //! //! if mtopi_val.has_interrupt() { -//! let interrupt_id = mtopi_val.interrupt_id(); -//! let priority = mtopi_val.priority(); +//! 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"); @@ -53,23 +53,6 @@ impl Mtopi { pub fn has_interrupt(&self) -> bool { self.iid() != 0 } - - /// Returns the interrupt priority, with higher values indicating higher priority - /// - /// This value is only meaningful when `has_interrupt()` returns true. - #[inline] - pub fn priority(&self) -> usize { - self.ipid() - } - - /// Returns the interrupt identifier - /// - /// A value of 0 indicates no interrupt is pending. Non-zero values identify - /// specific interrupt sources as defined by the interrupt controller configuration. - #[inline] - pub fn interrupt_id(&self) -> usize { - self.iid() - } } #[cfg(test)] @@ -88,16 +71,12 @@ mod tests { // Test helper methods assert!(!mtopi.has_interrupt()); - assert_eq!(mtopi.priority(), 0); - assert_eq!(mtopi.interrupt_id(), 0); // 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()); - assert_eq!(mtopi.priority(), 5); - assert_eq!(mtopi.interrupt_id(), 11); // Test maximum values for each field mtopi = Mtopi::from_bits((0xFFF << 16) | 0xFF); From f985f538ffd063c65c5e090a66f5b84de88f2e59 Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Tue, 7 Oct 2025 18:23:29 +0530 Subject: [PATCH 6/6] Fix bit usage, replace ipid with iprio and fixed documentations --- riscv/src/register/mtopi.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/riscv/src/register/mtopi.rs b/riscv/src/register/mtopi.rs index ee5cde5f..a8e51137 100644 --- a/riscv/src/register/mtopi.rs +++ b/riscv/src/register/mtopi.rs @@ -24,7 +24,7 @@ read_only_csr! { /// Machine Top Priority Interrupt Register Mtopi: 0x7C0, - mask: usize::MAX, + mask: 0x0FFF_FFFF, } read_only_csr_field! { @@ -41,8 +41,8 @@ read_only_csr_field! { /// Interrupt Priority ID (bits 0..7) /// /// Represents the priority level of the pending interrupt. - /// Higher numerical values indicate higher priority interrupts. - ipid: [0:7], + /// Lower numerical values indicate higher priority interrupts. + iprio: [0:7], } impl Mtopi {