aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/bors.toml2
-rw-r--r--.github/workflows/ci.yml2
-rw-r--r--README.md2
-rw-r--r--src/lib.rs2
-rw-r--r--src/peripheral/dcb.rs4
-rw-r--r--src/peripheral/dwt.rs220
-rw-r--r--src/peripheral/itm.rs124
-rw-r--r--src/peripheral/mod.rs14
-rw-r--r--src/peripheral/tpiu.rs135
9 files changed, 465 insertions, 40 deletions
diff --git a/.github/bors.toml b/.github/bors.toml
index 63d883f..0cf10c7 100644
--- a/.github/bors.toml
+++ b/.github/bors.toml
@@ -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",
]
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7289085..8600054 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,7 +16,7 @@ jobs:
include:
# Test MSRV
- - rust: 1.38.0
+ - rust: 1.40.0
# Test nightly but don't fail
- rust: nightly
diff --git a/README.md b/README.md
index 6bd8aed..6011ab2 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/src/lib.rs b/src/lib.rs
index 6a73692..a267750 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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))]
diff --git a/src/peripheral/dcb.rs b/src/peripheral/dcb.rs
index 5689cb4..056150b 100644
--- a/src/peripheral/dcb.rs
+++ b/src/peripheral/dcb.rs
@@ -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
diff --git a/src/peripheral/dwt.rs b/src/peripheral/dwt.rs
index 9e8e638..11dd545 100644
--- a/src/peripheral/dwt.rs
+++ b/src/peripheral/dwt.rs
@@ -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>,
@@ -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 {
@@ -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
@@ -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
@@ -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(())
+ }
+}
diff --git a/src/peripheral/itm.rs b/src/peripheral/itm.rs
index c0d560f..4d0aa22 100644
--- a/src/peripheral/itm.rs
+++ b/src/peripheral/itm.rs
@@ -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 {
@@ -20,7 +23,7 @@ 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>,
@@ -28,6 +31,22 @@ pub struct RegisterBlock {
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>,
@@ -69,3 +88,106 @@ impl Stim {
unsafe { ptr::read_volatile(self.register.get()) & 0b11 != 0 }
}
}
+
+/// The possible local timestamp options.
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+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, 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
+ });
+ }
+ }
+}
diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs
index 5c5e7ce..081aa0a 100644
--- a/src/peripheral/mod.rs
+++ b/src/peripheral/mod.rs
@@ -253,6 +253,7 @@ impl AC {
}
/// Cache and branch predictor maintenance operations
+#[allow(clippy::upper_case_acronyms)]
pub struct CBP {
_marker: PhantomData<*const ()>,
}
@@ -289,6 +290,7 @@ impl ops::Deref for CBP {
}
/// CPUID
+#[allow(clippy::upper_case_acronyms)]
pub struct CPUID {
_marker: PhantomData<*const ()>,
}
@@ -316,6 +318,7 @@ impl ops::Deref for CPUID {
}
/// Debug Control Block
+#[allow(clippy::upper_case_acronyms)]
pub struct DCB {
_marker: PhantomData<*const ()>,
}
@@ -343,6 +346,7 @@ impl ops::Deref for DCB {
}
/// Data Watchpoint and Trace unit
+#[allow(clippy::upper_case_acronyms)]
pub struct DWT {
_marker: PhantomData<*const ()>,
}
@@ -370,6 +374,7 @@ impl ops::Deref for DWT {
}
/// Flash Patch and Breakpoint unit
+#[allow(clippy::upper_case_acronyms)]
pub struct FPB {
_marker: PhantomData<*const ()>,
}
@@ -399,6 +404,7 @@ impl ops::Deref for FPB {
}
/// Floating Point Unit
+#[allow(clippy::upper_case_acronyms)]
pub struct FPU {
_marker: PhantomData<*const ()>,
}
@@ -433,6 +439,7 @@ impl ops::Deref for FPU {
/// `actlr`. It's called the "implementation control block" in the ARMv8-M
/// standard, but earlier standards contained the registers, just without a
/// name.
+#[allow(clippy::upper_case_acronyms)]
pub struct ICB {
_marker: PhantomData<*const ()>,
}
@@ -467,6 +474,7 @@ impl ops::DerefMut for ICB {
}
/// Instrumentation Trace Macrocell
+#[allow(clippy::upper_case_acronyms)]
pub struct ITM {
_marker: PhantomData<*const ()>,
}
@@ -504,6 +512,7 @@ impl ops::DerefMut for ITM {
}
/// Memory Protection Unit
+#[allow(clippy::upper_case_acronyms)]
pub struct MPU {
_marker: PhantomData<*const ()>,
}
@@ -531,6 +540,7 @@ impl ops::Deref for MPU {
}
/// Nested Vector Interrupt Controller
+#[allow(clippy::upper_case_acronyms)]
pub struct NVIC {
_marker: PhantomData<*const ()>,
}
@@ -558,6 +568,7 @@ impl ops::Deref for NVIC {
}
/// Security Attribution Unit
+#[allow(clippy::upper_case_acronyms)]
pub struct SAU {
_marker: PhantomData<*const ()>,
}
@@ -587,6 +598,7 @@ impl ops::Deref for SAU {
}
/// System Control Block
+#[allow(clippy::upper_case_acronyms)]
pub struct SCB {
_marker: PhantomData<*const ()>,
}
@@ -614,6 +626,7 @@ impl ops::Deref for SCB {
}
/// SysTick: System Timer
+#[allow(clippy::upper_case_acronyms)]
pub struct SYST {
_marker: PhantomData<*const ()>,
}
@@ -641,6 +654,7 @@ impl ops::Deref for SYST {
}
/// Trace Port Interface Unit
+#[allow(clippy::upper_case_acronyms)]
pub struct TPIU {
_marker: PhantomData<*const ()>,
}
diff --git a/src/peripheral/tpiu.rs b/src/peripheral/tpiu.rs
index 11cb79e..3ff5f55 100644
--- a/src/peripheral/tpiu.rs
+++ b/src/peripheral/tpiu.rs
@@ -4,6 +4,9 @@
use volatile_register::{RO, RW, WO};
+use crate::peripheral::TPIU;
+use bitfield::bitfield;
+
/// Register block
#[repr(C)]
pub struct RegisterBlock {
@@ -16,10 +19,10 @@ pub struct RegisterBlock {
pub acpr: RW<u32>,
reserved1: [u32; 55],
/// Selected Pin Control
- pub sppr: RW<u32>,
+ pub sppr: RW<Sppr>,
reserved2: [u32; 132],
/// Formatter and Flush Control
- pub ffcr: RW<u32>,
+ pub ffcr: RW<Ffcr>,
reserved3: [u32; 810],
/// Lock Access
pub lar: WO<u32>,
@@ -27,5 +30,131 @@ pub struct RegisterBlock {
pub lsr: RO<u32>,
reserved4: [u32; 4],
/// TPIU Type
- pub _type: RO<u32>,
+ pub _type: RO<Type>,
+}
+
+bitfield! {
+ /// Formatter and flush control register.
+ #[repr(C)]
+ #[derive(Clone, Copy)]
+ pub struct Ffcr(u32);
+ enfcont, set_enfcont: 1;
+}
+
+bitfield! {
+ /// TPIU Type Register.
+ #[repr(C)]
+ #[derive(Clone, Copy)]
+ pub struct Type(u32);
+ u8, fifosz, _: 8, 6;
+ ptinvalid, _: 9;
+ mancvalid, _: 10;
+ nrzvalid, _: 11;
+}
+
+bitfield! {
+ /// Selected pin protocol register.
+ #[repr(C)]
+ #[derive(Clone, Copy)]
+ pub struct Sppr(u32);
+ u8, txmode, set_txmode: 1, 0;
+}
+
+/// The available protocols for the trace output.
+#[repr(u8)]
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub enum TraceProtocol {
+ /// Parallel trace port mode
+ Parallel = 0b00,
+ /// Asynchronous SWO, using Manchester encoding
+ AsyncSWOManchester = 0b01,
+ /// Asynchronous SWO, using NRZ encoding
+ AsyncSWONRZ = 0b10,
+}
+impl core::convert::TryFrom<u8> for TraceProtocol {
+ type Error = ();
+
+ /// Tries to convert from a `TXMODE` field value. Fails if the set mode is
+ /// unknown (and thus unpredictable).
+ #[inline]
+ fn try_from(value: u8) -> Result<Self, Self::Error> {
+ match value {
+ x if x == Self::Parallel as u8 => Ok(Self::Parallel),
+ x if x == Self::AsyncSWOManchester as u8 => Ok(Self::AsyncSWOManchester),
+ x if x == Self::AsyncSWONRZ as u8 => Ok(Self::AsyncSWONRZ),
+ _ => Err(()), // unknown and unpredictable mode
+ }
+ }
+}
+
+/// The SWO options supported by the TPIU.
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub struct SWOSupports {
+ /// Whether UART/NRZ encoding is supported for SWO.
+ nrz_encoding: bool,
+ /// Whether Manchester encoding is supported for SWO.
+ manchester_encoding: bool,
+ /// Whether parallel trace port operation is supported.
+ parallel_operation: bool,
+ /// The minimum implemented FIFO queue size of the TPIU for trace data.
+ min_queue_size: u8,
+}
+
+impl TPIU {
+ /// Sets the prescaler value for a wanted baud rate of the Serial
+ /// Wire Output (SWO) in relation to a given asynchronous refernce
+ /// clock rate.
+ #[inline]
+ pub fn set_swo_baud_rate(&mut self, ref_clk_rate: u32, baud_rate: u32) {
+ unsafe {
+ self.acpr.write((ref_clk_rate / baud_rate) - 1);
+ }
+ }
+
+ /// The used protocol for the trace output. Return `None` if an
+ /// unknown (and thus unpredicable mode) is configured by means
+ /// other than
+ /// [`trace_output_protocol`](Self::set_trace_output_protocol).
+ #[inline]
+ pub fn trace_output_protocol(&self) -> Option<TraceProtocol> {
+ use core::convert::TryInto;
+ self.sppr.read().txmode().try_into().ok()
+ }
+
+ /// Sets the used protocol for the trace output.
+ #[inline]
+ pub fn set_trace_output_protocol(&mut self, proto: TraceProtocol) {
+ unsafe {
+ self.sppr.modify(|mut r| {
+ r.set_txmode(proto as u8);
+ r
+ });
+ }
+ }
+
+ /// Whether to enable the formatter. If disabled, only ITM and DWT
+ /// trace sources are passed through. Data from the ETM is
+ /// discarded.
+ #[inline]
+ pub fn enable_continuous_formatting(&mut self, bit: bool) {
+ unsafe {
+ self.ffcr.modify(|mut r| {
+ r.set_enfcont(bit);
+ r
+ });
+ }
+ }
+
+ /// Reads the supported trace output modes and the minimum size of
+ /// the TPIU FIFO queue for trace data.
+ #[inline]
+ pub fn swo_supports() -> SWOSupports {
+ let _type = unsafe { (*Self::ptr())._type.read() };
+ SWOSupports {
+ nrz_encoding: _type.nrzvalid(),
+ manchester_encoding: _type.mancvalid(),
+ parallel_operation: !_type.ptinvalid(),
+ min_queue_size: _type.fifosz(),
+ }
+ }
}