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();
@@ -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,37 +90,73 @@ impl<const FREQ: u32> DwtProfiler<FREQ> {
8090 ///
8191 /// # Panics
8292 /// asserts that the compile time constant `FREQ` matches the runtime provided `sysclk`
83- #[ must_use]
84- 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 > {
8594 assert ! ( FREQ == sysclk) ;
8695
96+ // check if our HW supports it
97+ if !dwt. has_cycle_counter ( ) {
98+ return Err ( DwtProfilerError :: CycleCounterUnsupported ) ;
99+ }
100+
87101 // Enable the DWT block
88102 dcb. enable_trace ( ) ;
89- #[ cfg( feature = "extended" ) ]
90- // Enable DebugMonitor exceptions to fire to track overflows
91- unsafe {
92- dcb. demcr . modify ( |f| f | 1 << 16 ) ;
93- }
94103 DWT :: unlock ( ) ;
95104
96105 // reset cycle count and enable it to run
97106 unsafe { dwt. cyccnt . write ( 0 ) } ;
98107 dwt. enable_cycle_counter ( ) ;
99108
100- 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 } )
101123 }
102124}
103125
104126impl < const FREQ : u32 > EmbeddedProfiler for DwtProfiler < FREQ > {
105127 fn read_clock ( & self ) -> EPInstant {
106128 // get the cycle count and add the rollover if we're extended
107- #[ allow( unused_mut) ]
108- let mut count = EPContainer :: from ( self . dwt . cyccnt . read ( ) ) ;
109- #[ cfg( feature = "extended" ) ]
110- {
111- count += EPContainer :: from ( ROLLOVER_COUNT . load ( Ordering :: Relaxed ) )
112- * EPContainer :: from ( u32:: MAX ) ;
113- }
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+ let rollover: EPContainer = ROLLOVER_COUNT . load ( Ordering :: Acquire ) . into ( ) ;
139+ let second = self . dwt . cyccnt . read ( ) ;
140+
141+ if first < second {
142+ // The usual case. We did not roll over between the first and second reading,
143+ // and because of that we also know we got a valid read on ROLLOVER_COUNT.
144+ rollover * ROLLOVER_AMOUNT + EPContainer :: from ( first)
145+ } else {
146+ // we rolled over sometime between the first and second read. We may or may not have
147+ // caught the right ROLLOVER_COUNT, so grab that again and then use the second reading.
148+ let rollover: EPContainer = ROLLOVER_COUNT . load ( Ordering :: Acquire ) . into ( ) ;
149+
150+ rollover * ROLLOVER_AMOUNT + EPContainer :: from ( second)
151+ }
152+ }
153+
154+ #[ cfg( not( feature = "extended" ) ) ]
155+ {
156+ // We aren't trying to be fancy here, we don't care if this rolled over from the last read.
157+ EPContainer :: from ( self . dwt . cyccnt . read ( ) )
158+ }
159+ } ;
114160
115161 // convert count and return the instant
116162 embedded_profiling:: convert_instant ( EPInstantGeneric :: < 1 , FREQ > :: from_ticks ( count) )
@@ -125,5 +171,5 @@ impl<const FREQ: u32> EmbeddedProfiler for DwtProfiler<FREQ> {
125171#[ exception]
126172#[ allow( non_snake_case) ]
127173fn DebugMonitor ( ) {
128- ROLLOVER_COUNT . fetch_add ( 1 , Ordering :: Relaxed ) ;
174+ ROLLOVER_COUNT . fetch_add ( 1 , Ordering :: Release ) ;
129175}
0 commit comments