Skip to content

Commit b2adadb

Browse files
committed
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
1 parent 79ec62c commit b2adadb

File tree

4 files changed

+71
-19
lines changed

4 files changed

+71
-19
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@ codegen-units = 1
1313
debug = true
1414
lto = true
1515
opt-level = 'z'
16+
17+
[patch.crates-io]
18+
cortex-m = { git = 'https://github.com/TDHolmes/cortex-m', package = 'cortex-m', branch = 'comparator-changes-only' }

embedded-profiling-examples/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ default = ["panic_persist"]
3131
usb = ["usb-device", "usbd-serial"]
3232
panic_persist = ["panic-persist"]
3333
panic_halt = ["panic-halt"]
34-
extended = ["ep-systick/extended", "embedded-profiling/container-u64"]
34+
extended = ["ep-systick/extended", "ep-dwt/extended", "embedded-profiling/container-u64"]
3535

3636
[[bin]]
3737
name = "delay_usb_dwt"

embedded-profiling-examples/src/bin/delay_usb_dwt.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ fn main() -> ! {
8181
// initialize our profiling timer & structure
8282
log::debug!("initializing our tracing stuff");
8383
let dwt_profiler = cortex_m::singleton!(: ep_dwt::DwtProfiler<CORE_FREQ> =
84-
ep_dwt::DwtProfiler::new(&mut core.DCB, core.DWT, CORE_FREQ))
84+
ep_dwt::DwtProfiler::new(&mut core.DCB, core.DWT, CORE_FREQ).unwrap())
8585
.unwrap();
8686
unsafe {
8787
ep::set_profiler(dwt_profiler).unwrap();

ep-dwt/src/lib.rs

Lines changed: 66 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
//! let mut core = CorePeripherals::take().unwrap();
2121
//! // (...)
2222
//! let dwt_profiler = cortex_m::singleton!(: ep_dwt::DwtProfiler::<CORE_FREQ> =
23-
//! ep_dwt::DwtProfiler::<CORE_FREQ>::new(&mut core.DCB, core.DWT, CORE_FREQ))
23+
//! ep_dwt::DwtProfiler::<CORE_FREQ>::new(&mut core.DCB, core.DWT, CORE_FREQ).unwrap())
2424
//! .unwrap();
2525
//! unsafe {
2626
//! embedded_profiling::set_profiler(dwt_profiler).unwrap();
@@ -53,7 +53,7 @@ use embedded_profiling::{EPContainer, EPInstant, EPInstantGeneric, EPSnapshot, E
5353
use cortex_m::peripheral::{DCB, DWT};
5454

5555
#[cfg(feature = "extended")]
56-
use core::sync::atomic::{AtomicU32, Ordering};
56+
use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
5757
#[cfg(feature = "extended")]
5858
use cortex_m_rt::exception;
5959

@@ -65,6 +65,16 @@ static ROLLOVER_COUNT: AtomicU32 = AtomicU32::new(0);
6565
// For extended mode to work, we really need a u64 container. Double check this.
6666
static_assertions::assert_type_eq_all!(EPContainer, u64);
6767

68+
#[derive(Debug)]
69+
/// Things that can go wrong when configuring the [`DWT`] hardware
70+
pub enum DwtProfilerError {
71+
/// [`cortex_m::peripheral::DWT::has_cycle_counter()`] reported that this hardware
72+
/// does not support cycle count hardware
73+
CycleCounterUnsupported,
74+
/// We failed to configure cycle count compare for the `extended` feature
75+
CycleCounterInvalidSettings,
76+
}
77+
6878
/// DWT trace unit implementing [`EmbeddedProfiler`].
6979
///
7080
/// The frequency of the [`DWT`] is encoded using the parameter `FREQ`.
@@ -80,36 +90,75 @@ impl<const FREQ: u32> DwtProfiler<FREQ> {
8090
///
8191
/// # Panics
8292
/// asserts that the compile time constant `FREQ` matches the runtime provided `sysclk`
83-
pub fn new(dcb: &mut DCB, mut dwt: DWT, sysclk: u32) -> Self {
93+
pub fn new(dcb: &mut DCB, mut dwt: DWT, sysclk: u32) -> Result<Self, DwtProfilerError> {
8494
assert!(FREQ == sysclk);
8595

96+
// check if our HW supports it
97+
if !dwt.has_cycle_counter() {
98+
return Err(DwtProfilerError::CycleCounterUnsupported);
99+
}
100+
86101
// Enable the DWT block
87102
dcb.enable_trace();
88-
#[cfg(feature = "extended")]
89-
// Enable DebugMonitor exceptions to fire to track overflows
90-
unsafe {
91-
dcb.demcr.modify(|f| f | 1 << 16);
92-
}
93103
DWT::unlock();
94104

95105
// reset cycle count and enable it to run
96106
unsafe { dwt.cyccnt.write(0) };
97107
dwt.enable_cycle_counter();
98108

99-
Self { dwt }
109+
if cfg!(feature = "extended") {
110+
use cortex_m::peripheral::dwt::{ComparatorFunction, CycleCountSettings, EmitOption};
111+
112+
// Enable DebugMonitor exceptions to fire to track overflows
113+
dcb.enable_debug_monitor();
114+
dwt.comp0
115+
.configure(ComparatorFunction::CycleCount(CycleCountSettings {
116+
emit: EmitOption::WatchpointDebugEvent,
117+
compare: 4_294_967_295, // just before overflow (2**32 - 1)
118+
}))
119+
.map_err(|_conf_err| DwtProfilerError::CycleCounterInvalidSettings)?
120+
}
121+
122+
Ok(Self { dwt })
100123
}
101124
}
102125

103126
impl<const FREQ: u32> EmbeddedProfiler for DwtProfiler<FREQ> {
104127
fn read_clock(&self) -> EPInstant {
105128
// get the cycle count and add the rollover if we're extended
106-
#[allow(unused_mut)]
107-
let mut count = EPContainer::from(self.dwt.cyccnt.read());
108-
#[cfg(feature = "extended")]
109-
{
110-
count += EPContainer::from(ROLLOVER_COUNT.load(Ordering::Relaxed))
111-
* EPContainer::from(u32::MAX);
112-
}
129+
let count: EPContainer = {
130+
#[cfg(feature = "extended")]
131+
{
132+
/// Every time we roll over, we should add 2**32
133+
const ROLLOVER_AMOUNT: EPContainer = 0x1_0000_0000;
134+
135+
// read the clock & ROLLOVER_COUNT. We read `cyccnt` twice because we need to detect
136+
// if we've rolled over, and if we have make sure we have the right value for ROLLOVER_COUNT.
137+
let first = self.dwt.cyccnt.read();
138+
compiler_fence(Ordering::SeqCst);
139+
let rollover: EPContainer = ROLLOVER_COUNT.load(Ordering::Acquire).into();
140+
compiler_fence(Ordering::SeqCst);
141+
let second = self.dwt.cyccnt.read();
142+
143+
if first < second {
144+
// The usual case. We did not roll over between the first and second reading,
145+
// and because of that we also know we got a valid read on ROLLOVER_COUNT.
146+
rollover * ROLLOVER_AMOUNT + EPContainer::from(first)
147+
} else {
148+
// we rolled over sometime between the first and second read. We may or may not have
149+
// caught the right ROLLOVER_COUNT, so grab that again and then use the second reading.
150+
let rollover: EPContainer = ROLLOVER_COUNT.load(Ordering::Acquire).into();
151+
152+
rollover * ROLLOVER_AMOUNT + EPContainer::from(second)
153+
}
154+
}
155+
156+
#[cfg(not(feature = "extended"))]
157+
{
158+
// We aren't trying to be fancy here, we don't care if this rolled over from the last read.
159+
EPContainer::from(self.dwt.cyccnt.read())
160+
}
161+
};
113162

114163
// convert count and return the instant
115164
embedded_profiling::convert_instant(EPInstantGeneric::<1, FREQ>::from_ticks(count))
@@ -124,5 +173,5 @@ impl<const FREQ: u32> EmbeddedProfiler for DwtProfiler<FREQ> {
124173
#[exception]
125174
#[allow(non_snake_case)]
126175
fn DebugMonitor() {
127-
ROLLOVER_COUNT.fetch_add(1, Ordering::Relaxed);
176+
ROLLOVER_COUNT.fetch_add(1, Ordering::Release);
128177
}

0 commit comments

Comments
 (0)