1
1
//! 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`.
2
20
3
21
use crate :: aclint:: mtimer:: { MTIME , MTIMECMP , MTIMER } ;
4
22
pub use crate :: hal_async:: delay:: DelayNs ;
@@ -21,101 +39,76 @@ extern "Rust" {
21
39
/// Tries to push a new timer to the timer queue assigned to the given HART ID.
22
40
/// If it fails (e.g., the timer queue is full), it returns back the timer that failed to be pushed.
23
41
///
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
- ///
29
42
/// # Safety
30
43
///
31
44
/// 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 > ;
33
46
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`.
36
49
/// 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.
38
51
///
39
52
/// # Safety
40
53
///
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
- ///
44
54
/// 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 > ;
47
56
}
48
57
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 .
51
60
#[ no_mangle]
52
61
#[ allow( non_snake_case) ]
53
62
fn MachineExternal ( ) {
63
+ // recover the MTIME and MTIMECMP registers for the current HART
54
64
let hart_id = riscv:: register:: mhartid:: read ( ) ;
55
65
let mtimer = unsafe { _riscv_peripheral_aclint_mtimer ( hart_id) } ;
56
66
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) ;
58
69
}
59
70
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 ) {
61
73
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
81
81
}
82
82
}
83
83
84
84
/// 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.
85
92
#[ derive( Clone ) ]
86
93
pub struct Delay {
87
- mtime : MTIME ,
88
94
hart_id : usize ,
89
- mtimecmp : MTIMECMP ,
90
95
freq : usize ,
96
+ mtime : MTIME ,
97
+ mtimecmp : MTIMECMP ,
91
98
}
92
99
93
100
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
-
105
101
/// 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.
111
102
#[ inline]
112
- pub fn new_mhartid ( mtimer : MTIMER , freq : usize ) -> Self {
103
+ pub fn new ( freq : usize ) -> Self {
113
104
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 ( ) ) ;
114
107
Self {
115
- mtime : mtimer. mtime ,
116
108
hart_id,
117
- mtimecmp : mtimer. mtimecmp_mhartid ( ) ,
118
109
freq,
110
+ mtime,
111
+ mtimecmp,
119
112
}
120
113
}
121
114
@@ -130,38 +123,84 @@ impl Delay {
130
123
pub fn set_freq ( & mut self , freq : usize ) {
131
124
self . freq = freq;
132
125
}
126
+ }
133
127
134
- /// Returns the `MTIME` register.
128
+ impl DelayNs for Delay {
135
129
#[ 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 ;
138
133
}
139
134
140
- /// Returns the `MTIMECMP` register.
141
135
#[ 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 ;
144
139
}
145
140
146
- /// Returns the hart ID.
147
141
#[ 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 ;
150
145
}
151
146
}
152
147
153
148
/// 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.
154
151
#[ derive( Debug ) ]
155
152
pub struct Timer {
153
+ hart_id : usize ,
154
+ freq : usize ,
155
+ mtime : MTIME ,
156
+ mtimecmp : MTIMECMP ,
156
157
expires : u64 ,
157
158
waker : Waker ,
158
159
}
159
160
160
161
impl Timer {
161
162
/// Creates a new timer queue entry.
162
163
#[ 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
165
204
}
166
205
167
206
/// Returns the tick at which the timer expires.
@@ -170,16 +209,16 @@ impl Timer {
170
209
self . expires
171
210
}
172
211
173
- /// Wakes the waker associated with this timer.
212
+ /// Returns the waker associated with this timer.
174
213
#[ inline]
175
- pub fn wake ( & self ) {
176
- self . waker . wake_by_ref ( ) ;
214
+ pub fn waker ( & self ) -> Waker {
215
+ self . waker . clone ( )
177
216
}
178
217
}
179
218
180
219
impl PartialEq for Timer {
181
220
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
183
222
}
184
223
}
185
224
@@ -197,14 +236,14 @@ impl PartialOrd for Timer {
197
236
}
198
237
}
199
238
200
- struct DelayAsync {
201
- delay : Delay ,
239
+ struct DelayAsync < ' a > {
240
+ delay : & ' a Delay ,
202
241
expires : u64 ,
203
242
pushed : bool ,
204
243
}
205
244
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 {
208
247
let t0 = delay. mtime . read ( ) ;
209
248
let expires = t0. wrapping_add ( n_ticks) ;
210
249
Self {
@@ -215,7 +254,7 @@ impl DelayAsync {
215
254
}
216
255
}
217
256
218
- impl Future for DelayAsync {
257
+ impl < ' a > Future for DelayAsync < ' a > {
219
258
type Output = ( ) ;
220
259
221
260
#[ inline]
@@ -224,41 +263,23 @@ impl Future for DelayAsync {
224
263
if !self . pushed {
225
264
// we only push the timer to the queue the first time we poll
226
265
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 (
234
267
self . delay . hart_id ,
268
+ self . delay . freq ,
235
269
self . delay . mtime ,
236
270
self . delay . mtimecmp ,
271
+ self . expires ,
272
+ cx. waker ( ) . clone ( ) ,
237
273
) ;
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 ) ;
238
279
}
239
280
Poll :: Pending
240
281
} else {
241
282
Poll :: Ready ( ( ) )
242
283
}
243
284
}
244
285
}
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
- }
0 commit comments