From f08efb9c20a6d80ed57357a5801b963b3541bfe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Fri, 6 Oct 2023 11:49:23 +0200 Subject: [PATCH] remove peripheral module --- CHANGELOG.md | 1 - Cargo.toml | 2 - src/lib.rs | 2 - src/macros.rs | 158 ----------------------- src/peripheral.rs | 44 ------- src/peripheral/plic.rs | 280 ----------------------------------------- 6 files changed, 487 deletions(-) delete mode 100644 src/peripheral.rs delete mode 100644 src/peripheral/plic.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 37803141..1f2aefa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Add `mstatus::uxl` and `mstatus::sxl` - Add `mstatus::ube`, `mstatus::sbe`, and `mstatus::mbe` endianness bit fields - Add `mstatush` registers (RISCV-32 only) -- Add generic implementation of a PLIC peripheral - Add `asm::fence()`, a wrapper for implementing a `fence` instruction - Add `asm::fence_i()`, a wrapper for implementing a `fence.i` instruction - Add `TryFrom` implementation for `mcause::{Interrupt, Exception}` and `scause::{Interrupt, Exception}` diff --git a/Cargo.toml b/Cargo.toml index 5dd3e61b..e12ce17e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,9 +20,7 @@ targets = [ [features] critical-section-single-hart = ["critical-section/restore-state-bool"] -plic = ["volatile-register"] [dependencies] critical-section = "1.1.0" embedded-hal = "0.2.6" -volatile-register = { version = "0.2.1", optional = true } diff --git a/src/lib.rs b/src/lib.rs index 463bef40..e5d1f281 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,8 +31,6 @@ pub mod asm; pub mod delay; pub mod interrupt; -#[cfg(feature = "plic")] -pub mod peripheral; pub mod register; #[macro_use] diff --git a/src/macros.rs b/src/macros.rs index ebcef985..012063a4 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -55,161 +55,3 @@ macro_rules! singleton { }) }; } - -/// Macro to create interfaces to PLIC contexts in PACs. -/// -/// This macro expects 5 arguments: -/// -/// - `PLIC`: name of the PLIC context interface structure to be created. -/// We recommend to leave `PLIC` for context 0 and `PLICx` for the remaining contexts. -/// -/// - `BASE`: base address of the PLIC peripheral of the target. -/// -/// - `CONTEXT`: context number assigned to the PLIC interface. -/// -/// - `INTERRUPT`: enum type of the external interruptions of the target. -/// This type must implement the [`crate::peripheral::plic::InterruptNumber`] trait. -/// -/// - `PRIORITY`: enum type of the priority levels supported by the target. -/// This type must implement the [`crate::peripheral::plic::PriorityNumber`] trait. -/// -/// # Note -/// -/// This macro requires the `plic` feature to be active. -#[cfg(feature = "plic")] -#[macro_export] -macro_rules! plic_context { - ($PLIC:ident, $BASE:literal, $CONTEXT:literal, $INTERRUPT:ident, $PRIORITY:ident) => { - /// Platform-Level Interrupt Controller (PLIC) context. - #[repr(transparent)] - pub struct $PLIC { - context: $crate::peripheral::PLIC<$BASE, $CONTEXT>, - } - - impl $PLIC { - /// Creates a new PLIC context interface. - pub const fn new() -> Self { - Self { - context: $crate::peripheral::PLIC::new(), - } - } - - /// Enables machine external interrupts. - #[inline(always)] - pub fn enable() { - $crate::peripheral::PLIC::<$BASE, $CONTEXT>::enable(); - } - - /// Disables machine external interrupts. - #[inline(always)] - pub fn disable() { - $crate::peripheral::PLIC::<$BASE, $CONTEXT>::disable(); - } - - /// Returns the priority level associated to a given interrupt source. - #[inline(always)] - pub fn priority(source: $INTERRUPT) -> $PRIORITY { - $crate::peripheral::PLIC::<$BASE, $CONTEXT>::priority(source) - } - - /// Getter method for the priority level associated to a given interrupt source. - #[inline(always)] - pub fn get_priority(&self, source: $INTERRUPT) -> $PRIORITY { - Self::priority(source) - } - - /// Sets the priority level of a given interrupt source. - /// - /// # Note - /// - /// Interrupt source priorities are shared among all the contexts of the PLIC. - /// Thus, changing the priority of sources may affect other PLIC contexts. - /// - /// # Safety - /// - /// Changing priority levels can break priority-based critical sections and compromise memory safety. - #[inline(always)] - pub unsafe fn set_priority(&mut self, source: $INTERRUPT, priority: $PRIORITY) { - self.context.set_priority(source, priority); - } - - /// Checks if an interrupt triggered by a given source is pending. - #[inline(always)] - pub fn is_interrupt_pending(source: $INTERRUPT) -> bool { - $crate::peripheral::PLIC::<$BASE, $CONTEXT>::is_interrupt_pending(source) - } - - /// Checks if an interrupt source is enabled for the PLIC context. - #[inline(always)] - pub fn is_interrupt_enabled(source: $INTERRUPT) -> bool { - $crate::peripheral::PLIC::<$BASE, $CONTEXT>::is_interrupt_enabled(source) - } - - /// Enables an interrupt source for the PLIC context. - /// - /// # Safety - /// - /// It performs non-atomic read-modify-write operations, which may lead to undefined behavior. - /// Additionally, Enabling an interrupt source can break mask-based critical sections. - #[inline(always)] - pub unsafe fn enable_interrupt(&mut self, source: $INTERRUPT) { - self.context.enable_interrupt(source); - } - - /// Disables an interrupt source for the PLIC context. - /// - /// # Safety - /// - /// It performs non-atomic read-modify-write operations, which may lead to undefined behavior. - #[inline(always)] - pub unsafe fn disable_interrupt(&mut self, source: $INTERRUPT) { - self.context.disable_interrupt(source); - } - - /// Returns the priority threshold of the PLIC context. - #[inline(always)] - pub fn threshold() -> $PRIORITY { - $crate::peripheral::PLIC::<$BASE, $CONTEXT>::threshold() - } - - /// Getter method for the priority threshold of the PLIC context. - #[inline(always)] - pub fn get_threshold(&self) -> $PRIORITY { - Self::threshold() - } - - /// Sets the priority threshold for for the PLIC context. - /// - /// # Safety - /// - /// Unmasking an interrupt source can break mask-based critical sections. - #[inline(always)] - pub unsafe fn set_threshold(&mut self, priority: $PRIORITY) { - self.context.set_threshold(priority); - } - - /// Claims the number of a pending interrupt for for the PLIC context. - /// If no interrupt is pending for this context, it returns [`None`]. - #[inline(always)] - pub fn claim() -> Option<$INTERRUPT> { - $crate::peripheral::PLIC::<$BASE, $CONTEXT>::claim() - } - - /// Marks a pending interrupt as complete from for the PLIC context. - #[inline(always)] - pub fn complete(source: $INTERRUPT) { - $crate::peripheral::PLIC::<$BASE, $CONTEXT>::complete(source); - } - - /// Resets the PLIC peripherals. - /// - /// # Safety - /// - /// It performs non-atomic read-modify-write operations, which may lead to undefined behavior. - #[inline(always)] - pub unsafe fn reset(&mut self) { - self.context.reset::<$INTERRUPT, $PRIORITY>(); - } - } - }; -} diff --git a/src/peripheral.rs b/src/peripheral.rs deleted file mode 100644 index d64522e9..00000000 --- a/src/peripheral.rs +++ /dev/null @@ -1,44 +0,0 @@ -//! RISC-V peripherals -use core::marker::PhantomData; - -// Platform-Level Interrupt Controller -#[cfg(feature = "plic")] -pub mod plic; - -/// Interface for a context of the PLIC peripheral. -/// -/// # Note -/// -/// This structure requires the `plic` feature. -/// -/// The RISC-V standard does not specify a fixed location for the PLIC. -/// Thus, we use const generics to map a PLIC to the desired memory location. -/// Each platform must specify the base address of the PLIC on the platform. -/// -/// The PLIC standard allows up to 15_872 different contexts for interfacing the PLIC. -/// Usually, each HART uses a dedicated context. In this way, they do not interfere -/// with each other when attending to external interruptions. -/// -/// You can use the [`crate::plic_context`] macro to generate a specific structure -/// for interfacing every PLIC context of your platform. The resulting structure -/// replaces generic types with the specific types of your target. -#[allow(clippy::upper_case_acronyms)] -#[cfg(feature = "plic")] -#[derive(Default)] -pub struct PLIC { - _marker: PhantomData<*const ()>, -} - -#[cfg(feature = "plic")] -impl PLIC { - /// Pointer to the register block - pub const PTR: *const self::plic::RegisterBlock = BASE as *const _; - - /// Creates a new interface for the PLIC peripheral. PACs can use this - /// function to add a PLIC interface to their `Peripherals` struct. - pub const fn new() -> Self { - Self { - _marker: PhantomData, - } - } -} diff --git a/src/peripheral/plic.rs b/src/peripheral/plic.rs deleted file mode 100644 index f52ef8fe..00000000 --- a/src/peripheral/plic.rs +++ /dev/null @@ -1,280 +0,0 @@ -//! Platform-Level Interrupt Controller (PLIC) peripheral. -//! -//! Specification: - -pub use super::PLIC; -use crate::register::mie; -use core::ops::Deref; -use volatile_register::{RO, RW}; - -/// Maximum number of interrupt sources supported by the PLIC standard. -const MAX_SOURCES: usize = 1_024; -/// Maximum number of words needed to represent interrupts with flags. -const MAX_FLAGS_WORDS: usize = MAX_SOURCES / (u32::BITS as usize); -/// Maximum number of contexts supported by the PLIC standard. -const MAX_CONTEXTS: usize = 15_872; - -/// Register block. -#[repr(C)] -pub struct RegisterBlock { - /// `0x0000_0000..=0x0000_0FFC` - Interrupt Priority Register. - pub priority: [RW; MAX_SOURCES], - /// `0x0000_1000..=0x0000_107C` - Interrupt Pending Register. - pub pending: [RO; MAX_FLAGS_WORDS], - /// `0x0000_1080..=0x0000_1FFC` - Reserved. - _reserved1: [u32; 0x03e0], - /// `0x0000_2000..=0x001F_1FFC` - Enable Registers (one per context). - pub enables: [ContextEnable; MAX_CONTEXTS], - /// `0x001F_2000..=0x001F_FFFF` - Reserved. - _reserved2: [u32; 0x3800], - /// `0x0020_0000..=0x03FF_FFFC` - State Registers (one per context). - pub states: [ContextState; MAX_CONTEXTS], -} - -/// Interrupt enable for a given context. -pub type ContextEnable = [RW; MAX_FLAGS_WORDS]; - -/// State of a single context. -#[repr(C)] -pub struct ContextState { - /// `0x0000_0000` - Priority Threshold Register. - pub threshold: RW, - /// `0x0000_0004` - Claim/Complete Register. - pub claim_complete: RW, - /// `0x0000_0008..=0x0000_0FFC` - Reserved. - _reserved: [u32; 0x3fe], -} - -impl PLIC { - /// Sets the Machine External Interrupt bit of the [`crate::register::mie`] CSR. - /// This bit must be set for the PLIC to trigger machine external interrupts. - #[inline] - pub fn enable() { - // SAFETY: atomic CSRRS instruction with no side effects - unsafe { mie::set_mext() }; - } - - /// Clears the Machine External Interrupt bit of the [`crate::register::mie`] CSR. - /// When cleared, the PLIC does not trigger machine external interrupts. - #[inline] - pub fn disable() { - // SAFETY: atomic CSRRC instruction with no side effects - unsafe { mie::clear_mext() }; - } - - /// Returns the priority level associated to a given interrupt source. - #[inline] - pub fn priority(source: I) -> P { - let source = usize::from(source.number()); - // SAFETY: atomic read with no side effects - let priority = unsafe { (*Self::PTR).priority[source].read() } as _; - P::try_from(priority).unwrap() - } - - /// Sets the priority level of a given interrupt source. - /// - /// # Note - /// - /// Interrupt source priorities are shared among all the contexts of the PLIC. - /// Thus, changing the priority of sources may affect other PLIC contexts. - /// - /// # Safety - /// - /// Changing priority levels can break priority-based critical sections and compromise memory safety. - #[inline] - pub unsafe fn set_priority( - &mut self, - source: I, - priority: P, - ) { - let source = usize::from(source.number()); - let priority = priority.number().into(); - // SAFETY: atomic write with no side effects - (*Self::PTR).priority[source].write(priority); - } - - /// Checks if an interrupt triggered by a given source is pending. - #[inline] - pub fn is_interrupt_pending(source: I) -> bool { - let source = usize::from(source.number()); - let mask: u32 = 1 << (source % MAX_FLAGS_WORDS); - // SAFETY: atomic read with no side effects - let flags = unsafe { (*Self::PTR).pending[source / MAX_FLAGS_WORDS].read() }; - (flags & mask) == mask - } - - /// Checks if an interrupt source is enabled for the PLIC context. - #[inline] - pub fn is_interrupt_enabled(source: I) -> bool { - let source = usize::from(source.number()); - let mask: u32 = 1 << (source % MAX_FLAGS_WORDS); - // SAFETY: atomic read with no side effects - let flags = unsafe { (*Self::PTR).enables[CONTEXT][source / MAX_FLAGS_WORDS].read() }; - (flags & mask) == mask - } - - /// Enables an interrupt source for the PLIC context. - /// - /// # Safety - /// - /// It performs non-atomic read-modify-write operations, which may lead to undefined behavior. - /// Additionally, Enabling an interrupt source can break mask-based critical sections. - #[inline] - pub unsafe fn enable_interrupt(&mut self, source: I) { - let source = usize::from(source.number()); - let mask: u32 = 1 << (source % MAX_FLAGS_WORDS); - self.enables[CONTEXT][source / MAX_FLAGS_WORDS].modify(|value| value | mask); - } - - /// Disables an interrupt source for the PLIC context. - /// - /// # Safety - /// - /// It performs non-atomic read-modify-write operations, which may lead to undefined behavior. - #[inline] - pub unsafe fn disable_interrupt(&mut self, source: I) { - let source = usize::from(source.number()); - let mask: u32 = 1 << (source % MAX_FLAGS_WORDS); - self.enables[CONTEXT][source / MAX_FLAGS_WORDS].modify(|value| value & !mask); - } - - /// Returns the priority threshold of the PLIC context. - #[inline] - pub fn threshold() -> P { - // SAFETY: atomic read with no side effects - let priority = unsafe { (*Self::PTR).states[CONTEXT].threshold.read() } as _; - P::try_from(priority).unwrap() - } - - /// Sets the priority threshold for for the PLIC context. - /// - /// # Safety - /// - /// Unmasking an interrupt source can break mask-based critical sections. - #[inline] - pub unsafe fn set_threshold(&mut self, priority: P) { - let priority = priority.number().into(); - // SAFETY: atomic write with no side effects - (*Self::PTR).states[CONTEXT].threshold.write(priority); - } - - /// Claims the number of a pending interrupt for for the PLIC context. - /// If no interrupt is pending for this context, it returns [`None`]. - #[inline] - pub fn claim() -> Option { - // SAFETY: atomic read with no side effects - let interrupt = unsafe { (*Self::PTR).states[CONTEXT].claim_complete.read() } as _; - match interrupt { - 0 => None, - i => Some(I::try_from(i).unwrap()), - } - } - - /// Marks a pending interrupt as complete from for the PLIC context. - /// - /// # Note - /// - /// If the source ID does not match an interrupt source that is - /// currently enabled for the target, the completion is silently ignored. - #[inline] - pub fn complete(source: I) { - let source = source.number().into(); - // SAFETY: atomic write with no side effects - unsafe { - (*Self::PTR).states[CONTEXT].claim_complete.write(source); - } - } - - /// Resets the PLIC peripherals. Namely, it performs the following operations: - /// - /// - Sets PLIC context threshold to the maximum interrupt level (i.e., never interrupt). - /// - Disables all the interrupt sources. - /// - Sets interrupt source priority to 0 (i.e., no interrupt). - /// - /// # Safety - /// - /// It performs non-atomic read-modify-write operations, which may lead to undefined behavior. - #[inline(always)] - pub unsafe fn reset(&mut self) { - self.set_threshold(P::try_from(P::MAX_PRIORITY_NUMBER).unwrap()); - let no_interrupt = P::try_from(0).unwrap(); - for source in (1..=I::MAX_INTERRUPT_NUMBER).filter_map(|n| I::try_from(n).ok()) { - self.disable_interrupt(source); - self.set_priority(source, no_interrupt); - } - } -} - -impl Deref for PLIC { - type Target = RegisterBlock; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - unsafe { &*Self::PTR } - } -} - -unsafe impl Send for PLIC {} - -/// Trait for enums of interrupt numbers. -/// -/// This trait should be implemented by a peripheral access crate (PAC) -/// on its enum of available external interrupts for a specific device. -/// Each variant must convert to a `u16` of its interrupt number. -/// -/// # Note -/// -/// Recall that the interrupt number `0` is reserved as "no interrupt". -/// -/// # Safety -/// -/// This trait must only be implemented on enums of external interrupts. Each -/// enum variant must represent a distinct value (no duplicates are permitted), -/// and must always return the same value (do not change at runtime). -/// All the interrupt numbers must be less than or equal to `MAX_INTERRUPT_NUMBER`. -/// `MAX_INTERRUPT_NUMBER` must coincide with the highest allowed interrupt number. -/// -/// These requirements ensure safe nesting of critical sections. -pub unsafe trait InterruptNumber: Copy { - /// Highest number assigned to an interrupt source. - const MAX_INTERRUPT_NUMBER: u16; - - /// Converts an interrupt source to its corresponding number. - fn number(self) -> u16; - - /// Tries to convert a number to a valid interrupt source. - /// If the conversion fails, it returns an error with the number back. - fn try_from(value: u16) -> Result; -} - -/// Trait for enums of interrupt priority numbers. -/// -/// This trait should be implemented by a peripheral access crate (PAC) -/// on its enum of available priority numbers for a specific device. -/// Each variant must convert to a `u8` of its priority level. -/// -/// # Note -/// -/// Recall that the priority number `0` is reserved as "never interrupt". -/// -/// # Safety -/// -/// This trait must only be implemented on enums of priority levels. Each -/// enum variant must represent a distinct value (no duplicates are permitted), -/// and must always return the same value (do not change at runtime). -/// There must be a valid priority number set to 0 (i.e., never interrupt). -/// All the priority level numbers must be less than or equal to `MAX_PRIORITY_NUMBER`. -/// `MAX_PRIORITY_NUMBER` must coincide with the highest allowed priority number. -/// -/// These requirements ensure safe nesting of critical sections. -pub unsafe trait PriorityNumber: Copy { - /// Number assigned to the highest priority level. - const MAX_PRIORITY_NUMBER: u8; - - /// Converts a priority level to its corresponding number. - fn number(self) -> u8; - - /// Tries to convert a number to a valid priority level. - /// If the conversion fails, it returns an error with the number back. - fn try_from(value: u8) -> Result; -}