diff options
Diffstat (limited to 'src/peripheral/mod.rs')
-rw-r--r-- | src/peripheral/mod.rs | 594 |
1 files changed, 594 insertions, 0 deletions
diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs new file mode 100644 index 0000000..af922b1 --- /dev/null +++ b/src/peripheral/mod.rs @@ -0,0 +1,594 @@ +//! Core peripherals. +//! +//! # API +//! +//! To use (most of) the peripheral API first you must get an *instance* of the peripheral. All the +//! core peripherals are modeled as singletons (there can only ever be, at most, one instance of any +//! one of them at any given point in time) and the only way to get an instance of them is through +//! the [`Peripherals::take`](struct.Peripherals.html#method.take) method. +//! +//! ``` no_run +//! # use cortex_m::peripheral::Peripherals; +//! let mut peripherals = Peripherals::take().unwrap(); +//! peripherals.DCB.enable_trace(); +//! ``` +//! +//! This method can only be successfully called *once* -- this is why the method returns an +//! `Option`. Subsequent calls to the method will result in a `None` value being returned. +//! +//! ``` no_run, should_panic +//! # use cortex_m::peripheral::Peripherals; +//! let ok = Peripherals::take().unwrap(); +//! let panics = Peripherals::take().unwrap(); +//! ``` +//! 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::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::cycle_count(); +//! ``` +//! +//! The singleton property can be *unsafely* bypassed using the `ptr` static method which is +//! available on all the peripheral types. This method is a useful building block for implementing +//! safe higher level abstractions. +//! +//! ``` 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 +//! +//! // actually safe because this is an atomic read with no side effects +//! let cyccnt = unsafe { (*DWT::PTR).cyccnt.read() }; +//! ``` +//! +//! # References +//! +//! - ARMv7-M Architecture Reference Manual (Issue E.b) - Chapter B3 + +use core::marker::PhantomData; +use core::ops; + +use crate::interrupt; + +#[cfg(cm7)] +pub mod ac; +#[cfg(not(armv6m))] +pub mod cbp; +pub mod cpuid; +pub mod dcb; +pub mod dwt; +#[cfg(not(armv6m))] +pub mod fpb; +// NOTE(native) is for documentation purposes +#[cfg(any(has_fpu, native))] +pub mod fpu; +pub mod icb; +#[cfg(all(not(armv6m), not(armv8m_base)))] +pub mod itm; +pub mod mpu; +pub mod nvic; +#[cfg(armv8m)] +pub mod sau; +pub mod scb; +pub mod syst; +#[cfg(not(armv6m))] +pub mod tpiu; + +#[cfg(test)] +mod test; + +// NOTE the `PhantomData` used in the peripherals proxy is to make them `Send` but *not* `Sync` + +/// Core peripherals +#[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, + + /// CPUID + pub CPUID: CPUID, + + /// Debug Control Block + pub DCB: DCB, + + /// Data Watchpoint and Trace unit + pub DWT: DWT, + + /// Flash Patch and Breakpoint unit. + /// Not available on Armv6-M. + pub FPB: FPB, + + /// Floating Point Unit. + pub FPU: FPU, + + /// Implementation Control Block. + /// + /// The name is from the v8-M spec, but the block existed in earlier + /// revisions, without a name. + pub ICB: ICB, + + /// Instrumentation Trace Macrocell. + /// Not available on Armv6-M and Armv8-M Baseline. + pub ITM: ITM, + + /// Memory Protection Unit + pub MPU: MPU, + + /// Nested Vector Interrupt Controller + pub NVIC: NVIC, + + /// Security Attribution Unit + pub SAU: SAU, + + /// System Control Block + pub SCB: SCB, + + /// SysTick: System Timer + pub SYST: SYST, + + /// Trace Port Interface Unit. + /// Not available on Armv6-M. + pub TPIU: TPIU, + + // Private field making `Peripherals` non-exhaustive. We don't use `#[non_exhaustive]` so we + // can support older Rust versions. + _priv: (), +} + +// NOTE `no_mangle` is used here to prevent linking different minor versions of this crate as that +// would let you `take` the core peripherals more than once (one per minor version) +#[no_mangle] +static CORE_PERIPHERALS: () = (); + +/// Set to `true` when `take` or `steal` was called to make `Peripherals` a singleton. +static mut TAKEN: bool = false; + +impl Peripherals { + /// Returns all the core peripherals *once* + #[inline] + pub fn take() -> Option<Self> { + interrupt::free(|_| { + if unsafe { TAKEN } { + None + } else { + Some(unsafe { Peripherals::steal() }) + } + }) + } + + /// Unchecked version of `Peripherals::take` + #[inline] + pub unsafe fn steal() -> Self { + TAKEN = true; + + Peripherals { + #[cfg(cm7)] + AC: AC { + _marker: PhantomData, + }, + CBP: CBP { + _marker: PhantomData, + }, + CPUID: CPUID { + _marker: PhantomData, + }, + DCB: DCB { + _marker: PhantomData, + }, + DWT: DWT { + _marker: PhantomData, + }, + FPB: FPB { + _marker: PhantomData, + }, + FPU: FPU { + _marker: PhantomData, + }, + ICB: ICB { + _marker: PhantomData, + }, + ITM: ITM { + _marker: PhantomData, + }, + MPU: MPU { + _marker: PhantomData, + }, + NVIC: NVIC { + _marker: PhantomData, + }, + SAU: SAU { + _marker: PhantomData, + }, + SCB: SCB { + _marker: PhantomData, + }, + SYST: SYST { + _marker: PhantomData, + }, + TPIU: TPIU { + _marker: PhantomData, + }, + _priv: (), + } + } +} + +/// 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 _; +} + +/// Cache and branch predictor maintenance operations +#[allow(clippy::upper_case_acronyms)] +pub struct CBP { + _marker: PhantomData<*const ()>, +} + +unsafe impl Send for CBP {} + +#[cfg(not(armv6m))] +impl CBP { + #[inline(always)] + pub(crate) const unsafe fn new() -> Self { + CBP { + _marker: PhantomData, + } + } + + /// Pointer to the register block + pub const PTR: *const self::cbp::RegisterBlock = 0xE000_EF50 as *const _; +} + +#[cfg(not(armv6m))] +impl ops::Deref for CBP { + type Target = self::cbp::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*Self::PTR } + } +} + +/// CPUID +#[allow(clippy::upper_case_acronyms)] +pub struct CPUID { + _marker: PhantomData<*const ()>, +} + +unsafe impl Send for CPUID {} + +impl CPUID { + /// Pointer to the register block + pub const PTR: *const self::cpuid::RegisterBlock = 0xE000_ED00 as *const _; +} + +impl ops::Deref for CPUID { + type Target = self::cpuid::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*Self::PTR } + } +} + +/// Debug Control Block +#[allow(clippy::upper_case_acronyms)] +pub struct DCB { + _marker: PhantomData<*const ()>, +} + +unsafe impl Send for DCB {} + +impl DCB { + /// Pointer to the register block + pub const PTR: *const dcb::RegisterBlock = 0xE000_EDF0 as *const _; +} + +impl ops::Deref for DCB { + type Target = self::dcb::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*DCB::PTR } + } +} + +/// Data Watchpoint and Trace unit +#[allow(clippy::upper_case_acronyms)] +pub struct DWT { + _marker: PhantomData<*const ()>, +} + +unsafe impl Send for DWT {} + +impl DWT { + /// Pointer to the register block + pub const PTR: *const dwt::RegisterBlock = 0xE000_1000 as *const _; +} + +impl ops::Deref for DWT { + type Target = self::dwt::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*Self::PTR } + } +} + +/// Flash Patch and Breakpoint unit +#[allow(clippy::upper_case_acronyms)] +pub struct FPB { + _marker: PhantomData<*const ()>, +} + +unsafe impl Send for FPB {} + +#[cfg(not(armv6m))] +impl FPB { + /// Pointer to the register block + pub const PTR: *const fpb::RegisterBlock = 0xE000_2000 as *const _; +} + +#[cfg(not(armv6m))] +impl ops::Deref for FPB { + type Target = self::fpb::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*Self::PTR } + } +} + +/// Floating Point Unit +#[allow(clippy::upper_case_acronyms)] +pub struct FPU { + _marker: PhantomData<*const ()>, +} + +unsafe impl Send for FPU {} + +#[cfg(any(has_fpu, native))] +impl FPU { + /// Pointer to the register block + pub const PTR: *const fpu::RegisterBlock = 0xE000_EF30 as *const _; +} + +#[cfg(any(has_fpu, native))] +impl ops::Deref for FPU { + type Target = self::fpu::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*Self::PTR } + } +} + +/// Implementation Control Block. +/// +/// This block contains implementation-defined registers like `ictr` and +/// `actlr`. It's called the "implementation control block" in the ARMv8-M +/// standard, but earlier standards contained the registers, just without a +/// name. +#[allow(clippy::upper_case_acronyms)] +pub struct ICB { + _marker: PhantomData<*const ()>, +} + +unsafe impl Send for ICB {} + +impl ICB { + /// Pointer to the register block + pub const PTR: *mut icb::RegisterBlock = 0xE000_E004 as *mut _; +} + +impl ops::Deref for ICB { + type Target = self::icb::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*Self::PTR } + } +} + +impl ops::DerefMut for ICB { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *Self::PTR } + } +} + +/// Instrumentation Trace Macrocell +#[allow(clippy::upper_case_acronyms)] +pub struct ITM { + _marker: PhantomData<*const ()>, +} + +unsafe impl Send for ITM {} + +#[cfg(all(not(armv6m), not(armv8m_base)))] +impl ITM { + /// Pointer to the register block + pub const PTR: *mut itm::RegisterBlock = 0xE000_0000 as *mut _; +} + +#[cfg(all(not(armv6m), not(armv8m_base)))] +impl ops::Deref for ITM { + type Target = self::itm::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*Self::PTR } + } +} + +#[cfg(all(not(armv6m), not(armv8m_base)))] +impl ops::DerefMut for ITM { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *Self::PTR } + } +} + +/// Memory Protection Unit +#[allow(clippy::upper_case_acronyms)] +pub struct MPU { + _marker: PhantomData<*const ()>, +} + +unsafe impl Send for MPU {} + +impl MPU { + /// Pointer to the register block + pub const PTR: *const mpu::RegisterBlock = 0xE000_ED90 as *const _; +} + +impl ops::Deref for MPU { + type Target = self::mpu::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*Self::PTR } + } +} + +/// Nested Vector Interrupt Controller +#[allow(clippy::upper_case_acronyms)] +pub struct NVIC { + _marker: PhantomData<*const ()>, +} + +unsafe impl Send for NVIC {} + +impl NVIC { + /// Pointer to the register block + pub const PTR: *const nvic::RegisterBlock = 0xE000_E100 as *const _; +} + +impl ops::Deref for NVIC { + type Target = self::nvic::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*Self::PTR } + } +} + +/// Security Attribution Unit +#[allow(clippy::upper_case_acronyms)] +pub struct SAU { + _marker: PhantomData<*const ()>, +} + +unsafe impl Send for SAU {} + +#[cfg(armv8m)] +impl SAU { + /// Pointer to the register block + pub const PTR: *const sau::RegisterBlock = 0xE000_EDD0 as *const _; +} + +#[cfg(armv8m)] +impl ops::Deref for SAU { + type Target = self::sau::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*Self::PTR } + } +} + +/// System Control Block +#[allow(clippy::upper_case_acronyms)] +pub struct SCB { + _marker: PhantomData<*const ()>, +} + +unsafe impl Send for SCB {} + +impl SCB { + /// Pointer to the register block + pub const PTR: *const scb::RegisterBlock = 0xE000_ED04 as *const _; +} + +impl ops::Deref for SCB { + type Target = self::scb::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*Self::PTR } + } +} + +/// SysTick: System Timer +#[allow(clippy::upper_case_acronyms)] +pub struct SYST { + _marker: PhantomData<*const ()>, +} + +unsafe impl Send for SYST {} + +impl SYST { + /// Pointer to the register block + pub const PTR: *const syst::RegisterBlock = 0xE000_E010 as *const _; +} + +impl ops::Deref for SYST { + type Target = self::syst::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*Self::PTR } + } +} + +/// Trace Port Interface Unit +#[allow(clippy::upper_case_acronyms)] +pub struct TPIU { + _marker: PhantomData<*const ()>, +} + +unsafe impl Send for TPIU {} + +#[cfg(not(armv6m))] +impl TPIU { + /// Pointer to the register block + pub const PTR: *const tpiu::RegisterBlock = 0xE004_0000 as *const _; +} + +#[cfg(not(armv6m))] +impl ops::Deref for TPIU { + type Target = self::tpiu::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*Self::PTR } + } +} |