Skip to content

Commit

Permalink
Merge #342
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
bors[bot] and tmplt authored Nov 27, 2021
2 parents 4b53689 + c1d434a commit ae1d2a6
Show file tree
Hide file tree
Showing 9 changed files with 465 additions and 40 deletions.
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

0 comments on commit ae1d2a6

Please sign in to comment.