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))
}