Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions riscv/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions riscv/src/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
13 changes: 13 additions & 0 deletions riscv/src/register/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! {
Expand Down
123 changes: 123 additions & 0 deletions riscv/src/register/mtopi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//! 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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mask is not correct. According to the specification, only bits 0 to 27 are used.

}

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you read the documentation... It feels like it is the other way around. Higher numbers indicate lower priority. Or at least that is my understanding...

ipid: [0:7],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the specification, this field is called iprio, no ipid. Can you share the documentation you are following for implementing this PR? Just to double check that I'm not missing anything.

}

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() {
// 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());
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);
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);
}
}
Loading