1use core::sync::atomic::{AtomicU32, Ordering};
4
5#[cfg(test)]
6use core::sync::atomic::AtomicBool;
7
8pub struct Timer {
13 inner_wraps: AtomicU32, outer_wraps: AtomicU32, reload_value: u32, tick_hz: u64, multiplier: u64, shift: u32, #[cfg(test)]
20 current_systick: AtomicU32,
21 #[cfg(test)]
22 after_v1_hook: Option<fn(&Timer)>, #[cfg(test)]
24 pendst_is_pending: AtomicBool, }
26
27impl Timer {
28 pub fn systick_handler(&self) {
32 let inner = self.inner_wraps.load(Ordering::Relaxed);
34 self.inner_wraps
37 .store(inner.wrapping_add(1), Ordering::SeqCst);
38 if inner == u32::MAX {
39 let outer = self.outer_wraps.load(Ordering::Relaxed).wrapping_add(1);
41 self.outer_wraps.store(outer, Ordering::SeqCst);
42 }
43 }
44
45 pub fn now(&self) -> u64 {
60 let reload = self.reload_value as u64;
61
62 loop {
63 let in1 = self.inner_wraps.load(Ordering::SeqCst) as u64;
66 let out1 = self.outer_wraps.load(Ordering::SeqCst) as u64;
67 let wraps_pre = (out1 << 32) | in1;
68
69 let val_before = self.get_syst() as u64;
71
72 let in2 = self.inner_wraps.load(Ordering::SeqCst) as u64;
74 let out2 = self.outer_wraps.load(Ordering::SeqCst) as u64;
75 let wraps_post = (out2 << 32) | in2;
76
77 if wraps_pre != wraps_post {
80 continue;
81 }
82
83 let is_pending = self.is_systick_pending_internal();
87 let val_after = self.get_syst() as u64;
88
89 let in3 = self.inner_wraps.load(Ordering::SeqCst) as u64;
91 let out3 = self.outer_wraps.load(Ordering::SeqCst) as u64;
92 let wraps_final = (out3 << 32) | in3;
93
94 if wraps_final != wraps_pre {
95 continue;
97 }
98
99 let wrap_occurred = is_pending || val_after > val_before;
103
104 let (wraps_u64, final_val) = if wrap_occurred {
108 (wraps_pre + 1, val_after)
110 } else {
111 (wraps_pre, val_after)
113 };
114
115 let reload_plus_1 = reload + 1;
117 let low_part = reload - final_val;
118
119 if wraps_u64 > (u64::MAX - low_part) / reload_plus_1 {
121 let cycles128 = (wraps_u64 as u128) * (reload_plus_1 as u128) + (low_part as u128);
122 let ticks128 = cycles128 * (self.multiplier as u128);
123 return (ticks128 >> self.shift) as u64;
124 }
125
126 let total_cycles = wraps_u64 * reload_plus_1 + low_part;
127
128 let (result, mul_overflow) = total_cycles.overflowing_mul(self.multiplier);
130 if mul_overflow {
131 let wide = (total_cycles as u128) * (self.multiplier as u128);
132 return (wide >> self.shift) as u64;
133 }
134 return result >> self.shift;
135 }
136 }
137
138 pub fn get_syst(&self) -> u32 {
140 #[cfg(test)]
141 return self.current_systick.load(Ordering::SeqCst);
142
143 #[cfg(all(not(test), feature = "cortex-m"))]
144 return cortex_m::peripheral::SYST::get_current();
145
146 #[cfg(all(not(test), not(feature = "cortex-m")))]
147 panic!("This module requires the cortex-m crate to be available");
148 }
149
150 pub const fn reload_value(&self) -> u32 {
152 self.reload_value
153 }
154
155 pub const fn tick_hz(&self) -> u64 {
157 self.tick_hz
158 }
159
160 fn is_systick_pending_internal(&self) -> bool {
162 #[cfg(test)]
163 return self.pendst_is_pending.load(Ordering::SeqCst);
164
165 #[cfg(all(not(test), feature = "cortex-m"))]
166 return cortex_m::peripheral::SCB::is_pendst_pending();
167
168 #[cfg(all(not(test), not(feature = "cortex-m")))]
169 return false; }
171
172 #[cfg(feature = "diagnostics")]
174 pub fn is_systick_pending(&self) -> bool {
175 self.is_systick_pending_internal()
176 }
177
178 const fn compute_shift(tick_hz: u64, systick_freq: u64) -> u32 {
180 let mut shift = 32;
181 let mut multiplier = (tick_hz << shift) / systick_freq;
182 while multiplier == 0 && shift < 64 {
183 shift += 1;
184 multiplier = (tick_hz << shift) / systick_freq;
185 }
186 shift
187 }
188
189 pub const fn new(tick_hz: u64, reload_value: u32, systick_freq: u64) -> Self {
211 if reload_value > (1 << 24) - 1 {
212 panic!("Reload value too large");
213 }
214 if reload_value == 0 {
215 panic!("Reload value cannot be 0");
216 }
217
218 let shift = Self::compute_shift(tick_hz, systick_freq);
220 let multiplier = (tick_hz << shift) / systick_freq;
221
222 Timer {
223 inner_wraps: AtomicU32::new(0),
224 outer_wraps: AtomicU32::new(0),
225 reload_value,
226 tick_hz,
227 multiplier,
228 shift,
229 #[cfg(test)]
230 current_systick: AtomicU32::new(0),
231 #[cfg(test)]
232 after_v1_hook: None,
233 #[cfg(test)]
234 pendst_is_pending: AtomicBool::new(false),
235 }
236 }
237
238 #[cfg(feature = "cortex-m")]
240 pub fn start(&self, syst: &mut cortex_m::peripheral::SYST) {
241 syst.set_clock_source(cortex_m::peripheral::syst::SystClkSource::Core);
242 syst.set_reload(self.reload_value);
243 syst.clear_current();
244 syst.enable_interrupt();
245 syst.enable_counter();
246 }
247
248 #[cfg(feature = "diagnostics")]
257 pub fn diagnose_timing_violation(
258 &self,
259 current_time: u64,
260 previous_time: u64,
261 systick_freq: u64,
262 ) -> Option<u32> {
263 if current_time >= previous_time {
264 return None; }
266
267 let backwards_jump = previous_time - current_time;
268 let wrap_period_ns = ((self.reload_value as u64 + 1) * 1_000_000_000) / systick_freq;
269
270 let backwards_jump_ns = (backwards_jump * 1_000_000_000) / self.tick_hz;
273
274 for observed_periods in 1..=3 {
277 let expected_jump = observed_periods * wrap_period_ns;
278 let tolerance = wrap_period_ns / 100; if backwards_jump_ns >= expected_jump.saturating_sub(tolerance)
281 && backwards_jump_ns <= expected_jump + tolerance
282 {
283 return Some(observed_periods as u32 + 1); }
285 }
286
287 None
288 }
289}
290
291impl Timer {
292 #[cfg(test)]
294 pub fn set_syst(&self, value: u32) {
295 debug_assert!(
296 value <= self.reload_value,
297 "set_syst: value {} exceeds reload {}",
298 value,
299 self.reload_value
300 );
301 self.current_systick.store(value, Ordering::SeqCst);
302 }
303
304 #[cfg(test)]
305 pub fn set_after_v1_hook(&mut self, hook: Option<fn(&Timer)>) {
306 self.after_v1_hook = hook;
307 }
308
309 #[cfg(test)]
310 pub fn set_pendst_pending(&self, val: bool) {
311 self.pendst_is_pending.store(val, Ordering::SeqCst);
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
320 #[should_panic]
321 fn test_zero_systick_freq() {
322 Timer::new(1000, 5, 0);
323 }
324
325 #[test]
326 fn test_timer_new() {
327 let timer = Timer::new(1000, 5, 12_000);
328 timer.inner_wraps.store(4, Ordering::Relaxed); timer.set_syst(3); assert_eq!(timer.now(), 2); }
332
333 #[test]
334 fn test_compute_shift() {
335 assert_eq!(Timer::compute_shift(1000, 12_000), 32);
336 assert_eq!(Timer::compute_shift(3, 16_000_000_000), 33);
338 }
339
340 #[test]
341 fn test_timer_initial_state() {
342 let timer = Timer::new(1000, 5, 12_000);
343 assert_eq!(timer.now(), 0);
344 }
345
346 struct TestTimer<const RELOAD: u32> {
347 timer: Timer,
348 }
349 impl<const RELOAD: u32> TestTimer<RELOAD> {
350 fn new(tick_hz: u64, systick_freq: u64) -> Self {
351 Self {
352 timer: Timer::new(tick_hz, RELOAD, systick_freq),
353 }
354 }
355 fn interrupt(&mut self) {
356 self.timer.systick_handler();
357 self.timer.set_syst(RELOAD);
358 }
359 fn set_tick(&mut self, tick: u32) -> u64 {
360 assert!(tick <= RELOAD);
361 self.timer.set_syst(tick);
362 self.timer.now()
363 }
364 }
365
366 #[test]
367 fn test_timer_matching_rates() {
368 let mut timer = TestTimer::<5>::new(1000, 1000);
369 assert_eq!(timer.set_tick(5), 0);
370 assert_eq!(timer.set_tick(4), 1);
371 assert_eq!(timer.set_tick(0), 5);
372 timer.interrupt();
373 assert_eq!(timer.set_tick(5), 6);
374 }
375
376 #[test]
377 fn test_timer_tick_rate_2x() {
378 let mut timer = TestTimer::<5>::new(2000, 1000);
379 assert_eq!(timer.set_tick(5), 0);
380 assert_eq!(timer.set_tick(4), 2);
381 assert_eq!(timer.set_tick(0), 10);
382 timer.interrupt();
383 assert_eq!(timer.set_tick(5), 12);
384 timer.interrupt();
385 assert_eq!(timer.set_tick(5), 24);
386 }
387
388 #[test]
389 fn test_systick_rate_2x() {
390 let mut timer = TestTimer::<5>::new(1000, 2000);
391 assert_eq!(timer.set_tick(5), 0);
392 assert_eq!(timer.set_tick(4), 0);
393 assert_eq!(timer.set_tick(3), 1);
394 assert_eq!(timer.set_tick(2), 1);
395 assert_eq!(timer.set_tick(0), 2);
396 timer.interrupt();
397 assert_eq!(timer.set_tick(5), 3);
398 timer.interrupt();
399 assert_eq!(timer.set_tick(5), 6);
400 }
401
402 #[test]
403 fn test_outer_wraps_wrapping() {
404 let mut timer = TestTimer::<5>::new(1000, 1000);
405 timer.timer.inner_wraps.store(u32::MAX, Ordering::Relaxed);
407 timer.timer.outer_wraps.store(u32::MAX, Ordering::Relaxed);
408 timer.timer.set_syst(5);
409
410 timer.interrupt();
412 assert_eq!(timer.set_tick(5), ((1u128 << 64) * 1000 / 1000) as u64);
415 }
416
417 #[test]
418 fn test_extreme_rates() {
419 let mut timer = TestTimer::<5>::new(1_000_000, 1000);
421 assert_eq!(timer.set_tick(5), 0);
422 timer.interrupt(); assert_eq!(timer.set_tick(5), 6000); let mut timer = TestTimer::<5>::new(1000, 1_000_000);
427 for _ in 0..1000 {
431 timer.interrupt();
432 }
433 assert_eq!(timer.set_tick(5), 5); }
435
436 #[test]
437 fn test_boundary_conditions() {
438 let mut timer = TestTimer::<1>::new(1000, 1000);
440 assert_eq!(timer.set_tick(1), 0);
441 assert_eq!(timer.set_tick(0), 1);
442 timer.interrupt();
443 assert_eq!(timer.set_tick(1), 2);
444
445 let mut timer = TestTimer::<0xFFFFFF>::new(1000, 1000);
447 assert_eq!(timer.set_tick(0xFFFFFF), 0);
448 assert_eq!(timer.set_tick(0xFFFF00), 255);
449 assert_eq!(timer.set_tick(0), 0xFFFFFF);
450 }
451
452 #[test]
453 fn test_partial_tick_accuracy() {
454 let mut timer = TestTimer::<100>::new(1000, 1000);
456 assert_eq!(timer.set_tick(100), 0); assert_eq!(timer.set_tick(75), 25); assert_eq!(timer.set_tick(50), 50); assert_eq!(timer.set_tick(25), 75); assert_eq!(timer.set_tick(0), 100); }
462
463 #[test]
464 fn test_interrupt_race() {
465 let mut timer = TestTimer::<5>::new(1000, 1000);
466 timer.interrupt();
467 timer.timer.set_syst(3);
468 let t1 = timer.timer.now();
469 timer.interrupt();
470 let t2 = timer.timer.now();
471 assert!(t2 > t1); }
473
474 #[test]
475 fn test_rapid_interrupts() {
476 let mut timer = TestTimer::<5>::new(1000, 1000);
477 for _ in 0..10 {
479 timer.interrupt();
480 }
481 assert_eq!(timer.set_tick(5), 60);
483
484 assert_eq!(timer.set_tick(2), 63);
486 }
487
488 #[test]
489 fn test_u64_overflow_scenario() {
490 let timer = Timer::new(10_000_000, 0xFFFFFF, 100_000_000);
495
496 let total_interrupts = 2560u64;
497 let outer = (total_interrupts >> 32) as u32;
498 let inner = total_interrupts as u32;
499
500 timer.outer_wraps.store(outer, Ordering::Relaxed);
501 timer.inner_wraps.store(inner, Ordering::Relaxed);
502
503 let expected_ticks = 4_296_645_011;
505 assert_eq!(timer.now(), expected_ticks);
506 }
507
508 #[test]
509 fn test_monotonicity_around_wrap() {
510 const RELOAD: u32 = 100;
511 let timer = Timer::new(1_000, RELOAD, 1_000);
512
513 timer.set_syst(1);
515 let t1 = timer.now();
516
517 timer.set_syst(RELOAD);
520 timer.set_pendst_pending(true);
521
522 let t2 = timer.now();
524
525 assert!(
529 t2 >= t1,
530 "Timer is not monotonic: t1 was {}, t2 was {}",
531 t1,
532 t2
533 );
534
535 assert_eq!(t1, 99);
539 assert_eq!(t2, 101);
540 }
541
542 #[test]
543 fn test_monotonicity_between_interrupts() {
544 const RELOAD: u32 = 100;
545 let timer = Timer::new(1_000, RELOAD, 1_000);
546
547 timer.set_syst(RELOAD);
549 let t1 = timer.now();
550
551 timer.set_syst(RELOAD / 2);
553 let t2 = timer.now();
554
555 timer.set_syst(0);
557 let t3 = timer.now();
558
559 assert!(t2 > t1, "t2 ({}) should be > t1 ({})", t2, t1);
561 assert!(t3 > t2, "t3 ({}) should be > t2 ({})", t3, t2);
562
563 assert_eq!(t1, 0);
565 assert_eq!(t2, 50);
566 assert_eq!(t3, 100);
567 }
568
569 const RELOAD: u32 = 100; #[test]
572 fn test_monotonicity_with_starved_isr() {
573 let timer = Timer::new(1_000, RELOAD, 1_000); timer.set_syst(1);
583 let t1 = timer.now();
584 assert_eq!(t1, 100 - 1);
585
586 timer.set_pendst_pending(true);
589 timer.set_syst(RELOAD - 10); let t2 = timer.now();
593 let expected_t2 = (0 + 1) * (RELOAD as u64 + 1) + (RELOAD as u64 - (RELOAD as u64 - 10));
594 assert_eq!(t2, expected_t2);
595 assert!(
596 t2 > t1,
597 "Time must advance after wrap. t1={}, t2={}",
598 t1,
599 t2
600 );
601
602 timer.set_syst(RELOAD - 20);
604
605 let t3 = timer.now();
607 let expected_t3 = (0 + 1) * (RELOAD as u64 + 1) + (RELOAD as u64 - (RELOAD as u64 - 20));
608 assert_eq!(t3, expected_t3);
609 assert!(
610 t3 > t2,
611 "Time must advance even if ISR is starved. t2={}, t3={}",
612 t2,
613 t3
614 );
615
616 timer.set_pendst_pending(false);
618 timer.systick_handler(); let t4 = timer.now();
622 let expected_t4 = 1 * (RELOAD as u64 + 1) + (RELOAD as u64 - (RELOAD as u64 - 20));
623 assert_eq!(t4, expected_t4);
624 assert_eq!(
625 t4, t3,
626 "Time should be consistent after ISR runs. t3={}, t4={}",
627 t3, t4
628 );
629 }
630
631 }
635
636#[cfg(test)]
637mod stress_test;