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))]