diff options
author | 2021-10-27 00:23:07 +0200 | |
---|---|---|
committer | 2021-10-27 00:23:07 +0200 | |
commit | 880b947c44e78fd07d7c231a3c0c82ed3e54b9ef (patch) | |
tree | c8f67545f42ab2311b57150f20c57c4e9903551a | |
parent | aa17958147ac57af6f10de170d869626397ba1d8 (diff) | |
parent | 6b013138b734b9bbeb24a345f75d2bcc1c69fa8d (diff) | |
download | cortex-m-880b947c44e78fd07d7c231a3c0c82ed3e54b9ef.tar.gz cortex-m-880b947c44e78fd07d7c231a3c0c82ed3e54b9ef.tar.zst cortex-m-880b947c44e78fd07d7c231a3c0c82ed3e54b9ef.zip |
Merge branch 'master' into feat/tracing
-rw-r--r-- | CHANGELOG.md | 31 | ||||
-rw-r--r-- | Cargo.toml | 5 | ||||
-rw-r--r-- | src/delay.rs | 32 | ||||
-rw-r--r-- | src/peripheral/ac.rs | 93 | ||||
-rw-r--r-- | src/peripheral/dwt.rs | 162 | ||||
-rw-r--r-- | src/peripheral/mod.rs | 39 | ||||
-rw-r--r-- | src/peripheral/nvic.rs | 2 | ||||
-rw-r--r-- | src/peripheral/scb.rs | 4 |
8 files changed, 347 insertions, 21 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d29633..2e7d1e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,34 @@ 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 +- Added `DWT.set_cycle_count` (#347). +- Added support for the Cortex-M7 TCM and cache access control registers. + There is a feature `cm7` to enable access to these. + +### Deprecated + +- `DWT::get_cycle_count` has been deprecated in favor of `DWT::cycle_count`. + This change was made for consistency with the [C-GETTER] convention. (#349) + +[C-GETTER]: https://rust-lang.github.io/api-guidelines/naming.html#c-getter + +## [v0.7.3] - 2021-07-03 + +### Fixed + +- Fixed compilation for native targets on non-x86 host systems (#336, #337). + +### Added + +- The `Delay` struct now offers direct `delay_us()` and `delay_ms()` methods + without having to go through the embedded-hal traits (#344). + ## [v0.7.2] - 2021-03-07 ### Fixed @@ -689,7 +717,8 @@ fn main() { - Functions to get the vector table - Wrappers over miscellaneous instructions like `bkpt` -[Unreleased]: https://github.com/rust-embedded/cortex-m/compare/v0.7.2...HEAD +[Unreleased]: https://github.com/rust-embedded/cortex-m/compare/v0.7.3...HEAD +[v0.7.3]: https://github.com/rust-embedded/cortex-m/compare/v0.7.2...v0.7.3 [v0.7.2]: https://github.com/rust-embedded/cortex-m/compare/v0.7.1...v0.7.2 [v0.7.1]: https://github.com/rust-embedded/cortex-m/compare/v0.7.0...v0.7.1 [v0.7.0]: https://github.com/rust-embedded/cortex-m/compare/v0.6.4...v0.7.0 @@ -11,7 +11,7 @@ license = "MIT OR Apache-2.0" name = "cortex-m" readme = "README.md" repository = "https://github.com/rust-embedded/cortex-m" -version = "0.7.2" +version = "0.7.3" edition = "2018" links = "cortex-m" # prevent multiple versions of this crate to be linked together @@ -22,7 +22,8 @@ bitfield = "0.13.2" embedded-hal = "0.2.4" [features] -cm7-r0p1 = [] +cm7 = [] +cm7-r0p1 = ["cm7"] inline-asm = [] linker-plugin-lto = [] diff --git a/src/delay.rs b/src/delay.rs index 2f5b8e0..8ed1fea 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -29,8 +29,10 @@ impl Delay { self.syst } - fn _delay_us(&mut self, us: u32) { - let ticks = (us as u64) * (self.ahb_frequency as u64) / 1_000_000; + /// Delay using the Cortex-M systick for a certain duration, in µs. + #[allow(clippy::missing_inline_in_public_items)] + pub fn delay_us(&mut self, us: u32) { + let ticks = (u64::from(us)) * (u64::from(self.ahb_frequency)) / 1_000_000; let full_cycles = ticks >> 24; if full_cycles > 0 { @@ -54,11 +56,10 @@ impl Delay { self.syst.disable_counter(); } -} -impl DelayMs<u32> for Delay { + /// Delay using the Cortex-M systick for a certain duration, in ms. #[inline] - fn delay_ms(&mut self, mut ms: u32) { + pub fn delay_ms(&mut self, mut ms: u32) { // 4294967 is the highest u32 value which you can multiply by 1000 without overflow while ms > 4294967 { self.delay_us(4294967000u32); @@ -68,33 +69,40 @@ impl DelayMs<u32> for Delay { } } +impl DelayMs<u32> for Delay { + #[inline] + fn delay_ms(&mut self, ms: u32) { + Delay::delay_ms(self, ms); + } +} + // This is a workaround to allow `delay_ms(42)` construction without specifying a type. impl DelayMs<i32> for Delay { #[inline(always)] fn delay_ms(&mut self, ms: i32) { assert!(ms >= 0); - self.delay_ms(ms as u32); + Delay::delay_ms(self, ms as u32); } } impl DelayMs<u16> for Delay { #[inline(always)] fn delay_ms(&mut self, ms: u16) { - self.delay_ms(u32::from(ms)); + Delay::delay_ms(self, u32::from(ms)); } } impl DelayMs<u8> for Delay { #[inline(always)] fn delay_ms(&mut self, ms: u8) { - self.delay_ms(u32::from(ms)); + Delay::delay_ms(self, u32::from(ms)); } } impl DelayUs<u32> for Delay { #[inline] fn delay_us(&mut self, us: u32) { - self._delay_us(us); + Delay::delay_us(self, us); } } @@ -103,20 +111,20 @@ impl DelayUs<i32> for Delay { #[inline(always)] fn delay_us(&mut self, us: i32) { assert!(us >= 0); - self.delay_us(us as u32); + Delay::delay_us(self, us as u32); } } impl DelayUs<u16> for Delay { #[inline(always)] fn delay_us(&mut self, us: u16) { - self.delay_us(u32::from(us)) + Delay::delay_us(self, u32::from(us)) } } impl DelayUs<u8> for Delay { #[inline(always)] fn delay_us(&mut self, us: u8) { - self.delay_us(u32::from(us)) + Delay::delay_us(self, u32::from(us)) } } diff --git a/src/peripheral/ac.rs b/src/peripheral/ac.rs new file mode 100644 index 0000000..1ac5be1 --- /dev/null +++ b/src/peripheral/ac.rs @@ -0,0 +1,93 @@ +//! Cortex-M7 TCM and Cache access control. + +use volatile_register::RW; + +/// Register block +#[repr(C)] +pub struct RegisterBlock { + /// Instruction Tightly-Coupled Memory Control Register + pub itcmcr: RW<u32>, + /// Data Tightly-Coupled Memory Control Register + pub dtcmcr: RW<u32>, + /// AHBP Control Register + pub ahbpcr: RW<u32>, + /// L1 Cache Control Register + pub cacr: RW<u32>, + /// AHB Slave Control Register + pub ahbscr: RW<u32>, + reserved0: u32, + /// Auxilary Bus Fault Status Register + pub abfsr: RW<u32>, +} + +/// ITCMCR and DTCMCR TCM enable bit. +pub const TCM_EN: u32 = 1; + +/// ITCMCR and DTCMCR TCM read-modify-write bit. +pub const TCM_RMW: u32 = 2; + +/// ITCMCR and DTCMCR TCM rety phase enable bit. +pub const TCM_RETEN: u32 = 4; + +/// ITCMCR and DTCMCR TCM size mask. +pub const TCM_SZ_MASK: u32 = 0x78; + +/// ITCMCR and DTCMCR TCM shift. +pub const TCM_SZ_SHIFT: usize = 3; + +/// AHBPCR AHBP enable bit. +pub const AHBPCR_EN: u32 = 1; + +/// AHBPCR AHBP size mask. +pub const AHBPCR_SZ_MASK: u32 = 0x0e; + +/// AHBPCR AHBP size shit. +pub const AHBPCR_SZ_SHIFT: usize = 1; + +/// CACR Shared cachedable-is-WT for data cache. +pub const CACR_SIWT: u32 = 1; + +/// CACR ECC in the instruction and data cache (disable). +pub const CACR_ECCDIS: u32 = 2; + +/// CACR Force Write-Through in the data cache. +pub const CACR_FORCEWT: u32 = 4; + +/// AHBSCR AHBS prioritization control mask. +pub const AHBSCR_CTL_MASK: u32 = 0x03; + +/// AHBSCR AHBS prioritization control shift. +pub const AHBSCR_CTL_SHIFT: usize = 0; + +/// AHBSCR Threshold execution prioity for AHBS traffic demotion, mask. +pub const AHBSCR_TPRI_MASK: u32 = 0x7fc; + +/// AHBSCR Threshold execution prioity for AHBS traffic demotion, shift. +pub const AHBSCR_TPRI_SHIFT: usize = 2; + +/// AHBSCR Failness counter initialization value, mask. +pub const AHBSCR_INITCOUNT_MASK: u32 = 0xf800; + +/// AHBSCR Failness counter initialization value, shift. +pub const AHBSCR_INITCOUNT_SHIFT: usize = 11; + +/// ABFSR Async fault on ITCM interface. +pub const ABFSR_ITCM: u32 = 1; + +/// ABFSR Async fault on DTCM interface. +pub const ABFSR_DTCM: u32 = 2; + +/// ABFSR Async fault on AHBP interface. +pub const ABFSR_AHBP: u32 = 4; + +/// ABFSR Async fault on AXIM interface. +pub const ABFSR_AXIM: u32 = 8; + +/// ABFSR Async fault on EPPB interface. +pub const ABFSR_EPPB: u32 = 16; + +/// ABFSR Indicates the type of fault on the AXIM interface, mask. +pub const ABFSR_AXIMTYPE_MASK: u32 = 0x300; + +/// ABFSR Indicates the type of fault on the AXIM interface, shift. +pub const ABFSR_AXIMTYPE_SHIFT: usize = 8; diff --git a/src/peripheral/dwt.rs b/src/peripheral/dwt.rs index 57c9063..720511b 100644 --- a/src/peripheral/dwt.rs +++ b/src/peripheral/dwt.rs @@ -59,6 +59,11 @@ bitfield! { get_cyccntena, set_cyccntena: 0; get_pcsamplena, set_pcsamplena: 12; get_exctrcena, set_exctrcena: 16; + get_noprfcnt, _: 24; + get_nocyccnt, _: 25; + get_noexttrig, _: 26; + get_notrcpkt, _: 27; + u8, get_numcomp, _: 31, 28; } /// Comparator @@ -86,7 +91,50 @@ bitfield! { } impl DWT { + /// Number of comparators implemented + /// + /// A value of zero indicates no comparator support. + #[inline] + pub fn num_comp(&self) -> u8 { + self.ctrl.read().get_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().get_notrcpkt() == false + } + + /// Returns `true` if the implementation includes external match signals + #[cfg(not(armv6m))] + #[inline] + pub fn has_external_match(&self) -> bool { + self.ctrl.read().get_noexttrig() == false + } + + /// Returns `true` if the implementation supports a cycle counter + #[cfg(not(armv6m))] + #[inline] + pub fn has_cycle_counter(&self) -> bool { + self.ctrl.read().get_nocyccnt() == false + } + + /// Returns `true` if the implementation the profiling counters + #[cfg(not(armv6m))] + #[inline] + pub fn has_profiling_counter(&self) -> bool { + self.ctrl.read().get_noprfcnt() == false + } + /// Enables the cycle counter + /// + /// The global trace enable ([`DCB::enable_trace`]) should be set before + /// enabling the cycle counter, the processor may ignore writes to the + /// cycle counter enable if the global trace is disabled + /// (implementation defined behaviour). + /// + /// [`DCB::enable_trace`]: crate::peripheral::DCB::enable_trace #[cfg(not(armv6m))] #[inline] pub fn enable_cycle_counter(&mut self) { @@ -98,6 +146,13 @@ impl DWT { } } + /// Returns `true` if the cycle counter is enabled + #[cfg(not(armv6m))] + #[inline] + pub fn cycle_counter_enabled(&self) -> bool { + self.ctrl.read().get_cyccntena() + } + /// Whether to enable exception tracing #[cfg(not(armv6m))] #[inline] @@ -125,11 +180,29 @@ impl DWT { /// 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() } } + /// Set the cycle count + #[cfg(not(armv6m))] + #[inline] + pub fn set_cycle_count(&mut self, count: u32) { + unsafe { self.cyccnt.write(count) } + } + /// Removes the software lock on the DWT /// /// Some devices, like the STM32F7, software lock the DWT after a power cycle. @@ -139,6 +212,95 @@ 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) } + } } /// Whether the comparator should match on read, write or read/write operations. diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs index 7692f06..081aa0a 100644 --- a/src/peripheral/mod.rs +++ b/src/peripheral/mod.rs @@ -10,7 +10,7 @@ //! ``` no_run //! # use cortex_m::peripheral::Peripherals; //! let mut peripherals = Peripherals::take().unwrap(); -//! peripherals.DWT.enable_cycle_counter(); +//! peripherals.DCB.enable_trace(); //! ``` //! //! This method can only be successfully called *once* -- this is why the method returns an @@ -23,17 +23,18 @@ //! ``` //! 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}; //! { //! let mut peripherals = Peripherals::take().unwrap(); +//! peripherals.DCB.enable_trace(); //! peripherals.DWT.enable_cycle_counter(); //! } // 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 @@ -44,6 +45,7 @@ //! # use cortex_m::peripheral::{DWT, Peripherals}; //! { //! let mut peripherals = Peripherals::take().unwrap(); +//! peripherals.DCB.enable_trace(); //! peripherals.DWT.enable_cycle_counter(); //! } // all the peripheral singletons are destroyed here //! @@ -60,6 +62,8 @@ use core::ops; use crate::interrupt; +#[cfg(cm7)] +pub mod ac; #[cfg(not(armv6m))] pub mod cbp; pub mod cpuid; @@ -91,6 +95,10 @@ mod test; #[allow(non_snake_case)] #[allow(clippy::manual_non_exhaustive)] pub struct Peripherals { + /// Cortex-M7 TCM and cache access control. + #[cfg(cm7)] + pub AC: AC, + /// Cache and branch predictor maintenance operations. /// Not available on Armv6-M. pub CBP: CBP, @@ -172,6 +180,10 @@ impl Peripherals { TAKEN = true; Peripherals { + #[cfg(cm7)] + AC: AC { + _marker: PhantomData, + }, CBP: CBP { _marker: PhantomData, }, @@ -219,6 +231,27 @@ impl Peripherals { } } +/// Access control +#[cfg(cm7)] +pub struct AC { + _marker: PhantomData<*const ()>, +} + +#[cfg(cm7)] +unsafe impl Send for AC {} + +#[cfg(cm7)] +impl AC { + /// Pointer to the register block + pub const PTR: *const self::ac::RegisterBlock = 0xE000_EF90 as *const _; + + /// Returns a pointer to the register block (to be deprecated in 0.7) + #[inline(always)] + pub const fn ptr() -> *const self::ac::RegisterBlock { + Self::PTR + } +} + /// Cache and branch predictor maintenance operations #[allow(clippy::upper_case_acronyms)] pub struct CBP { diff --git a/src/peripheral/nvic.rs b/src/peripheral/nvic.rs index 4332707..f0c5457 100644 --- a/src/peripheral/nvic.rs +++ b/src/peripheral/nvic.rs @@ -210,7 +210,7 @@ impl NVIC { /// # Unsafety /// /// Changing priority levels can break priority-based critical sections (see - /// [`register::basepri`](../register/basepri/index.html)) and compromise memory safety. + /// [`register::basepri`](crate::register::basepri)) and compromise memory safety. #[inline] pub unsafe fn set_priority<I>(&mut self, interrupt: I, prio: u8) where diff --git a/src/peripheral/scb.rs b/src/peripheral/scb.rs index b619328..688c431 100644 --- a/src/peripheral/scb.rs +++ b/src/peripheral/scb.rs @@ -832,7 +832,7 @@ impl SCB { } const SCB_AIRCR_VECTKEY: u32 = 0x05FA << 16; -const SCB_AIRCR_PRIGROUP_MASK: u32 = 0x5 << 8; +const SCB_AIRCR_PRIGROUP_MASK: u32 = 0x7 << 8; const SCB_AIRCR_SYSRESETREQ: u32 = 1 << 2; impl SCB { @@ -994,7 +994,7 @@ impl SCB { /// # Unsafety /// /// Changing priority levels can break priority-based critical sections (see - /// [`register::basepri`](../register/basepri/index.html)) and compromise memory safety. + /// [`register::basepri`](crate::register::basepri)) and compromise memory safety. #[inline] pub unsafe fn set_priority(&mut self, system_handler: SystemHandler, prio: u8) { let index = system_handler as u8; |