aboutsummaryrefslogtreecommitdiff
path: root/embassy-nrf
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-12-16 12:30:30 +0000
committerGitHub <[email protected]>2024-12-16 12:30:30 +0000
commit2c3bc75da6008afa7cacc1045954cef7e3d8740f (patch)
tree47661322d49d3e38717e2fc3f38e920c222138f7 /embassy-nrf
parent99ad61cecf4fe098feeced5524d3e60625137457 (diff)
parente1c00613288024623f7fde61f65c4c40c9a5381a (diff)
Merge pull request #3593 from bugadani/refactor
Rework time-driver contract.
Diffstat (limited to 'embassy-nrf')
-rw-r--r--embassy-nrf/Cargo.toml3
-rw-r--r--embassy-nrf/src/time_driver.rs194
2 files changed, 85 insertions, 112 deletions
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml
index 9da050a22..48f80bb5e 100644
--- a/embassy-nrf/Cargo.toml
+++ b/embassy-nrf/Cargo.toml
@@ -119,7 +119,7 @@ _nrf52 = ["_ppi"]
119_nrf51 = ["_ppi"] 119_nrf51 = ["_ppi"]
120_nrf91 = [] 120_nrf91 = []
121 121
122_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768"] 122_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768", "dep:embassy-time-queue-driver"]
123 123
124# trustzone state. 124# trustzone state.
125_s = [] 125_s = []
@@ -135,6 +135,7 @@ _nrf52832_anomaly_109 = []
135 135
136[dependencies] 136[dependencies]
137embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } 137embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true }
138embassy-time-queue-driver = { version = "0.1", path = "../embassy-time-queue-driver", optional = true }
138embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true } 139embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true }
139embassy-sync = { version = "0.6.1", path = "../embassy-sync" } 140embassy-sync = { version = "0.6.1", path = "../embassy-sync" }
140embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } 141embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] }
diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs
index 9ba38ec1b..a27fae9a8 100644
--- a/embassy-nrf/src/time_driver.rs
+++ b/embassy-nrf/src/time_driver.rs
@@ -1,11 +1,11 @@
1use core::cell::Cell; 1use core::cell::{Cell, RefCell};
2use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering}; 2use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
3use core::{mem, ptr};
4 3
5use critical_section::CriticalSection; 4use critical_section::CriticalSection;
6use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 5use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
7use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; 6use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;
8use embassy_time_driver::{AlarmHandle, Driver}; 7use embassy_time_driver::Driver;
8use embassy_time_queue_driver::Queue;
9 9
10use crate::interrupt::InterruptExt; 10use crate::interrupt::InterruptExt;
11use crate::{interrupt, pac}; 11use crate::{interrupt, pac};
@@ -94,11 +94,6 @@ mod test {
94 94
95struct AlarmState { 95struct AlarmState {
96 timestamp: Cell<u64>, 96 timestamp: Cell<u64>,
97
98 // This is really a Option<(fn(*mut ()), *mut ())>
99 // but fn pointers aren't allowed in const yet
100 callback: Cell<*const ()>,
101 ctx: Cell<*mut ()>,
102} 97}
103 98
104unsafe impl Send for AlarmState {} 99unsafe impl Send for AlarmState {}
@@ -107,26 +102,22 @@ impl AlarmState {
107 const fn new() -> Self { 102 const fn new() -> Self {
108 Self { 103 Self {
109 timestamp: Cell::new(u64::MAX), 104 timestamp: Cell::new(u64::MAX),
110 callback: Cell::new(ptr::null()),
111 ctx: Cell::new(ptr::null_mut()),
112 } 105 }
113 } 106 }
114} 107}
115 108
116const ALARM_COUNT: usize = 3;
117
118struct RtcDriver { 109struct RtcDriver {
119 /// Number of 2^23 periods elapsed since boot. 110 /// Number of 2^23 periods elapsed since boot.
120 period: AtomicU32, 111 period: AtomicU32,
121 alarm_count: AtomicU8,
122 /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. 112 /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
123 alarms: Mutex<[AlarmState; ALARM_COUNT]>, 113 alarms: Mutex<AlarmState>,
114 queue: Mutex<RefCell<Queue>>,
124} 115}
125 116
126embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { 117embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
127 period: AtomicU32::new(0), 118 period: AtomicU32::new(0),
128 alarm_count: AtomicU8::new(0), 119 alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
129 alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [const {AlarmState::new()}; ALARM_COUNT]), 120 queue: Mutex::new(RefCell::new(Queue::new())),
130}); 121});
131 122
132impl RtcDriver { 123impl RtcDriver {
@@ -169,13 +160,12 @@ impl RtcDriver {
169 self.next_period(); 160 self.next_period();
170 } 161 }
171 162
172 for n in 0..ALARM_COUNT { 163 let n = 0;
173 if r.events_compare(n).read() == 1 { 164 if r.events_compare(n).read() == 1 {
174 r.events_compare(n).write_value(0); 165 r.events_compare(n).write_value(0);
175 critical_section::with(|cs| { 166 critical_section::with(|cs| {
176 self.trigger_alarm(n, cs); 167 self.trigger_alarm(cs);
177 }) 168 });
178 }
179 } 169 }
180 } 170 }
181 171
@@ -186,38 +176,80 @@ impl RtcDriver {
186 self.period.store(period, Ordering::Relaxed); 176 self.period.store(period, Ordering::Relaxed);
187 let t = (period as u64) << 23; 177 let t = (period as u64) << 23;
188 178
189 for n in 0..ALARM_COUNT { 179 let n = 0;
190 let alarm = &self.alarms.borrow(cs)[n]; 180 let alarm = &self.alarms.borrow(cs);
191 let at = alarm.timestamp.get(); 181 let at = alarm.timestamp.get();
192 182
193 if at < t + 0xc00000 { 183 if at < t + 0xc00000 {
194 // just enable it. `set_alarm` has already set the correct CC val. 184 // just enable it. `set_alarm` has already set the correct CC val.
195 r.intenset().write(|w| w.0 = compare_n(n)); 185 r.intenset().write(|w| w.0 = compare_n(n));
196 }
197 } 186 }
198 }) 187 })
199 } 188 }
200 189
201 fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState { 190 fn trigger_alarm(&self, cs: CriticalSection) {
202 // safety: we're allowed to assume the AlarmState is created by us, and 191 let n = 0;
203 // we never create one that's out of bounds.
204 unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) }
205 }
206
207 fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
208 let r = rtc(); 192 let r = rtc();
209 r.intenclr().write(|w| w.0 = compare_n(n)); 193 r.intenclr().write(|w| w.0 = compare_n(n));
210 194
211 let alarm = &self.alarms.borrow(cs)[n]; 195 let alarm = &self.alarms.borrow(cs);
212 alarm.timestamp.set(u64::MAX); 196 alarm.timestamp.set(u64::MAX);
213 197
214 // Call after clearing alarm, so the callback can set another alarm. 198 // Call after clearing alarm, so the callback can set another alarm.
199 let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
200 while !self.set_alarm(cs, next) {
201 next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
202 }
203 }
204
205 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
206 let n = 0;
207 let alarm = &self.alarms.borrow(cs);
208 alarm.timestamp.set(timestamp);
209
210 let r = rtc();
211
212 let t = self.now();
213 if timestamp <= t {
214 // If alarm timestamp has passed the alarm will not fire.
215 // Disarm the alarm and return `false` to indicate that.
216 r.intenclr().write(|w| w.0 = compare_n(n));
217
218 alarm.timestamp.set(u64::MAX);
219
220 return false;
221 }
222
223 // If it hasn't triggered yet, setup it in the compare channel.
224
225 // Write the CC value regardless of whether we're going to enable it now or not.
226 // This way, when we enable it later, the right value is already set.
227
228 // nrf52 docs say:
229 // If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event.
230 // To workaround this, we never write a timestamp smaller than N+3.
231 // N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc.
232 //
233 // It is impossible for rtc to tick more than once because
234 // - this code takes less time than 1 tick
235 // - it runs with interrupts disabled so nothing else can preempt it.
236 //
237 // This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed
238 // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
239 // and we don't do that here.
240 let safe_timestamp = timestamp.max(t + 3);
241 r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF));
242
243 let diff = timestamp - t;
244 if diff < 0xc00000 {
245 r.intenset().write(|w| w.0 = compare_n(n));
246 } else {
247 // If it's too far in the future, don't setup the compare channel yet.
248 // It will be setup later by `next_period`.
249 r.intenclr().write(|w| w.0 = compare_n(n));
250 }
215 251
216 // safety: 252 true
217 // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`.
218 // - other than that we only store valid function pointers into alarm.callback
219 let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
220 f(alarm.ctx.get());
221 } 253 }
222} 254}
223 255
@@ -230,76 +262,16 @@ impl Driver for RtcDriver {
230 calc_now(period, counter) 262 calc_now(period, counter)
231 } 263 }
232 264
233 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { 265 fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
234 critical_section::with(|_| {
235 let id = self.alarm_count.load(Ordering::Relaxed);
236 if id < ALARM_COUNT as u8 {
237 self.alarm_count.store(id + 1, Ordering::Relaxed);
238 Some(AlarmHandle::new(id))
239 } else {
240 None
241 }
242 })
243 }
244
245 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
246 critical_section::with(|cs| {
247 let alarm = self.get_alarm(cs, alarm);
248
249 alarm.callback.set(callback as *const ());
250 alarm.ctx.set(ctx);
251 })
252 }
253
254 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
255 critical_section::with(|cs| { 266 critical_section::with(|cs| {
256 let n = alarm.id() as _; 267 let mut queue = self.queue.borrow(cs).borrow_mut();
257 let alarm = self.get_alarm(cs, alarm);
258 alarm.timestamp.set(timestamp);
259 268
260 let r = rtc(); 269 if queue.schedule_wake(at, waker) {
261 270 let mut next = queue.next_expiration(self.now());
262 let t = self.now(); 271 while !self.set_alarm(cs, next) {
263 if timestamp <= t { 272 next = queue.next_expiration(self.now());
264 // If alarm timestamp has passed the alarm will not fire. 273 }
265 // Disarm the alarm and return `false` to indicate that.
266 r.intenclr().write(|w| w.0 = compare_n(n));
267
268 alarm.timestamp.set(u64::MAX);
269
270 return false;
271 }
272
273 // If it hasn't triggered yet, setup it in the compare channel.
274
275 // Write the CC value regardless of whether we're going to enable it now or not.
276 // This way, when we enable it later, the right value is already set.
277
278 // nrf52 docs say:
279 // If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event.
280 // To workaround this, we never write a timestamp smaller than N+3.
281 // N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc.
282 //
283 // It is impossible for rtc to tick more than once because
284 // - this code takes less time than 1 tick
285 // - it runs with interrupts disabled so nothing else can preempt it.
286 //
287 // This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed
288 // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
289 // and we don't do that here.
290 let safe_timestamp = timestamp.max(t + 3);
291 r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF));
292
293 let diff = timestamp - t;
294 if diff < 0xc00000 {
295 r.intenset().write(|w| w.0 = compare_n(n));
296 } else {
297 // If it's too far in the future, don't setup the compare channel yet.
298 // It will be setup later by `next_period`.
299 r.intenclr().write(|w| w.0 = compare_n(n));
300 } 274 }
301
302 true
303 }) 275 })
304 } 276 }
305} 277}