-
Notifications
You must be signed in to change notification settings - Fork 188
Expand file tree
/
Copy pathitm.rs
More file actions
361 lines (322 loc) · 11.5 KB
/
itm.rs
File metadata and controls
361 lines (322 loc) · 11.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
//! Instrumentation Trace Macrocell
//!
//! The documentation in this module contains references to ARM specifications, namely:
//! - coresight: [*ARM CoreSight Architecture Specification*, Version 3.0](https://developer.arm.com/documentation/ihi0029/latest).
//!
//! *NOTE* Not available on Armv6-M and Armv8-M Baseline.
use core::cell::UnsafeCell;
use core::ptr;
use volatile_register::{RO, RW, WO};
use crate::peripheral::ITM;
use bitfield::bitfield;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Register block
#[repr(C)]
pub struct RegisterBlock {
/// Stimulus Port
pub stim: [Stim; 256],
reserved0: [u32; 640],
/// Trace Enable
pub ter: [RW<u32>; 8],
reserved1: [u32; 8],
/// Trace Privilege
pub tpr: RW<u32>,
reserved2: [u32; 15],
/// Trace Control
pub tcr: RW<Tcr>,
reserved3: [u32; 75],
/// Lock Access
pub lar: WO<u32>,
/// Lock Status
pub lsr: RO<Lsr>,
}
bitfield! {
/// Trace Control Register.
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Tcr(u32);
itmena, set_itmena: 0;
tsena, set_tsena: 1;
syncena, set_synena: 2;
txena, set_txena: 3;
swoena, set_swoena: 4;
u8, tsprescale, set_tsprescale: 9, 8;
u8, gtsfreq, set_gtsfreq: 11, 10;
u8, tracebusid, set_tracebusid: 22, 16;
busy, _: 23;
}
bitfield! {
/// Software Lock Status Register
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Lsr(u32);
sli, _: 0;
slk, _: 1;
}
/// Stimulus Port
pub struct Stim {
register: UnsafeCell<u32>,
}
impl Stim {
/// Writes an `u8` payload into the stimulus port
#[inline]
pub fn write_u8(&mut self, value: u8) {
unsafe { ptr::write_volatile(self.register.get() as *mut u8, value) }
}
/// Writes an `u16` payload into the stimulus port
#[inline]
pub fn write_u16(&mut self, value: u16) {
unsafe { ptr::write_volatile(self.register.get() as *mut u16, value) }
}
/// Writes an `u32` payload into the stimulus port
#[inline]
pub fn write_u32(&mut self, value: u32) {
unsafe { ptr::write_volatile(self.register.get(), value) }
}
/// Returns `true` if the stimulus port is ready to accept more data
#[cfg(not(armv8m))]
#[inline]
pub fn is_fifo_ready(&self) -> bool {
unsafe { ptr::read_volatile(self.register.get()) & 0b1 == 1 }
}
/// Returns `true` if the stimulus port is ready to accept more data
#[cfg(armv8m)]
#[inline]
pub fn is_fifo_ready(&self) -> bool {
// ARMv8-M adds a disabled bit; we indicate that we are ready to
// proceed with a stimulus write if the port is either ready (bit 0) or
// disabled (bit 1).
unsafe { ptr::read_volatile(self.register.get()) & 0b11 != 0 }
}
}
/// The possible local timestamp options.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum LocalTimestampOptions {
/// Disable local timestamps.
Disabled,
/// Enable local timestamps and use no prescaling.
Enabled,
/// Enable local timestamps and set the prescaler to divide the
/// reference clock by 4.
EnabledDiv4,
/// Enable local timestamps and set the prescaler to divide the
/// reference clock by 16.
EnabledDiv16,
/// Enable local timestamps and set the prescaler to divide the
/// reference clock by 64.
EnabledDiv64,
}
#[cfg(feature = "std")]
impl core::convert::TryFrom<u8> for LocalTimestampOptions {
type Error = ();
/// Converts an integer value to an enabled [LocalTimestampOptions]
/// variant. Accepted values are: 1, 4, 16, 64. Any other value
/// yields `Err(())`.
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
1 => Ok(Self::Enabled),
4 => Ok(Self::EnabledDiv4),
16 => Ok(Self::EnabledDiv16),
64 => Ok(Self::EnabledDiv64),
_ => Err(()),
}
}
}
/// The possible global timestamp options.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum GlobalTimestampOptions {
/// Disable global timestamps.
Disabled,
/// Generate a global timestamp approximately every 128 cycles.
Every128Cycles,
/// Generate a global timestamp approximately every 8921 cycles.
Every8192Cycles,
/// Generate a global timestamp after every packet, if the output FIFO is empty.
EveryPacket,
}
/// The possible clock sources for timestamp counters.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum TimestampClkSrc {
/// Clock timestamp counters using the system processor clock.
SystemClock,
/// Clock timestamp counters using the asynchronous clock from the
/// TPIU interface.
///
/// NOTE: The timestamp counter is held in reset while the output
/// line is idle.
AsyncTPIU,
}
/// Available settings for the ITM peripheral.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub struct ITMSettings {
/// Whether to enable ITM.
pub enable: bool,
/// Whether DWT packets should be forwarded to ITM.
pub forward_dwt: bool,
/// The local timestamp options that should be applied.
pub local_timestamps: LocalTimestampOptions,
/// The global timestamp options that should be applied.
pub global_timestamps: GlobalTimestampOptions,
/// The trace bus ID to use when multi-trace sources are in use.
/// `None` specifies that only a single trace source is in use and
/// has the same effect as `Some(0)`.
pub bus_id: Option<u8>,
/// The clock that should increase timestamp counters.
pub timestamp_clk_src: TimestampClkSrc,
}
/// Possible errors on [ITM::configure].
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum ITMConfigurationError {
/// Global timestamp generation is not supported on this target.
/// Request [`GlobalTimestampOptions::Disabled`] instead.
///
/// [`ITM_TCR`](struct@Tcr) register remains unchanged on this error.
GTS,
/// The requested timestamp clock source is not supported on this target.
///
/// *NOTE*: `GTSFREQ` in [`ITM_TCR`](struct@Tcr) field has
/// potentially been changed on this error.
TimestampClkSrc,
/// The target does not implement the local timestamp prescaler.
/// Request [`LocalTimestampOptions::Disabled`] or
/// [`LocalTimestampOptions::Disabled`] instead.
///
/// *NOTE*: `GTSFREQ` and `SWOENA` in [`ITM_TCR`](struct@Tcr) fields
/// have potentially changed on this error.
TSPrescale,
}
impl ITM {
/// Removes the software lock on the [`ITM`]. Must be called before
/// any mutating [`ITM`] functions if a software lock mechanism is
/// implemented. See [`has_software_lock`].
///
/// See (coresight, B2.3.10).
#[inline]
pub fn unlock(&mut self) {
// NOTE(unsafe) atomic write to a stateless, write-only register
unsafe { self.lar.write(0xC5AC_CE55) }
}
/// Adds the software lock on the [`ITM`]. Should be called after any other mutating [`ITM`] functions.
///
/// See (coresight, B2.3.10).
#[inline]
pub fn lock(&mut self) {
// NOTE(unsafe) atomic write to a stateless, write-only register
unsafe { self.lar.write(0) }
}
/// Checks whether the target implements the software lock
/// mechanism. If `true`, [`unlock`] must be called before any other
/// mutating [`ITM`] functions.
///
/// See (coresight, B2.3.10).
#[inline]
pub fn has_software_lock(&self) -> bool {
self.lsr.read().sli()
}
/// Checks whether the peripheral is locked.
///
/// See (coresight, B2.3.10).
#[inline]
pub fn locked(&self) -> bool {
self.lsr.read().slk()
}
/// Indicates whether the [`ITM`] is currently processing events.
/// Returns `true` if [`ITM`] events are present and are being drained.
#[inline]
pub fn busy(&self) -> bool {
self.tcr.read().busy()
}
/// Configures the [`ITM`] with the passed [`ITMSettings`]. Returns `true`
/// if the configuration was successfully applied.
#[allow(clippy::missing_inline_in_public_items)]
pub fn configure(&mut self, settings: ITMSettings) -> Result<(), ITMConfigurationError> {
use ITMConfigurationError as Error;
// The ITM must be unlocked before we apply any changes.
if self.has_software_lock() && self.locked() {
self.unlock();
while self.locked() {}
}
// The ITM must then be disabled before altering certain fields
// in order to avoid trace stream corruption.
//
// NOTE: this is only required before modifying the TraceBusID
// field, but better be on the safe side for now.
unsafe {
self.tcr.modify(|mut r| {
r.set_itmena(false);
r
});
while self.busy() {}
}
unsafe {
self.tcr.modify(|mut r| {
r.set_gtsfreq(match settings.global_timestamps {
GlobalTimestampOptions::Disabled => 0b00,
GlobalTimestampOptions::Every128Cycles => 0b01,
GlobalTimestampOptions::Every8192Cycles => 0b10,
GlobalTimestampOptions::EveryPacket => 0b11,
});
r
});
}
// GTSFREQ is potentially RAZ/WI
if settings.global_timestamps != GlobalTimestampOptions::Disabled
&& self.tcr.read().gtsfreq() == 0
{
return Err(Error::GTS);
}
unsafe {
self.tcr.modify(|mut r| {
r.set_swoena(match settings.timestamp_clk_src {
TimestampClkSrc::SystemClock => false,
TimestampClkSrc::AsyncTPIU => true,
});
r
});
}
// SWOENA is potentially either RAZ or RAO
if !{
match settings.timestamp_clk_src {
TimestampClkSrc::SystemClock => !self.tcr.read().swoena(),
TimestampClkSrc::AsyncTPIU => self.tcr.read().swoena(),
}
} {
return Err(Error::TimestampClkSrc);
}
unsafe {
self.tcr.modify(|mut r| {
r.set_tsprescale(match settings.local_timestamps {
LocalTimestampOptions::Disabled | LocalTimestampOptions::Enabled => 0b00,
LocalTimestampOptions::EnabledDiv4 => 0b10,
LocalTimestampOptions::EnabledDiv16 => 0b10,
LocalTimestampOptions::EnabledDiv64 => 0b11,
});
r
})
}
// TSPrescale is potentially RAZ/WI
if settings.local_timestamps != LocalTimestampOptions::Disabled
&& settings.local_timestamps != LocalTimestampOptions::Enabled
&& self.tcr.read().tsprescale() == 0
{
return Err(Error::TSPrescale);
}
unsafe {
self.tcr.modify(|mut r| {
r.set_tsena(settings.local_timestamps != LocalTimestampOptions::Disabled);
r.set_txena(settings.forward_dwt); // forward hardware event packets from the DWT to the ITM
r.set_tracebusid(settings.bus_id.unwrap_or(0));
// must be modified after TraceBusID, see last section in
// <https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/The-Instrumentation-Trace-Macrocell/Trace-Control-Register--ITM-TCR?lang=en>
r.set_itmena(settings.enable);
r
});
}
if self.has_software_lock() {
self.lock();
}
Ok(())
}
}