20
20
//! let mut core = CorePeripherals::take().unwrap();
21
21
//! // (...)
22
22
//! 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() )
24
24
//! .unwrap();
25
25
//! unsafe {
26
26
//! embedded_profiling::set_profiler(dwt_profiler).unwrap();
@@ -65,6 +65,16 @@ static ROLLOVER_COUNT: AtomicU32 = AtomicU32::new(0);
65
65
// For extended mode to work, we really need a u64 container. Double check this.
66
66
static_assertions:: assert_type_eq_all!( EPContainer , u64 ) ;
67
67
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
+
68
78
/// DWT trace unit implementing [`EmbeddedProfiler`].
69
79
///
70
80
/// The frequency of the [`DWT`] is encoded using the parameter `FREQ`.
@@ -80,37 +90,73 @@ impl<const FREQ: u32> DwtProfiler<FREQ> {
80
90
///
81
91
/// # Panics
82
92
/// 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 > {
85
94
assert ! ( FREQ == sysclk) ;
86
95
96
+ // check if our HW supports it
97
+ if !dwt. has_cycle_counter ( ) {
98
+ return Err ( DwtProfilerError :: CycleCounterUnsupported ) ;
99
+ }
100
+
87
101
// Enable the DWT block
88
102
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
- }
94
103
DWT :: unlock ( ) ;
95
104
96
105
// reset cycle count and enable it to run
97
106
unsafe { dwt. cyccnt . write ( 0 ) } ;
98
107
dwt. enable_cycle_counter ( ) ;
99
108
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 } )
101
123
}
102
124
}
103
125
104
126
impl < const FREQ : u32 > EmbeddedProfiler for DwtProfiler < FREQ > {
105
127
fn read_clock ( & self ) -> EPInstant {
106
128
// 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
+ } ;
114
160
115
161
// convert count and return the instant
116
162
embedded_profiling:: convert_instant ( EPInstantGeneric :: < 1 , FREQ > :: from_ticks ( count) )
@@ -125,5 +171,5 @@ impl<const FREQ: u32> EmbeddedProfiler for DwtProfiler<FREQ> {
125
171
#[ exception]
126
172
#[ allow( non_snake_case) ]
127
173
fn DebugMonitor ( ) {
128
- ROLLOVER_COUNT . fetch_add ( 1 , Ordering :: Relaxed ) ;
174
+ ROLLOVER_COUNT . fetch_add ( 1 , Ordering :: Release ) ;
129
175
}
0 commit comments