aboutsummaryrefslogtreecommitdiff
path: root/rtic-monotonics/src/nrf/rtc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rtic-monotonics/src/nrf/rtc.rs')
-rw-r--r--rtic-monotonics/src/nrf/rtc.rs100
1 files changed, 67 insertions, 33 deletions
diff --git a/rtic-monotonics/src/nrf/rtc.rs b/rtic-monotonics/src/nrf/rtc.rs
index 1f4e34f5..884b523a 100644
--- a/rtic-monotonics/src/nrf/rtc.rs
+++ b/rtic-monotonics/src/nrf/rtc.rs
@@ -44,6 +44,7 @@ use crate::{Monotonic, TimeoutError, TimerQueue};
use atomic_polyfill::{AtomicU32, Ordering};
use core::future::Future;
pub use fugit::{self, ExtU64, ExtU64Ceil};
+use rtic_time::half_period_counter::calculate_now;
#[doc(hidden)]
#[macro_export]
@@ -92,6 +93,16 @@ macro_rules! create_nrf_rtc2_monotonic_token {
}};
}
+struct TimerValueU24(u32);
+impl rtic_time::half_period_counter::TimerValue for TimerValueU24 {
+ const BITS: u32 = 24;
+}
+impl From<TimerValueU24> for u64 {
+ fn from(value: TimerValueU24) -> Self {
+ Self::from(value.0)
+ }
+}
+
macro_rules! make_rtc {
($mono_name:ident, $rtc:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
/// Monotonic timer queue implementation.
@@ -107,13 +118,49 @@ macro_rules! make_rtc {
/// Start the timer monotonic.
pub fn start(rtc: $rtc, _interrupt_token: impl crate::InterruptToken<Self>) {
unsafe { rtc.prescaler.write(|w| w.bits(0)) };
- rtc.intenset.write(|w| w.compare0().set().ovrflw().set());
- rtc.evtenset.write(|w| w.compare0().set().ovrflw().set());
- rtc.tasks_clear.write(|w| unsafe { w.bits(1) });
- rtc.tasks_start.write(|w| unsafe { w.bits(1) });
-
- $tq.initialize(Self {});
+ // Disable interrupts, as preparation
+ rtc.intenclr.write(|w| w
+ .compare0().clear()
+ .compare1().clear()
+ .ovrflw().clear()
+ );
+
+ // Configure compare registers
+ rtc.cc[0].write(|w| unsafe { w.bits(0) }); // Dynamic wakeup
+ rtc.cc[1].write(|w| unsafe { w.bits(0x80_0000) }); // Half-period
+
+ // Timing critical, make sure we don't get interrupted
+ critical_section::with(|_|{
+ // Reset the timer
+ rtc.tasks_clear.write(|w| unsafe { w.bits(1) });
+ rtc.tasks_start.write(|w| unsafe { w.bits(1) });
+
+ // Clear pending events.
+ // Should be close enough to the timer reset that we don't miss any events.
+ rtc.events_ovrflw.write(|w| w);
+ rtc.events_compare[0].write(|w| w);
+ rtc.events_compare[1].write(|w| w);
+
+ // Make sure overflow counter is synced with the timer value
+ $overflow.store(0, Ordering::SeqCst);
+
+ // Initialized the timer queue
+ $tq.initialize(Self {});
+
+ // Enable interrupts.
+ // Should be close enough to the timer reset that we don't miss any events.
+ rtc.intenset.write(|w| w
+ .compare0().set()
+ .compare1().set()
+ .ovrflw().set()
+ );
+ rtc.evtenset.write(|w| w
+ .compare0().set()
+ .compare1().set()
+ .ovrflw().set()
+ );
+ });
// SAFETY: We take full ownership of the peripheral and interrupt vector,
// plus we are not using any external shared resources so we won't impact
@@ -130,12 +177,6 @@ macro_rules! make_rtc {
&$tq
}
- #[inline(always)]
- fn is_overflow() -> bool {
- let rtc = unsafe { &*$rtc::PTR };
- rtc.events_ovrflw.read().bits() == 1
- }
-
/// Timeout at a specific time.
#[inline]
pub async fn timeout_at<F: Future>(
@@ -181,31 +222,24 @@ macro_rules! make_rtc {
type Duration = fugit::TimerDurationU64<32_768>;
fn now() -> Self::Instant {
- // In a critical section to not get a race between overflow updates and reading it
- // and the flag here.
- critical_section::with(|_| {
- let rtc = unsafe { &*$rtc::PTR };
- let cnt = rtc.counter.read().bits();
- // OVERFLOW HAPPENS HERE race needs to be handled
- let ovf = if Self::is_overflow() {
- $overflow.load(Ordering::Relaxed) + 1
- } else {
- $overflow.load(Ordering::Relaxed)
- } as u64;
-
- // Check and fix if above race happened
- let new_cnt = rtc.counter.read().bits();
- let cnt = if new_cnt >= cnt { cnt } else { new_cnt } as u64;
-
- Self::Instant::from_ticks((ovf << 24) | cnt)
- })
+ let rtc = unsafe { &*$rtc::PTR };
+ Self::Instant::from_ticks(calculate_now(
+ $overflow.load(Ordering::Relaxed),
+ || TimerValueU24(rtc.counter.read().bits())
+ ))
}
fn on_interrupt() {
let rtc = unsafe { &*$rtc::PTR };
- if Self::is_overflow() {
- $overflow.fetch_add(1, Ordering::SeqCst);
+ if rtc.events_ovrflw.read().bits() == 1 {
rtc.events_ovrflw.write(|w| unsafe { w.bits(0) });
+ let prev = $overflow.fetch_add(1, Ordering::Relaxed);
+ assert!(prev % 2 == 1, "Monotonic must have skipped an interrupt!");
+ }
+ if rtc.events_compare[1].read().bits() == 1 {
+ rtc.events_compare[1].write(|w| unsafe { w.bits(0) });
+ let prev = $overflow.fetch_add(1, Ordering::Relaxed);
+ assert!(prev % 2 == 0, "Monotonic must have skipped an interrupt!");
}
}
@@ -221,7 +255,7 @@ macro_rules! make_rtc {
fn set_compare(instant: Self::Instant) {
let rtc = unsafe { &*$rtc::PTR };
- unsafe { rtc.cc[0].write(|w| w.bits(instant.ticks() as u32 & 0xffffff)) };
+ unsafe { rtc.cc[0].write(|w| w.bits(instant.ticks() as u32 & 0xff_ffff)) };
}
fn clear_compare_flag() {