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
|
//! Instrumentation Trace Macrocell
//!
//! *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<u32>,
}
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;
}
/// 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(())`.
#[inline]
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,
}
impl ITM {
/// Removes the software lock on the ITM.
#[inline]
pub fn unlock(&mut self) {
// NOTE(unsafe) atomic write to a stateless, write-only register
unsafe { self.lar.write(0xC5AC_CE55) }
}
/// Configures the ITM with the passed [ITMSettings].
#[inline]
pub fn configure(&mut self, settings: ITMSettings) {
unsafe {
self.tcr.modify(|mut r| {
r.set_itmena(settings.enable);
r.set_tsena(settings.local_timestamps != LocalTimestampOptions::Disabled);
r.set_txena(settings.forward_dwt);
r.set_tsprescale(match settings.local_timestamps {
LocalTimestampOptions::Disabled | LocalTimestampOptions::Enabled => 0b00,
LocalTimestampOptions::EnabledDiv4 => 0b10,
LocalTimestampOptions::EnabledDiv16 => 0b10,
LocalTimestampOptions::EnabledDiv64 => 0b11,
});
r.set_gtsfreq(match settings.global_timestamps {
GlobalTimestampOptions::Disabled => 0b00,
GlobalTimestampOptions::Every128Cycles => 0b01,
GlobalTimestampOptions::Every8192Cycles => 0b10,
GlobalTimestampOptions::EveryPacket => 0b11,
});
r.set_swoena(match settings.timestamp_clk_src {
TimestampClkSrc::SystemClock => false,
TimestampClkSrc::AsyncTPIU => true,
});
r.set_tracebusid(settings.bus_id.unwrap_or(0));
r
});
}
}
}
|