Skip to content

Implement various interfaces for trace configuration #342

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Nov 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
720282f
tpiu: impl functions related to trace data output
tmplt Apr 28, 2021
f75f17f
dwt: impl functions related to trace generation
tmplt Apr 28, 2021
8c53e60
tpiu: fix always-zero field-comparison
tmplt Apr 29, 2021
be983a3
dwt: configure address comparison using struct
tmplt Apr 30, 2021
9305580
dwt: add missing #[inline]
tmplt Apr 30, 2021
9285dcc
dcb: add note about vendor-specific trace options
tmplt Apr 30, 2021
859f9d8
dwt: reimplement with bitfield
tmplt May 5, 2021
b533eb6
itm: impl functions related to trace generation
tmplt May 5, 2021
fb604a7
tpiu: fix flipped SWOSupports field, reimplement with bitfield
tmplt May 5, 2021
085e738
itm, dwt: limit some bitfields to u8
tmplt May 5, 2021
669c872
dwt: feature gate trace and PC samples out of armv6m
tmplt Sep 24, 2021
362ad2d
itm: fix field spelling
tmplt Sep 24, 2021
02853a4
itm: remove useless conversion
tmplt Sep 24, 2021
aa17958
allow clippy::upper_case_acronyms
tmplt Sep 24, 2021
880b947
Merge branch 'master' into feat/tracing
tmplt Oct 26, 2021
c470f8b
dwt, itm, tpiu: remove get_ prefix, as per Rust API guidelines
tmplt Nov 21, 2021
6ddc746
dwt: fix clippy::bool_comparison
tmplt Nov 21, 2021
35bb481
dwt: improve EmitOption docstring
tmplt Nov 21, 2021
021420b
dwt: don't inline Comparator::configure
tmplt Nov 21, 2021
0e64774
dwt: refactor out unnecessary explicit panic
tmplt Nov 21, 2021
d45bad7
tpiu: remove get_ prefix, as per Rust API guidelines
tmplt Nov 21, 2021
633a631
dwt: DWTError -> DwtError for in-crate consistency
tmplt Nov 21, 2021
09929b1
dwt: mark ComparatorFunction, DwtError as non-exhaustive
tmplt Nov 21, 2021
c37f80b
itm: properly document ITMSettings
tmplt Nov 21, 2021
1efe319
tpiu: use bitfield for SPPR
tmplt Nov 21, 2021
5a92298
tpiu: improve TYPE field documentation
tmplt Nov 21, 2021
92c15ed
dwt, itm, tpiu: derive common traits for structs/enums
tmplt Nov 21, 2021
360fb33
dwt: refactor enable_exception_tracing into enable/disable funs
tmplt Nov 27, 2021
c1d434a
bump MSRV
tmplt Nov 27, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/bors.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ delete_merged_branches = true
required_approvals = 1
status = [
"ci-linux (stable)",
"ci-linux (1.38.0)",
"ci-linux (1.40.0)",
"rustfmt",
"clippy",
]
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:

include:
# Test MSRV
- rust: 1.38.0
- rust: 1.40.0

# Test nightly but don't fail
- rust: nightly
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This project is developed and maintained by the [Cortex-M team][team].

## Minimum Supported Rust Version (MSRV)

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.
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.

## License

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
//!
//! # Minimum Supported Rust Version (MSRV)
//!
//! This crate is guaranteed to compile on stable Rust 1.38 and up. It *might*
//! 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.

#![cfg_attr(feature = "inline-asm", feature(asm))]
Expand Down
4 changes: 4 additions & 0 deletions src/peripheral/dcb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ impl DCB {
/// `peripheral::DWT` cycle counter to work properly.
/// As by STM documentation, this flag is not reset on
/// soft-reset, only on power reset.
///
/// Note: vendor-specific registers may have to be set to completely
/// enable tracing. For example, on the STM32F401RE, `TRACE_MODE`
/// and `TRACE_IOEN` must be configured in `DBGMCU_CR` register.
#[inline]
pub fn enable_trace(&mut self) {
// set bit 24 / TRCENA
Expand Down
220 changes: 188 additions & 32 deletions src/peripheral/dwt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ use volatile_register::WO;
use volatile_register::{RO, RW};

use crate::peripheral::DWT;
use bitfield::bitfield;

/// Register block
#[repr(C)]
pub struct RegisterBlock {
/// Control
pub ctrl: RW<u32>,
pub ctrl: RW<Ctrl>,
/// Cycle Count
#[cfg(not(armv6m))]
pub cyccnt: RW<u32>,
Expand Down Expand Up @@ -50,6 +51,21 @@ pub struct RegisterBlock {
pub lsr: RO<u32>,
}

bitfield! {
/// Control register.
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Ctrl(u32);
cyccntena, set_cyccntena: 0;
pcsamplena, set_pcsamplena: 12;
exctrcena, set_exctrcena: 16;
noprfcnt, _: 24;
nocyccnt, _: 25;
noexttrig, _: 26;
notrcpkt, _: 27;
u8, numcomp, _: 31, 28;
}

/// Comparator
#[repr(C)]
pub struct Comparator {
Expand All @@ -58,58 +74,57 @@ pub struct Comparator {
/// Comparator Mask
pub mask: RW<u32>,
/// Comparator Function
pub function: RW<u32>,
pub function: RW<Function>,
reserved: u32,
}

// DWT CTRL register fields
const NUMCOMP_OFFSET: u32 = 28;
const NOTRCPKT: u32 = 1 << 27;
const NOEXTTRIG: u32 = 1 << 26;
const NOCYCCNT: u32 = 1 << 25;
const NOPRFCNT: u32 = 1 << 24;
const CYCCNTENA: u32 = 1 << 0;
bitfield! {
#[repr(C)]
#[derive(Copy, Clone)]
/// Comparator FUNCTIONn register.
pub struct Function(u32);
u8, function, set_function: 3, 0;
emitrange, set_emitrange: 5;
cycmatch, set_cycmatch: 7;
datavmatch, set_datavmatch: 8;
matched, _: 24;
}

impl DWT {
/// Number of comparators implemented
///
/// A value of zero indicates no comparator support.
#[inline]
pub fn num_comp() -> u8 {
// NOTE(unsafe) atomic read with no side effects
unsafe { ((*Self::ptr()).ctrl.read() >> NUMCOMP_OFFSET) as u8 }
pub fn num_comp(&self) -> u8 {
self.ctrl.read().numcomp()
}

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

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

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

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

/// Enables the cycle counter
Expand All @@ -123,22 +138,55 @@ impl DWT {
#[cfg(not(armv6m))]
#[inline]
pub fn enable_cycle_counter(&mut self) {
unsafe { self.ctrl.modify(|r| r | CYCCNTENA) }
unsafe {
self.ctrl.modify(|mut r| {
r.set_cyccntena(true);
r
});
}
}

/// Disables the cycle counter
/// Returns `true` if the cycle counter is enabled
#[cfg(not(armv6m))]
#[inline]
pub fn disable_cycle_counter(&mut self) {
unsafe { self.ctrl.modify(|r| r & !CYCCNTENA) }
pub fn cycle_counter_enabled(&self) -> bool {
self.ctrl.read().cyccntena()
}

/// Returns `true` if the cycle counter is enabled
/// Enables exception tracing
#[cfg(not(armv6m))]
#[inline]
pub fn cycle_counter_enabled() -> bool {
// NOTE(unsafe) atomic read with no side effects
unsafe { (*Self::ptr()).ctrl.read() & CYCCNTENA != 0 }
pub fn enable_exception_tracing(&mut self) {
unsafe {
self.ctrl.modify(|mut r| {
r.set_exctrcena(true);
r
});
}
}

/// Disables exception tracing
#[cfg(not(armv6m))]
#[inline]
pub fn disable_exception_tracing(&mut self) {
unsafe {
self.ctrl.modify(|mut r| {
r.set_exctrcena(false);
r
});
}
}

/// Whether to periodically generate PC samples
#[cfg(not(armv6m))]
#[inline]
pub fn enable_pc_samples(&mut self, bit: bool) {
unsafe {
self.ctrl.modify(|mut r| {
r.set_pcsamplena(bit);
r
});
}
}

/// Returns the current clock cycle count
Expand Down Expand Up @@ -266,3 +314,111 @@ impl DWT {
unsafe { self.foldcnt.write(count as u32) }
}
}

/// Whether the comparator should match on read, write or read/write operations.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum AccessType {
/// Generate packet only when matched adress is read from.
ReadOnly,
/// Generate packet only when matched adress is written to.
WriteOnly,
/// Generate packet when matched adress is both read from and written to.
ReadWrite,
}

/// The sequence of packet(s) that should be emitted on comparator match.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum EmitOption {
/// Emit only trace data value packet.
Data,
/// Emit only trace address packet.
Address,
/// Emit only trace PC value packet
///
/// *NOTE* only compatible with [AccessType::ReadWrite].
PC,
/// Emit trace address and data value packets.
AddressData,
/// Emit trace PC value and data value packets.
PCData,
}

/// Settings for address matching
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub struct ComparatorAddressSettings {
/// The address to match against.
pub address: u32,
/// The address mask to match against.
pub mask: u32,
/// What sequence of packet(s) to emit on comparator match.
pub emit: EmitOption,
/// Whether to match on read, write or read/write operations.
pub access_type: AccessType,
}

/// The available functions of a DWT comparator.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[non_exhaustive]
pub enum ComparatorFunction {
/// Compare accessed memory addresses.
Address(ComparatorAddressSettings),
}

/// Possible error values returned on [Comparator::configure].
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[non_exhaustive]
pub enum DwtError {
/// Invalid combination of [AccessType] and [EmitOption].
InvalidFunction,
}

impl Comparator {
/// Configure the function of the comparator
#[allow(clippy::missing_inline_in_public_items)]
pub fn configure(&self, settings: ComparatorFunction) -> Result<(), DwtError> {
match settings {
ComparatorFunction::Address(settings) => unsafe {
// FUNCTION, EMITRANGE
// See Table C1-14
let (function, emit_range) = match (&settings.access_type, &settings.emit) {
(AccessType::ReadOnly, EmitOption::Data) => (0b1100, false),
(AccessType::ReadOnly, EmitOption::Address) => (0b1100, true),
(AccessType::ReadOnly, EmitOption::AddressData) => (0b1110, true),
(AccessType::ReadOnly, EmitOption::PCData) => (0b1110, false),

(AccessType::WriteOnly, EmitOption::Data) => (0b1101, false),
(AccessType::WriteOnly, EmitOption::Address) => (0b1101, true),
(AccessType::WriteOnly, EmitOption::AddressData) => (0b1111, true),
(AccessType::WriteOnly, EmitOption::PCData) => (0b1111, false),

(AccessType::ReadWrite, EmitOption::Data) => (0b0010, false),
(AccessType::ReadWrite, EmitOption::Address) => (0b0001, true),
(AccessType::ReadWrite, EmitOption::AddressData) => (0b0010, true),
(AccessType::ReadWrite, EmitOption::PCData) => (0b0011, false),

(AccessType::ReadWrite, EmitOption::PC) => (0b0001, false),
(_, EmitOption::PC) => return Err(DwtError::InvalidFunction),
};

self.function.modify(|mut r| {
r.set_function(function);
r.set_emitrange(emit_range);

// don't compare data value
r.set_datavmatch(false);

// don't compare cycle counter value
// NOTE: only needed for comparator 0, but is SBZP.
r.set_cycmatch(false);

r
});

self.comp.write(settings.address);
self.mask.write(settings.mask);
},
}

Ok(())
}
}
Loading