Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for additional DWT counters #349

Merged
merged 3 commits into from
Aug 14, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Added
- Added support for additional DWT counters (#349)
- CPI counter
- Exception overhead counter
- LSU counter
- Folded-instruction counter

## [v0.7.3] - 2021-07-03

### Fixed
Expand Down
166 changes: 165 additions & 1 deletion src/peripheral/dwt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,56 @@ pub struct Comparator {
reserved: u32,
}

// DWT CTRL register fields
Copy link
Contributor

@thejpster thejpster Aug 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth trying to add a link to the Arm Armv7-M ARM here, to explain where the constants came from.

https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/The-Data-Watchpoint-and-Trace-unit/Control-register--DWT-CTRL might work.

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;

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 }
}

/// 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 }
}

/// 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 }
}

/// 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 }
}

/// 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 }
}

/// Enables the cycle counter
///
/// The global trace enable ([`DCB::enable_trace`]) should be set before
Expand All @@ -74,13 +123,39 @@ impl DWT {
#[cfg(not(armv6m))]
#[inline]
pub fn enable_cycle_counter(&mut self) {
unsafe { self.ctrl.modify(|r| r | 1) }
unsafe { self.ctrl.modify(|r| r | CYCCNTENA) }
}

/// Disables the cycle counter
#[cfg(not(armv6m))]
#[inline]
pub fn disable_cycle_counter(&mut self) {
unsafe { self.ctrl.modify(|r| r & !CYCCNTENA) }
}

/// Returns `true` if the cycle counter is enabled
#[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 }
}

/// Returns the current clock cycle count
#[cfg(not(armv6m))]
#[inline]
#[deprecated(
since = "0.7.4",
note = "Use `cycle_count` which follows the C-GETTER convention"
)]
pub fn get_cycle_count() -> u32 {
Self::cycle_count()
}

/// Returns the current clock cycle count
#[cfg(not(armv6m))]
#[inline]
pub fn cycle_count() -> u32 {
// NOTE(unsafe) atomic read with no side effects
unsafe { (*Self::ptr()).cyccnt.read() }
}
Expand All @@ -94,4 +169,93 @@ impl DWT {
// NOTE(unsafe) atomic write to a stateless, write-only register
unsafe { (*Self::ptr()).lar.write(0xC5AC_CE55) }
}

/// Get the CPI count
///
/// Counts additional cycles required to execute multi-cycle instructions,
/// except those recorded by [`lsu_count`], and counts any instruction fetch
/// stalls.
///
/// [`lsu_count`]: DWT::lsu_count
#[cfg(not(armv6m))]
#[inline]
pub fn cpi_count() -> u8 {
// NOTE(unsafe) atomic read with no side effects
unsafe { (*Self::ptr()).cpicnt.read() as u8 }
}

/// Set the CPI count
#[cfg(not(armv6m))]
#[inline]
pub fn set_cpi_count(&mut self, count: u8) {
unsafe { self.cpicnt.write(count as u32) }
}

/// Get the total cycles spent in exception processing
#[cfg(not(armv6m))]
#[inline]
pub fn exception_count() -> u8 {
// NOTE(unsafe) atomic read with no side effects
unsafe { (*Self::ptr()).exccnt.read() as u8 }
}

/// Set the exception count
#[cfg(not(armv6m))]
#[inline]
pub fn set_exception_count(&mut self, count: u8) {
unsafe { self.exccnt.write(count as u32) }
}

/// Get the total number of cycles that the processor is sleeping
///
/// ARM recommends that this counter counts all cycles when the processor is sleeping,
/// regardless of whether a WFI or WFE instruction, or the sleep-on-exit functionality,
/// caused the entry to sleep mode.
/// However, all sleep features are implementation defined and therefore when
/// this counter counts is implementation defined.
#[cfg(not(armv6m))]
#[inline]
pub fn sleep_count() -> u8 {
// NOTE(unsafe) atomic read with no side effects
unsafe { (*Self::ptr()).sleepcnt.read() as u8 }
}

/// Set the sleep count
#[cfg(not(armv6m))]
#[inline]
pub fn set_sleep_count(&mut self, count: u8) {
unsafe { self.sleepcnt.write(count as u32) }
}

/// Get the additional cycles required to execute all load or store instructions
#[cfg(not(armv6m))]
#[inline]
pub fn lsu_count() -> u8 {
// NOTE(unsafe) atomic read with no side effects
unsafe { (*Self::ptr()).lsucnt.read() as u8 }
}

/// Set the lsu count
#[cfg(not(armv6m))]
#[inline]
pub fn set_lsu_count(&mut self, count: u8) {
unsafe { self.lsucnt.write(count as u32) }
}

/// Get the folded instruction count
///
/// Increments on each instruction that takes 0 cycles.
#[cfg(not(armv6m))]
#[inline]
pub fn fold_count() -> u8 {
// NOTE(unsafe) atomic read with no side effects
unsafe { (*Self::ptr()).foldcnt.read() as u8 }
}

/// Set the folded instruction count
#[cfg(not(armv6m))]
#[inline]
pub fn set_fold_count(&mut self, count: u8) {
unsafe { self.foldcnt.write(count as u32) }
}
}
4 changes: 2 additions & 2 deletions src/peripheral/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
//! ```
//! A part of the peripheral API doesn't require access to a peripheral instance. This part of the
//! API is provided as static methods on the peripheral types. One example is the
//! [`DWT::get_cycle_count`](struct.DWT.html#method.get_cycle_count) method.
//! [`DWT::cycle_count`](struct.DWT.html#method.cycle_count) method.
//!
//! ``` no_run
//! # use cortex_m::peripheral::{DWT, Peripherals};
Expand All @@ -33,7 +33,7 @@
//! } // all the peripheral singletons are destroyed here
//!
//! // but this method can be called without a DWT instance
//! let cyccnt = DWT::get_cycle_count();
//! let cyccnt = DWT::cycle_count();
//! ```
//!
//! The singleton property can be *unsafely* bypassed using the `ptr` static method which is
Expand Down