Skip to content

Commit

Permalink
Differentiate comparator 0 as the only one capable of cycle compare
Browse files Browse the repository at this point in the history
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 rust-embedded#376
  • Loading branch information
TDHolmes committed Jan 2, 2022
1 parent bc00af3 commit 3d5013a
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 62 deletions.
163 changes: 107 additions & 56 deletions src/peripheral/dwt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -66,16 +69,39 @@ 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
pub mask: RW<u32>,
/// Comparator Function
pub function: RW<Function>,
reserved: u32,
_supported_functions: core::marker::PhantomData<SupportedFunctions>,
}

bitfield! {
Expand Down Expand Up @@ -402,63 +428,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,
Expand Down Expand Up @@ -487,9 +538,9 @@ impl Comparator {
self.comp.write(settings.compare);
self.mask.write(0); // SBZ, see Page 784/C1-724
}

Ok(())
}
}

Ok(())
}
}
21 changes: 15 additions & 6 deletions src/peripheral/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand Down

0 comments on commit 3d5013a

Please sign in to comment.