aboutsummaryrefslogtreecommitdiff
path: root/rtic-monotonics/src
diff options
context:
space:
mode:
Diffstat (limited to 'rtic-monotonics/src')
-rw-r--r--rtic-monotonics/src/imxrt.rs10
-rw-r--r--rtic-monotonics/src/nrf/rtc.rs100
-rw-r--r--rtic-monotonics/src/nrf/timer.rs101
-rw-r--r--rtic-monotonics/src/stm32.rs6
4 files changed, 135 insertions, 82 deletions
diff --git a/rtic-monotonics/src/imxrt.rs b/rtic-monotonics/src/imxrt.rs
index 5f9fc088..ecf9129b 100644
--- a/rtic-monotonics/src/imxrt.rs
+++ b/rtic-monotonics/src/imxrt.rs
@@ -141,13 +141,15 @@ macro_rules! make_timer {
// so it gets combined with rollover interrupt
ral::write_reg!(ral::gpt, gpt, OCR[1], 0x0000_0000);
+ // Initialize timer queue
+ $tq.initialize(Self {});
+
// Enable the timer
ral::modify_reg!(ral::gpt, gpt, CR, EN: 1);
ral::modify_reg!(ral::gpt, gpt, CR,
ENMOD: 0, // Keep state when disabled
);
- $tq.initialize(Self {});
// 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
@@ -244,13 +246,15 @@ macro_rules! make_timer {
let (rollover, half_rollover) = ral::read_reg!(ral::gpt, gpt, SR, ROV, OF1);
if rollover != 0 {
- $period.fetch_add(1, Ordering::Relaxed);
+ let prev = $period.fetch_add(1, Ordering::Relaxed);
ral::write_reg!(ral::gpt, gpt, SR, ROV: 1);
+ assert!(prev % 2 == 1, "Monotonic must have skipped an interrupt!");
}
if half_rollover != 0 {
- $period.fetch_add(1, Ordering::Relaxed);
+ let prev = $period.fetch_add(1, Ordering::Relaxed);
ral::write_reg!(ral::gpt, gpt, SR, OF1: 1);
+ assert!(prev % 2 == 0, "Monotonic must have skipped an interrupt!");
}
}
}
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() {
diff --git a/rtic-monotonics/src/nrf/timer.rs b/rtic-monotonics/src/nrf/timer.rs
index 0488ca9b..2f83f40b 100644
--- a/rtic-monotonics/src/nrf/timer.rs
+++ b/rtic-monotonics/src/nrf/timer.rs
@@ -30,6 +30,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;
#[cfg(feature = "nrf52810")]
use nrf52810_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2};
@@ -139,17 +140,45 @@ macro_rules! make_timer {
// 1 MHz
timer.prescaler.write(|w| unsafe { w.prescaler().bits(4) });
timer.bitmode.write(|w| w.bitmode()._32bit());
- timer
- .intenset
- .modify(|_, w| w.compare0().set().compare1().set());
- timer.cc[1].write(|w| unsafe { w.cc().bits(0) }); // Overflow
- timer.tasks_clear.write(|w| unsafe { w.bits(1) });
- timer.tasks_start.write(|w| unsafe { w.bits(1) });
- $tq.initialize(Self {});
+ // Disable interrupts, as preparation
+ timer.intenclr.modify(|_, w| w
+ .compare0().clear()
+ .compare1().clear()
+ .compare2().clear()
+ );
+
+ // Configure compare registers
+ timer.cc[0].write(|w| unsafe { w.cc().bits(0) }); // Dynamic wakeup
+ timer.cc[1].write(|w| unsafe { w.cc().bits(0x0000_0000) }); // Overflow
+ timer.cc[2].write(|w| unsafe { w.cc().bits(0x8000_0000) }); // Half-period
+
+ // Timing critical, make sure we don't get interrupted
+ critical_section::with(|_|{
+ // Reset the timer
+ timer.tasks_clear.write(|w| unsafe { w.bits(1) });
+ timer.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.
+ timer.events_compare[0].write(|w| w);
+ timer.events_compare[1].write(|w| w);
+ timer.events_compare[2].write(|w| w);
- timer.events_compare[0].write(|w| w);
- timer.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.
+ timer.intenset.modify(|_, w| w
+ .compare0().set()
+ .compare1().set()
+ .compare2().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
@@ -166,12 +195,6 @@ macro_rules! make_timer {
&$tq
}
- #[inline(always)]
- fn is_overflow() -> bool {
- let timer = unsafe { &*$timer::PTR };
- timer.events_compare[1].read().bits() & 1 != 0
- }
-
/// Timeout at a specific time.
#[inline]
pub async fn timeout_at<F: Future>(
@@ -216,44 +239,34 @@ macro_rules! make_timer {
type Duration = fugit::TimerDurationU64<1_000_000>;
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 timer = unsafe { &*$timer::PTR };
- timer.tasks_capture[2].write(|w| unsafe { w.bits(1) });
- let cnt = timer.cc[2].read().bits();
-
- let unhandled_overflow = if Self::is_overflow() {
- // The overflow has not been handled yet, so add an extra to the read overflow.
- 1
- } else {
- 0
- };
-
- timer.tasks_capture[2].write(|w| unsafe { w.bits(1) });
- let new_cnt = timer.cc[2].read().bits();
- let cnt = if new_cnt >= cnt { cnt } else { new_cnt } as u64;
-
- Self::Instant::from_ticks(
- (unhandled_overflow + $overflow.load(Ordering::Relaxed) as u64) << 32
- | cnt as u64,
- )
- })
+ let timer = unsafe { &*$timer::PTR };
+
+ Self::Instant::from_ticks(calculate_now(
+ $overflow.load(Ordering::Relaxed),
+ || {
+ timer.tasks_capture[3].write(|w| unsafe { w.bits(1) });
+ timer.cc[3].read().bits()
+ }
+ ))
}
fn on_interrupt() {
let timer = unsafe { &*$timer::PTR };
// If there is a compare match on channel 1, it is an overflow
- if Self::is_overflow() {
+ if timer.events_compare[1].read().bits() & 1 != 0 {
timer.events_compare[1].write(|w| w);
- $overflow.fetch_add(1, Ordering::SeqCst);
+ let prev = $overflow.fetch_add(1, Ordering::Relaxed);
+ assert!(prev % 2 == 1, "Monotonic must have skipped an interrupt!");
}
- }
- fn enable_timer() {}
-
- fn disable_timer() {}
+ // If there is a compare match on channel 2, it is a half-period overflow
+ if timer.events_compare[2].read().bits() & 1 != 0 {
+ timer.events_compare[2].write(|w| w);
+ let prev = $overflow.fetch_add(1, Ordering::Relaxed);
+ assert!(prev % 2 == 0, "Monotonic must have skipped an interrupt!");
+ }
+ }
fn set_compare(instant: Self::Instant) {
let timer = unsafe { &*$timer::PTR };
diff --git a/rtic-monotonics/src/stm32.rs b/rtic-monotonics/src/stm32.rs
index c86005ef..601196a3 100644
--- a/rtic-monotonics/src/stm32.rs
+++ b/rtic-monotonics/src/stm32.rs
@@ -272,12 +272,14 @@ macro_rules! make_timer {
// Full period
if $timer.sr().read().uif() {
$timer.sr().modify(|r| r.set_uif(false));
- $overflow.fetch_add(1, Ordering::Relaxed);
+ let prev = $overflow.fetch_add(1, Ordering::Relaxed);
+ assert!(prev % 2 == 1, "Monotonic must have missed an interrupt!");
}
// Half period
if $timer.sr().read().ccif(2) {
$timer.sr().modify(|r| r.set_ccif(2, false));
- $overflow.fetch_add(1, Ordering::Relaxed);
+ let prev = $overflow.fetch_add(1, Ordering::Relaxed);
+ assert!(prev % 2 == 0, "Monotonic must have missed an interrupt!");
}
}
}