Skip to content

Commit

Permalink
Generics for interrupts and exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
romancardenas committed May 30, 2024
1 parent 794b7e0 commit 6c75aa0
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 205 deletions.
8 changes: 4 additions & 4 deletions riscv-pac/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ impl PacNumberEnum {
fn from_number(number: #num_type) -> Result<Self, #num_type> {
if #valid_condition {
// SAFETY: The number is valid for this enum
Ok(unsafe { core::mem::transmute(number) })
Ok(unsafe { core::mem::transmute::<#num_type, Self>(number) })
} else {
Err(number)
}
Expand Down Expand Up @@ -148,7 +148,7 @@ impl PacNumberEnum {
/// ```rust
/// use riscv_pac::*;
///
/// #[repr(u16)]
/// #[repr(usize)]
/// #[pac_enum(unsafe ExceptionNumber)]
/// #[derive(Clone, Copy, Debug, Eq, PartialEq)]
/// enum Exception {
Expand Down Expand Up @@ -190,8 +190,8 @@ pub fn pac_enum(attr: TokenStream, item: TokenStream) -> TokenStream {
}

let trait_impl = match attrs[1] {
"ExceptionNumber" => pac_enum.quote("ExceptionNumber", "u16", "MAX_EXCEPTION_NUMBER"),
"InterruptNumber" => pac_enum.quote("InterruptNumber", "u16", "MAX_INTERRUPT_NUMBER"),
"ExceptionNumber" => pac_enum.quote("ExceptionNumber", "usize", "MAX_EXCEPTION_NUMBER"),
"InterruptNumber" => pac_enum.quote("InterruptNumber", "usize", "MAX_INTERRUPT_NUMBER"),
"PriorityNumber" => pac_enum.quote("PriorityNumber", "u8", "MAX_PRIORITY_NUMBER"),
"HartIdNumber" => pac_enum.quote("HartIdNumber", "u16", "MAX_HART_ID_NUMBER"),
_ => panic!("Unknown trait '{}'. Expected: 'ExceptionNumber', 'InterruptNumber', 'PriorityNumber', or 'HartIdNumber'", attrs[1]),
Expand Down
16 changes: 8 additions & 8 deletions riscv-pac/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub use riscv_pac_macros::*;
///
/// This trait should be implemented by a peripheral access crate (PAC) on its enum of available
/// exceptions for a specific device. Alternatively, the `riscv` crate provides a default
/// implementation for the RISC-V ISA. Each variant must convert to a `u16` of its exception number.
/// implementation for the RISC-V ISA. Each variant must convert to a `usize` of its exception number.
///
/// # Safety
///
Expand All @@ -19,21 +19,21 @@ pub use riscv_pac_macros::*;
/// * `MAX_EXCEPTION_NUMBER` must coincide with the highest allowed exception number.
pub unsafe trait ExceptionNumber: Copy {
/// Highest number assigned to an exception.
const MAX_EXCEPTION_NUMBER: u16;
const MAX_EXCEPTION_NUMBER: usize;

/// Converts an exception to its corresponding number.
fn number(self) -> u16;
fn number(self) -> usize;

/// Tries to convert a number to a valid exception.
/// If the conversion fails, it returns an error with the number back.
fn from_number(value: u16) -> Result<Self, u16>;
fn from_number(value: usize) -> Result<Self, usize>;
}

/// Trait for enums of target-specific interrupt numbers.
///
/// This trait should be implemented by a peripheral access crate (PAC) on its enum of available
/// interrupts for a specific device. Alternatively, the `riscv` crate provides a default
/// implementation for the RISC-V ISA. Each variant must convert to a `u16` of its interrupt number.
/// implementation for the RISC-V ISA. Each variant must convert to a `usize` of its interrupt number.
///
/// # Safety
///
Expand All @@ -45,14 +45,14 @@ pub unsafe trait ExceptionNumber: Copy {
/// * `MAX_INTERRUPT_NUMBER` must coincide with the highest allowed interrupt number.
pub unsafe trait InterruptNumber: Copy {
/// Highest number assigned to an interrupt source.
const MAX_INTERRUPT_NUMBER: u16;
const MAX_INTERRUPT_NUMBER: usize;

/// Converts an interrupt source to its corresponding number.
fn number(self) -> u16;
fn number(self) -> usize;

/// Tries to convert a number to a valid interrupt source.
/// If the conversion fails, it returns an error with the number back.
fn from_number(value: u16) -> Result<Self, u16>;
fn from_number(value: usize) -> Result<Self, usize>;
}

/// Marker trait for enums of target-specific core interrupt numbers.
Expand Down
2 changes: 1 addition & 1 deletion riscv-pac/tests/ui/fail_wrong_repr.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-
1 | #[riscv_pac::pac_enum(unsafe InterruptNumber)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: source type: `u16` (16 bits)
= note: source type: `usize` (64 bits)
= note: target type: `Interrupt` (8 bits)
= note: this error originates in the attribute macro `riscv_pac::pac_enum` (in Nightly builds, run with -Z macro-backtrace for more info)
4 changes: 2 additions & 2 deletions riscv-pac/tests/ui/pass_test.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use riscv_pac::*;

#[repr(u16)]
#[repr(usize)]
#[pac_enum(unsafe ExceptionNumber)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Exception {
E1 = 1,
E3 = 3,
}

#[repr(u16)]
#[repr(usize)]
#[pac_enum(unsafe InterruptNumber)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Interrupt {
Expand Down
1 change: 1 addition & 0 deletions riscv/src/interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ pub mod machine {
r
}
}

pub mod supervisor {
use crate::register::{sepc, sstatus};

Expand Down
169 changes: 73 additions & 96 deletions riscv/src/register/mcause.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,7 @@
use riscv_pac::CoreInterruptNumber;
pub use riscv_pac::{ExceptionNumber, InterruptNumber}; // re-export useful riscv-pac traits

/// mcause register
#[derive(Clone, Copy, Debug)]
pub struct Mcause {
bits: usize,
}

impl From<usize> for Mcause {
#[inline]
fn from(bits: usize) -> Self {
Self { bits }
}
}

/// Trap Cause
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Trap {
Interrupt(Interrupt),
Exception(Exception),
}

/// Interrupt
/// Standard M-mode RISC-V interrupts
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(usize)]
pub enum Interrupt {
Expand All @@ -33,10 +13,32 @@ pub enum Interrupt {
MachineTimer = 7,
SupervisorExternal = 9,
MachineExternal = 11,
Unknown,
}

/// Exception
/// SAFETY: `Interrupt` represents the standard RISC-V interrupts
unsafe impl InterruptNumber for Interrupt {
const MAX_INTERRUPT_NUMBER: usize = Self::MachineExternal as usize;

#[inline]
fn number(self) -> usize {
self as usize
}

#[inline]
fn from_number(value: usize) -> Result<Self, usize> {
if value > 11 || value % 2 == 0 {
Err(value)
} else {
// SAFETY: valid interrupt number
unsafe { Ok(core::mem::transmute::<usize, Self>(value)) }
}
}
}

/// SAFETY: `Interrupt` represents the standard RISC-V core interrupts
unsafe impl CoreInterruptNumber for Interrupt {}

/// Standard M-mode RISC-V exceptions
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(usize)]
pub enum Exception {
Expand All @@ -54,93 +56,52 @@ pub enum Exception {
InstructionPageFault = 12,
LoadPageFault = 13,
StorePageFault = 15,
Unknown,
}

impl From<usize> for Interrupt {
#[inline]
fn from(nr: usize) -> Self {
if nr > 11 || nr % 2 == 0 {
Self::Unknown
} else {
// SAFETY: valid interrupt number
unsafe { core::mem::transmute::<usize, Self>(nr) }
}
}
}

impl TryFrom<Interrupt> for usize {
type Error = Interrupt;

#[inline]
fn try_from(value: Interrupt) -> Result<Self, Self::Error> {
match value {
Interrupt::Unknown => Err(Self::Error::Unknown),
_ => Ok(value as Self),
}
}
}

/// SAFETY: `Interrupt` represents the standard RISC-V interrupts
unsafe impl InterruptNumber for Interrupt {
const MAX_INTERRUPT_NUMBER: u16 = Self::MachineExternal as u16;
/// SAFETY: `Exception` represents the standard RISC-V exceptions
unsafe impl ExceptionNumber for Exception {
const MAX_EXCEPTION_NUMBER: usize = Self::StorePageFault as usize;

#[inline]
fn number(self) -> u16 {
self as u16
fn number(self) -> usize {
self as usize
}

#[inline]
fn from_number(value: u16) -> Result<Self, u16> {
match (value as usize).into() {
Self::Unknown => Err(value),
value => Ok(value),
}
}
}

/// SAFETY: `Interrupt` represents the standard RISC-V core interrupts
unsafe impl CoreInterruptNumber for Interrupt {}

impl From<usize> for Exception {
#[inline]
fn from(nr: usize) -> Self {
if nr == 10 || nr == 14 || nr > 15 {
Self::Unknown
fn from_number(value: usize) -> Result<Self, usize> {
if value == 10 || value == 14 || value > 15 {
Err(value)
} else {
// SAFETY: valid exception number
unsafe { core::mem::transmute::<usize, Self>(nr) }
unsafe { Ok(core::mem::transmute::<usize, Self>(value)) }
}
}
}

impl TryFrom<Exception> for usize {
type Error = Exception;

#[inline]
fn try_from(value: Exception) -> Result<Self, Self::Error> {
match value {
Exception::Unknown => Err(Self::Error::Unknown),
_ => Ok(value as Self),
}
}
/// Trap Cause
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Trap<I, E> {
Interrupt(I),
Exception(E),
}

/// SAFETY: `Exception` represents the standard RISC-V exceptions
unsafe impl ExceptionNumber for Exception {
const MAX_EXCEPTION_NUMBER: u16 = Self::StorePageFault as u16;
/// Trap Error
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TrapError {
InvalidInterrupt(usize),
InvalidException(usize),
}

#[inline]
fn number(self) -> u16 {
self as u16
}
/// mcause register
#[derive(Clone, Copy, Debug)]
pub struct Mcause {
bits: usize,
}

impl From<usize> for Mcause {
#[inline]
fn from_number(value: u16) -> Result<Self, u16> {
match (value as usize).into() {
Self::Unknown => Err(value),
value => Ok(value),
}
fn from(bits: usize) -> Self {
Self { bits }
}
}

Expand All @@ -157,16 +118,32 @@ impl Mcause {
self.bits & !(1 << (usize::BITS as usize - 1))
}

/// Trap Cause
/// Try to get the trap cause
#[inline]
pub fn cause(&self) -> Trap {
pub fn try_cause<I, E>(&self) -> Result<Trap<I, E>, TrapError>
where
I: CoreInterruptNumber,
E: ExceptionNumber,
{
if self.is_interrupt() {
Trap::Interrupt(Interrupt::from(self.code()))
match I::from_number(self.code()) {
Ok(interrupt) => Ok(Trap::Interrupt(interrupt)),
Err(code) => Err(TrapError::InvalidInterrupt(code)),
}
} else {
Trap::Exception(Exception::from(self.code()))
match E::from_number(self.code()) {
Ok(exception) => Ok(Trap::Exception(exception)),
Err(code) => Err(TrapError::InvalidException(code)),
}
}
}

/// Trap Cause
#[inline]
pub fn cause<I: CoreInterruptNumber, E: ExceptionNumber>(&self) -> Trap<I, E> {
self.try_cause().unwrap()
}

/// Is trap cause an interrupt.
#[inline]
pub fn is_interrupt(&self) -> bool {
Expand Down
Loading

0 comments on commit 6c75aa0

Please sign in to comment.