From 2aedaf2be9948fe6d62db2b39c77410b0e829e8c Mon Sep 17 00:00:00 2001 From: Tyler Holmes Date: Tue, 14 Dec 2021 07:56:35 -0800 Subject: [PATCH] fix cyccnt extension based on cortex-m change fire just before overflow instead and don't reset cyccnt latest cortex-m changes make `new` falible remove debug code --- Cargo.toml | 3 + embedded-profiling-examples/Cargo.toml | 2 +- .../src/bin/delay_usb_dwt.rs | 2 +- ep-dwt/src/lib.rs | 80 +++++++++++++++---- 4 files changed, 68 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fc2e48f..c0fd52b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,6 @@ codegen-units = 1 debug = true lto = true opt-level = 'z' + +[patch.crates-io] +cortex-m = { git = 'https://github.com/TDHolmes/cortex-m', package = 'cortex-m', branch = 'comparator-changes-only' } diff --git a/embedded-profiling-examples/Cargo.toml b/embedded-profiling-examples/Cargo.toml index 3568620..6bdff7d 100644 --- a/embedded-profiling-examples/Cargo.toml +++ b/embedded-profiling-examples/Cargo.toml @@ -31,7 +31,7 @@ default = ["panic_persist"] usb = ["usb-device", "usbd-serial"] panic_persist = ["panic-persist"] panic_halt = ["panic-halt"] -extended = ["ep-systick/extended", "embedded-profiling/container-u64"] +extended = ["ep-systick/extended", "ep-dwt/extended", "embedded-profiling/container-u64"] [[bin]] name = "delay_usb_dwt" diff --git a/embedded-profiling-examples/src/bin/delay_usb_dwt.rs b/embedded-profiling-examples/src/bin/delay_usb_dwt.rs index ae9e007..d041af3 100644 --- a/embedded-profiling-examples/src/bin/delay_usb_dwt.rs +++ b/embedded-profiling-examples/src/bin/delay_usb_dwt.rs @@ -81,7 +81,7 @@ fn main() -> ! { // initialize our profiling timer & structure log::debug!("initializing our tracing stuff"); let dwt_profiler = cortex_m::singleton!(: ep_dwt::DwtProfiler = - ep_dwt::DwtProfiler::new(&mut core.DCB, core.DWT, CORE_FREQ)) + ep_dwt::DwtProfiler::new(&mut core.DCB, core.DWT, CORE_FREQ).unwrap()) .unwrap(); unsafe { ep::set_profiler(dwt_profiler).unwrap(); diff --git a/ep-dwt/src/lib.rs b/ep-dwt/src/lib.rs index a829df7..5d8d160 100644 --- a/ep-dwt/src/lib.rs +++ b/ep-dwt/src/lib.rs @@ -20,7 +20,7 @@ //! let mut core = CorePeripherals::take().unwrap(); //! // (...) //! let dwt_profiler = cortex_m::singleton!(: ep_dwt::DwtProfiler:: = -//! ep_dwt::DwtProfiler::::new(&mut core.DCB, core.DWT, CORE_FREQ)) +//! ep_dwt::DwtProfiler::::new(&mut core.DCB, core.DWT, CORE_FREQ).unwrap()) //! .unwrap(); //! unsafe { //! embedded_profiling::set_profiler(dwt_profiler).unwrap(); @@ -65,6 +65,16 @@ static ROLLOVER_COUNT: AtomicU32 = AtomicU32::new(0); // For extended mode to work, we really need a u64 container. Double check this. static_assertions::assert_type_eq_all!(EPContainer, u64); +#[derive(Debug)] +/// Things that can go wrong when configuring the [`DWT`] hardware +pub enum DwtProfilerError { + /// [`cortex_m::peripheral::DWT::has_cycle_counter()`] reported that this hardware + /// does not support cycle count hardware + CycleCounterUnsupported, + /// We failed to configure cycle count compare for the `extended` feature + CycleCounterInvalidSettings, +} + /// DWT trace unit implementing [`EmbeddedProfiler`]. /// /// The frequency of the [`DWT`] is encoded using the parameter `FREQ`. @@ -80,37 +90,73 @@ impl DwtProfiler { /// /// # Panics /// asserts that the compile time constant `FREQ` matches the runtime provided `sysclk` - #[must_use] - pub fn new(dcb: &mut DCB, mut dwt: DWT, sysclk: u32) -> Self { + pub fn new(dcb: &mut DCB, mut dwt: DWT, sysclk: u32) -> Result { assert!(FREQ == sysclk); + // check if our HW supports it + if !dwt.has_cycle_counter() { + return Err(DwtProfilerError::CycleCounterUnsupported); + } + // Enable the DWT block dcb.enable_trace(); - #[cfg(feature = "extended")] - // Enable DebugMonitor exceptions to fire to track overflows - unsafe { - dcb.demcr.modify(|f| f | 1 << 16); - } DWT::unlock(); // reset cycle count and enable it to run unsafe { dwt.cyccnt.write(0) }; dwt.enable_cycle_counter(); - Self { dwt } + if cfg!(feature = "extended") { + use cortex_m::peripheral::dwt::{ComparatorFunction, CycleCountSettings, EmitOption}; + + // Enable DebugMonitor exceptions to fire to track overflows + dcb.enable_debug_monitor(); + dwt.comp0 + .configure(ComparatorFunction::CycleCount(CycleCountSettings { + emit: EmitOption::WatchpointDebugEvent, + compare: 4_294_967_295, // just before overflow (2**32 - 1) + })) + .map_err(|_conf_err| DwtProfilerError::CycleCounterInvalidSettings)? + } + + Ok(Self { dwt }) } } impl EmbeddedProfiler for DwtProfiler { fn read_clock(&self) -> EPInstant { // get the cycle count and add the rollover if we're extended - #[allow(unused_mut)] - let mut count = EPContainer::from(self.dwt.cyccnt.read()); - #[cfg(feature = "extended")] - { - count += EPContainer::from(ROLLOVER_COUNT.load(Ordering::Relaxed)) - * EPContainer::from(u32::MAX); - } + let count: EPContainer = { + #[cfg(feature = "extended")] + { + /// Every time we roll over, we should add 2**32 + const ROLLOVER_AMOUNT: EPContainer = 0x1_0000_0000; + + // read the clock & ROLLOVER_COUNT. We read `cyccnt` twice because we need to detect + // if we've rolled over, and if we have make sure we have the right value for ROLLOVER_COUNT. + let first = self.dwt.cyccnt.read(); + let rollover: EPContainer = ROLLOVER_COUNT.load(Ordering::Acquire).into(); + let second = self.dwt.cyccnt.read(); + + if first < second { + // The usual case. We did not roll over between the first and second reading, + // and because of that we also know we got a valid read on ROLLOVER_COUNT. + rollover * ROLLOVER_AMOUNT + EPContainer::from(first) + } else { + // we rolled over sometime between the first and second read. We may or may not have + // caught the right ROLLOVER_COUNT, so grab that again and then use the second reading. + let rollover: EPContainer = ROLLOVER_COUNT.load(Ordering::Acquire).into(); + + rollover * ROLLOVER_AMOUNT + EPContainer::from(second) + } + } + + #[cfg(not(feature = "extended"))] + { + // We aren't trying to be fancy here, we don't care if this rolled over from the last read. + EPContainer::from(self.dwt.cyccnt.read()) + } + }; // convert count and return the instant embedded_profiling::convert_instant(EPInstantGeneric::<1, FREQ>::from_ticks(count)) @@ -125,5 +171,5 @@ impl EmbeddedProfiler for DwtProfiler { #[exception] #[allow(non_snake_case)] fn DebugMonitor() { - ROLLOVER_COUNT.fetch_add(1, Ordering::Relaxed); + ROLLOVER_COUNT.fetch_add(1, Ordering::Release); }