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
5353use 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" ) ]
5858use 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.
6666static_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
103126impl < 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) ]
126175fn DebugMonitor ( ) {
127- ROLLOVER_COUNT . fetch_add ( 1 , Ordering :: Relaxed ) ;
176+ ROLLOVER_COUNT . fetch_add ( 1 , Ordering :: Release ) ;
128177}
0 commit comments