From 9e263ccb1be765735771ba9df53cc945d8e7c7f6 Mon Sep 17 00:00:00 2001 From: Tyler Holmes <tyler@holmesengineering.com> Date: Sat, 1 Jan 2022 16:01:11 -0800 Subject: [PATCH] Differentiate comparator 0 as the only one capable of cycle compare Statically enforces that only comparator 0 on `armv7m` can be configured for cycle comparison by introducing a marker trait differentiating comparator 0 and the rest of them, and only implementing the ability for this configuration on comparator 0. Closes #376 --- src/peripheral/dwt.rs | 163 +++++++++++++++++++++++++++-------------- src/peripheral/test.rs | 21 ++++-- 2 files changed, 122 insertions(+), 62 deletions(-) diff --git a/src/peripheral/dwt.rs b/src/peripheral/dwt.rs index 72575d37..e01829fd 100644 --- a/src/peripheral/dwt.rs +++ b/src/peripheral/dwt.rs @@ -37,10 +37,13 @@ pub struct RegisterBlock { pub pcsr: RO<u32>, /// Comparators #[cfg(armv6m)] - pub c: [Comparator; 2], + pub comp: [Comparator<NoCycleCompare>; 2], + #[cfg(not(armv6m))] + /// Cycle count compare enabled Comparator + pub comp0: Comparator<HasCycleCompare>, #[cfg(not(armv6m))] /// Comparators - pub c: [Comparator; 16], + pub comp: [Comparator<NoCycleCompare>; 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<SupportedFunctions: ComparatorSupportedFunctions> { /// Comparator pub comp: RW<u32>, /// Comparator Mask @@ -76,6 +101,7 @@ pub struct Comparator { /// Comparator Function pub function: RW<Function>, reserved: u32, + _supported_functions: core::marker::PhantomData<SupportedFunctions>, } bitfield! { @@ -414,63 +440,88 @@ 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<SupportedFunctions: ComparatorSupportedFunctions> Comparator<SupportedFunctions> { + /// 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<NoCycleCompare> { + /// 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<HasCycleCompare> { + /// Configure the function of the [`Comparator`]. Has support for cycle count comparison. + #[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) => { let function = match &settings.emit { EmitOption::PCData => 0b0001, @@ -499,9 +550,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..4c19be24 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.comp[0].comp), 0xE000_1030); + assert_eq!(address(&dwt.comp[0].mask), 0xE000_1034); + assert_eq!(address(&dwt.comp[0].function), 0xE000_1038); + } + if cfg!(armv6m) { + assert_eq!(address(&dwt.comp[0].comp), 0xE000_1020); + assert_eq!(address(&dwt.comp[0].mask), 0xE000_1024); + assert_eq!(address(&dwt.comp[0].function), 0xE000_1028); + } + #[cfg(not(armv6m))] assert_eq!(address(&dwt.lar), 0xE000_1FB0); #[cfg(not(armv6m))]