rtic_time/half_period_counter.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
//! Utility to implement a race condition free half-period based monotonic.
//!
//! # Background
//!
//! Monotonics are continuous and never wrap (in a reasonable amount of time), while
//! the underlying hardware usually wraps frequently and has interrupts to indicate that
//! a wrap happened.
//!
//! The biggest problem when implementing a monotonic from such hardware is that there exists
//! a non-trivial race condition while reading data from the timer. Let's assume we increment
//! a period counter every time an overflow interrupt happens.
//! Which should we then read first when computing the current time? The period counter or
//! the timer value?
//! - When reading the timer value first, an overflow interrupt could happen before we read
//! the period counter, causing the calculated time to be much too high
//! - When reading the period counter first, the timer value could overflow before we
//! read it, causing the calculated time to be much too low
//!
//! The reason this is non-trivil to solve is because even critical sections do not help
//! much - the inherent problem here is that the timer value continues to change, and there
//! is no way to read it together with the period counter in an atomic way.
//!
//! # Solution
//!
//! This module provides utilities to solve this problem in a reliable, race-condition free way.
//! A second interrupt must be added at the half-period mark, which effectively converts the period counter
//! to a half-period counter. This creates one bit of overlap between the
//! timer value and the period counter, which makes it mathematically possible to solve the
//! race condition.
//!
//! The following steps have to be fulfilled to make this reliable:
//! - The period counter gets incremented twice per period; once when the timer overflow happens and once
//! at the half-period mark. For example, a 16-bit timer would require the period counter to be
//! incremented at the values `0x0000` and `0x8000`.
//! - The timer value and the period counter must be in sync. After the overflow interrupt
//! was processed, the period counter must be even, and after the half-way interrupt was
//! processed, the period counter must be odd.
//! - Both the overflow interrupt and the half-way interrupt must be processed within half a
//! timer period. This means those interrupts should be the highest priority in the
//! system - disabling them for more than half a period will cause the monotonic to misbehave.
//!
//! If those conditions are fulfilled, the [`calculate_now`] function will reliably
//! return the correct time value.
//!
//! # Why does this work?
//!
//! It's complicated. In essence, this one bit of overlap gets used to make
//! it irrelevant whether the period counter was already incremented or not.
//! For example, during the second part of the timer period, it is irrelevant if the
//! period counter is `2` (before the interrupt) or `3` (after the interrupt) - [`calculate_now`]
//! will yield the same result. Then half a period later, in the first part of the next timer period,
//! it is irrelevant if the period counter is `3` or `4` - they again will yield the same result.
//!
//! This means that as long as we read the period counter **before** the timer value, we will
//! always get the correct result, given that the interrupts are not delayed by more than half a period.
//!
//! # Example
//!
//! This example takes a 16-bit timer and uses a 32-bit period counter
//! to extend the timer to 47-bit. Note that one bit gets lost because
//! this method requires the period counter to be increased twice per period.
//!
//! The resulting time value is returned as a `u64`.
//!
//! ```rust
//! # fn timer_stop() {}
//! # fn timer_reset() {}
//! # fn timer_enable_overflow_interrupt() {}
//! # fn timer_enable_compare_interrupt(_val: u16) {}
//! # fn timer_start() {}
//! # fn overflow_interrupt_happened() -> bool { false }
//! # fn compare_interrupt_happened() -> bool { false }
//! # fn clear_overflow_interrupt() {}
//! # fn clear_compare_interrupt() {}
//! # fn timer_get_value() -> u16 { 0 }
//! use core::sync::atomic::{AtomicU32, Ordering};
//!
//! static HALF_PERIOD_COUNTER: AtomicU32 = AtomicU32::new(0);
//!
//! struct MyMonotonic;
//!
//! impl MyMonotonic {
//! fn init() {
//! timer_stop();
//! timer_reset();
//! HALF_PERIOD_COUNTER.store(0, Ordering::SeqCst);
//! timer_enable_overflow_interrupt();
//! timer_enable_compare_interrupt(0x8000);
//! // Both the period counter and the timer are reset
//! // to zero and the interrupts are enabled.
//! // This means the period counter and the timer value
//! // are in sync, so we can now enable the timer.
//! timer_start();
//! }
//!
//! fn on_interrupt() {
//! if overflow_interrupt_happened() {
//! clear_overflow_interrupt();
//! let prev = HALF_PERIOD_COUNTER.fetch_add(1, Ordering::Relaxed);
//! assert!(prev % 2 == 1, "Monotonic must have skipped an interrupt!");
//! }
//! if compare_interrupt_happened() {
//! clear_compare_interrupt();
//! let prev = HALF_PERIOD_COUNTER.fetch_add(1, Ordering::Relaxed);
//! assert!(prev % 2 == 0, "Monotonic must have skipped an interrupt!");
//! }
//! }
//!
//! fn now() -> u64 {
//! rtic_time::half_period_counter::calculate_now(
//! || HALF_PERIOD_COUNTER.load(Ordering::Relaxed),
//! || timer_get_value(),
//! )
//! }
//! }
//! ```
//!
use core::sync::atomic::{compiler_fence, Ordering};
/// The value of the timer's count register.
pub trait TimerValue {
/// Bit size of the timer. Does not need to be a multiple of `8`.
const BITS: u32;
}
macro_rules! impl_timer_value {
($t:ty) => {
impl TimerValue for $t {
const BITS: u32 = Self::BITS;
}
};
}
impl_timer_value!(u8);
impl_timer_value!(u16);
impl_timer_value!(u32);
impl_timer_value!(u64);
/// Operations a type has to support
/// in order to be used as the return value
/// of [`calculate_now`].
pub trait TimerOps: Copy {
/// All bits set to `1`.
const MAX: Self;
/// The lowest bit set to `1`.
const ONE: Self;
/// The `^` operation.
fn xor(self, other: Self) -> Self;
/// The `&` operation.
fn and(self, other: Self) -> Self;
/// The `+` operation.
fn add(self, other: Self) -> Self;
/// The `<<` operation.
fn left_shift(self, amount: u32) -> Self;
}
macro_rules! impl_timer_ops {
($t:ty) => {
impl TimerOps for $t {
const MAX: Self = Self::MAX;
const ONE: Self = 1;
#[inline]
fn xor(self, other: Self) -> Self {
self ^ other
}
#[inline]
fn and(self, other: Self) -> Self {
self & other
}
#[inline]
fn add(self, other: Self) -> Self {
self + other
}
#[inline]
fn left_shift(self, amount: u32) -> Self {
self << amount
}
}
};
}
impl_timer_ops!(u16);
impl_timer_ops!(u32);
impl_timer_ops!(u64);
impl_timer_ops!(u128);
/// Calculates the current time from the half period counter and the timer value.
///
/// # Arguments
///
/// * `half_periods` - A closure/function that when called produces the period counter value. If read from an atomic, can use `Ordering::Relaxed`.
/// * `timer_value` - A closure/function that when called produces the current timer value.
pub fn calculate_now<P, T, F1, F2, O>(half_periods: F1, timer_value: F2) -> O
where
T: TimerValue,
O: From<P> + From<T> + TimerOps,
F1: FnOnce() -> P,
F2: FnOnce() -> T,
{
// This is timing critical; for fast-overflowing timers (like the 1MHz 16-bit timers on STM32),
// it could lead to erroneous behavior if preempted in between the two reads.
// Hence the critical section.
let (half_periods, timer_value) = critical_section::with(|_| {
// Important: half_periods **must** be read first.
// Otherwise the mathematical principle that prevents
// the race condition does not work.
let half_periods = O::from(half_periods());
compiler_fence(Ordering::Acquire);
let timer_value = O::from(timer_value());
(half_periods, timer_value)
});
// Credits to the `time-driver` of `embassy-stm32`.
//
// Given that our clock counter value is 32 bits.
//
// Clock timekeeping works with something we call "periods", which are time intervals
// of 2^31 ticks. The Clock counter value is 32 bits, so one "overflow cycle" is 2 periods.
//
// A `period` count is maintained in parallel to the Timer hardware `counter`, like this:
// - `period` and `counter` start at 0
// - `period` is incremented on overflow (at counter value 0)
// - `period` is incremented "midway" between overflows (at counter value 0x8000_0000)
//
// Therefore, when `period` is even, counter is in 0..0x7FFF_FFFF. When odd, counter is in 0x8000_0000..0xFFFF_FFFF
// This allows for now() to return the correct value even if it races an overflow.
//
// To get `now()`, `period` is read first, then `counter` is read. If the counter value matches
// the expected range for the `period` parity, we're done. If it doesn't, this means that
// a new period start has raced us between reading `period` and `counter`, so we assume the `counter` value
// corresponds to the next period.
let upper_half = half_periods.left_shift(T::BITS - 1);
let lower_half = O::ONE.left_shift(T::BITS - 1).and(upper_half);
upper_half.add(lower_half.xor(timer_value))
}