Skip to content

Commit 731f32f

Browse files
authored
Fix potential rollover edgecase in SysTick extended mode (#13)
Previously it was possible for us to have read a SYSTICK value just before overflow, have the rollover happen & exception fire, incrementing ROLLOVER_COUNT, resulting in a count off by a full 2**24. Now, we do two reads of the systick counter with ROLLOVER_COUNT read between them. We can now check if a rollover happened sometime between the two reads. If it has, we know to use the second reading and re-read ROLLOVER_COUNT to make sure we have the incremented value. If we didn't see any rollover, we're happy and use the first readings of the SYSTICK and ROLLOVER_COUNT.
1 parent d62846a commit 731f32f

File tree

1 file changed

+35
-14
lines changed

1 file changed

+35
-14
lines changed

ep-systick/src/lib.rs

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,18 @@
5454
use cortex_m::peripheral::{syst::SystClkSource, SYST};
5555
use embedded_profiling::{EPContainer, EPInstant, EPInstantGeneric, EPSnapshot, EmbeddedProfiler};
5656

57+
#[cfg(feature = "extended")]
58+
use core::sync::atomic::{AtomicU32, Ordering};
59+
5760
#[cfg(feature = "extended")]
5861
/// Tracker of `systick` cycle count overflows to extend systick's 24 bit timer
59-
static ROLLOVER_COUNT: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
62+
static ROLLOVER_COUNT: AtomicU32 = AtomicU32::new(0);
6063

6164
/// The reload value of the [`systick`](cortex_m::peripheral::SYST) peripheral. Also is the max it can go (2**24).
6265
const SYSTICK_RELOAD: u32 = 0x00FF_FFFF;
66+
/// the resolution of [`systick`](cortex_m::peripheral::SYST), 2**24
67+
#[cfg(feature = "extended")]
68+
const SYSTICK_RESOLUTION: EPContainer = 0x0100_0000;
6369

6470
/// [`systick`](cortex_m::peripheral::SYST) implementation of [`EmbeddedProfiler`].
6571
///
@@ -97,20 +103,35 @@ impl<const FREQ: u32> SysTickProfiler<FREQ> {
97103

98104
impl<const FREQ: u32> EmbeddedProfiler for SysTickProfiler<FREQ> {
99105
fn read_clock(&self) -> EPInstant {
100-
let raw_ticks = SYST::get_current();
101-
#[allow(unused_mut)]
102-
let mut count = EPContainer::from(SYSTICK_RELOAD - raw_ticks);
106+
// Read SYSTICK count and maybe account for rollovers
107+
let count = {
108+
#[cfg(feature = "extended")]
109+
{
110+
// read the clock & ROLLOVER_COUNT. We read `SYST` twice because we need to detect
111+
// if we've rolled over, and if we have make sure we have the right value for ROLLOVER_COUNT.
112+
let first = SYST::get_current();
113+
let rollover_count: EPContainer = ROLLOVER_COUNT.load(Ordering::Acquire).into();
114+
let second = SYST::get_current();
103115

104-
#[cfg(feature = "extended")]
105-
{
106-
/// the resolution of [`systick`](cortex_m::peripheral::SYST), 2**24
107-
const SYSTICK_RESOLUTION: EPContainer = 16_777_216;
116+
// Since the SYSTICK counter is a count down timer, check if first is larger than second
117+
if first > second {
118+
// The usual case. We did not roll over between the first and second reading,
119+
// and because of that we also know we got a valid read on ROLLOVER_COUNT.
120+
rollover_count * SYSTICK_RESOLUTION + EPContainer::from(SYSTICK_RELOAD - first)
121+
} else {
122+
// we rolled over sometime between the first and second read. We may or may not have
123+
// caught the right ROLLOVER_COUNT, so grab that again and then use the second reading.
124+
let rollover_count: EPContainer = ROLLOVER_COUNT.load(Ordering::Acquire).into();
125+
rollover_count * SYSTICK_RESOLUTION + EPContainer::from(SYSTICK_RELOAD - second)
126+
}
127+
}
108128

109-
// add on the number of times we've rolled over (systick resolution is 2**24)
110-
let rollover_count =
111-
EPContainer::from(ROLLOVER_COUNT.load(core::sync::atomic::Ordering::Acquire));
112-
count += rollover_count * SYSTICK_RESOLUTION;
113-
}
129+
#[cfg(not(feature = "extended"))]
130+
{
131+
// We aren't trying to be fancy here, we don't care if this rolled over from the last read.
132+
EPContainer::from(SYSTICK_RELOAD - SYST::get_current())
133+
}
134+
};
114135

115136
embedded_profiling::convert_instant(EPInstantGeneric::<1, FREQ>::from_ticks(count))
116137
}
@@ -127,5 +148,5 @@ use cortex_m_rt::exception;
127148
#[exception]
128149
#[allow(non_snake_case)]
129150
fn SysTick() {
130-
ROLLOVER_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
151+
ROLLOVER_COUNT.fetch_add(1, Ordering::Release);
131152
}

0 commit comments

Comments
 (0)