#![cfg_attr(not(test), no_std)]
use cortex_m::peripheral::{syst::SystClkSource, SYST};
use embedded_profiling::{EPContainer, EPInstant, EPInstantGeneric, EPSnapshot, EmbeddedProfiler};
#[cfg(feature = "extended")]
use core::sync::atomic::{AtomicU32, Ordering};
#[cfg(feature = "extended")]
static ROLLOVER_COUNT: AtomicU32 = AtomicU32::new(0);
const SYSTICK_RELOAD: u32 = 0x00FF_FFFF;
#[cfg(feature = "extended")]
const SYSTICK_RESOLUTION: EPContainer = 0x0100_0000;
pub struct SysTickProfiler<const FREQ: u32> {
#[allow(unused)]
systick: SYST,
}
impl<const FREQ: u32> SysTickProfiler<FREQ> {
#[must_use]
pub fn new(mut systick: SYST, sysclk: u32) -> Self {
assert!(FREQ == sysclk);
systick.disable_counter();
systick.set_clock_source(SystClkSource::Core);
systick.clear_current();
systick.set_reload(SYSTICK_RELOAD);
systick.enable_counter();
#[cfg(feature = "extended")]
systick.enable_interrupt();
Self { systick }
}
}
impl<const FREQ: u32> EmbeddedProfiler for SysTickProfiler<FREQ> {
fn read_clock(&self) -> EPInstant {
let count = {
#[cfg(feature = "extended")]
{
let first = SYST::get_current();
let rollover_count: EPContainer = ROLLOVER_COUNT.load(Ordering::Acquire).into();
let second = SYST::get_current();
if first > second {
rollover_count * SYSTICK_RESOLUTION + EPContainer::from(SYSTICK_RELOAD - first)
} else {
let rollover_count: EPContainer = ROLLOVER_COUNT.load(Ordering::Acquire).into();
rollover_count * SYSTICK_RESOLUTION + EPContainer::from(SYSTICK_RELOAD - second)
}
}
#[cfg(not(feature = "extended"))]
{
EPContainer::from(SYSTICK_RELOAD - SYST::get_current())
}
};
embedded_profiling::convert_instant(EPInstantGeneric::<1, FREQ>::from_ticks(count))
}
fn log_snapshot(&self, snapshot: &EPSnapshot) {
log::info!("{}", snapshot);
}
}
#[cfg(feature = "extended")]
use cortex_m_rt::exception;
#[cfg(feature = "extended")]
#[exception]
#[allow(non_snake_case)]
fn SysTick() {
ROLLOVER_COUNT.fetch_add(1, Ordering::Release);
}