diff --git a/CHANGELOG.md b/CHANGELOG.md index ebcd2c49..ce7699d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Also fixes `VectActive::from` to take a `u16` and subtract `16` for `VectActive::Interrupt`s to match `SBC::vect_active()` (#373). - DWT: add `configure` API for address, cycle count comparison (#342, #367). + - Differentiated the first `DWT` `Comparator` as the only one able to do cycle + count comparisons, and only on `armv7m` (#377). + - renamed the field from `c` to `comp0` and `comps[15]` for `armv7m` and + `comps[2]` for `armv6m` (#377). + - Made the `has_*` implementation checks associated functions (#377). - ITM: add `configure` API (#342). - TPIU: add API for *Formatter and Flush Control* (FFCR) and *Selected Pin Control* (SPPR) registers (#342). - TPIU: add `swo_supports` for checking what SWO configurations the target supports. (#381) diff --git a/src/peripheral/dwt.rs b/src/peripheral/dwt.rs index 72575d37..c83d420e 100644 --- a/src/peripheral/dwt.rs +++ b/src/peripheral/dwt.rs @@ -37,10 +37,13 @@ pub struct RegisterBlock { pub pcsr: RO, /// Comparators #[cfg(armv6m)] - pub c: [Comparator; 2], + pub comps: [Comparator; 2], + #[cfg(not(armv6m))] + /// Cycle count compare enabled Comparator + pub comp0: Comparator, #[cfg(not(armv6m))] /// Comparators - pub c: [Comparator; 16], + pub comps: [Comparator; 15], #[cfg(not(armv6m))] reserved: [u32; 932], /// Lock Access @@ -66,9 +69,31 @@ bitfield! { u8, numcomp, _: 31, 28; } +mod private { + /// A public trait inaccessible by external users to ensure no one else can + /// impl a sealed trait outside of the crate of origin. For more info on this + /// design pattern, see https://rust-lang.github.io/api-guidelines/future-proofing.html + pub trait Sealed {} +} + +/// A zero-sized marker trait indicating the capabilities of a given comparator. +pub trait ComparatorSupportedFunctions: private::Sealed {} + +/// Marker indicating that this comparator has cycle comparison abilities. This +/// is the case only for the first comparator for armv7m. +pub enum HasCycleCompare {} +impl ComparatorSupportedFunctions for HasCycleCompare {} +impl private::Sealed for HasCycleCompare {} + +/// Marker indicating this comparator does not have cycle comparison abilities. This +/// is the case for all armv6m comparators and comparators 1-15 for armv7m. +pub enum NoCycleCompare {} +impl ComparatorSupportedFunctions for NoCycleCompare {} +impl private::Sealed for NoCycleCompare {} + /// Comparator #[repr(C)] -pub struct Comparator { +pub struct Comparator { /// Comparator pub comp: RW, /// Comparator Mask @@ -76,6 +101,7 @@ pub struct Comparator { /// Comparator Function pub function: RW, reserved: u32, + _supported_functions: core::marker::PhantomData, } bitfield! { @@ -101,29 +127,33 @@ impl DWT { /// /// A value of zero indicates no comparator support. #[inline] - pub fn num_comp(&self) -> u8 { - self.ctrl.read().numcomp() + pub fn num_comp() -> u8 { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*DWT::PTR).ctrl.read().numcomp() } } /// Returns `true` if the the implementation supports sampling and exception tracing #[cfg(not(armv6m))] #[inline] - pub fn has_exception_trace(&self) -> bool { - !self.ctrl.read().notrcpkt() + pub fn has_exception_trace() -> bool { + // NOTE(unsafe) atomic read with no side effects + unsafe { !(*DWT::PTR).ctrl.read().notrcpkt() } } /// Returns `true` if the implementation includes external match signals #[cfg(not(armv6m))] #[inline] - pub fn has_external_match(&self) -> bool { - !self.ctrl.read().noexttrig() + pub fn has_external_match() -> bool { + // NOTE(unsafe) atomic read with no side effects + unsafe { !(*DWT::PTR).ctrl.read().noexttrig() } } /// Returns `true` if the implementation supports a cycle counter #[inline] - pub fn has_cycle_counter(&self) -> bool { + pub fn has_cycle_counter() -> bool { #[cfg(not(armv6m))] - return !self.ctrl.read().nocyccnt(); + // NOTE(unsafe) atomic read with no side effects + return unsafe { !(*DWT::PTR).ctrl.read().nocyccnt() }; #[cfg(armv6m)] return false; @@ -132,8 +162,9 @@ impl DWT { /// Returns `true` if the implementation the profiling counters #[cfg(not(armv6m))] #[inline] - pub fn has_profiling_counter(&self) -> bool { - !self.ctrl.read().noprfcnt() + pub fn has_profiling_counter() -> bool { + // NOTE(unsafe) atomic read with no side effects + unsafe { !(*DWT::PTR).ctrl.read().noprfcnt() } } /// Enables the cycle counter @@ -414,64 +445,96 @@ pub enum ComparatorFunction { pub enum DwtError { /// Invalid combination of [AccessType] and [EmitOption]. InvalidFunction, + /// An unsupported function was requested, such as [`CycleCount`](ComparatorFunction::CycleCount) on + /// `armv6m`, or on a comparator other than 0 on `armv7m`. + UnsupportedFunction, } -impl Comparator { - /// Configure the function of the comparator +impl Comparator { + /// Private function for configuring address compare on any [`Comparator`] since they all support this. + /// Utilized publicly through [`Comparator::configure`]. + fn configure_address_compare( + &self, + settings: ComparatorAddressSettings, + ) -> Result<(), DwtError> { + // 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::ReadOnly, EmitOption::WatchpointDebugEvent) => (0b0101, false), + (AccessType::ReadOnly, EmitOption::CompareMatchEvent) => (0b1001, 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::WriteOnly, EmitOption::WatchpointDebugEvent) => (0b0110, false), + (AccessType::WriteOnly, EmitOption::CompareMatchEvent) => (0b1010, 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::WatchpointDebugEvent) => (0b0111, false), + (AccessType::ReadWrite, EmitOption::CompareMatchEvent) => (0b1011, false), + + (AccessType::ReadWrite, EmitOption::PC) => (0b0001, false), + (_, EmitOption::PC) => return Err(DwtError::InvalidFunction), + }; + + unsafe { + 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); + // SBZ as needed, see Page 784/C1-724 + r.set_datavsize(0); + r.set_datavaddr0(0); + r.set_datavaddr1(0); + + r + }); + + self.comp.write(settings.address); + self.mask.write(settings.mask); + } + + Ok(()) + } +} + +impl Comparator { + /// Configure the function of the [`Comparator`]. Does not support cycle count comparison. #[allow(clippy::missing_inline_in_public_items)] pub fn configure(&self, settings: ComparatorFunction) -> Result<(), DwtError> { match settings { - ComparatorFunction::Address(settings) => { - // 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::ReadOnly, EmitOption::WatchpointDebugEvent) => (0b0101, false), - (AccessType::ReadOnly, EmitOption::CompareMatchEvent) => (0b1001, 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::WriteOnly, EmitOption::WatchpointDebugEvent) => (0b0110, false), - (AccessType::WriteOnly, EmitOption::CompareMatchEvent) => (0b1010, 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::WatchpointDebugEvent) => (0b0111, false), - (AccessType::ReadWrite, EmitOption::CompareMatchEvent) => (0b1011, false), - - (AccessType::ReadWrite, EmitOption::PC) => (0b0001, false), - (_, EmitOption::PC) => return Err(DwtError::InvalidFunction), - }; - - unsafe { - 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); - // SBZ as needed, see Page 784/C1-724 - r.set_datavsize(0); - r.set_datavaddr0(0); - r.set_datavaddr1(0); - - r - }); + ComparatorFunction::Address(settings) => self.configure_address_compare(settings), + ComparatorFunction::CycleCount(_settings) => Err(DwtError::UnsupportedFunction), + } + } +} - self.comp.write(settings.address); - self.mask.write(settings.mask); - } - } +impl Comparator { + /// Configure the function of the [`Comparator`]. Has support for cycle count comparison + /// and checks [`DWT::has_cycle_counter`] for hardware support if + /// [`CycleCount`](ComparatorFunction::CycleCount) is requested. + #[allow(clippy::missing_inline_in_public_items)] + pub fn configure(&self, settings: ComparatorFunction) -> Result<(), DwtError> { + match settings { + ComparatorFunction::Address(settings) => self.configure_address_compare(settings), ComparatorFunction::CycleCount(settings) => { + // Check if the HW advertises that it has the cycle counter or not + if !DWT::has_cycle_counter() { + return Err(DwtError::UnsupportedFunction); + } + let function = match &settings.emit { EmitOption::PCData => 0b0001, EmitOption::WatchpointDebugEvent => 0b0100, @@ -499,9 +562,9 @@ impl Comparator { self.comp.write(settings.compare); self.mask.write(0); // SBZ, see Page 784/C1-724 } + + Ok(()) } } - - Ok(()) } } diff --git a/src/peripheral/test.rs b/src/peripheral/test.rs index cab064aa..f2ab4122 100644 --- a/src/peripheral/test.rs +++ b/src/peripheral/test.rs @@ -42,12 +42,21 @@ fn dwt() { #[cfg(not(armv6m))] assert_eq!(address(&dwt.foldcnt), 0xE000_1018); assert_eq!(address(&dwt.pcsr), 0xE000_101C); - assert_eq!(address(&dwt.c[0].comp), 0xE000_1020); - assert_eq!(address(&dwt.c[0].mask), 0xE000_1024); - assert_eq!(address(&dwt.c[0].function), 0xE000_1028); - assert_eq!(address(&dwt.c[1].comp), 0xE000_1030); - assert_eq!(address(&dwt.c[1].mask), 0xE000_1034); - assert_eq!(address(&dwt.c[1].function), 0xE000_1038); + if cfg!(not(armv6m)) { + assert_eq!(address(&dwt.comp0.comp), 0xE000_1020); + assert_eq!(address(&dwt.comp0.mask), 0xE000_1024); + assert_eq!(address(&dwt.comp0.function), 0xE000_1028); + + assert_eq!(address(&dwt.comps[0].comp), 0xE000_1030); + assert_eq!(address(&dwt.comps[0].mask), 0xE000_1034); + assert_eq!(address(&dwt.comps[0].function), 0xE000_1038); + } + if cfg!(armv6m) { + assert_eq!(address(&dwt.comps[0].comp), 0xE000_1020); + assert_eq!(address(&dwt.comps[0].mask), 0xE000_1024); + assert_eq!(address(&dwt.comps[0].function), 0xE000_1028); + } + #[cfg(not(armv6m))] assert_eq!(address(&dwt.lar), 0xE000_1FB0); #[cfg(not(armv6m))] diff --git a/testsuite/src/main.rs b/testsuite/src/main.rs index 46ab629b..12a5770f 100644 --- a/testsuite/src/main.rs +++ b/testsuite/src/main.rs @@ -28,12 +28,13 @@ mod tests { #[test] #[cfg(not(feature = "semihosting"))] // QEMU does not model the cycle counter + #[cfg_attr(armv6m, allow(unused_variables))] fn cycle_count(p: &mut cortex_m::Peripherals) { + use cortex_m::peripheral::DWT; + #[cfg(not(armv6m))] { - use cortex_m::peripheral::DWT; - - assert!(p.DWT.has_cycle_counter()); + assert!(DWT::has_cycle_counter()); p.DCB.enable_trace(); p.DWT.disable_cycle_counter(); @@ -48,7 +49,7 @@ mod tests { #[cfg(armv6m)] { - assert!(!p.DWT.has_cycle_counter()); + assert!(!DWT::has_cycle_counter()); } } }