diff --git a/.github/workflows/riscv-peripheral.yaml b/.github/workflows/riscv-peripheral.yaml index 8797a8a4..571e383f 100644 --- a/.github/workflows/riscv-peripheral.yaml +++ b/.github/workflows/riscv-peripheral.yaml @@ -31,10 +31,8 @@ jobs: with: toolchain: ${{ matrix.toolchain }} targets: ${{ matrix.target }} - - name: Build (no features) + - name: Build run: cargo build --package riscv-peripheral --target ${{ matrix.target }} - - name: Build (all features) - run: cargo build --package riscv-peripheral --target ${{ matrix.target }} --all-features # On MacOS, Ubuntu, and Windows, we run the tests. build-others: @@ -48,10 +46,8 @@ jobs: steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable - - name: Build (no features) + - name: Test run: cargo test --package riscv-peripheral - - name: Build (all features) - run: cargo test --package riscv-peripheral --all-features # Job to check that all the builds succeeded build-check: diff --git a/riscv-peripheral/CHANGELOG.md b/riscv-peripheral/CHANGELOG.md index f005e02d..2d68d7fb 100644 --- a/riscv-peripheral/CHANGELOG.md +++ b/riscv-peripheral/CHANGELOG.md @@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Changed + +- Rework of CLINT peripheral to use methods instead of associated functions. + This change follows the `svd2rust` pattern, making the ecosystem more consistent. +- Simplify `clint_codegen!` macro using the `Deref` trait. +- Rework of PLIC peripherals to use methods instead of associated functions. + This change follows the `svd2rust` pattern, making the ecosystem more consistent. +- Simplify `plic_codegen!` macro using the `Deref` trait. + +### Removed + +- Removed support for `embedded-hal-async`, as it was not flexible enough to be + used in different targets (single HART, multi HART...). Instead, each chip must + have its own `chip-hal-async` crate that properly adapts to its specific needs. + ### Fixed - `clippy` fixes diff --git a/riscv-peripheral/Cargo.toml b/riscv-peripheral/Cargo.toml index 96e804bc..2de680a7 100644 --- a/riscv-peripheral/Cargo.toml +++ b/riscv-peripheral/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "riscv-peripheral" -version = "0.2.1" +version = "0.3.0-rc.1" edition = "2021" rust-version = "1.75" repository = "https://github.com/rust-embedded/riscv" @@ -15,16 +15,10 @@ license = "ISC" [dependencies] embedded-hal = "1.0.0" -embedded-hal-async = { version = "1.0.0", optional = true } +paste = "1.0" riscv = { path = "../riscv", version = "0.13.0" } riscv-pac = { path = "../riscv-pac", version = "0.2.0" } -[dev-dependencies] -heapless = "0.8.0" - -[features] -aclint-hal-async = ["embedded-hal-async"] - [package.metadata.docs.rs] all-features = true default-target = "riscv64imac-unknown-none-elf" diff --git a/riscv-peripheral/examples/e310x.rs b/riscv-peripheral/examples/e310x.rs index 60e28c61..08b3c77a 100644 --- a/riscv-peripheral/examples/e310x.rs +++ b/riscv-peripheral/examples/e310x.rs @@ -1,36 +1,16 @@ //! Peripheral definitions for the E310x chip. +//! //! This is a simple example of how to use the `riscv-peripheral` crate to generate //! peripheral definitions for a target. -use riscv_pac::{ - result::{Error, Result}, - ExternalInterruptNumber, HartIdNumber, InterruptNumber, PriorityNumber, -}; - #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[riscv::pac_enum(unsafe HartIdNumber)] pub enum HartId { H0 = 0, } -unsafe impl HartIdNumber for HartId { - const MAX_HART_ID_NUMBER: usize = Self::H0 as usize; - - #[inline] - fn number(self) -> usize { - self as _ - } - - #[inline] - fn from_number(number: usize) -> Result { - match number { - 0 => Ok(Self::H0), - _ => Err(Error::InvalidVariant(number)), - } - } -} - #[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(usize)] +#[riscv::pac_enum(unsafe ExternalInterruptNumber)] pub enum Interrupt { WATCHDOG = 1, RTC = 2, @@ -86,29 +66,8 @@ pub enum Interrupt { I2C0 = 52, } -unsafe impl InterruptNumber for Interrupt { - const MAX_INTERRUPT_NUMBER: usize = Self::I2C0 as usize; - - #[inline] - fn number(self) -> usize { - self as _ - } - - #[inline] - fn from_number(number: usize) -> Result { - if number == 0 || number > Self::MAX_INTERRUPT_NUMBER { - Err(Error::InvalidVariant(number)) - } else { - // SAFETY: valid interrupt number - Ok(unsafe { core::mem::transmute::(number) }) - } - } -} - -unsafe impl ExternalInterruptNumber for Interrupt {} - #[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(usize)] +#[riscv::pac_enum(unsafe PriorityNumber)] pub enum Priority { P0 = 0, P1 = 1, @@ -120,87 +79,15 @@ pub enum Priority { P7 = 7, } -unsafe impl PriorityNumber for Priority { - const MAX_PRIORITY_NUMBER: usize = Self::P7 as usize; - - #[inline] - fn number(self) -> usize { - self as _ - } - - #[inline] - fn from_number(number: usize) -> Result { - if number > Self::MAX_PRIORITY_NUMBER { - Err(Error::InvalidVariant(number)) - } else { - // SAFETY: valid priority number - Ok(unsafe { core::mem::transmute::(number) }) - } - } -} - -#[cfg(feature = "aclint-hal-async")] -riscv_peripheral::clint_codegen!( - base 0x0200_0000, - freq 32_768, - async_delay, - mtimecmps [mtimecmp0=(HartId::H0,"`H0`")], - msips [msip0=(HartId::H0,"`H0`")], -); - -#[cfg(not(feature = "aclint-hal-async"))] riscv_peripheral::clint_codegen!( base 0x0200_0000, - freq 32_768, - mtimecmps [mtimecmp0=(HartId::H0,"`H0`")], - msips [msip0=(HartId::H0,"`H0`")], + mtime_freq 32_768, + harts [HartId::H0 => 0], ); riscv_peripheral::plic_codegen!( base 0x0C00_0000, - ctxs [ctx0=(HartId::H0,"`H0`")], + harts [HartId::H0 => 0], ); -#[cfg(feature = "aclint-hal-async")] -/// extern functions needed by the `riscv-peripheral` crate for the `async` feature. -/// -/// # Note -/// -/// The functionality in this module is just to illustrate how to enable the `async` feature -/// The timer queue used here, while functional, is unsound and should not be used in production. -/// In this case, you should protect the timer queue with a mutex or critical section. -/// For a more robust implementation, use proper timer queues such as the ones provided by `embassy-time` -mod async_no_mangle { - use super::CLINT; - use heapless::binary_heap::{BinaryHeap, Min}; - use riscv_peripheral::{aclint::mtimer::MTIMER, hal_async::aclint::Timer}; - - const N_TIMERS: usize = 16; - static mut TIMER_QUEUE: BinaryHeap = BinaryHeap::new(); - - #[no_mangle] - fn _riscv_peripheral_aclint_mtimer() -> MTIMER { - CLINT::mtimer() - } - - #[no_mangle] - fn _riscv_peripheral_aclint_push_timer(t: Timer) -> Result<(), Timer> { - unsafe { TIMER_QUEUE.push(t) } - } - - #[no_mangle] - fn _riscv_peripheral_aclint_wake_timers(current_tick: u64) -> Option { - let mut next_expires = None; - while let Some(t) = unsafe { TIMER_QUEUE.peek() } { - if t.expires() > current_tick { - next_expires = Some(t.expires()); - break; - } - let t = unsafe { TIMER_QUEUE.pop() }.unwrap(); - t.waker().wake_by_ref(); - } - next_expires - } -} - fn main() {} diff --git a/riscv-peripheral/src/aclint.rs b/riscv-peripheral/src/aclint.rs index 096aa4f3..8601f8b2 100644 --- a/riscv-peripheral/src/aclint.rs +++ b/riscv-peripheral/src/aclint.rs @@ -1,7 +1,7 @@ //! Devices for the Core Local Interruptor (CLINT) and Advanced CLINT (ACLINT) peripherals. //! //! CLINT pecification: -//! ACLINT Specification: +//! ACLINT Specification: pub mod mswi; pub mod mtimer; @@ -15,95 +15,84 @@ pub use riscv_pac::HartIdNumber; // re-export useful riscv-pac traits /// /// * This trait must only be implemented on a PAC of a target with a CLINT peripheral. /// * The CLINT peripheral base address `BASE` must be valid for the target device. +/// * The CLINT peripheral clock frequency `MTIME_FREQ` must be valid for the target device. pub unsafe trait Clint: Copy { /// Base address of the CLINT peripheral. const BASE: usize; + /// Clock frequency of the CLINT's [`MTIME`](mtimer::MTIME) register. + const MTIME_FREQ: usize; } /// Interface for a CLINT peripheral. /// /// The RISC-V standard does not specify a fixed location for the CLINT. /// Thus, each platform must specify the base address of the CLINT on the platform. -/// The base address, as well as all the associated types, are defined in the [`Clint`] trait. +/// The base address and clock frequency are defined in the [`Clint`] trait. /// /// The CLINT standard allows up to 4_095 different HARTs connected to the CLINT. /// Each HART has an assigned index starting from 0 to up to 4_094. /// In this way, each HART's timer and software interrupts can be independently configured. #[allow(clippy::upper_case_acronyms)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct CLINT { +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct CLINT { _marker: core::marker::PhantomData, } impl CLINT { - const MTIMECMP_OFFSET: usize = 0x4000; - - const MTIME_OFFSET: usize = 0xBFF8; - - /// Returns the `MSWI` peripheral. #[inline] - pub const fn mswi() -> mswi::MSWI { - // SAFETY: valid base address - unsafe { mswi::MSWI::new(C::BASE) } + /// Creates a new `CLINT` peripheral. + pub const fn new() -> Self { + Self { + _marker: core::marker::PhantomData, + } } - /// Returns the `MTIMER` peripheral. + /// Returns the [`MSWI`](mswi::MSWI) device. #[inline] - pub const fn mtimer() -> mtimer::MTIMER { - // SAFETY: valid base address - unsafe { - mtimer::MTIMER::new( - C::BASE + Self::MTIMECMP_OFFSET, - C::BASE + Self::MTIME_OFFSET, - ) - } + pub const fn mswi(self) -> mswi::MSWI { + mswi::MSWI::new() } -} -#[cfg(test)] -pub(crate) mod test { - use super::HartIdNumber; - use riscv_pac::result::{Error, Result}; - - #[derive(Clone, Copy, Debug, Eq, PartialEq)] - #[repr(usize)] - pub(crate) enum HartId { - H0 = 0, - H1 = 1, - H2 = 2, + /// Returns the [`MTIMER`](mtimer::MTIMER) device. + #[inline] + pub const fn mtimer(self) -> mtimer::MTIMER { + mtimer::MTIMER::new() } - unsafe impl HartIdNumber for HartId { - const MAX_HART_ID_NUMBER: usize = Self::H2 as usize; - - #[inline] - fn number(self) -> usize { - self as _ - } - - #[inline] - fn from_number(number: usize) -> Result { - if number > Self::MAX_HART_ID_NUMBER { - Err(Error::InvalidVariant(number)) - } else { - // SAFETY: valid context number - Ok(unsafe { core::mem::transmute::(number) }) - } - } + /// Returns `true` if a machine timer **OR** software interrupt is pending. + #[inline] + pub fn is_interrupting(self) -> bool { + self.mswi().is_interrupting() || self.mtimer().is_interrupting() } - #[test] - fn check_hart_id_enum() { - assert_eq!(HartId::H0.number(), 0); - assert_eq!(HartId::H1.number(), 1); - assert_eq!(HartId::H2.number(), 2); + /// Returns `true` if machine timer **OR** software interrupts are enabled. + #[inline] + pub fn is_enabled(self) -> bool { + self.mswi().is_enabled() || self.mtimer().is_enabled() + } - assert_eq!(HartId::from_number(0), Ok(HartId::H0)); - assert_eq!(HartId::from_number(1), Ok(HartId::H1)); - assert_eq!(HartId::from_number(2), Ok(HartId::H2)); + /// Enables machine timer **AND** software interrupts to allow the CLINT to trigger interrupts. + /// + /// # Safety + /// + /// Enabling the `CLINT` may break mask-based critical sections. + #[inline] + pub unsafe fn enable(self) { + self.mswi().enable(); + self.mtimer().enable(); + } - assert_eq!(HartId::from_number(3), Err(Error::InvalidVariant(3))); + /// Disables machine timer **AND** software interrupts to prevent the CLINT from triggering interrupts. + #[inline] + pub fn disable(self) { + self.mswi().disable(); + self.mtimer().disable(); } +} + +#[cfg(test)] +pub(crate) mod test { + use crate::test::HartId; #[allow(dead_code)] #[test] @@ -111,16 +100,22 @@ pub(crate) mod test { // Call CLINT macro with a base address and a list of mtimecmps for easing access to per-HART mtimecmp regs. crate::clint_codegen!( base 0x0200_0000, - mtimecmps [mtimecmp0=(HartId::H0,"`H0`"), mtimecmp1=(HartId::H1,"`H1`"), mtimecmp2=(HartId::H2,"`H2`")], - msips [msip0=(HartId::H0,"`H0`"), msip1=(HartId::H1,"`H1`"), msip2=(HartId::H2,"`H2`")], + mtime_freq 32_768, + harts [HartId::H0 => 0, HartId::H1 => 1, HartId::H2 => 2], ); - let mswi = CLINT::mswi(); - let mtimer = CLINT::mtimer(); + let clint = CLINT::new(); - assert_eq!(mswi.msip0.get_ptr() as usize, 0x0200_0000); - assert_eq!(mtimer.mtimecmp0.get_ptr() as usize, 0x0200_4000); - assert_eq!(mtimer.mtime.get_ptr() as usize, 0x0200_bff8); + let mswi = clint.mswi(); + let mtimer = clint.mtimer(); + + let msip0 = mswi.msip(HartId::H0); + let msip1 = mswi.msip(HartId::H1); + let msip2 = mswi.msip(HartId::H2); + + assert_eq!(msip0.get_ptr() as usize, 0x0200_0000); + assert_eq!(msip1.get_ptr() as usize, 0x0200_0000 + 4); // 4 bytes per register + assert_eq!(msip2.get_ptr() as usize, 0x0200_0000 + 2 * 4); let mtimecmp0 = mtimer.mtimecmp(HartId::H0); let mtimecmp1 = mtimer.mtimecmp(HartId::H1); @@ -130,13 +125,15 @@ pub(crate) mod test { assert_eq!(mtimecmp1.get_ptr() as usize, 0x0200_4000 + 8); // 8 bytes per register assert_eq!(mtimecmp2.get_ptr() as usize, 0x0200_4000 + 2 * 8); - assert_eq!(CLINT::mtime(), mtimer.mtime); - assert_eq!(CLINT::mtimecmp0(), mtimer.mtimecmp(HartId::H0)); - assert_eq!(CLINT::mtimecmp1(), mtimer.mtimecmp(HartId::H1)); - assert_eq!(CLINT::mtimecmp2(), mtimer.mtimecmp(HartId::H2)); + let mtime = mtimer.mtime(); + assert_eq!(mtime.get_ptr() as usize, 0x0200_bff8); + + assert_eq!(clint.mtimecmp0(), mtimer.mtimecmp(HartId::H0)); + assert_eq!(clint.mtimecmp1(), mtimer.mtimecmp(HartId::H1)); + assert_eq!(clint.mtimecmp2(), mtimer.mtimecmp(HartId::H2)); - assert_eq!(CLINT::msip0(), mswi.msip(HartId::H0)); - assert_eq!(CLINT::msip1(), mswi.msip(HartId::H1)); - assert_eq!(CLINT::msip2(), mswi.msip(HartId::H2)); + assert_eq!(clint.msip0(), mswi.msip(HartId::H0)); + assert_eq!(clint.msip1(), mswi.msip(HartId::H1)); + assert_eq!(clint.msip2(), mswi.msip(HartId::H2)); } } diff --git a/riscv-peripheral/src/aclint/mswi.rs b/riscv-peripheral/src/aclint/mswi.rs index 7906fa71..afedea3c 100644 --- a/riscv-peripheral/src/aclint/mswi.rs +++ b/riscv-peripheral/src/aclint/mswi.rs @@ -1,52 +1,97 @@ //! Machine-level Software Interrupt Device. -pub use super::HartIdNumber; +pub use super::{Clint, HartIdNumber}; use crate::common::unsafe_peripheral; +use riscv::register::{mhartid, mie, mip}; + +/// Trait for a Machine-level Software Interrupt device. +/// +/// # Note +/// +/// For CLINT peripherals, this trait is automatically implemented. +/// +/// # Safety +/// +/// * This trait must only be implemented on a PAC of a target with an MSWI device. +/// * The MSWI device base address `BASE` must be valid for the target. +pub unsafe trait Mswi: Copy { + /// Base address of the MSWI peripheral. + const BASE: usize; +} + +// SAFETY: the offset of the MSWI peripheral is fixed in the CLINT peripheral +unsafe impl Mswi for C { + const BASE: usize = C::BASE; +} /// MSWI peripheral. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[repr(transparent)] -pub struct MSWI { - /// `MSIP` register for HART ID 0. In multi-HART architectures, - /// use [`MSWI::msip`] for accessing the `MSIP` of other HARTs. - pub msip0: MSIP, +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct MSWI { + _marker: core::marker::PhantomData, } -impl MSWI { - /// Creates a new `MSWI` peripheral from a base address. - /// - /// # Safety - /// - /// The base address must point to a valid `MSWI` peripheral. +impl MSWI { + /// Creates a new `MSWI` device. #[inline] - pub const unsafe fn new(address: usize) -> Self { + pub const fn new() -> Self { Self { - msip0: MSIP::new(address), + _marker: core::marker::PhantomData, } } - /// Returns the `MSIP` register for the HART which ID is `hart_id`. + /// Returns the base address of the `MSWI` device. + #[inline] + const fn as_ptr(self) -> *const u32 { + M::BASE as *const u32 + } + + /// Returns `true` if a machine software interrupt is pending. + #[inline] + pub fn is_interrupting(self) -> bool { + mip::read().msoft() + } + + /// Returns `true` if machine software interrupts are enabled. + #[inline] + pub fn is_enabled(self) -> bool { + mie::read().msoft() + } + + /// Enables machine software interrupts in the current HART. /// - /// # Note + /// # Safety /// - /// For HART ID 0, you can simply use [`MSWI::msip0`]. + /// Enabling interrupts may break mask-based critical sections. + #[inline] + pub unsafe fn enable(self) { + mie::set_msoft(); + } + + /// Disables machine software interrupts in the current HART. #[inline] - pub fn msip(&self, hart_id: H) -> MSIP { + pub fn disable(self) { + // SAFETY: it is safe to disable interrupts + unsafe { mie::clear_msoft() }; + } + + /// Returns the `MSIP` register for the HART which ID is `hart_id`. + #[inline] + pub fn msip(self, hart_id: H) -> MSIP { // SAFETY: `hart_id` is valid for the target - unsafe { MSIP::new(self.msip0.get_ptr().add(hart_id.number()) as _) } + unsafe { MSIP::new(self.as_ptr().add(hart_id.number()) as _) } } /// Returns the `MSIP` register for the current HART. /// /// # Note /// - /// This function determines the current HART ID by reading the [`riscv::register::mhartid`] CSR. + /// This function determines the current HART ID by reading the `mhartid` CSR. /// Thus, it can only be used in M-mode. For S-mode, use [`MSWI::msip`] instead. #[inline] - pub fn msip_mhartid(&self) -> MSIP { - let hart_id = riscv::register::mhartid::read(); + pub fn msip_mhartid(self) -> MSIP { + let hart_id = mhartid::read(); // SAFETY: `hart_id` is valid for the target and is the current hart - unsafe { MSIP::new(self.msip0.get_ptr().add(hart_id) as _) } + unsafe { MSIP::new(self.as_ptr().add(hart_id) as _) } } } @@ -74,29 +119,22 @@ impl MSIP { #[cfg(test)] mod test { - use super::super::test::HartId; use super::*; #[test] fn test_mswi() { - // slice to emulate the interrupt pendings register - let raw_reg = [0u32; HartId::MAX_HART_ID_NUMBER + 1]; + // Variable to emulate the interrupt pending register + let mut raw_reg = 0u32; // SAFETY: valid memory address - let mswi = unsafe { MSWI::new(raw_reg.as_ptr() as _) }; - - for (i, hart_id) in (0..raw_reg.len()) - .map(|i| HartId::from_number(i).unwrap()) - .enumerate() - { - let msip = mswi.msip(hart_id); - assert!(!msip.is_pending()); - assert_eq!(raw_reg[i], 0); - msip.pend(); - assert!(msip.is_pending()); - assert_ne!(raw_reg[i], 0); - msip.unpend(); - assert!(!msip.is_pending()); - assert_eq!(raw_reg[i], 0); - } + let msip = unsafe { MSIP::new(&mut raw_reg as *mut u32 as usize) }; + + assert!(!msip.is_pending()); + assert_eq!(raw_reg, 0); + msip.pend(); + assert!(msip.is_pending()); + assert_ne!(raw_reg, 0); + msip.unpend(); + assert!(!msip.is_pending()); + assert_eq!(raw_reg, 0); } } diff --git a/riscv-peripheral/src/aclint/mtimer.rs b/riscv-peripheral/src/aclint/mtimer.rs index 9220a947..76fa6554 100644 --- a/riscv-peripheral/src/aclint/mtimer.rs +++ b/riscv-peripheral/src/aclint/mtimer.rs @@ -1,54 +1,115 @@ //! Machine-level Timer Device. -pub use super::HartIdNumber; +pub use super::{Clint, HartIdNumber}; use crate::common::safe_peripheral; +use riscv::register::{mhartid, mie, mip}; -/// MTIMER peripheral. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct MTIMER { - /// `MTIMECMP` register for HART ID 0. In multi-HART architectures, - /// use [`MTIMER::mtimecmp`] for accessing the `MTIMECMP` of other HARTs. - pub mtimecmp0: MTIMECMP, - /// The `MTIME` register is shared among all the HARTs. - pub mtime: MTIME, +/// Trait for an MTIMER device. +/// +/// # Note +/// +/// For CLINT peripherals, this trait is automatically implemented. +/// +/// # Safety +/// +/// * This trait must only be implemented on a PAC of a target with an MTIMER device. +/// * The `MTIMECMP` registers base address `MTIMECMP_BASE` must be valid for the target. +/// * The `MTIME` registers base address `MTIME_BASE` must be valid for the target. +/// * The `MTIME` clock frequency `MTIME_FREQ` must be valid for the target. +pub unsafe trait Mtimer: Copy { + /// Base address of the MTIMECMP registers. + const MTIMECMP_BASE: usize; + /// Base address of the MTIME register. + const MTIME_BASE: usize; + /// Clock frequency of the MTIME register. + const MTIME_FREQ: usize; } -impl MTIMER { - /// Creates a new `MTIMER` peripheral from a base address. - /// - /// # Safety - /// - /// The base addresses must point to valid `MTIMECMP` and `MTIME` peripherals. +// SAFETY: the offset of the MSWI peripheral is fixed in the CLINT peripheral +unsafe impl Mtimer for C { + const MTIMECMP_BASE: usize = C::BASE + 0x4000; + const MTIME_BASE: usize = C::BASE + 0xBFF8; + const MTIME_FREQ: usize = C::MTIME_FREQ; +} + +/// MTIMER device. +/// +/// It has a single fixed-frequency monotonic time counter ([`MTIME`]) +/// register and a time compare register ([`MTIMECMP`]) for each HART. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct MTIMER { + _marker: core::marker::PhantomData, +} + +impl MTIMER { + /// Creates a new `MTIMER` device. #[inline] - pub const unsafe fn new(mtimecmp: usize, mtime: usize) -> Self { + pub const fn new() -> Self { Self { - mtimecmp0: MTIMECMP::new(mtimecmp), - mtime: MTIME::new(mtime), + _marker: core::marker::PhantomData, } } - /// Returns the `MTIMECMP` register for the HART which ID is `hart_id`. + /// Returns the base address of the `MTIMECMP` registers. + #[inline] + const fn mtimecmp_as_ptr(self) -> *const u64 { + M::MTIMECMP_BASE as *const u64 + } + + /// Returns `true` if a machine timer interrupt is pending. + #[inline] + pub fn is_interrupting(self) -> bool { + mip::read().mtimer() + } + + /// Returns `true` if machine timer interrupts are enabled. + #[inline] + pub fn is_enabled(self) -> bool { + mie::read().mtimer() + } + + /// Enables machine timer interrupts in the current HART. /// - /// # Note + /// # Safety /// - /// For HART ID 0, you can simply use [`MTIMER::mtimecmp0`]. + /// Enabling interrupts may break mask-based critical sections. + #[inline] + pub unsafe fn enable(self) { + mie::set_mtimer(); + } + + /// Disables machine timer interrupts in the current HART. + #[inline] + pub fn disable(self) { + // SAFETY: it is safe to disable interrupts + unsafe { mie::clear_mtimer() }; + } + + /// Returns the `MTIME` register. + #[inline] + pub const fn mtime(self) -> MTIME { + // SAFETY: valid base address + unsafe { MTIME::new(M::MTIME_BASE) } + } + + /// Returns the `MTIMECMP` register for the HART which ID is `hart_id`. #[inline] - pub fn mtimecmp(&self, hart_id: H) -> MTIMECMP { + pub fn mtimecmp(self, hart_id: H) -> MTIMECMP { // SAFETY: `hart_id` is valid for the target - unsafe { MTIMECMP::new(self.mtimecmp0.get_ptr().add(hart_id.number()) as _) } + unsafe { MTIMECMP::new(self.mtimecmp_as_ptr().add(hart_id.number()) as _) } } /// Returns the `MTIMECMP` register for the current HART. /// /// # Note /// - /// This function determines the current HART ID by reading the [`riscv::register::mhartid`] CSR. + /// This function determines the current HART ID by reading the [`mhartid`] CSR. /// Thus, it can only be used in M-mode. For S-mode, use [`MTIMER::mtimecmp`] instead. #[inline] - pub fn mtimecmp_mhartid(&self) -> MTIMECMP { - let hart_id = riscv::register::mhartid::read(); + pub fn mtimecmp_mhartid(self) -> MTIMECMP { + let hart_id = mhartid::read(); // SAFETY: `hart_id` is valid for the target and is the current hart - unsafe { MTIMECMP::new(self.mtimecmp0.get_ptr().add(hart_id) as _) } + unsafe { MTIMECMP::new(self.mtimecmp_as_ptr().add(hart_id) as _) } } } @@ -57,36 +118,3 @@ safe_peripheral!(MTIMECMP, u64, RW); // MTIME register. safe_peripheral!(MTIME, u64, RW); - -#[cfg(test)] -mod test { - use super::super::test::HartId; - use super::*; - - #[test] - fn check_mtimer() { - // slice to emulate the mtimecmp registers - let raw_mtimecmp = [0u64; HartId::MAX_HART_ID_NUMBER + 1]; - let raw_mtime = 0u64; - // SAFETY: valid memory addresses - let mtimer = - unsafe { MTIMER::new(raw_mtimecmp.as_ptr() as _, &raw_mtime as *const u64 as _) }; - - assert_eq!( - mtimer.mtimecmp(HartId::H0).get_ptr() as usize, - raw_mtimecmp.as_ptr() as usize - ); - assert_eq!(mtimer.mtimecmp(HartId::H1).get_ptr() as usize, unsafe { - raw_mtimecmp.as_ptr().offset(1) - } - as usize); - assert_eq!(mtimer.mtimecmp(HartId::H2).get_ptr() as usize, unsafe { - raw_mtimecmp.as_ptr().offset(2) - } - as usize); - assert_eq!( - mtimer.mtime.get_ptr() as usize, - &raw_mtime as *const u64 as _ - ); - } -} diff --git a/riscv-peripheral/src/aclint/sswi.rs b/riscv-peripheral/src/aclint/sswi.rs index b9724881..b6eed9d1 100644 --- a/riscv-peripheral/src/aclint/sswi.rs +++ b/riscv-peripheral/src/aclint/sswi.rs @@ -2,69 +2,74 @@ pub use super::HartIdNumber; use crate::common::unsafe_peripheral; +use riscv::register::{sie, sip}; + +/// Trait for a Supervisor-level Software Interrupt device. +/// +/// # Safety +/// +/// * This trait must only be implemented on a PAC of a target with an SSWI device. +/// * The SSWI device base address `BASE` must be valid for the target. +pub unsafe trait Sswi: Copy { + /// Base address of the SSWI peripheral. + const BASE: usize; +} /// SSWI peripheral. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[repr(transparent)] -pub struct SSWI { - /// `SETSSIP` register for HART ID 0. In multi-HART architectures, - /// use [`SSWI::setssip`] for accessing the `SETSSIP` of other HARTs. - pub setssip0: SETSSIP, +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct SSWI { + _marker: core::marker::PhantomData, } -impl SSWI { - /// Creates a new `SSWI` peripheral from a base address. - /// - /// # Safety - /// - /// The base address must point to a valid `SSWI` peripheral. +impl SSWI { + /// Creates a new `SSWI` device. #[inline] - pub const unsafe fn new(address: usize) -> Self { + pub const fn new() -> Self { Self { - setssip0: SETSSIP::new(address), + _marker: core::marker::PhantomData, } } + /// Returns the base address of the `SSWI` device. + #[inline] + const fn as_ptr(self) -> *const u32 { + S::BASE as *const u32 + } + /// Returns `true` if a supervisor software interrupt is pending. #[inline] - pub fn is_interrupting() -> bool { - riscv::register::sip::read().ssoft() + pub fn is_interrupting(self) -> bool { + sip::read().ssoft() } - /// Returns `true` if Supervisor Software Interrupts are enabled. + /// Returns `true` if supervisor software interrupts are enabled. #[inline] - pub fn is_enabled() -> bool { - riscv::register::mie::read().ssoft() + pub fn is_enabled(self) -> bool { + sie::read().ssoft() } - /// Sets the Supervisor Software Interrupt bit of the `mie` CSR. - /// This bit must be set for the `SSWI` to trigger supervisor software interrupts. + /// Enables supervisor software interrupts in the current HART. /// /// # Safety /// - /// Enabling the `SSWI` may break mask-based critical sections. + /// Enabling interrupts may break mask-based critical sections. #[inline] - pub unsafe fn enable() { - riscv::register::mie::set_ssoft(); + pub unsafe fn enable(self) { + sie::set_ssoft(); } - /// Clears the Supervisor Software Interrupt bit of the `mie` CSR. - /// When cleared, the `SSWI` cannot trigger supervisor software interrupts. + /// Disables supervisor software interrupts in the current HART. #[inline] - pub fn disable() { + pub fn disable(self) { // SAFETY: it is safe to disable interrupts - unsafe { riscv::register::mie::clear_ssoft() }; + unsafe { sie::clear_ssoft() }; } /// Returns the `SETSSIP` register for the HART which ID is `hart_id`. - /// - /// # Note - /// - /// For HART ID 0, you can simply use [`SSWI::setssip0`]. #[inline] - pub fn setssip(&self, hart_id: H) -> SETSSIP { + pub fn setssip(self, hart_id: H) -> SETSSIP { // SAFETY: `hart_id` is valid for the target - unsafe { SETSSIP::new(self.setssip0.get_ptr().add(hart_id.number()) as _) } + unsafe { SETSSIP::new(self.as_ptr().add(hart_id.number()) as _) } } } @@ -92,29 +97,22 @@ impl SETSSIP { #[cfg(test)] mod test { - use super::super::test::HartId; use super::*; #[test] fn test_sswi() { - // slice to emulate the interrupt pendings register - let raw_reg = [0u32; HartId::MAX_HART_ID_NUMBER + 1]; + // Variable to emulate the interrupt pending register + let mut raw_reg = 0u32; // SAFETY: valid memory address - let mswi = unsafe { SSWI::new(raw_reg.as_ptr() as _) }; - - for (i, hart_id) in (0..raw_reg.len()) - .map(|i| HartId::from_number(i).unwrap()) - .enumerate() - { - let setssip = mswi.setssip(hart_id); - assert!(!setssip.is_pending()); - assert_eq!(raw_reg[i], 0); - setssip.pend(); - assert!(setssip.is_pending()); - assert_ne!(raw_reg[i], 0); - setssip.unpend(); - assert!(!setssip.is_pending()); - assert_eq!(raw_reg[i], 0); - } + let setssip = unsafe { SETSSIP::new(&mut raw_reg as *mut u32 as usize) }; + + assert!(!setssip.is_pending()); + assert_eq!(raw_reg, 0); + setssip.pend(); + assert!(setssip.is_pending()); + assert_ne!(raw_reg, 0); + setssip.unpend(); + assert!(!setssip.is_pending()); + assert_eq!(raw_reg, 0); } } diff --git a/riscv-peripheral/src/hal/aclint.rs b/riscv-peripheral/src/hal/aclint.rs index 143fe852..296decf1 100644 --- a/riscv-peripheral/src/hal/aclint.rs +++ b/riscv-peripheral/src/hal/aclint.rs @@ -1,46 +1,14 @@ //! Delay trait implementation for (A)CLINT peripherals -use crate::aclint::mtimer::MTIME; -pub use crate::hal::delay::DelayNs; +use crate::aclint::mtimer::{Mtimer, MTIMER}; +use crate::hal::delay::DelayNs; -/// Delay implementation for (A)CLINT peripherals. -pub struct Delay { - mtime: MTIME, - freq: usize, -} - -impl Delay { - /// Creates a new `Delay` instance. - #[inline] - pub const fn new(mtime: MTIME, freq: usize) -> Self { - Self { mtime, freq } - } - - /// Returns the frequency of the `MTIME` register. - #[inline] - pub const fn get_freq(&self) -> usize { - self.freq - } - - /// Sets the frequency of the `MTIME` register. - #[inline] - pub fn set_freq(&mut self, freq: usize) { - self.freq = freq; - } - - /// Returns the `MTIME` register. - #[inline] - pub const fn get_mtime(&self) -> MTIME { - self.mtime - } -} - -impl DelayNs for Delay { +impl DelayNs for MTIMER { #[inline] fn delay_ns(&mut self, ns: u32) { - let t0 = self.mtime.read(); + let t0 = self.mtime().read(); let ns_64: u64 = ns.into(); - let n_ticks = ns_64 * self.freq as u64 / 1_000_000_000; - while self.mtime.read().wrapping_sub(t0) < n_ticks {} + let n_ticks = ns_64 * M::MTIME_FREQ as u64 / 1_000_000_000; + while self.mtime().read().wrapping_sub(t0) < n_ticks {} } } diff --git a/riscv-peripheral/src/hal_async.rs b/riscv-peripheral/src/hal_async.rs deleted file mode 100644 index fdb657e9..00000000 --- a/riscv-peripheral/src/hal_async.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! async trait implementations for embedded-hal - -pub use embedded_hal_async::*; // re-export embedded-hal-async to allow macros to use it - -#[cfg(feature = "aclint-hal-async")] -pub mod aclint; // ACLINT and CLINT peripherals diff --git a/riscv-peripheral/src/hal_async/aclint.rs b/riscv-peripheral/src/hal_async/aclint.rs deleted file mode 100644 index c7cd7ab7..00000000 --- a/riscv-peripheral/src/hal_async/aclint.rs +++ /dev/null @@ -1,269 +0,0 @@ -//! Asynchronous delay implementation for the (A)CLINT peripheral. -//! -//! # Note -//! -//! The asynchronous delay implementation for the (A)CLINT peripheral relies on the machine-level timer interrupts. -//! Therefore, it needs to schedule the machine-level timer interrupts via the [`MTIMECMP`] register assigned to the current HART. -//! Thus, the [`Delay`] instance must be created on the same HART that is used to call the asynchronous delay methods. -//! -//! # Requirements -//! -//! The following `extern "Rust"` functions must be implemented: -//! -//! - `fn _riscv_peripheral_aclint_mtimer(hart_id: usize) -> MTIMER`: This function returns the `MTIMER` register for the given HART ID. -//! - `fn _riscv_peripheral_aclint_push_timer(t: Timer) -> Result<(), Timer>`: This function pushes a new timer to a timer queue assigned to the given HART ID. -//! If it fails (e.g., the timer queue is full), it returns back the timer that failed to be pushed. -//! The logic of timer queues are application-specific and are not provided by this crate. -//! - `fn _riscv_peripheral_aclint_wake_timers(current_tick: u64) -> Option`: -//! This function pops all the expired timers from a timer queue assigned to the current HART ID and wakes their associated wakers. -//! The function returns the next [`MTIME`] tick at which the next timer expires. If the queue is empty, it returns `None`. - -use crate::aclint::mtimer::{MTIME, MTIMECMP, MTIMER}; -pub use crate::hal_async::delay::DelayNs; -use core::{ - cmp::{Eq, Ord, PartialEq, PartialOrd}, - future::Future, - pin::Pin, - task::{Context, Poll, Waker}, -}; - -extern "Rust" { - /// Returns the `MTIMER` register for the current HART ID. - /// This is necessary for [`MachineTimer`] to obtain the corresponding `MTIMER` register. - /// - /// # Safety - /// - /// Do not call this function directly. It is only meant to be called by [`MachineTimer`]. - fn _riscv_peripheral_aclint_mtimer() -> MTIMER; - - /// Tries to push a new timer to the timer queue assigned to the `MTIMER` register for the current HART ID. - /// If it fails (e.g., the timer queue is full), it returns back the timer that failed to be pushed. - /// - /// # Safety - /// - /// Do not call this function directly. It is only meant to be called by [`DelayAsync`]. - fn _riscv_peripheral_aclint_push_timer(t: Timer) -> Result<(), Timer>; - - /// Pops all the expired timers from the timer queue assigned to the `MTIMER` register for the - /// current HART ID and wakes their associated wakers. Once it is done, if the queue is empty, - /// it returns `None`. Alternatively, if the queue is not empty but the earliest timer has not expired - /// yet, it returns `Some(next_expires)` where `next_expires` is the tick at which this timer expires. - /// - /// # Safety - /// - /// Do not call this function directly. It is only meant to be called by [`MachineTimer`] and [`DelayAsync`]. - fn _riscv_peripheral_aclint_wake_timers(current_tick: u64) -> Option; -} - -/// Machine-level timer interrupt handler. This handler is triggered whenever the `MTIME` -/// register reaches the value of the `MTIMECMP` register of the current HART. -#[no_mangle] -#[allow(non_snake_case)] -fn MachineTimer() { - // recover the MTIME and MTIMECMP registers for the current HART - let mtimer = unsafe { _riscv_peripheral_aclint_mtimer() }; - let (mtime, mtimercmp) = (mtimer.mtime, mtimer.mtimecmp_mhartid()); - // schedule the next machine timer interrupt - schedule_machine_timer(mtime, mtimercmp); -} - -/// Schedules the next machine timer interrupt for the given HART ID according to the timer queue. -fn schedule_machine_timer(mtime: MTIME, mtimercmp: MTIMECMP) { - unsafe { riscv::register::mie::clear_mtimer() }; // disable machine timer interrupts to avoid reentrancy - let current_tick = mtime.read(); - if let Some(next_expires) = unsafe { _riscv_peripheral_aclint_wake_timers(current_tick) } { - debug_assert!(next_expires > current_tick); - mtimercmp.write(next_expires); // schedule next interrupt at next_expires - unsafe { riscv::register::mie::set_mtimer() }; // enable machine timer interrupts - } -} - -/// Asynchronous delay implementation for (A)CLINT peripherals. -/// -/// # Note -/// -/// The asynchronous delay implementation for (A)CLINT peripherals relies on the machine-level timer interrupts. -/// Therefore, it needs to schedule the machine-level timer interrupts via the [`MTIMECMP`] register assigned to the current HART. -/// Thus, the [`Delay`] instance must be created on the same HART that is used to call the asynchronous delay methods. -/// Additionally, the rest of the application must not modify the [`MTIMER`] register assigned to the current HART. -#[derive(Clone)] -pub struct Delay { - freq: usize, - mtime: MTIME, - mtimecmp: MTIMECMP, -} - -impl Delay { - /// Creates a new `Delay` instance for the current HART. - #[inline] - pub fn new(freq: usize) -> Self { - let mtimer = unsafe { _riscv_peripheral_aclint_mtimer() }; - let (mtime, mtimecmp) = (mtimer.mtime, mtimer.mtimecmp_mhartid()); - Self { - freq, - mtime, - mtimecmp, - } - } - - /// Returns the frequency of the `MTIME` register. - #[inline] - pub const fn get_freq(&self) -> usize { - self.freq - } - - /// Sets the frequency of the `MTIME` register. - #[inline] - pub fn set_freq(&mut self, freq: usize) { - self.freq = freq; - } -} - -impl DelayNs for Delay { - #[inline] - async fn delay_ns(&mut self, ns: u32) { - let n_ticks = ns as u64 * self.get_freq() as u64 / 1_000_000_000; - DelayAsync::new(self, n_ticks).await; - } - - #[inline] - async fn delay_us(&mut self, us: u32) { - let n_ticks = us as u64 * self.get_freq() as u64 / 1_000_000; - DelayAsync::new(self, n_ticks).await; - } - - #[inline] - async fn delay_ms(&mut self, ms: u32) { - let n_ticks = ms as u64 * self.get_freq() as u64 / 1_000; - DelayAsync::new(self, n_ticks).await; - } -} - -/// Timer queue entry. -/// When pushed to the timer queue via the `_riscv_peripheral_aclint_push_timer` function, -/// this entry provides the necessary information to adapt it to the timer queue implementation. -#[derive(Debug)] -pub struct Timer { - freq: usize, - mtime: MTIME, - mtimecmp: MTIMECMP, - expires: u64, - waker: Waker, -} - -impl Timer { - /// Creates a new timer queue entry. - #[inline] - const fn new( - freq: usize, - mtime: MTIME, - mtimecmp: MTIMECMP, - expires: u64, - waker: Waker, - ) -> Self { - Self { - freq, - mtime, - mtimecmp, - expires, - waker, - } - } - - /// Returns the frequency of the [`MTIME`] register associated with this timer. - #[inline] - pub const fn freq(&self) -> usize { - self.freq - } - - /// Returns the [`MTIME`] register associated with this timer. - #[inline] - pub const fn mtime(&self) -> MTIME { - self.mtime - } - - /// Returns the [`MTIMECMP`] register associated with this timer. - #[inline] - pub const fn mtimecmp(&self) -> MTIMECMP { - self.mtimecmp - } - - /// Returns the tick at which the timer expires. - #[inline] - pub const fn expires(&self) -> u64 { - self.expires - } - - /// Returns the waker associated with this timer. - #[inline] - pub fn waker(&self) -> Waker { - self.waker.clone() - } -} - -impl PartialEq for Timer { - fn eq(&self, other: &Self) -> bool { - self.freq == other.freq && self.expires == other.expires - } -} - -impl Eq for Timer {} - -impl Ord for Timer { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.expires.cmp(&other.expires) - } -} - -impl PartialOrd for Timer { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.expires.cmp(&other.expires)) - } -} - -struct DelayAsync<'a> { - delay: &'a Delay, - expires: u64, - pushed: bool, -} - -impl<'a> DelayAsync<'a> { - pub fn new(delay: &'a Delay, n_ticks: u64) -> Self { - let t0 = delay.mtime.read(); - let expires = t0.wrapping_add(n_ticks); - Self { - delay, - expires, - pushed: false, - } - } -} - -impl<'a> Future for DelayAsync<'a> { - type Output = (); - - #[inline] - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - if self.delay.mtime.read() < self.expires { - if !self.pushed { - // we only push the timer to the queue the first time we poll - self.pushed = true; - let timer = Timer::new( - self.delay.freq, - self.delay.mtime, - self.delay.mtimecmp, - self.expires, - cx.waker().clone(), - ); - unsafe { - _riscv_peripheral_aclint_push_timer(timer).expect("timer queue is full"); - }; - // we also need to reschedule the machine timer interrupt - schedule_machine_timer(self.delay.mtime, self.delay.mtimecmp); - } - Poll::Pending - } else { - Poll::Ready(()) - } - } -} diff --git a/riscv-peripheral/src/lib.rs b/riscv-peripheral/src/lib.rs index b34b63a8..26bf0803 100644 --- a/riscv-peripheral/src/lib.rs +++ b/riscv-peripheral/src/lib.rs @@ -1,9 +1,4 @@ //! Standard RISC-V peripherals for embedded systems written in Rust. -//! -//! ## Features -//! -//! - `aclint-hal-async`: enables the [`hal_async::delay::DelayNs`] implementation for the ACLINT peripheral. -//! This feature relies on external functions that must be provided by the user. See [`hal_async::aclint`] for more information. #![deny(missing_docs)] #![no_std] @@ -13,9 +8,36 @@ pub use riscv_pac::result; // re-export the result module pub mod common; // common definitions for all peripherals pub mod hal; // trait implementations for embedded-hal -#[cfg(feature = "embedded-hal-async")] -pub mod hal_async; // async trait implementations for embedded-hal pub mod macros; // macros for easing the definition of peripherals in PACs pub mod aclint; // ACLINT and CLINT peripherals pub mod plic; // PLIC peripheral + +#[cfg(test)] +mod test { + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[riscv::pac_enum(unsafe ExternalInterruptNumber)] + pub(crate) enum Interrupt { + I1 = 1, + I2 = 2, + I3 = 3, + I4 = 4, + } + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[riscv::pac_enum(unsafe PriorityNumber)] + pub(crate) enum Priority { + P0 = 0, + P1 = 1, + P2 = 2, + P3 = 3, + } + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[riscv::pac_enum(unsafe HartIdNumber)] + pub(crate) enum HartId { + H0 = 0, + H1 = 1, + H2 = 2, + } +} diff --git a/riscv-peripheral/src/macros.rs b/riscv-peripheral/src/macros.rs index a6272fe8..932a85c1 100644 --- a/riscv-peripheral/src/macros.rs +++ b/riscv-peripheral/src/macros.rs @@ -1,29 +1,28 @@ //! Utility macros for generating standard peripherals-related code in RISC-V PACs. +pub use paste::paste; + /// Macro to create interfaces to CLINT peripherals in PACs. /// The resulting struct will be named `CLINT`, and will provide safe access to the CLINT registers. /// -/// This macro expects 5 different argument types: +/// This macro expects 3 different argument types: /// /// - Base address (**MANDATORY**): base address of the CLINT peripheral of the target. -/// - Frequency (**OPTIONAL**): clock frequency (in Hz) of the `MTIME` register. It enables the `delay` method of the `CLINT` struct. -/// - Async flag (**OPTIONAL**): It enables the `async_delay` method of the `CLINT struct`. -/// You must activate the `embedded-hal-async` feature to use this flag. -/// - Per-HART mtimecmp registers (**OPTIONAL**): a list of `mtimecmp` registers for easing access to per-HART mtimecmp regs. -/// - Per-HART msip registers (**OPTIONAL**): a list of `msip` registers for easing access to per-HART msip regs. +/// - MTIME Frequency (**MANDATORY**): clock frequency (in Hz) of the `MTIME` register. +/// - HART map (**OPTIONAL**): a list of HART IDs and their corresponding numbers. /// /// Check the examples below for more details about the usage and syntax of this macro. /// /// # Example /// -/// ## Base address only +/// ## Mandatory fields only /// /// ``` -/// riscv_peripheral::clint_codegen!(base 0x0200_0000, freq 32_768,); // do not forget the ending comma! +/// riscv_peripheral::clint_codegen!(base 0x0200_0000, mtime_freq 32_768,); // do not forget the ending comma! /// -/// let mswi = CLINT::mswi(); // MSWI peripheral -/// let mtimer = CLINT::mtimer(); // MTIMER peripheral -/// let delay = CLINT::delay(); // For the `embedded_hal::delay::DelayNs` trait +/// let clint = CLINT::new(); // Create a new CLINT peripheral +/// let mswi = clint.mswi(); // MSWI peripheral +/// let mtimer = clint.mtimer(); // MTIMER peripheral /// ``` /// /// ## Base address and per-HART mtimecmp registers @@ -33,38 +32,26 @@ /// /// /// HART IDs for the target CLINT peripheral /// #[derive(Clone, Copy, Debug, Eq, PartialEq)] +/// #[riscv::pac_enum(unsafe HartIdNumber)] /// pub enum HartId { H0 = 0, H1 = 1, H2 = 2 } /// -/// // Implement `HartIdNumber` for `HartId` -/// unsafe impl riscv_peripheral::aclint::HartIdNumber for HartId { -/// const MAX_HART_ID_NUMBER: usize = Self::H2 as usize; -/// fn number(self) -> usize { self as _ } -/// fn from_number(number: usize) -> Result { -/// match number { -/// 0 => Ok(HartId::H0), -/// 1 => Ok(HartId::H1), -/// 2 => Ok(HartId::H2), -/// _ => Err(Error::InvalidVariant(number)), -/// } -/// } -/// } -/// /// riscv_peripheral::clint_codegen!( /// base 0x0200_0000, -/// mtimecmps [mtimecmp0 = (HartId::H0, "`H0`"), mtimecmp1 = (HartId::H1, "`H1`"), mtimecmp2 = (HartId::H2, "`H2`")], -/// msips [msip0=(HartId::H0,"`H0`"), msip1=(HartId::H1,"`H1`"), msip2=(HartId::H2,"`H2`")], // do not forget the ending comma! +/// mtime_freq 32_768, +/// harts [HartId::H0 => 0, HartId::H1 => 1, HartId::H2 => 2], // do not forget the ending comma! /// ); /// -/// let mswi = CLINT::mswi(); // MSWI peripheral -/// let mtimer = CLINT::mtimer(); // MTIMER peripheral +/// let clint = CLINT::new(); // Create a new CLINT peripheral +/// let mswi = clint.mswi(); // MSWI peripheral +/// let mtimer = clint.mtimer(); // MTIMER peripheral /// -/// let mtimecmp0 = CLINT::mtimecmp0(); // mtimecmp register for HART 0 -/// let mtimecmp1 = CLINT::mtimecmp1(); // mtimecmp register for HART 1 -/// let mtimecmp2 = CLINT::mtimecmp2(); // mtimecmp register for HART 2 +/// let mtimecmp0 = clint.mtimecmp0(); // mtimecmp register for HART 0 +/// let mtimecmp1 = clint.mtimecmp1(); // mtimecmp register for HART 1 +/// let mtimecmp2 = clint.mtimecmp2(); // mtimecmp register for HART 2 /// -/// let msip0 = CLINT::msip0(); // msip register for HART 0 -/// let msip1 = CLINT::msip1(); // msip register for HART 1 -/// let msip2 = CLINT::msip2(); // msip register for HART 2 +/// let msip0 = clint.msip0(); // msip register for HART 0 +/// let msip1 = clint.msip1(); // msip register for HART 1 +/// let msip2 = clint.msip2(); // msip register for HART 2 /// ``` #[macro_export] macro_rules! clint_codegen { @@ -72,193 +59,66 @@ macro_rules! clint_codegen { #[allow(unused_imports)] use CLINT as _; // assert that the CLINT struct is defined }; - (base $addr:literal, $($tail:tt)*) => { + (base $addr:literal, mtime_freq $freq:literal, $($tail:tt)*) => { /// CLINT peripheral #[allow(clippy::upper_case_acronyms)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] - pub struct CLINT; - - unsafe impl $crate::aclint::Clint for CLINT { - const BASE: usize = $addr; - } + pub struct CLINT($crate::aclint::CLINT); impl CLINT { - /// Returns `true` if a machine timer **OR** software interrupt is pending. - #[inline] - pub fn is_interrupting() -> bool { - Self::mswi_is_interrupting() || Self::mtimer_is_interrupting() - } - - /// Returns `true` if machine timer **OR** software interrupts are enabled. - pub fn is_enabled() -> bool { - Self::mswi_is_enabled() || Self::mtimer_is_enabled() - } - - /// Enables machine timer **AND** software interrupts to allow the CLINT to trigger interrupts. - /// - /// # Safety - /// - /// Enabling the `CLINT` may break mask-based critical sections. + /// Creates a new `CLINT` peripheral. #[inline] - pub unsafe fn enable() { - Self::mswi_enable(); - Self::mtimer_enable(); - } - - /// Disables machine timer **AND** software interrupts to prevent the CLINT from triggering interrupts. - #[inline] - pub fn disable() { - Self::mswi_disable(); - Self::mtimer_disable(); - } - - /// Returns `true` if a machine software interrupt is pending. - #[inline] - pub fn mswi_is_interrupting() -> bool { - $crate::riscv::register::mip::read().msoft() - } - - /// Returns `true` if Machine Software Interrupts are enabled. - #[inline] - pub fn mswi_is_enabled() -> bool { - $crate::riscv::register::mie::read().msoft() - } - - /// Enables the `MSWI` peripheral. - /// - /// # Safety - /// - /// Enabling the `MSWI` may break mask-based critical sections. - #[inline] - pub unsafe fn mswi_enable() { - $crate::riscv::register::mie::set_msoft(); - } - - /// Disables the `MSWI` peripheral. - #[inline] - pub fn mswi_disable() { - // SAFETY: it is safe to disable interrupts - unsafe { $crate::riscv::register::mie::clear_msoft() }; - } - - /// Returns the `MSWI` peripheral. - #[inline] - pub const fn mswi() -> $crate::aclint::mswi::MSWI { - $crate::aclint::CLINT::::mswi() - } - - /// Returns `true` if a machine timer interrupt is pending. - #[inline] - pub fn mtimer_is_interrupting() -> bool { - $crate::riscv::register::mip::read().mtimer() - } - - /// Returns `true` if Machine Timer Interrupts are enabled. - #[inline] - pub fn mtimer_is_enabled() -> bool { - $crate::riscv::register::mie::read().mtimer() - } - - /// Sets the Machine Timer Interrupt bit of the `mie` CSR. - /// This bit must be set for the `MTIMER` to trigger machine timer interrupts. - /// - /// # Safety - /// - /// Enabling the `MTIMER` may break mask-based critical sections. - #[inline] - pub unsafe fn mtimer_enable() { - $crate::riscv::register::mie::set_mtimer(); + pub const fn new() -> Self { + Self($crate::aclint::CLINT::new()) } + } - /// Clears the Machine Timer Interrupt bit of the `mie` CSR. - /// When cleared, the `MTIMER` cannot trigger machine timer interrupts. - #[inline] - pub fn mtimer_disable() { - // SAFETY: it is safe to disable interrupts - unsafe { $crate::riscv::register::mie::clear_mtimer() }; - } + unsafe impl $crate::aclint::Clint for CLINT { + const BASE: usize = $addr; + const MTIME_FREQ: usize = $freq; + } - /// Returns the `MTIMER` peripheral. - #[inline] - pub const fn mtimer() -> $crate::aclint::mtimer::MTIMER { - $crate::aclint::CLINT::::mtimer() - } + impl core::ops::Deref for CLINT { + type Target = $crate::aclint::CLINT; - /// Returns the `MTIME` register of the `MTIMER` peripheral. #[inline] - pub const fn mtime() -> $crate::aclint::mtimer::MTIME { - Self::mtimer().mtime + fn deref(&self) -> &Self::Target { + &self.0 } } - $crate::clint_codegen!($($tail)*); - }; - (freq $freq:literal, $($tail:tt)*) => { - impl CLINT { - /// Returns the frequency of the `MTIME` register. - #[inline] - pub const fn freq() -> usize { - $freq - } - /// Delay implementation for CLINT peripherals. - /// - /// # Note - /// - /// You must export the [`embedded_hal::delay::DelayNs`] trait in order to use delay methods. + impl core::ops::DerefMut for CLINT { #[inline] - pub const fn delay() -> $crate::hal::aclint::Delay { - $crate::hal::aclint::Delay::new(Self::mtime(), Self::freq()) + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 } } + $crate::clint_codegen!($($tail)*); }; - (async_delay, $($tail:tt)*) => { - impl CLINT { - /// Asynchronous delay implementation for CLINT peripherals. - /// - /// # Note - /// - /// You must export the [`embedded_hal_async::delay::DelayNs`] trait in order to use delay methods. - /// - /// This implementation relies on the machine-level timer interrupts to wake futures. - /// Therefore, it needs to schedule the machine-level timer interrupts via the `MTIMECMP` register assigned to the current HART. - /// Thus, the `Delay` instance must be created on the same HART that is used to call the asynchronous delay methods. - /// Additionally, the rest of the application must not modify the `MTIMER` register assigned to the current HART. - #[inline] - pub fn async_delay() -> $crate::hal_async::aclint::Delay { - $crate::hal_async::aclint::Delay::new(Self::freq()) + (harts [$($hart:expr => $num:literal),+], $($tail:tt)*) => { + $crate::macros::paste! { + impl CLINT { + $( + #[doc = "Returns the `msip` register for HART [`"] + #[doc = stringify!($hart)] + #[doc = "`]."] + #[inline] + pub fn [](&self) -> $crate::aclint::mswi::MSIP { + self.mswi().msip($hart) + } + #[doc = "Returns the `mtimecmp` register for HART [`"] + #[doc = stringify!($hart)] + #[doc = "`]."] + #[inline] + pub fn [](&self) -> $crate::aclint::mtimer::MTIMECMP { + self.mtimer().mtimecmp($hart) + } + )* } } $crate::clint_codegen!($($tail)*); }; - (msips [$($fn:ident = ($hart:expr , $shart:expr)),+], $($tail:tt)*) => { - impl CLINT { - $( - #[doc = "Returns the `msip` register for HART "] - #[doc = $shart] - #[doc = "."] - #[inline] - pub fn $fn() -> $crate::aclint::mswi::MSIP { - Self::mswi().msip($hart) - } - )* - } - $crate::clint_codegen!($($tail)*); - }; - (mtimecmps [$($fn:ident = ($hart:expr , $shart:expr)),+], $($tail:tt)*) => { - impl CLINT { - $( - #[doc = "Returns the `mtimecmp` register for HART "] - #[doc = $shart] - #[doc = "."] - #[inline] - pub fn $fn() -> $crate::aclint::mtimer::MTIMECMP { - Self::mtimer().mtimecmp($hart) - } - )* - } - $crate::clint_codegen!($($tail)*); - }; } /// Macro to create interfaces to PLIC peripherals in PACs. @@ -267,7 +127,7 @@ macro_rules! clint_codegen { /// This macro expects 2 different argument types: /// /// - Base address (**MANDATORY**): base address of the PLIC peripheral of the target. -/// - Per-HART contexts (**OPTIONAL**): a list of `ctx` contexts for easing access to per-HART PLIC contexts. +/// - HART map (**OPTIONAL**): a list of HART IDs and their corresponding numbers. /// /// Check the examples below for more details about the usage and syntax of this macro. /// @@ -280,8 +140,34 @@ macro_rules! clint_codegen { /// /// riscv_peripheral::plic_codegen!(base 0x0C00_0000,); // do not forget the ending comma! /// -/// let priorities = PLIC::priorities(); // Priorities registers -/// let pendings = PLIC::pendings(); // Pendings registers +/// let plic = PLIC::new(); // Create a new PLIC peripheral +/// let priorities = plic.priorities(); // Priorities registers +/// let pendings = plic.pendings(); // Pendings registers +/// ``` +/// +/// ## Base address and per-HART context proxies +/// +/// ``` +/// use riscv_pac::result::{Error, Result}; +/// +/// /// HART IDs for the target CLINT peripheral +/// #[derive(Clone, Copy, Debug, Eq, PartialEq)] +/// #[riscv::pac_enum(unsafe HartIdNumber)] +/// pub enum HartId { H0 = 0, H1 = 1, H2 = 2 } +/// +/// riscv_peripheral::plic_codegen!( +/// base 0x0C00_0000, +/// harts [HartId::H0 => 0, HartId::H1 => 1, HartId::H2 => 2], // do not forget the ending comma! +/// ); +/// +/// let plic = PLIC::new(); // Create a new PLIC peripheral +/// let ctx0 = plic.ctx0(); // Context proxy for HART 0 +/// let ctx1 = plic.ctx1(); // Context proxy for HART 1 +/// let ctx2 = plic.ctx2(); // Context proxy for HART 2 +/// +/// assert_eq!(ctx0, plic.ctx(HartId::H0)); +/// assert_eq!(ctx1, plic.ctx(HartId::H1)); +/// assert_eq!(ctx2, plic.ctx(HartId::H2)); /// ``` #[macro_export] macro_rules! plic_codegen { @@ -293,84 +179,51 @@ macro_rules! plic_codegen { /// PLIC peripheral #[allow(clippy::upper_case_acronyms)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] - pub struct PLIC; - - unsafe impl $crate::plic::Plic for PLIC { - const BASE: usize = $addr; - } + pub struct PLIC($crate::plic::PLIC); impl PLIC { - /// Returns `true` if a machine external interrupt is pending. - #[inline] - pub fn is_interrupting() -> bool { - $crate::riscv::register::mip::read().mext() - } - - /// Returns true if Machine External Interrupts are enabled. - #[inline] - pub fn is_enabled() -> bool { - $crate::riscv::register::mie::read().mext() - } - - /// Enables machine external interrupts to allow the PLIC to trigger interrupts. - /// - /// # Safety - /// - /// Enabling the `PLIC` may break mask-based critical sections. - #[inline] - pub unsafe fn enable() { - $crate::riscv::register::mie::set_mext(); - } - - /// Disables machine external interrupts to prevent the PLIC from triggering interrupts. + /// Creates a new `CLINT` peripheral. #[inline] - pub fn disable() { - // SAFETY: it is safe to disable interrupts - unsafe { $crate::riscv::register::mie::clear_mext() }; + pub const fn new() -> Self { + Self($crate::plic::PLIC::new()) } + } - /// Returns the priorities register of the PLIC. - #[inline] - pub fn priorities() -> $crate::plic::priorities::PRIORITIES { - $crate::plic::PLIC::::priorities() - } + unsafe impl $crate::plic::Plic for PLIC { + const BASE: usize = $addr; + } - /// Returns the pendings register of the PLIC. - #[inline] - pub fn pendings() -> $crate::plic::pendings::PENDINGS { - $crate::plic::PLIC::::pendings() - } + impl core::ops::Deref for PLIC { + type Target = $crate::plic::PLIC; - /// Returns the context proxy of a given PLIC HART context. #[inline] - pub fn ctx(hart_id: H) -> $crate::plic::CTX { - $crate::plic::PLIC::::ctx(hart_id) + fn deref(&self) -> &Self::Target { + &self.0 } + } - /// Returns the PLIC HART context for the current HART. - /// - /// # Note - /// - /// This function determines the current HART ID by reading the [`riscv::register::mhartid`] CSR. - /// Thus, it can only be used in M-mode. For S-mode, use [`PLIC::ctx`] instead. + impl core::ops::DerefMut for PLIC { #[inline] - pub fn ctx_mhartid() -> $crate::plic::CTX { - $crate::plic::PLIC::::ctx_mhartid() + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 } } + $crate::plic_codegen!($($tail)*); }; - (ctxs [$($fn:ident = ($ctx:expr , $sctx:expr)),+], $($tail:tt)*) => { - impl PLIC { - $( - #[doc = "Returns a PLIC context proxy for context of HART "] - #[doc = $sctx] - #[doc = "."] - #[inline] - pub fn $fn() -> $crate::plic::CTX { - Self::ctx($ctx) - } - )* + (harts [$($hart:expr => $num:literal),+], $($tail:tt)*) => { + $crate::macros::paste! { + impl PLIC { + $( + #[doc = "Returns a PLIC context proxy for context of HART [`"] + #[doc = stringify!($hart)] + #[doc = "`]."] + #[inline] + pub fn [](&self) -> $crate::plic::CTX { + self.ctx($hart) + } + )* + } } $crate::plic_codegen!($($tail)*); }; diff --git a/riscv-peripheral/src/plic.rs b/riscv-peripheral/src/plic.rs index f2f378b8..76830350 100644 --- a/riscv-peripheral/src/plic.rs +++ b/riscv-peripheral/src/plic.rs @@ -11,6 +11,8 @@ pub mod threshold; // re-export useful riscv-pac traits pub use riscv_pac::{HartIdNumber, InterruptNumber, PriorityNumber}; +use riscv::register::{mhartid, mie, mip}; + /// Trait for a PLIC peripheral. /// /// # Safety @@ -43,26 +45,65 @@ impl PLIC

{ const PENDINGS_OFFSET: usize = 0x1000; - /// Returns the priorities register of the PLIC. + /// Creates a new `PLIC` peripheral. + #[inline] + pub const fn new() -> Self { + Self { + _marker: core::marker::PhantomData, + } + } + + /// Returns `true` if a machine external interrupt is pending. + #[inline] + pub fn is_interrupting(self) -> bool { + mip::read().mext() + } + + /// Returns true if machine external interrupts are enabled. + #[inline] + pub fn is_enabled(self) -> bool { + mie::read().mext() + } + + /// Enables machine external interrupts to allow the PLIC to trigger interrupts. + /// + /// # Safety + /// + /// Enabling the `PLIC` may break mask-based critical sections. + #[inline] + pub unsafe fn enable(self) { + mie::set_mext(); + } + + /// Disables machine external interrupts to prevent the PLIC from triggering interrupts. + #[inline] + pub fn disable(self) { + // SAFETY: it is safe to disable interrupts + unsafe { mie::clear_mext() }; + } + + /// Returns the [`PRIORITIES`](priorities::PRIORITIES) register of the PLIC. + /// /// This register allows to set the priority level of each interrupt source. /// The priority level of each interrupt source is shared among all the contexts. #[inline] - pub fn priorities() -> priorities::PRIORITIES { + pub const fn priorities(self) -> priorities::PRIORITIES { // SAFETY: valid address unsafe { priorities::PRIORITIES::new(P::BASE + Self::PRIORITIES_OFFSET) } } - /// Returns the pendings register of the PLIC. + /// Returns the [`PENDINGS`](pendings::PENDINGS) register of the PLIC. + /// /// This register allows to check if a particular interrupt source is pending. #[inline] - pub fn pendings() -> pendings::PENDINGS { + pub const fn pendings(self) -> pendings::PENDINGS { // SAFETY: valid address unsafe { pendings::PENDINGS::new(P::BASE + Self::PENDINGS_OFFSET) } } /// Returns a proxy to access to all the PLIC registers of a given HART context. #[inline] - pub fn ctx(hart_id: H) -> CTX

{ + pub fn ctx(self, hart_id: H) -> CTX

{ // SAFETY: valid context number unsafe { CTX::new(hart_id.number() as _) } } @@ -71,11 +112,11 @@ impl PLIC

{ /// /// # Note /// - /// This function determines the current HART ID by reading the [`riscv::register::mhartid`] CSR. + /// This function determines the current HART ID by reading the [`mhartid`] CSR. /// Thus, it can only be used in M-mode. For S-mode, use [`PLIC::ctx`] instead. #[inline] - pub fn ctx_mhartid() -> CTX

{ - let hart_id = riscv::register::mhartid::read(); + pub fn ctx_mhartid(self) -> CTX

{ + let hart_id = mhartid::read(); // SAFETY: `hart_id` is valid for the target and is the current hart unsafe { CTX::new(hart_id as _) } } @@ -145,155 +186,28 @@ impl CTX

{ #[cfg(test)] pub(crate) mod test { - use riscv_pac::result::{Error, Result}; - use riscv_pac::{ExternalInterruptNumber, HartIdNumber, InterruptNumber, PriorityNumber}; - - #[derive(Clone, Copy, Debug, Eq, PartialEq)] - pub(crate) enum Interrupt { - I1 = 1, - I2 = 2, - I3 = 3, - I4 = 4, - } - - #[derive(Clone, Copy, Debug, Eq, PartialEq)] - pub(crate) enum Priority { - P0 = 0, - P1 = 1, - P2 = 2, - P3 = 3, - } - - #[derive(Clone, Copy, Debug, Eq, PartialEq)] - pub(crate) enum Context { - C0 = 0, - C1 = 1, - C2 = 2, - } - - unsafe impl InterruptNumber for Interrupt { - const MAX_INTERRUPT_NUMBER: usize = Self::I4 as usize; - - #[inline] - fn number(self) -> usize { - self as _ - } - - #[inline] - fn from_number(number: usize) -> Result { - match number { - 1 => Ok(Interrupt::I1), - 2 => Ok(Interrupt::I2), - 3 => Ok(Interrupt::I3), - 4 => Ok(Interrupt::I4), - _ => Err(Error::InvalidVariant(number)), - } - } - } - - unsafe impl ExternalInterruptNumber for Interrupt {} - - unsafe impl PriorityNumber for Priority { - const MAX_PRIORITY_NUMBER: usize = Self::P3 as usize; - - #[inline] - fn number(self) -> usize { - self as _ - } - - #[inline] - fn from_number(number: usize) -> Result { - match number { - 0 => Ok(Priority::P0), - 1 => Ok(Priority::P1), - 2 => Ok(Priority::P2), - 3 => Ok(Priority::P3), - _ => Err(Error::InvalidVariant(number)), - } - } - } - - unsafe impl HartIdNumber for Context { - const MAX_HART_ID_NUMBER: usize = Self::C2 as usize; - - #[inline] - fn number(self) -> usize { - self as _ - } - - #[inline] - fn from_number(number: usize) -> Result { - match number { - 0 => Ok(Context::C0), - 1 => Ok(Context::C1), - 2 => Ok(Context::C2), - _ => Err(Error::InvalidVariant(number)), - } - } - } - - #[test] - fn check_interrupt_enum() { - assert_eq!(Interrupt::I1.number(), 1); - assert_eq!(Interrupt::I2.number(), 2); - assert_eq!(Interrupt::I3.number(), 3); - assert_eq!(Interrupt::I4.number(), 4); - - assert_eq!(Interrupt::from_number(1), Ok(Interrupt::I1)); - assert_eq!(Interrupt::from_number(2), Ok(Interrupt::I2)); - assert_eq!(Interrupt::from_number(3), Ok(Interrupt::I3)); - assert_eq!(Interrupt::from_number(4), Ok(Interrupt::I4)); - - assert_eq!(Interrupt::from_number(0), Err(Error::InvalidVariant(0)),); - assert_eq!(Interrupt::from_number(5), Err(Error::InvalidVariant(5)),); - } - - #[test] - fn check_priority_enum() { - assert_eq!(Priority::P0.number(), 0); - assert_eq!(Priority::P1.number(), 1); - assert_eq!(Priority::P2.number(), 2); - assert_eq!(Priority::P3.number(), 3); - - assert_eq!(Priority::from_number(0), Ok(Priority::P0)); - assert_eq!(Priority::from_number(1), Ok(Priority::P1)); - assert_eq!(Priority::from_number(2), Ok(Priority::P2)); - assert_eq!(Priority::from_number(3), Ok(Priority::P3)); - - assert_eq!(Priority::from_number(4), Err(Error::InvalidVariant(4)),); - } - - #[test] - fn check_context_enum() { - assert_eq!(Context::C0.number(), 0); - assert_eq!(Context::C1.number(), 1); - assert_eq!(Context::C2.number(), 2); - - assert_eq!(Context::from_number(0), Ok(Context::C0)); - assert_eq!(Context::from_number(1), Ok(Context::C1)); - assert_eq!(Context::from_number(2), Ok(Context::C2)); - - assert_eq!(Context::from_number(3), Err(Error::InvalidVariant(3)),); - } + use crate::test::HartId; + use riscv_pac::HartIdNumber; #[allow(dead_code)] #[test] fn check_plic() { crate::plic_codegen!( base 0x0C00_0000, - ctxs [ctx0 = (Context::C0, "`C0`"), ctx1 = (Context::C1, "`C1`"), ctx2 = (Context::C2, "`C2`")], + harts [HartId::H0 => 0, HartId::H1 => 1, HartId::H2 => 2], ); - let priorities = PLIC::priorities(); - let pendings = PLIC::pendings(); + let plic = PLIC::new(); + let priorities = plic.priorities(); + let pendings = plic.pendings(); assert_eq!(priorities.address(), 0x0C00_0000); assert_eq!(pendings.address(), 0x0C00_1000); - for i in 0..=Context::MAX_HART_ID_NUMBER { - let context = Context::from_number(i).unwrap(); + for i in 0..=HartId::MAX_HART_ID_NUMBER { + let hart_id = HartId::from_number(i).unwrap(); - let ctx = PLIC::ctx(context); + let ctx = plic.ctx(hart_id); assert_eq!(ctx.enables().address(), 0x0C00_0000 + 0x2000 + i * 0x80); assert_eq!( @@ -306,8 +220,8 @@ pub(crate) mod test { ); } - assert_eq!(PLIC::ctx0(), PLIC::ctx(Context::C0)); - assert_eq!(PLIC::ctx1(), PLIC::ctx(Context::C1)); - assert_eq!(PLIC::ctx2(), PLIC::ctx(Context::C2)); + assert_eq!(plic.ctx0(), plic.ctx(HartId::H0)); + assert_eq!(plic.ctx1(), plic.ctx(HartId::H1)); + assert_eq!(plic.ctx2(), plic.ctx(HartId::H2)); } } diff --git a/riscv-peripheral/src/plic/claim.rs b/riscv-peripheral/src/plic/claim.rs index d99a543d..945f2b35 100644 --- a/riscv-peripheral/src/plic/claim.rs +++ b/riscv-peripheral/src/plic/claim.rs @@ -30,8 +30,8 @@ impl CLAIM { #[cfg(test)] mod test { - use super::super::test::Interrupt; use super::*; + use crate::test::Interrupt; use riscv_pac::InterruptNumber; #[test] diff --git a/riscv-peripheral/src/plic/enables.rs b/riscv-peripheral/src/plic/enables.rs index 9891b32c..016eb863 100644 --- a/riscv-peripheral/src/plic/enables.rs +++ b/riscv-peripheral/src/plic/enables.rs @@ -145,8 +145,8 @@ impl ENABLES { #[cfg(test)] mod test { - use super::super::test::Interrupt; use super::*; + use crate::test::Interrupt; #[test] fn test_enables() { diff --git a/riscv-peripheral/src/plic/pendings.rs b/riscv-peripheral/src/plic/pendings.rs index 594991da..12b170ce 100644 --- a/riscv-peripheral/src/plic/pendings.rs +++ b/riscv-peripheral/src/plic/pendings.rs @@ -40,8 +40,8 @@ impl PENDINGS { #[cfg(test)] mod test { - use super::super::test::Interrupt; use super::*; + use crate::test::Interrupt; #[test] fn test_pendings() { diff --git a/riscv-peripheral/src/plic/priorities.rs b/riscv-peripheral/src/plic/priorities.rs index b5e44857..725cd6d9 100644 --- a/riscv-peripheral/src/plic/priorities.rs +++ b/riscv-peripheral/src/plic/priorities.rs @@ -69,8 +69,8 @@ impl PRIORITIES { #[cfg(test)] mod test { - use super::super::test::{Interrupt, Priority}; use super::*; + use crate::test::{Interrupt, Priority}; use riscv_pac::InterruptNumber; #[test] diff --git a/riscv-peripheral/src/plic/threshold.rs b/riscv-peripheral/src/plic/threshold.rs index 936a13b1..f41e4682 100644 --- a/riscv-peripheral/src/plic/threshold.rs +++ b/riscv-peripheral/src/plic/threshold.rs @@ -35,8 +35,8 @@ impl THRESHOLD { #[cfg(test)] mod test { - use super::super::test::Priority; use super::*; + use crate::test::Priority; #[test] fn test_threshold() {