diff options
Diffstat (limited to 'embassy-nrf/src/time_driver.rs')
| -rw-r--r-- | embassy-nrf/src/time_driver.rs | 156 |
1 files changed, 120 insertions, 36 deletions
diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index 03f4c2e2b..35f65bd64 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs | |||
| @@ -1,28 +1,35 @@ | |||
| 1 | use core::cell::{Cell, RefCell}; | 1 | use core::cell::{Cell, RefCell}; |
| 2 | use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; | 2 | #[cfg(not(feature = "_grtc"))] |
| 3 | use core::sync::atomic::{AtomicU32, Ordering, compiler_fence}; | ||
| 3 | 4 | ||
| 4 | use critical_section::CriticalSection; | 5 | use critical_section::CriticalSection; |
| 5 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 6 | use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; | 6 | use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; |
| 7 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 7 | use embassy_time_driver::Driver; | 8 | use embassy_time_driver::Driver; |
| 8 | use embassy_time_queue_utils::Queue; | 9 | use embassy_time_queue_utils::Queue; |
| 9 | 10 | ||
| 10 | use crate::interrupt::InterruptExt; | 11 | use crate::interrupt::InterruptExt; |
| 12 | #[cfg(feature = "_grtc")] | ||
| 13 | use crate::pac::grtc::vals::Busy; | ||
| 11 | use crate::{interrupt, pac}; | 14 | use crate::{interrupt, pac}; |
| 12 | 15 | ||
| 13 | #[cfg(feature = "_nrf54l")] | 16 | #[cfg(feature = "_grtc")] |
| 14 | fn rtc() -> pac::rtc::Rtc { | 17 | fn rtc() -> pac::grtc::Grtc { |
| 15 | pac::RTC30 | 18 | pac::GRTC |
| 16 | } | 19 | } |
| 17 | #[cfg(not(feature = "_nrf54l"))] | 20 | |
| 21 | #[cfg(not(feature = "_grtc"))] | ||
| 18 | fn rtc() -> pac::rtc::Rtc { | 22 | fn rtc() -> pac::rtc::Rtc { |
| 19 | pac::RTC1 | 23 | pac::RTC1 |
| 20 | } | 24 | } |
| 21 | 25 | ||
| 22 | /// Calculate the timestamp from the period count and the tick count. | 26 | /// Calculate the timestamp from the period count and the tick count. |
| 23 | /// | 27 | /// |
| 24 | /// The RTC counter is 24 bit. Ticking at 32768hz, it overflows every ~8 minutes. This is | 28 | /// For nRF54 devices and newer, the GRTC counter is 52 bits, so the time driver uses the |
| 25 | /// too short. We must make it "never" overflow. | 29 | /// syscounter and ignores the periods handling, since it overflows every 142 years. |
| 30 | /// | ||
| 31 | /// For most other devices, the RTC counter is 24 bit. Ticking at 32768hz, it overflows every ~8 minutes. | ||
| 32 | /// This is too short. We must make it "never" overflow. | ||
| 26 | /// | 33 | /// |
| 27 | /// The obvious way would be to count overflow periods. Every time the counter overflows, | 34 | /// The obvious way would be to count overflow periods. Every time the counter overflows, |
| 28 | /// increase a `periods` variable. `now()` simply does `periods << 24 + counter`. So, the logic | 35 | /// increase a `periods` variable. `now()` simply does `periods << 24 + counter`. So, the logic |
| @@ -64,14 +71,39 @@ fn rtc() -> pac::rtc::Rtc { | |||
| 64 | /// `period` is a 32bit integer, so It overflows on 2^32 * 2^23 / 32768 seconds of uptime, which is 34865 | 71 | /// `period` is a 32bit integer, so It overflows on 2^32 * 2^23 / 32768 seconds of uptime, which is 34865 |
| 65 | /// years. For comparison, flash memory like the one containing your firmware is usually rated to retain | 72 | /// years. For comparison, flash memory like the one containing your firmware is usually rated to retain |
| 66 | /// data for only 10-20 years. 34865 years is long enough! | 73 | /// data for only 10-20 years. 34865 years is long enough! |
| 74 | #[cfg(not(feature = "_grtc"))] | ||
| 67 | fn calc_now(period: u32, counter: u32) -> u64 { | 75 | fn calc_now(period: u32, counter: u32) -> u64 { |
| 68 | ((period as u64) << 23) + ((counter ^ ((period & 1) << 23)) as u64) | 76 | ((period as u64) << 23) + ((counter ^ ((period & 1) << 23)) as u64) |
| 69 | } | 77 | } |
| 70 | 78 | ||
| 79 | #[cfg(feature = "_grtc")] | ||
| 80 | fn syscounter() -> u64 { | ||
| 81 | let r = rtc(); | ||
| 82 | r.syscounter(0).active().write(|w| w.set_active(true)); | ||
| 83 | loop { | ||
| 84 | let countl: u32 = r.syscounter(0).syscounterl().read(); | ||
| 85 | let counth = r.syscounter(0).syscounterh().read(); | ||
| 86 | |||
| 87 | if counth.busy() == Busy::READY && !counth.overflow() { | ||
| 88 | let counth: u32 = counth.value(); | ||
| 89 | let count = countl as u64 | ((counth as u64) << 32); | ||
| 90 | r.syscounter(0).active().write(|w| w.set_active(false)); | ||
| 91 | return count; | ||
| 92 | } | ||
| 93 | // If overflow or not ready, loop will re-read both registers | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | #[cfg(not(feature = "_grtc"))] | ||
| 71 | fn compare_n(n: usize) -> u32 { | 98 | fn compare_n(n: usize) -> u32 { |
| 72 | 1 << (n + 16) | 99 | 1 << (n + 16) |
| 73 | } | 100 | } |
| 74 | 101 | ||
| 102 | #[cfg(feature = "_grtc")] | ||
| 103 | fn compare_n(n: usize) -> u32 { | ||
| 104 | 1 << n // GRTC uses bits 0-11 for COMPARE[0-11] | ||
| 105 | } | ||
| 106 | |||
| 75 | #[cfg(test)] | 107 | #[cfg(test)] |
| 76 | mod test { | 108 | mod test { |
| 77 | use super::*; | 109 | use super::*; |
| @@ -108,6 +140,7 @@ impl AlarmState { | |||
| 108 | 140 | ||
| 109 | struct RtcDriver { | 141 | struct RtcDriver { |
| 110 | /// Number of 2^23 periods elapsed since boot. | 142 | /// Number of 2^23 periods elapsed since boot. |
| 143 | #[cfg(not(feature = "_grtc"))] | ||
| 111 | period: AtomicU32, | 144 | period: AtomicU32, |
| 112 | /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. | 145 | /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. |
| 113 | alarms: Mutex<AlarmState>, | 146 | alarms: Mutex<AlarmState>, |
| @@ -115,6 +148,7 @@ struct RtcDriver { | |||
| 115 | } | 148 | } |
| 116 | 149 | ||
| 117 | embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { | 150 | embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { |
| 151 | #[cfg(not(feature = "_grtc"))] | ||
| 118 | period: AtomicU32::new(0), | 152 | period: AtomicU32::new(0), |
| 119 | alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), | 153 | alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), |
| 120 | queue: Mutex::new(RefCell::new(Queue::new())), | 154 | queue: Mutex::new(RefCell::new(Queue::new())), |
| @@ -123,25 +157,43 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { | |||
| 123 | impl RtcDriver { | 157 | impl RtcDriver { |
| 124 | fn init(&'static self, irq_prio: crate::interrupt::Priority) { | 158 | fn init(&'static self, irq_prio: crate::interrupt::Priority) { |
| 125 | let r = rtc(); | 159 | let r = rtc(); |
| 126 | r.cc(3).write(|w| w.set_compare(0x800000)); | 160 | // Chips without GRTC needs to deal with overflow |
| 161 | #[cfg(not(feature = "_grtc"))] | ||
| 162 | { | ||
| 163 | r.cc(3).write(|w| w.set_compare(0x800000)); | ||
| 127 | 164 | ||
| 128 | r.intenset().write(|w| { | 165 | r.intenset().write(|w| { |
| 129 | w.set_ovrflw(true); | 166 | w.set_ovrflw(true); |
| 130 | w.set_compare(3, true); | 167 | w.set_compare(3, true); |
| 131 | }); | 168 | }); |
| 169 | } | ||
| 132 | 170 | ||
| 171 | #[cfg(feature = "_grtc")] | ||
| 172 | { | ||
| 173 | r.mode().write(|w| { | ||
| 174 | w.set_syscounteren(true); | ||
| 175 | }); | ||
| 176 | } | ||
| 133 | r.tasks_clear().write_value(1); | 177 | r.tasks_clear().write_value(1); |
| 134 | r.tasks_start().write_value(1); | 178 | r.tasks_start().write_value(1); |
| 135 | 179 | ||
| 136 | // Wait for clear | 180 | // Wait for clear |
| 181 | #[cfg(not(feature = "_grtc"))] | ||
| 137 | while r.counter().read().0 != 0 {} | 182 | while r.counter().read().0 != 0 {} |
| 138 | 183 | ||
| 139 | #[cfg(feature = "_nrf54l")] | 184 | #[cfg(feature = "_grtc")] |
| 185 | loop { | ||
| 186 | if r.status().lftimer().read().ready() { | ||
| 187 | break; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | #[cfg(feature = "_grtc")] | ||
| 140 | { | 192 | { |
| 141 | interrupt::RTC30.set_priority(irq_prio); | 193 | interrupt::GRTC_1.set_priority(irq_prio); |
| 142 | unsafe { interrupt::RTC30.enable() }; | 194 | unsafe { interrupt::GRTC_1.enable() }; |
| 143 | } | 195 | } |
| 144 | #[cfg(not(feature = "_nrf54l"))] | 196 | #[cfg(not(feature = "_grtc"))] |
| 145 | { | 197 | { |
| 146 | interrupt::RTC1.set_priority(irq_prio); | 198 | interrupt::RTC1.set_priority(irq_prio); |
| 147 | unsafe { interrupt::RTC1.enable() }; | 199 | unsafe { interrupt::RTC1.enable() }; |
| @@ -150,11 +202,14 @@ impl RtcDriver { | |||
| 150 | 202 | ||
| 151 | fn on_interrupt(&self) { | 203 | fn on_interrupt(&self) { |
| 152 | let r = rtc(); | 204 | let r = rtc(); |
| 205 | |||
| 206 | #[cfg(not(feature = "_grtc"))] | ||
| 153 | if r.events_ovrflw().read() == 1 { | 207 | if r.events_ovrflw().read() == 1 { |
| 154 | r.events_ovrflw().write_value(0); | 208 | r.events_ovrflw().write_value(0); |
| 155 | self.next_period(); | 209 | self.next_period(); |
| 156 | } | 210 | } |
| 157 | 211 | ||
| 212 | #[cfg(not(feature = "_grtc"))] | ||
| 158 | if r.events_compare(3).read() == 1 { | 213 | if r.events_compare(3).read() == 1 { |
| 159 | r.events_compare(3).write_value(0); | 214 | r.events_compare(3).write_value(0); |
| 160 | self.next_period(); | 215 | self.next_period(); |
| @@ -169,6 +224,7 @@ impl RtcDriver { | |||
| 169 | } | 224 | } |
| 170 | } | 225 | } |
| 171 | 226 | ||
| 227 | #[cfg(not(feature = "_grtc"))] | ||
| 172 | fn next_period(&self) { | 228 | fn next_period(&self) { |
| 173 | critical_section::with(|cs| { | 229 | critical_section::with(|cs| { |
| 174 | let r = rtc(); | 230 | let r = rtc(); |
| @@ -190,7 +246,10 @@ impl RtcDriver { | |||
| 190 | fn trigger_alarm(&self, cs: CriticalSection) { | 246 | fn trigger_alarm(&self, cs: CriticalSection) { |
| 191 | let n = 0; | 247 | let n = 0; |
| 192 | let r = rtc(); | 248 | let r = rtc(); |
| 249 | #[cfg(not(feature = "_grtc"))] | ||
| 193 | r.intenclr().write(|w| w.0 = compare_n(n)); | 250 | r.intenclr().write(|w| w.0 = compare_n(n)); |
| 251 | #[cfg(feature = "_grtc")] | ||
| 252 | r.intenclr(1).write(|w| w.0 = compare_n(n)); | ||
| 194 | 253 | ||
| 195 | let alarm = &self.alarms.borrow(cs); | 254 | let alarm = &self.alarms.borrow(cs); |
| 196 | alarm.timestamp.set(u64::MAX); | 255 | alarm.timestamp.set(u64::MAX); |
| @@ -214,7 +273,10 @@ impl RtcDriver { | |||
| 214 | if timestamp <= t { | 273 | if timestamp <= t { |
| 215 | // If alarm timestamp has passed the alarm will not fire. | 274 | // If alarm timestamp has passed the alarm will not fire. |
| 216 | // Disarm the alarm and return `false` to indicate that. | 275 | // Disarm the alarm and return `false` to indicate that. |
| 276 | #[cfg(not(feature = "_grtc"))] | ||
| 217 | r.intenclr().write(|w| w.0 = compare_n(n)); | 277 | r.intenclr().write(|w| w.0 = compare_n(n)); |
| 278 | #[cfg(feature = "_grtc")] | ||
| 279 | r.intenclr(1).write(|w| w.0 = compare_n(n)); | ||
| 218 | 280 | ||
| 219 | alarm.timestamp.set(u64::MAX); | 281 | alarm.timestamp.set(u64::MAX); |
| 220 | 282 | ||
| @@ -226,7 +288,7 @@ impl RtcDriver { | |||
| 226 | // Write the CC value regardless of whether we're going to enable it now or not. | 288 | // Write the CC value regardless of whether we're going to enable it now or not. |
| 227 | // This way, when we enable it later, the right value is already set. | 289 | // This way, when we enable it later, the right value is already set. |
| 228 | 290 | ||
| 229 | // nrf52 docs say: | 291 | // nrf52 docs say : |
| 230 | // If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event. | 292 | // If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event. |
| 231 | // To workaround this, we never write a timestamp smaller than N+3. | 293 | // To workaround this, we never write a timestamp smaller than N+3. |
| 232 | // N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc. | 294 | // N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc. |
| @@ -238,22 +300,39 @@ impl RtcDriver { | |||
| 238 | // This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed | 300 | // This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed |
| 239 | // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time, | 301 | // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time, |
| 240 | // and we don't do that here. | 302 | // and we don't do that here. |
| 241 | let safe_timestamp = timestamp.max(t + 3); | 303 | #[cfg(not(feature = "_grtc"))] |
| 242 | r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF)); | 304 | { |
| 243 | 305 | let safe_timestamp = timestamp.max(t + 3); | |
| 244 | let diff = timestamp - t; | 306 | r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF)); |
| 245 | if diff < 0xc00000 { | 307 | let diff = timestamp - t; |
| 246 | r.intenset().write(|w| w.0 = compare_n(n)); | 308 | if diff < 0xc00000 { |
| 247 | 309 | r.intenset().write(|w| w.0 = compare_n(n)); | |
| 248 | // If we have not passed the timestamp, we can be sure the alarm will be invoked. Otherwise, | 310 | |
| 249 | // we need to retry setting the alarm. | 311 | // If we have not passed the timestamp, we can be sure the alarm will be invoked. Otherwise, |
| 250 | if self.now() + 2 <= timestamp { | 312 | // we need to retry setting the alarm. |
| 313 | if self.now() + 2 <= timestamp { | ||
| 314 | return true; | ||
| 315 | } | ||
| 316 | } else { | ||
| 317 | // If it's too far in the future, don't setup the compare channel yet. | ||
| 318 | // It will be setup later by `next_period`. | ||
| 319 | r.intenclr().write(|w| w.0 = compare_n(n)); | ||
| 251 | return true; | 320 | return true; |
| 252 | } | 321 | } |
| 253 | } else { | 322 | } |
| 254 | // If it's too far in the future, don't setup the compare channel yet. | 323 | |
| 255 | // It will be setup later by `next_period`. | 324 | // The nRF54 datasheet states that 'The EVENTS_COMPARE[n] event is generated immediately if the |
| 256 | r.intenclr().write(|w| w.0 = compare_n(n)); | 325 | // configured compare value at CC[n] is less than the current SYSCOUNTER value.'. This means we |
| 326 | // can write the expected timestamp and be sure the alarm is triggered. | ||
| 327 | #[cfg(feature = "_grtc")] | ||
| 328 | { | ||
| 329 | let ccl = timestamp as u32; | ||
| 330 | let cch = (timestamp >> 32) as u32 & 0xFFFFF; // 20 bits for CCH | ||
| 331 | |||
| 332 | r.cc(n).ccl().write_value(ccl); | ||
| 333 | r.cc(n).cch().write(|w| w.set_cch(cch)); | ||
| 334 | r.intenset(1).write(|w| w.0 = compare_n(n)); | ||
| 335 | |||
| 257 | return true; | 336 | return true; |
| 258 | } | 337 | } |
| 259 | } | 338 | } |
| @@ -261,6 +340,7 @@ impl RtcDriver { | |||
| 261 | } | 340 | } |
| 262 | 341 | ||
| 263 | impl Driver for RtcDriver { | 342 | impl Driver for RtcDriver { |
| 343 | #[cfg(not(feature = "_grtc"))] | ||
| 264 | fn now(&self) -> u64 { | 344 | fn now(&self) -> u64 { |
| 265 | // `period` MUST be read before `counter`, see comment at the top for details. | 345 | // `period` MUST be read before `counter`, see comment at the top for details. |
| 266 | let period = self.period.load(Ordering::Relaxed); | 346 | let period = self.period.load(Ordering::Relaxed); |
| @@ -269,10 +349,14 @@ impl Driver for RtcDriver { | |||
| 269 | calc_now(period, counter) | 349 | calc_now(period, counter) |
| 270 | } | 350 | } |
| 271 | 351 | ||
| 352 | #[cfg(feature = "_grtc")] | ||
| 353 | fn now(&self) -> u64 { | ||
| 354 | syscounter() | ||
| 355 | } | ||
| 356 | |||
| 272 | fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { | 357 | fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { |
| 273 | critical_section::with(|cs| { | 358 | critical_section::with(|cs| { |
| 274 | let mut queue = self.queue.borrow(cs).borrow_mut(); | 359 | let mut queue = self.queue.borrow(cs).borrow_mut(); |
| 275 | |||
| 276 | if queue.schedule_wake(at, waker) { | 360 | if queue.schedule_wake(at, waker) { |
| 277 | let mut next = queue.next_expiration(self.now()); | 361 | let mut next = queue.next_expiration(self.now()); |
| 278 | while !self.set_alarm(cs, next) { | 362 | while !self.set_alarm(cs, next) { |
| @@ -283,14 +367,14 @@ impl Driver for RtcDriver { | |||
| 283 | } | 367 | } |
| 284 | } | 368 | } |
| 285 | 369 | ||
| 286 | #[cfg(feature = "_nrf54l")] | 370 | #[cfg(feature = "_grtc")] |
| 287 | #[cfg(feature = "rt")] | 371 | #[cfg(feature = "rt")] |
| 288 | #[interrupt] | 372 | #[interrupt] |
| 289 | fn RTC30() { | 373 | fn GRTC_1() { |
| 290 | DRIVER.on_interrupt() | 374 | DRIVER.on_interrupt() |
| 291 | } | 375 | } |
| 292 | 376 | ||
| 293 | #[cfg(not(feature = "_nrf54l"))] | 377 | #[cfg(not(feature = "_grtc"))] |
| 294 | #[cfg(feature = "rt")] | 378 | #[cfg(feature = "rt")] |
| 295 | #[interrupt] | 379 | #[interrupt] |
| 296 | fn RTC1() { | 380 | fn RTC1() { |
