Skip to content

Commit 406a283

Browse files
committed
add support for extending the cycle counter to u64
This enables tracking time intervals of more than a couple seconds on modern processors.
1 parent 14b27b7 commit 406a283

File tree

3 files changed

+68
-13
lines changed

3 files changed

+68
-13
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1010
### Added
1111

1212
- CI changelog entry enforcer
13+
- New feature `extend` to track DWT cycle counter overflows and extend
14+
the range to `u64`.
1315

1416
## [v1.0.0] - 2021-12-25
1517

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ version = "1.0.0"
44
authors = [
55
"The Real-Time Interrupt-driven Concurrency developers",
66
"Emil Fresk <[email protected]>",
7+
"Robert Jördens <[email protected]>",
78
]
89
categories = ["concurrency", "embedded", "no-std"]
910
description = "RTIC Monotonic implemenation based on Systick and DWT"
@@ -15,9 +16,13 @@ edition = "2021"
1516
[lib]
1617
name = "dwt_systick_monotonic"
1718

19+
[features]
20+
extend = []
21+
1822
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1923

2024
[dependencies]
2125
cortex-m = "0.7"
2226
rtic-monotonic = "1.0.0"
2327
fugit = "0.3.0"
28+
cfg-if = "1.0"

src/lib.rs

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,33 @@
33
#![no_std]
44

55
use cortex_m::peripheral::{syst::SystClkSource, DCB, DWT, SYST};
6-
pub use fugit::{self, ExtU32};
6+
pub use fugit::{self, ExtU32, ExtU64};
77
use rtic_monotonic::Monotonic;
88

9-
/// DWT and Systick combination implementing `embedded_time::Clock` and `rtic_monotonic::Monotonic`
9+
/// DWT and Systick combination implementing `rtic_monotonic::Monotonic`.
10+
///
11+
/// This implementation is tickless. It does not use periodic interrupts to count
12+
/// "ticks" (like `systick-monotonic`) but only to obtain actual desired compare
13+
/// events and to manage overflows.
1014
///
1115
/// The frequency of the DWT and SysTick is encoded using the parameter `TIMER_HZ`.
16+
/// They must be equal.
17+
///
18+
/// Note that the SysTick interrupt must not be disabled longer than half the
19+
/// cycle counter overflow period (typically a couple seconds).
1220
pub struct DwtSystick<const TIMER_HZ: u32> {
1321
dwt: DWT,
1422
systick: SYST,
23+
#[cfg(feature = "extend")]
24+
last: u64,
1525
}
1626

1727
impl<const TIMER_HZ: u32> DwtSystick<TIMER_HZ> {
1828
/// Enable the DWT and provide a new `Monotonic` based on DWT and SysTick.
1929
///
2030
/// Note that the `sysclk` parameter should come from e.g. the HAL's clock generation function
21-
/// so the real speed and the declared speed can be compared.
31+
/// so the speed calculated at runtime and the declared speed (generic parameter
32+
/// `TIMER_HZ`) can be compared.
2233
#[inline(always)]
2334
pub fn new(dcb: &mut DCB, dwt: DWT, systick: SYST, sysclk: u32) -> Self {
2435
assert!(TIMER_HZ == sysclk);
@@ -30,19 +41,49 @@ impl<const TIMER_HZ: u32> DwtSystick<TIMER_HZ> {
3041

3142
// We do not start the counter here, it is started in `reset`.
3243

33-
DwtSystick { dwt, systick }
44+
DwtSystick {
45+
dwt,
46+
systick,
47+
#[cfg(feature = "extend")]
48+
last: 0,
49+
}
3450
}
3551
}
3652

3753
impl<const TIMER_HZ: u32> Monotonic for DwtSystick<TIMER_HZ> {
38-
const DISABLE_INTERRUPT_ON_EMPTY_QUEUE: bool = true;
39-
40-
type Instant = fugit::TimerInstantU32<TIMER_HZ>;
41-
type Duration = fugit::TimerDurationU32<TIMER_HZ>;
42-
43-
#[inline(always)]
44-
fn now(&mut self) -> Self::Instant {
45-
Self::Instant::from_ticks(self.dwt.cyccnt.read())
54+
cfg_if::cfg_if! {
55+
if #[cfg(not(feature = "extend"))] {
56+
const DISABLE_INTERRUPT_ON_EMPTY_QUEUE: bool = true;
57+
58+
type Instant = fugit::TimerInstantU32<TIMER_HZ>;
59+
type Duration = fugit::TimerDurationU32<TIMER_HZ>;
60+
61+
#[inline(always)]
62+
fn now(&mut self) -> Self::Instant {
63+
Self::Instant::from_ticks(self.dwt.cyccnt.read())
64+
}
65+
} else {
66+
// Need to detect and track overflows.
67+
const DISABLE_INTERRUPT_ON_EMPTY_QUEUE: bool = false;
68+
69+
type Instant = fugit::TimerInstantU64<TIMER_HZ>;
70+
type Duration = fugit::TimerDurationU64<TIMER_HZ>;
71+
72+
#[inline(always)]
73+
fn now(&mut self) -> Self::Instant {
74+
let mut high = (self.last >> 32) as u32;
75+
let low = self.last as u32;
76+
let now = self.dwt.cyccnt.read();
77+
78+
// Detect CYCCNT overflow
79+
if now.wrapping_sub(low) >= 1 << 31 {
80+
high = high.wrapping_add(1);
81+
}
82+
self.last = ((high as u64) << 32) | (now as u64);
83+
84+
Self::Instant::from_ticks(self.last)
85+
}
86+
}
4687
}
4788

4889
unsafe fn reset(&mut self) {
@@ -71,7 +112,7 @@ impl<const TIMER_HZ: u32> Monotonic for DwtSystick<TIMER_HZ> {
71112
Some(x) => max.min(x.ticks()).max(1),
72113
};
73114

74-
self.systick.set_reload(dur);
115+
self.systick.set_reload(dur as u32);
75116
self.systick.clear_current();
76117
}
77118

@@ -84,4 +125,11 @@ impl<const TIMER_HZ: u32> Monotonic for DwtSystick<TIMER_HZ> {
84125
fn clear_compare_flag(&mut self) {
85126
// NOOP with SysTick interrupt
86127
}
128+
129+
#[cfg(feature = "extend")]
130+
fn on_interrupt(&mut self) {
131+
// Ensure `now()` is called regularly to track overflows.
132+
// Since SysTick is narrower than CYCCNT, this is sufficient.
133+
self.now();
134+
}
87135
}

0 commit comments

Comments
 (0)