libs/kaptan-stm32f7: add DWT microsecond clock
This commit is contained in:
1 parent
1c7fccc4a0
commit
d7028a9bc1
1 file changed
+94
@@ -0,0 +1,94 @@
|
||||
//! DWT-backed monotonic microsecond clock for STM32F722.
|
||||
|
||||
use cortex_m::peripheral::{DCB, DWT};
|
||||
use kaptan_time::{MonotonicClock, Timestamp};
|
||||
|
||||
/// A DWT cycle counter extended into monotonic microseconds.
|
||||
///
|
||||
/// `now` must be called at least once during every 32-bit cycle-counter wrap
|
||||
/// interval. At the Kakute reset clock this is approximately 268 seconds; the
|
||||
/// board periodic service calls it much more frequently.
|
||||
pub struct DwtMicrosecondClock {
|
||||
cycles_per_microsecond: u32,
|
||||
previous_cycles: u32,
|
||||
elapsed_cycles: u64,
|
||||
}
|
||||
|
||||
/// Invalid fixed clock configuration for the DWT timestamp source.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum DwtClockStartError {
|
||||
/// A zero-megahertz core clock cannot produce microsecond timestamps.
|
||||
ZeroFrequency,
|
||||
}
|
||||
|
||||
impl DwtMicrosecondClock {
|
||||
/// Enables the DWT cycle counter for a fixed integer-MHz core clock.
|
||||
///
|
||||
/// `MEGAHERTZ` must match the board's core clock. This function is called
|
||||
/// once during board initialization before interrupts are enabled.
|
||||
pub fn start<const MEGAHERTZ: u32>(
|
||||
dcb: &mut DCB,
|
||||
dwt: &mut DWT,
|
||||
) -> Result<Self, DwtClockStartError> {
|
||||
if MEGAHERTZ == 0 {
|
||||
return Err(DwtClockStartError::ZeroFrequency);
|
||||
}
|
||||
|
||||
dcb.enable_trace();
|
||||
DWT::unlock();
|
||||
dwt.set_cycle_count(0);
|
||||
dwt.enable_cycle_counter();
|
||||
|
||||
Ok(Self {
|
||||
cycles_per_microsecond: MEGAHERTZ,
|
||||
previous_cycles: 0,
|
||||
elapsed_cycles: 0,
|
||||
})
|
||||
}
|
||||
|
||||
/// Captures the current monotonic microsecond timestamp.
|
||||
pub fn now(&mut self) -> Timestamp {
|
||||
self.advance_to(DWT::cycle_count())
|
||||
}
|
||||
|
||||
fn advance_to(&mut self, current_cycles: u32) -> Timestamp {
|
||||
let elapsed = current_cycles.wrapping_sub(self.previous_cycles);
|
||||
self.previous_cycles = current_cycles;
|
||||
self.elapsed_cycles = self.elapsed_cycles.saturating_add(u64::from(elapsed));
|
||||
|
||||
Timestamp::from_microseconds(self.elapsed_cycles / u64::from(self.cycles_per_microsecond))
|
||||
}
|
||||
}
|
||||
|
||||
impl MonotonicClock for DwtMicrosecondClock {
|
||||
fn now(&mut self) -> Timestamp {
|
||||
Self::now(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DwtMicrosecondClock;
|
||||
|
||||
fn clock(previous_cycles: u32) -> DwtMicrosecondClock {
|
||||
DwtMicrosecondClock {
|
||||
cycles_per_microsecond: 16,
|
||||
previous_cycles,
|
||||
elapsed_cycles: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn converts_cycles_to_microseconds() {
|
||||
let mut clock = clock(0);
|
||||
|
||||
assert_eq!(clock.advance_to(160).as_microseconds(), 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extends_a_single_cycle_counter_wrap() {
|
||||
let mut clock = clock(u32::MAX - 15);
|
||||
|
||||
assert_eq!(clock.advance_to(16).as_microseconds(), 2);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user