1
1
//! Asynchronous delay implementation for the (A)CLINT peripheral.
2
2
3
- use crate :: aclint:: mtimer:: MTIME ;
4
- pub use crate :: hal:: aclint:: Delay ;
3
+ use crate :: aclint:: mtimer:: { MTIME , MTIMECMP , MTIMER } ;
5
4
pub use crate :: hal_async:: delay:: DelayNs ;
6
5
use core:: {
6
+ cmp:: { Eq , Ord , PartialEq , PartialOrd } ,
7
7
future:: Future ,
8
8
pin:: Pin ,
9
9
task:: { Context , Poll , Waker } ,
10
10
} ;
11
11
12
- struct DelayAsync {
12
+ extern "Rust" {
13
+ /// Returns the `MTIMER` register for the given HART ID.
14
+ /// This is necessary for [`MachineExternal`] to obtain the corresponding `MTIMER` register.
15
+ ///
16
+ /// # Safety
17
+ ///
18
+ /// Do not call this function directly. It is only meant to be called by [`MachineExternal`].
19
+ fn _riscv_peripheral_aclint_mtimer ( hart_id : usize ) -> MTIMER ;
20
+
21
+ /// Tries to push a new timer to the timer queue assigned to the given HART ID.
22
+ /// If it fails (e.g., the timer queue is full), it returns back the timer that failed to be pushed.
23
+ ///
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
+ /// # Safety
30
+ ///
31
+ /// 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 > ;
33
+
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)`.
36
+ /// 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.
38
+ ///
39
+ /// # Safety
40
+ ///
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
+ /// 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 > > ;
47
+ }
48
+
49
+ /// Machine-level timer interrupt handler.
50
+ /// This handler is triggered whenever the `MTIME` register reaches the value of the `MTIMECMP` register.
51
+ #[ no_mangle]
52
+ #[ allow( non_snake_case) ]
53
+ fn MachineExternal ( ) {
54
+ let hart_id = riscv:: register:: mhartid:: read ( ) ;
55
+ let mtimer = unsafe { _riscv_peripheral_aclint_mtimer ( hart_id) } ;
56
+ let ( mtime, mtimercmp) = ( mtimer. mtime , mtimer. mtimecmp_mhartid ( ) ) ;
57
+ schedule_machine_external ( hart_id, mtime, mtimercmp) ;
58
+ }
59
+
60
+ fn schedule_machine_external ( hart_id : usize , mtime : MTIME , mtimercmp : MTIMECMP ) {
61
+ 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
+ }
81
+ }
82
+ }
83
+
84
+ /// Asynchronous delay implementation for (A)CLINT peripherals.
85
+ #[ derive( Clone ) ]
86
+ pub struct Delay {
13
87
mtime : MTIME ,
14
- t0 : u64 ,
15
- n_ticks : u64 ,
16
- waker : Option < Waker > ,
88
+ hart_id : usize ,
89
+ mtimecmp : MTIMECMP ,
90
+ freq : usize ,
91
+ }
92
+
93
+ 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
+ /// 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
+ #[ inline]
112
+ pub fn new_mhartid ( mtimer : MTIMER , freq : usize ) -> Self {
113
+ let hart_id = riscv:: register:: mhartid:: read ( ) ;
114
+ Self {
115
+ mtime : mtimer. mtime ,
116
+ hart_id,
117
+ mtimecmp : mtimer. mtimecmp_mhartid ( ) ,
118
+ freq,
119
+ }
120
+ }
121
+
122
+ /// Returns the frequency of the `MTIME` register.
123
+ #[ inline]
124
+ pub const fn get_freq ( & self ) -> usize {
125
+ self . freq
126
+ }
127
+
128
+ /// Sets the frequency of the `MTIME` register.
129
+ #[ inline]
130
+ pub fn set_freq ( & mut self , freq : usize ) {
131
+ self . freq = freq;
132
+ }
133
+
134
+ /// Returns the `MTIME` register.
135
+ #[ inline]
136
+ pub const fn get_mtime ( & self ) -> MTIME {
137
+ self . mtime
138
+ }
139
+
140
+ /// Returns the `MTIMECMP` register.
141
+ #[ inline]
142
+ pub const fn get_mtimecmp ( & self ) -> MTIMECMP {
143
+ self . mtimecmp
144
+ }
145
+
146
+ /// Returns the hart ID.
147
+ #[ inline]
148
+ pub const fn get_hart_id ( & self ) -> usize {
149
+ self . hart_id
150
+ }
151
+ }
152
+
153
+ /// Timer queue entry.
154
+ #[ derive( Debug ) ]
155
+ pub struct Timer {
156
+ expires : u64 ,
157
+ waker : Waker ,
158
+ }
159
+
160
+ impl Timer {
161
+ /// Creates a new timer queue entry.
162
+ #[ inline]
163
+ pub fn new ( expires : u64 , waker : Waker ) -> Self {
164
+ Self { expires, waker }
165
+ }
166
+
167
+ /// Returns the tick at which the timer expires.
168
+ #[ inline]
169
+ pub const fn expires ( & self ) -> u64 {
170
+ self . expires
171
+ }
172
+
173
+ /// Wakes the waker associated with this timer.
174
+ #[ inline]
175
+ pub fn wake ( & self ) {
176
+ self . waker . wake_by_ref ( ) ;
177
+ }
178
+ }
179
+
180
+ impl PartialEq for Timer {
181
+ fn eq ( & self , other : & Self ) -> bool {
182
+ self . expires == other. expires
183
+ }
184
+ }
185
+
186
+ impl Eq for Timer { }
187
+
188
+ impl Ord for Timer {
189
+ fn cmp ( & self , other : & Self ) -> core:: cmp:: Ordering {
190
+ self . expires . cmp ( & other. expires )
191
+ }
192
+ }
193
+
194
+ impl PartialOrd for Timer {
195
+ fn partial_cmp ( & self , other : & Self ) -> Option < core:: cmp:: Ordering > {
196
+ Some ( self . expires . cmp ( & other. expires ) )
197
+ }
198
+ }
199
+
200
+ struct DelayAsync {
201
+ delay : Delay ,
202
+ expires : u64 ,
203
+ pushed : bool ,
17
204
}
18
205
19
206
impl DelayAsync {
20
- pub fn new ( mtime : MTIME , n_ticks : u64 ) -> Self {
21
- let t0 = mtime. read ( ) ;
207
+ pub fn new ( delay : Delay , n_ticks : u64 ) -> Self {
208
+ let t0 = delay. mtime . read ( ) ;
209
+ let expires = t0. wrapping_add ( n_ticks) ;
22
210
Self {
23
- mtime,
24
- t0,
25
- n_ticks,
26
- waker : None ,
211
+ delay,
212
+ expires,
213
+ pushed : false ,
27
214
}
28
215
}
29
216
}
@@ -32,21 +219,26 @@ impl Future for DelayAsync {
32
219
type Output = ( ) ;
33
220
34
221
#[ inline]
35
- fn poll ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
36
- match self . mtime . read ( ) . wrapping_sub ( self . t0 ) < self . n_ticks {
37
- true => {
38
- self . get_mut ( ) . waker = Some ( cx. waker ( ) . clone ( ) ) ;
39
- Poll :: Pending
40
- }
41
- false => {
42
- if let Some ( waker) = self . get_mut ( ) . waker . take ( ) {
43
- waker. wake ( ) ;
44
- } else {
45
- // corner case: delay expired before polling for the first time
46
- cx. waker ( ) . wake_by_ref ( ) ;
222
+ fn poll ( mut self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
223
+ if self . delay . mtime . read ( ) < self . expires {
224
+ if !self . pushed {
225
+ // we only push the timer to the queue the first time we poll
226
+ 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" ) ;
47
231
} ;
48
- Poll :: Ready ( ( ) )
232
+ // we also need to schedule the interrupt if the timer we just pushed is the earliest one
233
+ schedule_machine_external (
234
+ self . delay . hart_id ,
235
+ self . delay . mtime ,
236
+ self . delay . mtimecmp ,
237
+ ) ;
49
238
}
239
+ Poll :: Pending
240
+ } else {
241
+ Poll :: Ready ( ( ) )
50
242
}
51
243
}
52
244
}
@@ -55,18 +247,18 @@ impl DelayNs for Delay {
55
247
#[ inline]
56
248
async fn delay_ns ( & mut self , ns : u32 ) {
57
249
let n_ticks = ns as u64 * self . get_freq ( ) as u64 / 1_000_000_000 ;
58
- DelayAsync :: new ( self . get_mtime ( ) , n_ticks) . await ;
250
+ DelayAsync :: new ( self . clone ( ) , n_ticks) . await ;
59
251
}
60
252
61
253
#[ inline]
62
254
async fn delay_us ( & mut self , us : u32 ) {
63
255
let n_ticks = us as u64 * self . get_freq ( ) as u64 / 1_000_000 ;
64
- DelayAsync :: new ( self . get_mtime ( ) , n_ticks) . await ;
256
+ DelayAsync :: new ( self . clone ( ) , n_ticks) . await ;
65
257
}
66
258
67
259
#[ inline]
68
260
async fn delay_ms ( & mut self , ms : u32 ) {
69
261
let n_ticks = ms as u64 * self . get_freq ( ) as u64 / 1_000 ;
70
- DelayAsync :: new ( self . get_mtime ( ) , n_ticks) . await ;
262
+ DelayAsync :: new ( self . clone ( ) , n_ticks) . await ;
71
263
}
72
264
}
0 commit comments