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 10 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
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
167 changes: 164 additions & 3 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,16 @@ pub struct RegisterBlock {
pub lsr: RO<u32>,
}

bitfield! {
/// Control register.
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Ctrl(u32);
get_cyccntena, set_cyccntena: 0;
get_pcsamplena, set_pcsamplena: 12;
get_exctrcena, set_exctrcena: 16;
}

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

bitfield! {
#[repr(C)]
#[derive(Copy, Clone)]
/// Comparator FUNCTIONn register.
pub struct Function(u32);
u8, get_function, set_function: 3, 0;
get_emitrange, set_emitrange: 5;
get_cycmatch, set_cycmatch: 7;
get_datavmatch, set_datavmatch: 8;
get_matched, _: 24;
}

impl DWT {
/// Enables the cycle counter
#[cfg(not(armv6m))]
#[inline]
pub fn enable_cycle_counter(&mut self) {
unsafe { self.ctrl.modify(|r| r | 1) }
unsafe {
self.ctrl.modify(|mut r| {
r.set_cyccntena(true);
r
});
}
}

/// Whether to enable exception tracing
// TODO find out if this is supported om armv6m
#[inline]
pub fn enable_exception_tracing(&mut self, bit: bool) {
unsafe {
self.ctrl.modify(|mut r| {
r.set_exctrcena(bit);
r
});
}
}

/// Whether to periodically generate PC samples
// TODO find out if this is supported on 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 All @@ -88,3 +140,112 @@ impl DWT {
unsafe { (*Self::ptr()).lar.write(0xC5AC_CE55) }
}
}

/// Whether the comparator should match on read, write or read/write operations.
#[derive(Debug, PartialEq)]
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, PartialEq)]
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)]
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)]
pub enum ComparatorFunction {
/// Compare accessed memory addresses.
Address(ComparatorAddressSettings),
}

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

impl Comparator {
/// Configure the function of the comparator
#[inline]
pub fn configure(&self, settings: ComparatorFunction) -> Result<(), DWTError> {
match settings {
ComparatorFunction::Address(settings) => unsafe {
if settings.emit == EmitOption::PC && settings.access_type != AccessType::ReadWrite
{
return Err(DWTError::InvalidFunction);
}

self.function.modify(|mut r| {
// don't compare data value
r.set_datavmatch(false);

// dont compare cycle counter value
// NOTE: only needed forp comparator 0, but is SBZP.
r.set_cycmatch(false);

// 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) => unreachable!(), // cannot return Err here; handled above
};
r.set_function(function);
r.set_emitrange(emit_range);

r
});

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

Ok(())
}
}
124 changes: 123 additions & 1 deletion src/peripheral/itm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ use core::ptr;

use volatile_register::{RO, RW, WO};

use crate::peripheral::ITM;
use bitfield::bitfield;

/// Register block
#[repr(C)]
pub struct RegisterBlock {
Expand All @@ -20,14 +23,30 @@ pub struct RegisterBlock {
pub tpr: RW<u32>,
reserved2: [u32; 15],
/// Trace Control
pub tcr: RW<u32>,
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);
get_itmena, set_itmena: 0;
get_tsena, set_tsena: 1;
get_syncena, set_synena: 2;
get_txena, set_txena: 3;
get_swoena, set_swoena: 4;
u8, get_tsprescale, set_tsprecale: 9, 8;
u8, get_gtsfreq, set_gtsfreq: 11, 10;
u8, get_tracebusid, set_tracebusid: 22, 16;
busy, _: 23;
}

/// Stimulus Port
pub struct Stim {
register: UnsafeCell<u32>,
Expand Down Expand Up @@ -69,3 +88,106 @@ impl Stim {
unsafe { ptr::read_volatile(self.register.get()) & 0b11 != 0 }
}
}

/// The possible local timestamp options.
#[derive(Debug, PartialEq)]
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,
}

/// The possible global timestamp options.
#[derive(Debug)]
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)]
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,
}

/// blah
#[derive(Debug)]
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_tsprecale(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).into());

r
});
}
}
}
Loading