Skip to content

Commit 5b012c0

Browse files
committed
more flexible async implementation
1 parent 552063c commit 5b012c0

File tree

4 files changed

+139
-121
lines changed

4 files changed

+139
-121
lines changed

riscv-peripheral/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ riscv-pac = { path = "../riscv-pac", version = "0.1.0" }
1515
aclint-hal-async = ["embedded-hal-async"]
1616

1717
[package.metadata.docs.rs]
18+
all-features = true
1819
default-target = "riscv64imac-unknown-none-elf"
1920
targets = [
2021
"riscv32i-unknown-none-elf", "riscv32imc-unknown-none-elf", "riscv32imac-unknown-none-elf",

riscv-peripheral/src/hal_async/aclint.rs

Lines changed: 126 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,22 @@
11
//! Asynchronous delay implementation for the (A)CLINT peripheral.
2+
//!
3+
//! # Note
4+
//!
5+
//! The asynchronous delay implementation for the (A)CLINT peripheral relies on the machine-level timer interrupts.
6+
//! Therefore, it needs to schedule the machine-level timer interrupts via the [`MTIMECMP`] register assigned to the current HART.
7+
//! Thus, the [`Delay`] instance must be created on the same HART that is used to call the asynchronous delay methods.
8+
//!
9+
//! # Requirements
10+
//!
11+
//! The following `extern "Rust"` functions must be implemented:
12+
//!
13+
//! - `fn _riscv_peripheral_aclint_mtimer(hart_id: usize) -> MTIMER`: This function returns the `MTIMER` register for the given HART ID.
14+
//! - `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.
15+
//! If it fails (e.g., the timer queue is full), it returns back the timer that failed to be pushed.
16+
//! The logic of timer queues are application-specific and are not provided by this crate.
17+
//! - `fn _riscv_peripheral_aclint_wake_timers(hart_id: usize, current_tick: u64) -> Option<u64>`:
18+
//! This function pops all the expired timers from a timer queue assigned to the given HART ID and wakes their associated wakers.
19+
//! The function returns the next [`MTIME`] tick at which the next timer expires. If the queue is empty, it returns `None`.
220
321
use crate::aclint::mtimer::{MTIME, MTIMECMP, MTIMER};
422
pub use crate::hal_async::delay::DelayNs;
@@ -21,101 +39,76 @@ extern "Rust" {
2139
/// Tries to push a new timer to the timer queue assigned to the given HART ID.
2240
/// If it fails (e.g., the timer queue is full), it returns back the timer that failed to be pushed.
2341
///
24-
/// # Note
25-
///
26-
/// the [`Delay`] reference allows to access the `MTIME` and `MTIMECMP` registers,
27-
/// as well as handy information such as the HART ID or the clock frequency of the `MTIMER` peripheral.
28-
///
2942
/// # Safety
3043
///
3144
/// Do not call this function directly. It is only meant to be called by [`DelayAsync`].
32-
fn _riscv_peripheral_push_timer(hart_id: usize, delay: &Delay, t: Timer) -> Result<(), Timer>;
45+
fn _riscv_peripheral_aclint_push_timer(t: Timer) -> Result<(), Timer>;
3346

34-
/// Pops a expired timer from the timer queue assigned to the given HART ID.
35-
/// If the queue is empty, it returns `Err(None)`.
47+
/// Pops all the expired timers from the timer queue assigned to the given HART ID and wakes their associated wakers.
48+
/// Once it is done, if the queue is empty, it returns `None`.
3649
/// Alternatively, if the queue is not empty but the earliest timer has not expired yet,
37-
/// it returns `Err(Some(next_expires))` where `next_expires` is the tick at which this timer expires.
50+
/// it returns `Some(next_expires)` where `next_expires` is the tick at which this timer expires.
3851
///
3952
/// # Safety
4053
///
41-
/// It is extremely important that this function only returns a timer that has expired.
42-
/// Otherwise, the timer will be lost and the waker will never be called.
43-
///
4454
/// Do not call this function directly. It is only meant to be called by [`MachineExternal`] and [`DelayAsync`].
45-
fn _riscv_peripheral_pop_timer(hart_id: usize, current_tick: u64)
46-
-> Result<Timer, Option<u64>>;
55+
fn _riscv_peripheral_aclint_wake_timers(hart_id: usize, current_tick: u64) -> Option<u64>;
4756
}
4857

49-
/// Machine-level timer interrupt handler.
50-
/// This handler is triggered whenever the `MTIME` register reaches the value of the `MTIMECMP` register.
58+
/// Machine-level timer interrupt handler. This handler is triggered whenever the `MTIME`
59+
/// register reaches the value of the `MTIMECMP` register of the current HART.
5160
#[no_mangle]
5261
#[allow(non_snake_case)]
5362
fn MachineExternal() {
63+
// recover the MTIME and MTIMECMP registers for the current HART
5464
let hart_id = riscv::register::mhartid::read();
5565
let mtimer = unsafe { _riscv_peripheral_aclint_mtimer(hart_id) };
5666
let (mtime, mtimercmp) = (mtimer.mtime, mtimer.mtimecmp_mhartid());
57-
schedule_machine_external(hart_id, mtime, mtimercmp);
67+
// schedule the next machine timer interrupt
68+
schedule_machine_timer(hart_id, mtime, mtimercmp);
5869
}
5970

60-
fn schedule_machine_external(hart_id: usize, mtime: MTIME, mtimercmp: MTIMECMP) {
71+
/// Schedules the next machine timer interrupt for the given HART ID according to the timer queue.
72+
fn schedule_machine_timer(hart_id: usize, mtime: MTIME, mtimercmp: MTIMECMP) {
6173
unsafe { riscv::register::mie::clear_mtimer() }; // disable machine timer interrupts to avoid reentrancy
62-
loop {
63-
let current_tick = mtime.read();
64-
let timer = unsafe { _riscv_peripheral_pop_timer(hart_id, current_tick) };
65-
match timer {
66-
Ok(timer) => {
67-
debug_assert!(timer.expires() <= current_tick);
68-
timer.wake();
69-
}
70-
Err(e) => {
71-
if let Some(next_expires) = e {
72-
debug_assert!(next_expires > current_tick);
73-
mtimercmp.write(next_expires); // schedule next interrupt at next_expires
74-
unsafe { riscv::register::mie::set_mtimer() }; // enable machine timer interrupts again
75-
} else {
76-
mtimercmp.write(u64::MAX); // write max to clear and "disable" the interrupt
77-
}
78-
break;
79-
}
80-
}
74+
let current_tick = mtime.read();
75+
if let Some(next_expires) =
76+
unsafe { _riscv_peripheral_aclint_wake_timers(hart_id, current_tick) }
77+
{
78+
debug_assert!(next_expires > current_tick);
79+
mtimercmp.write(next_expires); // schedule next interrupt at next_expires
80+
unsafe { riscv::register::mie::set_mtimer() }; // enable machine timer interrupts again if necessary
8181
}
8282
}
8383

8484
/// Asynchronous delay implementation for (A)CLINT peripherals.
85+
///
86+
/// # Note
87+
///
88+
/// The asynchronous delay implementation for (A)CLINT peripherals relies on the machine-level timer interrupts.
89+
/// Therefore, it needs to schedule the machine-level timer interrupts via the [`MTIMECMP`] register assigned to the current HART.
90+
/// Thus, the [`Delay`] instance must be created on the same HART that is used to call the asynchronous delay methods.
91+
/// Additionally, the rest of the application must not modify the [`MTIMER`] register assigned to the current HART.
8592
#[derive(Clone)]
8693
pub struct Delay {
87-
mtime: MTIME,
8894
hart_id: usize,
89-
mtimecmp: MTIMECMP,
9095
freq: usize,
96+
mtime: MTIME,
97+
mtimecmp: MTIMECMP,
9198
}
9299

93100
impl Delay {
94-
/// Creates a new `Delay` instance.
95-
#[inline]
96-
pub fn new<H: riscv_pac::HartIdNumber>(mtimer: MTIMER, hart_id: H, freq: usize) -> Self {
97-
Self {
98-
mtime: mtimer.mtime,
99-
hart_id: hart_id.number() as _,
100-
mtimecmp: mtimer.mtimecmp(hart_id),
101-
freq,
102-
}
103-
}
104-
105101
/// Creates a new `Delay` instance for the current HART.
106-
/// This function determines the current HART ID by reading the [`riscv::register::mhartid`] CSR.
107-
///
108-
/// # Note
109-
///
110-
/// This function can only be used in M-mode. For S-mode, use [`Delay::new_mhartid`] instead.
111102
#[inline]
112-
pub fn new_mhartid(mtimer: MTIMER, freq: usize) -> Self {
103+
pub fn new(freq: usize) -> Self {
113104
let hart_id = riscv::register::mhartid::read();
105+
let mtimer = unsafe { _riscv_peripheral_aclint_mtimer(hart_id) };
106+
let (mtime, mtimecmp) = (mtimer.mtime, mtimer.mtimecmp_mhartid());
114107
Self {
115-
mtime: mtimer.mtime,
116108
hart_id,
117-
mtimecmp: mtimer.mtimecmp_mhartid(),
118109
freq,
110+
mtime,
111+
mtimecmp,
119112
}
120113
}
121114

@@ -130,38 +123,84 @@ impl Delay {
130123
pub fn set_freq(&mut self, freq: usize) {
131124
self.freq = freq;
132125
}
126+
}
133127

134-
/// Returns the `MTIME` register.
128+
impl DelayNs for Delay {
135129
#[inline]
136-
pub const fn get_mtime(&self) -> MTIME {
137-
self.mtime
130+
async fn delay_ns(&mut self, ns: u32) {
131+
let n_ticks = ns as u64 * self.get_freq() as u64 / 1_000_000_000;
132+
DelayAsync::new(self, n_ticks).await;
138133
}
139134

140-
/// Returns the `MTIMECMP` register.
141135
#[inline]
142-
pub const fn get_mtimecmp(&self) -> MTIMECMP {
143-
self.mtimecmp
136+
async fn delay_us(&mut self, us: u32) {
137+
let n_ticks = us as u64 * self.get_freq() as u64 / 1_000_000;
138+
DelayAsync::new(self, n_ticks).await;
144139
}
145140

146-
/// Returns the hart ID.
147141
#[inline]
148-
pub const fn get_hart_id(&self) -> usize {
149-
self.hart_id
142+
async fn delay_ms(&mut self, ms: u32) {
143+
let n_ticks = ms as u64 * self.get_freq() as u64 / 1_000;
144+
DelayAsync::new(self, n_ticks).await;
150145
}
151146
}
152147

153148
/// Timer queue entry.
149+
/// When pushed to the timer queue via the `_riscv_peripheral_aclint_push_timer` function,
150+
/// this entry provides the necessary information to adapt it to the timer queue implementation.
154151
#[derive(Debug)]
155152
pub struct Timer {
153+
hart_id: usize,
154+
freq: usize,
155+
mtime: MTIME,
156+
mtimecmp: MTIMECMP,
156157
expires: u64,
157158
waker: Waker,
158159
}
159160

160161
impl Timer {
161162
/// Creates a new timer queue entry.
162163
#[inline]
163-
pub fn new(expires: u64, waker: Waker) -> Self {
164-
Self { expires, waker }
164+
const fn new(
165+
hart_id: usize,
166+
freq: usize,
167+
mtime: MTIME,
168+
mtimecmp: MTIMECMP,
169+
expires: u64,
170+
waker: Waker,
171+
) -> Self {
172+
Self {
173+
hart_id,
174+
freq,
175+
mtime,
176+
mtimecmp,
177+
expires,
178+
waker,
179+
}
180+
}
181+
182+
/// Returns the HART ID associated with this timer.
183+
#[inline]
184+
pub const fn hart_id(&self) -> usize {
185+
self.hart_id
186+
}
187+
188+
/// Returns the frequency of the [`MTIME`] register associated with this timer.
189+
#[inline]
190+
pub const fn freq(&self) -> usize {
191+
self.freq
192+
}
193+
194+
/// Returns the [`MTIME`] register associated with this timer.
195+
#[inline]
196+
pub const fn mtime(&self) -> MTIME {
197+
self.mtime
198+
}
199+
200+
/// Returns the [`MTIMECMP`] register associated with this timer.
201+
#[inline]
202+
pub const fn mtimecmp(&self) -> MTIMECMP {
203+
self.mtimecmp
165204
}
166205

167206
/// Returns the tick at which the timer expires.
@@ -170,16 +209,16 @@ impl Timer {
170209
self.expires
171210
}
172211

173-
/// Wakes the waker associated with this timer.
212+
/// Returns the waker associated with this timer.
174213
#[inline]
175-
pub fn wake(&self) {
176-
self.waker.wake_by_ref();
214+
pub fn waker(&self) -> Waker {
215+
self.waker.clone()
177216
}
178217
}
179218

180219
impl PartialEq for Timer {
181220
fn eq(&self, other: &Self) -> bool {
182-
self.expires == other.expires
221+
self.hart_id == other.hart_id && self.freq == other.freq && self.expires == other.expires
183222
}
184223
}
185224

@@ -197,14 +236,14 @@ impl PartialOrd for Timer {
197236
}
198237
}
199238

200-
struct DelayAsync {
201-
delay: Delay,
239+
struct DelayAsync<'a> {
240+
delay: &'a Delay,
202241
expires: u64,
203242
pushed: bool,
204243
}
205244

206-
impl DelayAsync {
207-
pub fn new(delay: Delay, n_ticks: u64) -> Self {
245+
impl<'a> DelayAsync<'a> {
246+
pub fn new(delay: &'a Delay, n_ticks: u64) -> Self {
208247
let t0 = delay.mtime.read();
209248
let expires = t0.wrapping_add(n_ticks);
210249
Self {
@@ -215,7 +254,7 @@ impl DelayAsync {
215254
}
216255
}
217256

218-
impl Future for DelayAsync {
257+
impl<'a> Future for DelayAsync<'a> {
219258
type Output = ();
220259

221260
#[inline]
@@ -224,41 +263,23 @@ impl Future for DelayAsync {
224263
if !self.pushed {
225264
// we only push the timer to the queue the first time we poll
226265
self.pushed = true;
227-
let timer = Timer::new(self.expires, cx.waker().clone());
228-
unsafe {
229-
_riscv_peripheral_push_timer(self.delay.hart_id, &self.delay, timer)
230-
.expect("timer queue is full");
231-
};
232-
// we also need to schedule the interrupt if the timer we just pushed is the earliest one
233-
schedule_machine_external(
266+
let timer = Timer::new(
234267
self.delay.hart_id,
268+
self.delay.freq,
235269
self.delay.mtime,
236270
self.delay.mtimecmp,
271+
self.expires,
272+
cx.waker().clone(),
237273
);
274+
unsafe {
275+
_riscv_peripheral_aclint_push_timer(timer).expect("timer queue is full");
276+
};
277+
// we also need to reschedule the machine timer interrupt
278+
schedule_machine_timer(self.delay.hart_id, self.delay.mtime, self.delay.mtimecmp);
238279
}
239280
Poll::Pending
240281
} else {
241282
Poll::Ready(())
242283
}
243284
}
244285
}
245-
246-
impl DelayNs for Delay {
247-
#[inline]
248-
async fn delay_ns(&mut self, ns: u32) {
249-
let n_ticks = ns as u64 * self.get_freq() as u64 / 1_000_000_000;
250-
DelayAsync::new(self.clone(), n_ticks).await;
251-
}
252-
253-
#[inline]
254-
async fn delay_us(&mut self, us: u32) {
255-
let n_ticks = us as u64 * self.get_freq() as u64 / 1_000_000;
256-
DelayAsync::new(self.clone(), n_ticks).await;
257-
}
258-
259-
#[inline]
260-
async fn delay_ms(&mut self, ms: u32) {
261-
let n_ticks = ms as u64 * self.get_freq() as u64 / 1_000;
262-
DelayAsync::new(self.clone(), n_ticks).await;
263-
}
264-
}

riscv-peripheral/src/lib.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
//! Standard RISC-V peripherals for embedded systems written in Rust
1+
//! Standard RISC-V peripherals for embedded systems written in Rust.
2+
//!
3+
//! ## Features
4+
//!
5+
//! - `aclint-hal-async`: enables the [`hal_async::delay::DelayNs`] implementation for the ACLINT peripheral.
6+
//! This feature relies on external functions that must be provided by the user. See [`hal_async::aclint`] for more information.
27
38
#![deny(missing_docs)]
49
#![no_std]

0 commit comments

Comments
 (0)