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();
@@ -53,7 +53,7 @@ use embedded_profiling::{EPContainer, EPInstant, EPInstantGeneric, EPSnapshot, E
53
53
use cortex_m:: peripheral:: { DCB , DWT } ;
54
54
55
55
#[ cfg( feature = "extended" ) ]
56
- use core:: sync:: atomic:: { AtomicU32 , Ordering } ;
56
+ use core:: sync:: atomic:: { compiler_fence , AtomicU32 , Ordering } ;
57
57
#[ cfg( feature = "extended" ) ]
58
58
use cortex_m_rt:: exception;
59
59
@@ -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,36 +90,75 @@ 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
- 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 > {
84
94
assert ! ( FREQ == sysclk) ;
85
95
96
+ // check if our HW supports it
97
+ if !dwt. has_cycle_counter ( ) {
98
+ return Err ( DwtProfilerError :: CycleCounterUnsupported ) ;
99
+ }
100
+
86
101
// Enable the DWT block
87
102
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
- }
93
103
DWT :: unlock ( ) ;
94
104
95
105
// reset cycle count and enable it to run
96
106
unsafe { dwt. cyccnt . write ( 0 ) } ;
97
107
dwt. enable_cycle_counter ( ) ;
98
108
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 } )
100
123
}
101
124
}
102
125
103
126
impl < const FREQ : u32 > EmbeddedProfiler for DwtProfiler < FREQ > {
104
127
fn read_clock ( & self ) -> EPInstant {
105
128
// 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
+ } ;
113
162
114
163
// convert count and return the instant
115
164
embedded_profiling:: convert_instant ( EPInstantGeneric :: < 1 , FREQ > :: from_ticks ( count) )
@@ -124,5 +173,5 @@ impl<const FREQ: u32> EmbeddedProfiler for DwtProfiler<FREQ> {
124
173
#[ exception]
125
174
#[ allow( non_snake_case) ]
126
175
fn DebugMonitor ( ) {
127
- ROLLOVER_COUNT . fetch_add ( 1 , Ordering :: Relaxed ) ;
176
+ ROLLOVER_COUNT . fetch_add ( 1 , Ordering :: Release ) ;
128
177
}
0 commit comments