Skip to content

Commit ae1d2a6

Browse files
bors[bot]tmplt
andauthored
Merge #342
342: Implement various interfaces for trace configuration r=adamgreig a=tmplt I'm working on tracing support and aim to implement functions that abstract the configuration of relevant peripherals. Of chief interest is `DWT`, `ITM` and `TPIU`. Some propored abstractions will go against what is established in the crate; I will ask for comments on these. Co-authored-by: Viktor Sonesten <v@tmplt.dev>
2 parents 4b53689 + c1d434a commit ae1d2a6

File tree

9 files changed

+465
-40
lines changed

9 files changed

+465
-40
lines changed

.github/bors.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ delete_merged_branches = true
33
required_approvals = 1
44
status = [
55
"ci-linux (stable)",
6-
"ci-linux (1.38.0)",
6+
"ci-linux (1.40.0)",
77
"rustfmt",
88
"clippy",
99
]

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616

1717
include:
1818
# Test MSRV
19-
- rust: 1.38.0
19+
- rust: 1.40.0
2020

2121
# Test nightly but don't fail
2222
- rust: nightly

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ This project is developed and maintained by the [Cortex-M team][team].
1111

1212
## Minimum Supported Rust Version (MSRV)
1313

14-
This crate is guaranteed to compile on stable Rust 1.38 and up. It might compile with older versions but that may change in any new patch release.
14+
This crate is guaranteed to compile on stable Rust 1.40 and up. It might compile with older versions but that may change in any new patch release.
1515

1616
## License
1717

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
//!
5353
//! # Minimum Supported Rust Version (MSRV)
5454
//!
55-
//! This crate is guaranteed to compile on stable Rust 1.38 and up. It *might*
55+
//! This crate is guaranteed to compile on stable Rust 1.40 and up. It *might*
5656
//! compile with older versions but that may change in any new patch release.
5757
5858
#![cfg_attr(feature = "inline-asm", feature(asm))]

src/peripheral/dcb.rs

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ impl DCB {
2525
/// `peripheral::DWT` cycle counter to work properly.
2626
/// As by STM documentation, this flag is not reset on
2727
/// soft-reset, only on power reset.
28+
///
29+
/// Note: vendor-specific registers may have to be set to completely
30+
/// enable tracing. For example, on the STM32F401RE, `TRACE_MODE`
31+
/// and `TRACE_IOEN` must be configured in `DBGMCU_CR` register.
2832
#[inline]
2933
pub fn enable_trace(&mut self) {
3034
// set bit 24 / TRCENA

src/peripheral/dwt.rs

+188-32
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ use volatile_register::WO;
55
use volatile_register::{RO, RW};
66

77
use crate::peripheral::DWT;
8+
use bitfield::bitfield;
89

910
/// Register block
1011
#[repr(C)]
1112
pub struct RegisterBlock {
1213
/// Control
13-
pub ctrl: RW<u32>,
14+
pub ctrl: RW<Ctrl>,
1415
/// Cycle Count
1516
#[cfg(not(armv6m))]
1617
pub cyccnt: RW<u32>,
@@ -50,6 +51,21 @@ pub struct RegisterBlock {
5051
pub lsr: RO<u32>,
5152
}
5253

54+
bitfield! {
55+
/// Control register.
56+
#[repr(C)]
57+
#[derive(Copy, Clone)]
58+
pub struct Ctrl(u32);
59+
cyccntena, set_cyccntena: 0;
60+
pcsamplena, set_pcsamplena: 12;
61+
exctrcena, set_exctrcena: 16;
62+
noprfcnt, _: 24;
63+
nocyccnt, _: 25;
64+
noexttrig, _: 26;
65+
notrcpkt, _: 27;
66+
u8, numcomp, _: 31, 28;
67+
}
68+
5369
/// Comparator
5470
#[repr(C)]
5571
pub struct Comparator {
@@ -58,58 +74,57 @@ pub struct Comparator {
5874
/// Comparator Mask
5975
pub mask: RW<u32>,
6076
/// Comparator Function
61-
pub function: RW<u32>,
77+
pub function: RW<Function>,
6278
reserved: u32,
6379
}
6480

65-
// DWT CTRL register fields
66-
const NUMCOMP_OFFSET: u32 = 28;
67-
const NOTRCPKT: u32 = 1 << 27;
68-
const NOEXTTRIG: u32 = 1 << 26;
69-
const NOCYCCNT: u32 = 1 << 25;
70-
const NOPRFCNT: u32 = 1 << 24;
71-
const CYCCNTENA: u32 = 1 << 0;
81+
bitfield! {
82+
#[repr(C)]
83+
#[derive(Copy, Clone)]
84+
/// Comparator FUNCTIONn register.
85+
pub struct Function(u32);
86+
u8, function, set_function: 3, 0;
87+
emitrange, set_emitrange: 5;
88+
cycmatch, set_cycmatch: 7;
89+
datavmatch, set_datavmatch: 8;
90+
matched, _: 24;
91+
}
7292

7393
impl DWT {
7494
/// Number of comparators implemented
7595
///
7696
/// A value of zero indicates no comparator support.
7797
#[inline]
78-
pub fn num_comp() -> u8 {
79-
// NOTE(unsafe) atomic read with no side effects
80-
unsafe { ((*Self::ptr()).ctrl.read() >> NUMCOMP_OFFSET) as u8 }
98+
pub fn num_comp(&self) -> u8 {
99+
self.ctrl.read().numcomp()
81100
}
82101

83102
/// Returns `true` if the the implementation supports sampling and exception tracing
84103
#[cfg(not(armv6m))]
85104
#[inline]
86-
pub fn has_exception_trace() -> bool {
87-
// NOTE(unsafe) atomic read with no side effects
88-
unsafe { (*Self::ptr()).ctrl.read() & NOTRCPKT == 0 }
105+
pub fn has_exception_trace(&self) -> bool {
106+
!self.ctrl.read().notrcpkt()
89107
}
90108

91109
/// Returns `true` if the implementation includes external match signals
92110
#[cfg(not(armv6m))]
93111
#[inline]
94-
pub fn has_external_match() -> bool {
95-
// NOTE(unsafe) atomic read with no side effects
96-
unsafe { (*Self::ptr()).ctrl.read() & NOEXTTRIG == 0 }
112+
pub fn has_external_match(&self) -> bool {
113+
!self.ctrl.read().noexttrig()
97114
}
98115

99116
/// Returns `true` if the implementation supports a cycle counter
100117
#[cfg(not(armv6m))]
101118
#[inline]
102-
pub fn has_cycle_counter() -> bool {
103-
// NOTE(unsafe) atomic read with no side effects
104-
unsafe { (*Self::ptr()).ctrl.read() & NOCYCCNT == 0 }
119+
pub fn has_cycle_counter(&self) -> bool {
120+
!self.ctrl.read().nocyccnt()
105121
}
106122

107123
/// Returns `true` if the implementation the profiling counters
108124
#[cfg(not(armv6m))]
109125
#[inline]
110-
pub fn has_profiling_counter() -> bool {
111-
// NOTE(unsafe) atomic read with no side effects
112-
unsafe { (*Self::ptr()).ctrl.read() & NOPRFCNT == 0 }
126+
pub fn has_profiling_counter(&self) -> bool {
127+
!self.ctrl.read().noprfcnt()
113128
}
114129

115130
/// Enables the cycle counter
@@ -123,22 +138,55 @@ impl DWT {
123138
#[cfg(not(armv6m))]
124139
#[inline]
125140
pub fn enable_cycle_counter(&mut self) {
126-
unsafe { self.ctrl.modify(|r| r | CYCCNTENA) }
141+
unsafe {
142+
self.ctrl.modify(|mut r| {
143+
r.set_cyccntena(true);
144+
r
145+
});
146+
}
127147
}
128148

129-
/// Disables the cycle counter
149+
/// Returns `true` if the cycle counter is enabled
130150
#[cfg(not(armv6m))]
131151
#[inline]
132-
pub fn disable_cycle_counter(&mut self) {
133-
unsafe { self.ctrl.modify(|r| r & !CYCCNTENA) }
152+
pub fn cycle_counter_enabled(&self) -> bool {
153+
self.ctrl.read().cyccntena()
134154
}
135155

136-
/// Returns `true` if the cycle counter is enabled
156+
/// Enables exception tracing
137157
#[cfg(not(armv6m))]
138158
#[inline]
139-
pub fn cycle_counter_enabled() -> bool {
140-
// NOTE(unsafe) atomic read with no side effects
141-
unsafe { (*Self::ptr()).ctrl.read() & CYCCNTENA != 0 }
159+
pub fn enable_exception_tracing(&mut self) {
160+
unsafe {
161+
self.ctrl.modify(|mut r| {
162+
r.set_exctrcena(true);
163+
r
164+
});
165+
}
166+
}
167+
168+
/// Disables exception tracing
169+
#[cfg(not(armv6m))]
170+
#[inline]
171+
pub fn disable_exception_tracing(&mut self) {
172+
unsafe {
173+
self.ctrl.modify(|mut r| {
174+
r.set_exctrcena(false);
175+
r
176+
});
177+
}
178+
}
179+
180+
/// Whether to periodically generate PC samples
181+
#[cfg(not(armv6m))]
182+
#[inline]
183+
pub fn enable_pc_samples(&mut self, bit: bool) {
184+
unsafe {
185+
self.ctrl.modify(|mut r| {
186+
r.set_pcsamplena(bit);
187+
r
188+
});
189+
}
142190
}
143191

144192
/// Returns the current clock cycle count
@@ -266,3 +314,111 @@ impl DWT {
266314
unsafe { self.foldcnt.write(count as u32) }
267315
}
268316
}
317+
318+
/// Whether the comparator should match on read, write or read/write operations.
319+
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
320+
pub enum AccessType {
321+
/// Generate packet only when matched adress is read from.
322+
ReadOnly,
323+
/// Generate packet only when matched adress is written to.
324+
WriteOnly,
325+
/// Generate packet when matched adress is both read from and written to.
326+
ReadWrite,
327+
}
328+
329+
/// The sequence of packet(s) that should be emitted on comparator match.
330+
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
331+
pub enum EmitOption {
332+
/// Emit only trace data value packet.
333+
Data,
334+
/// Emit only trace address packet.
335+
Address,
336+
/// Emit only trace PC value packet
337+
///
338+
/// *NOTE* only compatible with [AccessType::ReadWrite].
339+
PC,
340+
/// Emit trace address and data value packets.
341+
AddressData,
342+
/// Emit trace PC value and data value packets.
343+
PCData,
344+
}
345+
346+
/// Settings for address matching
347+
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
348+
pub struct ComparatorAddressSettings {
349+
/// The address to match against.
350+
pub address: u32,
351+
/// The address mask to match against.
352+
pub mask: u32,
353+
/// What sequence of packet(s) to emit on comparator match.
354+
pub emit: EmitOption,
355+
/// Whether to match on read, write or read/write operations.
356+
pub access_type: AccessType,
357+
}
358+
359+
/// The available functions of a DWT comparator.
360+
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
361+
#[non_exhaustive]
362+
pub enum ComparatorFunction {
363+
/// Compare accessed memory addresses.
364+
Address(ComparatorAddressSettings),
365+
}
366+
367+
/// Possible error values returned on [Comparator::configure].
368+
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
369+
#[non_exhaustive]
370+
pub enum DwtError {
371+
/// Invalid combination of [AccessType] and [EmitOption].
372+
InvalidFunction,
373+
}
374+
375+
impl Comparator {
376+
/// Configure the function of the comparator
377+
#[allow(clippy::missing_inline_in_public_items)]
378+
pub fn configure(&self, settings: ComparatorFunction) -> Result<(), DwtError> {
379+
match settings {
380+
ComparatorFunction::Address(settings) => unsafe {
381+
// FUNCTION, EMITRANGE
382+
// See Table C1-14
383+
let (function, emit_range) = match (&settings.access_type, &settings.emit) {
384+
(AccessType::ReadOnly, EmitOption::Data) => (0b1100, false),
385+
(AccessType::ReadOnly, EmitOption::Address) => (0b1100, true),
386+
(AccessType::ReadOnly, EmitOption::AddressData) => (0b1110, true),
387+
(AccessType::ReadOnly, EmitOption::PCData) => (0b1110, false),
388+
389+
(AccessType::WriteOnly, EmitOption::Data) => (0b1101, false),
390+
(AccessType::WriteOnly, EmitOption::Address) => (0b1101, true),
391+
(AccessType::WriteOnly, EmitOption::AddressData) => (0b1111, true),
392+
(AccessType::WriteOnly, EmitOption::PCData) => (0b1111, false),
393+
394+
(AccessType::ReadWrite, EmitOption::Data) => (0b0010, false),
395+
(AccessType::ReadWrite, EmitOption::Address) => (0b0001, true),
396+
(AccessType::ReadWrite, EmitOption::AddressData) => (0b0010, true),
397+
(AccessType::ReadWrite, EmitOption::PCData) => (0b0011, false),
398+
399+
(AccessType::ReadWrite, EmitOption::PC) => (0b0001, false),
400+
(_, EmitOption::PC) => return Err(DwtError::InvalidFunction),
401+
};
402+
403+
self.function.modify(|mut r| {
404+
r.set_function(function);
405+
r.set_emitrange(emit_range);
406+
407+
// don't compare data value
408+
r.set_datavmatch(false);
409+
410+
// don't compare cycle counter value
411+
// NOTE: only needed for comparator 0, but is SBZP.
412+
r.set_cycmatch(false);
413+
414+
r
415+
});
416+
417+
self.comp.write(settings.address);
418+
self.mask.write(settings.mask);
419+
},
420+
}
421+
422+
Ok(())
423+
}
424+
}

0 commit comments

Comments
 (0)