From e9cd65d8471abd778acb6143d0664a33a256c1b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Mon, 15 Jan 2024 13:10:26 +0100 Subject: [PATCH] added example for async delays with CLINT --- riscv-peripheral/Cargo.toml | 3 ++ riscv-peripheral/examples/e310x.rs | 42 ++++++++++++++++++++++++ riscv-peripheral/src/hal_async/aclint.rs | 28 ++++------------ riscv-peripheral/src/macros.rs | 6 ---- 4 files changed, 52 insertions(+), 27 deletions(-) diff --git a/riscv-peripheral/Cargo.toml b/riscv-peripheral/Cargo.toml index 6da7a9fa..d0d4e499 100644 --- a/riscv-peripheral/Cargo.toml +++ b/riscv-peripheral/Cargo.toml @@ -11,6 +11,9 @@ embedded-hal-async = { version = "1.0.0", optional = true } riscv = { path = "../riscv", version = "0.11.0" } riscv-pac = { path = "../riscv-pac", version = "0.1.0" } +[dev-dependencies] +heapless = "0.8.0" + [features] aclint-hal-async = ["embedded-hal-async"] diff --git a/riscv-peripheral/examples/e310x.rs b/riscv-peripheral/examples/e310x.rs index 7b80cb84..c0801f24 100644 --- a/riscv-peripheral/examples/e310x.rs +++ b/riscv-peripheral/examples/e310x.rs @@ -159,4 +159,46 @@ riscv_peripheral::plic_codegen!( ctxs [ctx0=(HartId::H0,"`H0`")], ); +#[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/hal_async/aclint.rs b/riscv-peripheral/src/hal_async/aclint.rs index dc84f11b..c7cd7ab7 100644 --- a/riscv-peripheral/src/hal_async/aclint.rs +++ b/riscv-peripheral/src/hal_async/aclint.rs @@ -11,7 +11,6 @@ //! 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. -//! This function is implemented by the [`crate::clint_codegen`] macro when asyn_delay is provided. //! - `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. @@ -37,7 +36,7 @@ extern "Rust" { /// 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 given HART ID. + /// 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 @@ -45,10 +44,10 @@ extern "Rust" { /// 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 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. + /// 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 /// @@ -75,7 +74,7 @@ fn schedule_machine_timer(mtime: MTIME, mtimercmp: MTIMECMP) { 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 again if necessary + unsafe { riscv::register::mie::set_mtimer() }; // enable machine timer interrupts } } @@ -89,7 +88,6 @@ fn schedule_machine_timer(mtime: MTIME, mtimercmp: MTIMECMP) { /// Additionally, the rest of the application must not modify the [`MTIMER`] register assigned to the current HART. #[derive(Clone)] pub struct Delay { - hart_id: usize, freq: usize, mtime: MTIME, mtimecmp: MTIMECMP, @@ -99,11 +97,9 @@ impl Delay { /// Creates a new `Delay` instance for the current HART. #[inline] pub fn new(freq: usize) -> Self { - let hart_id = riscv::register::mhartid::read(); let mtimer = unsafe { _riscv_peripheral_aclint_mtimer() }; let (mtime, mtimecmp) = (mtimer.mtime, mtimer.mtimecmp_mhartid()); Self { - hart_id, freq, mtime, mtimecmp, @@ -148,7 +144,6 @@ impl DelayNs for Delay { /// this entry provides the necessary information to adapt it to the timer queue implementation. #[derive(Debug)] pub struct Timer { - hart_id: usize, freq: usize, mtime: MTIME, mtimecmp: MTIMECMP, @@ -160,7 +155,6 @@ impl Timer { /// Creates a new timer queue entry. #[inline] const fn new( - hart_id: usize, freq: usize, mtime: MTIME, mtimecmp: MTIMECMP, @@ -168,7 +162,6 @@ impl Timer { waker: Waker, ) -> Self { Self { - hart_id, freq, mtime, mtimecmp, @@ -177,12 +170,6 @@ impl Timer { } } - /// Returns the HART ID associated with this timer. - #[inline] - pub const fn hart_id(&self) -> usize { - self.hart_id - } - /// Returns the frequency of the [`MTIME`] register associated with this timer. #[inline] pub const fn freq(&self) -> usize { @@ -216,7 +203,7 @@ impl Timer { impl PartialEq for Timer { fn eq(&self, other: &Self) -> bool { - self.hart_id == other.hart_id && self.freq == other.freq && self.expires == other.expires + self.freq == other.freq && self.expires == other.expires } } @@ -262,7 +249,6 @@ impl<'a> Future for DelayAsync<'a> { // we only push the timer to the queue the first time we poll self.pushed = true; let timer = Timer::new( - self.delay.hart_id, self.delay.freq, self.delay.mtime, self.delay.mtimecmp, diff --git a/riscv-peripheral/src/macros.rs b/riscv-peripheral/src/macros.rs index 1d96339f..f4c6e8b4 100644 --- a/riscv-peripheral/src/macros.rs +++ b/riscv-peripheral/src/macros.rs @@ -214,12 +214,6 @@ macro_rules! clint_codegen { $crate::clint_codegen!($($tail)*); }; (async_delay, $($tail:tt)*) => { - - #[no_mangle] - const fn _riscv_peripheral_aclint_mtimer() -> $crate::aclint::mtimer::MTIMER { - CLINT::mtimer() - } - impl CLINT { /// Asynchronous delay implementation for CLINT peripherals. ///