feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
Vendored
+209
@@ -0,0 +1,209 @@
|
||||
//! Miscellaneous assembly instructions
|
||||
|
||||
// When inline assembly is enabled, pull in the assembly routines here. `call_asm!` will invoke
|
||||
// these routines.
|
||||
#[cfg(feature = "inline-asm")]
|
||||
#[path = "../asm/inline.rs"]
|
||||
pub(crate) mod inline;
|
||||
|
||||
/// Puts the processor in Debug state. Debuggers can pick this up as a "breakpoint".
|
||||
///
|
||||
/// **NOTE** calling `bkpt` when the processor is not connected to a debugger will cause an
|
||||
/// exception.
|
||||
#[inline(always)]
|
||||
pub fn bkpt() {
|
||||
call_asm!(__bkpt());
|
||||
}
|
||||
|
||||
/// Blocks the program for *at least* `cycles` CPU cycles.
|
||||
///
|
||||
/// This is implemented in assembly so its execution time is independent of the optimization
|
||||
/// level, however it is dependent on the specific architecture and core configuration.
|
||||
///
|
||||
/// NOTE that the delay can take much longer if interrupts are serviced during its execution
|
||||
/// and the execution time may vary with other factors. This delay is mainly useful for simple
|
||||
/// timer-less initialization of peripherals if and only if accurate timing is not essential. In
|
||||
/// any other case please use a more accurate method to produce a delay.
|
||||
#[inline]
|
||||
pub fn delay(cycles: u32) {
|
||||
call_asm!(__delay(cycles: u32));
|
||||
}
|
||||
|
||||
/// A no-operation. Useful to prevent delay loops from being optimized away.
|
||||
#[inline]
|
||||
pub fn nop() {
|
||||
call_asm!(__nop());
|
||||
}
|
||||
|
||||
/// Generate an Undefined Instruction exception.
|
||||
///
|
||||
/// Can be used as a stable alternative to `core::intrinsics::abort`.
|
||||
#[inline]
|
||||
pub fn udf() -> ! {
|
||||
call_asm!(__udf() -> !)
|
||||
}
|
||||
|
||||
/// Wait For Event
|
||||
#[inline]
|
||||
pub fn wfe() {
|
||||
call_asm!(__wfe())
|
||||
}
|
||||
|
||||
/// Wait For Interrupt
|
||||
#[inline]
|
||||
pub fn wfi() {
|
||||
call_asm!(__wfi())
|
||||
}
|
||||
|
||||
/// Send Event
|
||||
#[inline]
|
||||
pub fn sev() {
|
||||
call_asm!(__sev())
|
||||
}
|
||||
|
||||
/// Instruction Synchronization Barrier
|
||||
///
|
||||
/// Flushes the pipeline in the processor, so that all instructions following the `ISB` are fetched
|
||||
/// from cache or memory, after the instruction has been completed.
|
||||
#[inline]
|
||||
pub fn isb() {
|
||||
call_asm!(__isb())
|
||||
}
|
||||
|
||||
/// Data Synchronization Barrier
|
||||
///
|
||||
/// Acts as a special kind of memory barrier. No instruction in program order after this instruction
|
||||
/// can execute until this instruction completes. This instruction completes only when both:
|
||||
///
|
||||
/// * any explicit memory access made before this instruction is complete
|
||||
/// * all cache and branch predictor maintenance operations before this instruction complete
|
||||
#[inline]
|
||||
pub fn dsb() {
|
||||
call_asm!(__dsb())
|
||||
}
|
||||
|
||||
/// Data Memory Barrier
|
||||
///
|
||||
/// Ensures that all explicit memory accesses that appear in program order before the `DMB`
|
||||
/// instruction are observed before any explicit memory accesses that appear in program order
|
||||
/// after the `DMB` instruction.
|
||||
#[inline]
|
||||
pub fn dmb() {
|
||||
call_asm!(__dmb())
|
||||
}
|
||||
|
||||
/// Test Target
|
||||
///
|
||||
/// Queries the Security state and access permissions of a memory location.
|
||||
/// Returns a Test Target Response Payload (cf section D1.2.215 of
|
||||
/// Armv8-M Architecture Reference Manual).
|
||||
#[inline]
|
||||
#[cfg(armv8m)]
|
||||
// The __tt function does not dereference the pointer received.
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn tt(addr: *mut u32) -> u32 {
|
||||
let addr = addr as u32;
|
||||
call_asm!(__tt(addr: u32) -> u32)
|
||||
}
|
||||
|
||||
/// Test Target Unprivileged
|
||||
///
|
||||
/// Queries the Security state and access permissions of a memory location for an unprivileged
|
||||
/// access to that location.
|
||||
/// Returns a Test Target Response Payload (cf section D1.2.215 of
|
||||
/// Armv8-M Architecture Reference Manual).
|
||||
#[inline]
|
||||
#[cfg(armv8m)]
|
||||
// The __ttt function does not dereference the pointer received.
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn ttt(addr: *mut u32) -> u32 {
|
||||
let addr = addr as u32;
|
||||
call_asm!(__ttt(addr: u32) -> u32)
|
||||
}
|
||||
|
||||
/// Test Target Alternate Domain
|
||||
///
|
||||
/// Queries the Security state and access permissions of a memory location for a Non-Secure access
|
||||
/// to that location. This instruction is only valid when executing in Secure state and is
|
||||
/// undefined if used from Non-Secure state.
|
||||
/// Returns a Test Target Response Payload (cf section D1.2.215 of
|
||||
/// Armv8-M Architecture Reference Manual).
|
||||
#[inline]
|
||||
#[cfg(armv8m)]
|
||||
// The __tta function does not dereference the pointer received.
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn tta(addr: *mut u32) -> u32 {
|
||||
let addr = addr as u32;
|
||||
call_asm!(__tta(addr: u32) -> u32)
|
||||
}
|
||||
|
||||
/// Test Target Alternate Domain Unprivileged
|
||||
///
|
||||
/// Queries the Security state and access permissions of a memory location for a Non-Secure and
|
||||
/// unprivileged access to that location. This instruction is only valid when executing in Secure
|
||||
/// state and is undefined if used from Non-Secure state.
|
||||
/// Returns a Test Target Response Payload (cf section D1.2.215 of
|
||||
/// Armv8-M Architecture Reference Manual).
|
||||
#[inline]
|
||||
#[cfg(armv8m)]
|
||||
// The __ttat function does not dereference the pointer received.
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn ttat(addr: *mut u32) -> u32 {
|
||||
let addr = addr as u32;
|
||||
call_asm!(__ttat(addr: u32) -> u32)
|
||||
}
|
||||
|
||||
/// Branch and Exchange Non-secure
|
||||
///
|
||||
/// See section C2.4.26 of Armv8-M Architecture Reference Manual for details.
|
||||
/// Undefined if executed in Non-Secure state.
|
||||
#[inline]
|
||||
#[cfg(armv8m)]
|
||||
pub unsafe fn bx_ns(addr: u32) {
|
||||
call_asm!(__bxns(addr: u32));
|
||||
}
|
||||
|
||||
/// Semihosting syscall.
|
||||
///
|
||||
/// This method is used by cortex-m-semihosting to provide semihosting syscalls.
|
||||
#[inline]
|
||||
pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 {
|
||||
call_asm!(__sh_syscall(nr: u32, arg: u32) -> u32)
|
||||
}
|
||||
|
||||
/// Bootstrap.
|
||||
///
|
||||
/// Clears CONTROL.SPSEL (setting the main stack to be the active stack),
|
||||
/// updates the main stack pointer to the address in `msp`, then jumps
|
||||
/// to the address in `rv`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `msp` and `rv` must point to valid stack memory and executable code,
|
||||
/// respectively.
|
||||
#[inline]
|
||||
pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! {
|
||||
// Ensure thumb mode is set.
|
||||
let rv = (rv as u32) | 1;
|
||||
let msp = msp as u32;
|
||||
call_asm!(__bootstrap(msp: u32, rv: u32) -> !);
|
||||
}
|
||||
|
||||
/// Bootload.
|
||||
///
|
||||
/// Reads the initial stack pointer value and reset vector from
|
||||
/// the provided vector table address, sets the active stack to
|
||||
/// the main stack, sets the main stack pointer to the new initial
|
||||
/// stack pointer, then jumps to the reset vector.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The provided `vector_table` must point to a valid vector
|
||||
/// table, with a valid stack pointer as the first word and
|
||||
/// a valid reset vector as the second word.
|
||||
#[inline]
|
||||
pub unsafe fn bootload(vector_table: *const u32) -> ! {
|
||||
let msp = core::ptr::read_volatile(vector_table);
|
||||
let rv = core::ptr::read_volatile(vector_table.offset(1));
|
||||
bootstrap(msp as *const u32, rv as *const u32);
|
||||
}
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
/// An internal macro to invoke an assembly routine.
|
||||
///
|
||||
/// Depending on whether the unstable `inline-asm` feature is enabled, this will either call into
|
||||
/// the inline assembly implementation directly, or through the FFI shim (see `asm/lib.rs`).
|
||||
macro_rules! call_asm {
|
||||
( $func:ident ( $($args:ident: $tys:ty),* ) $(-> $ret:ty)? ) => {{
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
match () {
|
||||
#[cfg(feature = "inline-asm")]
|
||||
() => crate::asm::inline::$func($($args),*),
|
||||
|
||||
#[cfg(not(feature = "inline-asm"))]
|
||||
() => {
|
||||
extern "C" {
|
||||
fn $func($($args: $tys),*) $(-> $ret)?;
|
||||
}
|
||||
|
||||
$func($($args),*)
|
||||
},
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
Vendored
+238
@@ -0,0 +1,238 @@
|
||||
//! Cortex-M Security Extensions
|
||||
//!
|
||||
//! This module provides several helper functions to support Armv8-M and Armv8.1-M Security
|
||||
//! Extensions.
|
||||
//! Most of this implementation is directly inspired by the "Armv8-M Security Extensions:
|
||||
//! Requirements on Development Tools" document available here:
|
||||
//! https://developer.arm.com/docs/ecm0359818/latest
|
||||
//!
|
||||
//! Please note that the TT instructions support as described part 4 of the document linked above is
|
||||
//! not part of CMSE but is still present in this module. The TT instructions return the
|
||||
//! configuration of the Memory Protection Unit at an address.
|
||||
//!
|
||||
//! # Notes
|
||||
//!
|
||||
//! * Non-Secure Unprivileged code will always read zeroes from TestTarget and should not use it.
|
||||
//! * Non-Secure Privileged code can check current (AccessType::Current) and Non-Secure Unprivileged
|
||||
//! accesses (AccessType::Unprivileged).
|
||||
//! * Secure Unprivileged code can check Non-Secure Unprivileged accesses (AccessType::NonSecure).
|
||||
//! * Secure Privileged code can check all access types.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! use cortex_m::cmse::{TestTarget, AccessType};
|
||||
//!
|
||||
//! // suspect_address was given by Non-Secure to a Secure function to write at it.
|
||||
//! // But is it allowed to?
|
||||
//! let suspect_address_test = TestTarget::check(0xDEADBEEF as *mut u32,
|
||||
//! AccessType::NonSecureUnprivileged);
|
||||
//! if suspect_address_test.ns_read_and_writable() {
|
||||
//! // Non-Secure can not read or write this address!
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use crate::asm::{tt, tta, ttat, ttt};
|
||||
use bitfield::bitfield;
|
||||
|
||||
/// Memory access behaviour: determine which privilege execution mode is used and which Memory
|
||||
/// Protection Unit (MPU) is used.
|
||||
#[derive(PartialEq, Copy, Clone, Debug)]
|
||||
pub enum AccessType {
|
||||
/// Access using current privilege level and reading from current security state MPU.
|
||||
/// Uses the TT instruction.
|
||||
Current,
|
||||
/// Unprivileged access reading from current security state MPU. Uses the TTT instruction.
|
||||
Unprivileged,
|
||||
/// Access using current privilege level reading from Non-Secure MPU. Uses the TTA instruction.
|
||||
/// Undefined if used from Non-Secure state.
|
||||
NonSecure,
|
||||
/// Unprivilege access reading from Non-Secure MPU. Uses the TTAT instruction.
|
||||
/// Undefined if used from Non-Secure state.
|
||||
NonSecureUnprivileged,
|
||||
}
|
||||
|
||||
/// Abstraction of TT instructions and helper functions to determine the security and privilege
|
||||
/// attribute of a target address, accessed in different ways.
|
||||
#[derive(PartialEq, Copy, Clone, Debug)]
|
||||
pub struct TestTarget {
|
||||
tt_resp: TtResp,
|
||||
access_type: AccessType,
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
/// Test Target Response Payload
|
||||
///
|
||||
/// Provides the response payload from a TT, TTA, TTT or TTAT instruction.
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
struct TtResp(u32);
|
||||
impl Debug;
|
||||
mregion, _: 7, 0;
|
||||
sregion, _: 15, 8;
|
||||
mrvalid, _: 16;
|
||||
srvalid, _: 17;
|
||||
r, _: 18;
|
||||
rw, _: 19;
|
||||
nsr, _: 20;
|
||||
nsrw, _: 21;
|
||||
s, _: 22;
|
||||
irvalid, _: 23;
|
||||
iregion, _: 31, 24;
|
||||
}
|
||||
|
||||
impl TestTarget {
|
||||
/// Creates a Test Target Response Payload by testing addr using access_type.
|
||||
#[inline]
|
||||
pub fn check(addr: *mut u32, access_type: AccessType) -> Self {
|
||||
let tt_resp = match access_type {
|
||||
AccessType::Current => TtResp(tt(addr)),
|
||||
AccessType::Unprivileged => TtResp(ttt(addr)),
|
||||
AccessType::NonSecure => TtResp(tta(addr)),
|
||||
AccessType::NonSecureUnprivileged => TtResp(ttat(addr)),
|
||||
};
|
||||
|
||||
TestTarget {
|
||||
tt_resp,
|
||||
access_type,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a Test Target Response Payload by testing the zone from addr to addr + size - 1
|
||||
/// using access_type.
|
||||
/// Returns None if:
|
||||
/// * the address zone overlaps SAU, IDAU or MPU region boundaries
|
||||
/// * size is 0
|
||||
/// * addr + size - 1 overflows
|
||||
#[inline]
|
||||
pub fn check_range(addr: *mut u32, size: usize, access_type: AccessType) -> Option<Self> {
|
||||
let begin: usize = addr as usize;
|
||||
// Last address of the range (addr + size - 1). This also checks if size is 0.
|
||||
let end: usize = begin.checked_add(size.checked_sub(1)?)?;
|
||||
|
||||
// Regions are aligned at 32-byte boundaries. If the address range fits in one 32-byte
|
||||
// address line, a single TT instruction suffices. This is the case when the following
|
||||
// constraint holds.
|
||||
let single_check: bool = (begin % 32).checked_add(size)? <= 32usize;
|
||||
|
||||
let test_start = TestTarget::check(addr, access_type);
|
||||
|
||||
if single_check {
|
||||
Some(test_start)
|
||||
} else {
|
||||
let test_end = TestTarget::check(end as *mut u32, access_type);
|
||||
// Check that the range does not cross SAU, IDAU or MPU region boundaries.
|
||||
if test_start != test_end {
|
||||
None
|
||||
} else {
|
||||
Some(test_start)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Access type that was used for this test target.
|
||||
#[inline]
|
||||
pub fn access_type(self) -> AccessType {
|
||||
self.access_type
|
||||
}
|
||||
|
||||
/// Get the raw u32 value returned by the TT instruction used.
|
||||
#[inline]
|
||||
pub fn as_u32(self) -> u32 {
|
||||
self.tt_resp.0
|
||||
}
|
||||
|
||||
/// Read accessibility of the target address. Only returns the MPU settings without checking
|
||||
/// the Security state of the target.
|
||||
/// For Unprivileged and NonSecureUnprivileged access types, returns the permissions for
|
||||
/// unprivileged access, regardless of whether the current mode is privileged or unprivileged.
|
||||
/// Returns false if the TT instruction was executed from an unprivileged mode
|
||||
/// and the NonSecure access type was not specified.
|
||||
/// Returns false if the address matches multiple MPU regions.
|
||||
#[inline]
|
||||
pub fn readable(self) -> bool {
|
||||
self.tt_resp.r()
|
||||
}
|
||||
|
||||
/// Read and write accessibility of the target address. Only returns the MPU settings without
|
||||
/// checking the Security state of the target.
|
||||
/// For Unprivileged and NonSecureUnprivileged access types, returns the permissions for
|
||||
/// unprivileged access, regardless of whether the current mode is privileged or unprivileged.
|
||||
/// Returns false if the TT instruction was executed from an unprivileged mode
|
||||
/// and the NonSecure access type was not specified.
|
||||
/// Returns false if the address matches multiple MPU regions.
|
||||
#[inline]
|
||||
pub fn read_and_writable(self) -> bool {
|
||||
self.tt_resp.rw()
|
||||
}
|
||||
|
||||
/// Indicate the MPU region number containing the target address.
|
||||
/// Returns None if the value is not valid:
|
||||
/// * the MPU is not implemented or MPU_CTRL.ENABLE is set to zero
|
||||
/// * the register argument specified by the MREGION field does not match any enabled MPU regions
|
||||
/// * the address matched multiple MPU regions
|
||||
/// * the address specified by the SREGION field is exempt from the secure memory attribution
|
||||
/// * the TT instruction was executed from an unprivileged mode and the A flag was not specified.
|
||||
#[inline]
|
||||
pub fn mpu_region(self) -> Option<u8> {
|
||||
if self.tt_resp.srvalid() {
|
||||
// Cast is safe as SREGION field is defined on 8 bits.
|
||||
Some(self.tt_resp.sregion() as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates the Security attribute of the target address. Independent of AccessType.
|
||||
/// Always zero when the test target is done in the Non-Secure state.
|
||||
#[inline]
|
||||
pub fn secure(self) -> bool {
|
||||
self.tt_resp.s()
|
||||
}
|
||||
|
||||
/// Non-Secure Read accessibility of the target address.
|
||||
/// Same as readable() && !secure()
|
||||
#[inline]
|
||||
pub fn ns_readable(self) -> bool {
|
||||
self.tt_resp.nsr()
|
||||
}
|
||||
|
||||
/// Non-Secure Read and Write accessibility of the target address.
|
||||
/// Same as read_and_writable() && !secure()
|
||||
#[inline]
|
||||
pub fn ns_read_and_writable(self) -> bool {
|
||||
self.tt_resp.nsrw()
|
||||
}
|
||||
|
||||
/// Indicate the IDAU region number containing the target address. Independent of AccessType.
|
||||
/// Returns None if the value is not valid:
|
||||
/// * the IDAU cannot provide a region number
|
||||
/// * the address is exempt from security attribution
|
||||
/// * the test target is done from Non-Secure state
|
||||
#[inline]
|
||||
pub fn idau_region(self) -> Option<u8> {
|
||||
if self.tt_resp.irvalid() {
|
||||
// Cast is safe as IREGION field is defined on 8 bits.
|
||||
Some(self.tt_resp.iregion() as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicate the SAU region number containing the target address. Independent of AccessType.
|
||||
/// Returns None if the value is not valid:
|
||||
/// * SAU_CTRL.ENABLE is set to zero
|
||||
/// * the register argument specified in the SREGION field does not match any enabled SAU regions
|
||||
/// * the address specified matches multiple enabled SAU regions
|
||||
/// * the address specified by the SREGION field is exempt from the secure memory attribution
|
||||
/// * the TT instruction was executed from the Non-secure state or the Security Extension is not
|
||||
/// implemented
|
||||
#[inline]
|
||||
pub fn sau_region(self) -> Option<u8> {
|
||||
if self.tt_resp.srvalid() {
|
||||
// Cast is safe as SREGION field is defined on 8 bits.
|
||||
Some(self.tt_resp.sregion() as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
#[cfg(all(cortex_m, feature = "critical-section-single-core"))]
|
||||
mod single_core_critical_section {
|
||||
use critical_section::{set_impl, Impl, RawRestoreState};
|
||||
|
||||
use crate::interrupt;
|
||||
use crate::register::primask;
|
||||
|
||||
struct SingleCoreCriticalSection;
|
||||
set_impl!(SingleCoreCriticalSection);
|
||||
|
||||
unsafe impl Impl for SingleCoreCriticalSection {
|
||||
unsafe fn acquire() -> RawRestoreState {
|
||||
let was_active = primask::read().is_active();
|
||||
interrupt::disable();
|
||||
was_active
|
||||
}
|
||||
|
||||
unsafe fn release(was_active: RawRestoreState) {
|
||||
// Only re-enable interrupts if they were enabled before the critical section.
|
||||
if was_active {
|
||||
interrupt::enable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+136
@@ -0,0 +1,136 @@
|
||||
//! A delay driver based on SysTick.
|
||||
|
||||
use crate::peripheral::{syst::SystClkSource, SYST};
|
||||
use embedded_hal::blocking::delay::{DelayMs, DelayUs};
|
||||
|
||||
/// System timer (SysTick) as a delay provider.
|
||||
pub struct Delay {
|
||||
syst: SYST,
|
||||
frequency: u32,
|
||||
}
|
||||
|
||||
impl Delay {
|
||||
/// Configures the system timer (SysTick) as a delay provider.
|
||||
///
|
||||
/// `ahb_frequency` is a frequency of the AHB bus in Hz.
|
||||
#[inline]
|
||||
pub fn new(syst: SYST, ahb_frequency: u32) -> Self {
|
||||
Self::with_source(syst, ahb_frequency, SystClkSource::Core)
|
||||
}
|
||||
|
||||
/// Configures the system timer (SysTick) as a delay provider
|
||||
/// with a clock source.
|
||||
///
|
||||
/// `frequency` is the frequency of your `clock_source` in Hz.
|
||||
#[inline]
|
||||
pub fn with_source(mut syst: SYST, frequency: u32, clock_source: SystClkSource) -> Self {
|
||||
syst.set_clock_source(clock_source);
|
||||
|
||||
Delay { syst, frequency }
|
||||
}
|
||||
|
||||
/// Releases the system timer (SysTick) resource.
|
||||
#[inline]
|
||||
pub fn free(self) -> SYST {
|
||||
self.syst
|
||||
}
|
||||
|
||||
/// 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.frequency)) / 1_000_000;
|
||||
|
||||
let full_cycles = ticks >> 24;
|
||||
if full_cycles > 0 {
|
||||
self.syst.set_reload(0xffffff);
|
||||
self.syst.clear_current();
|
||||
self.syst.enable_counter();
|
||||
|
||||
for _ in 0..full_cycles {
|
||||
while !self.syst.has_wrapped() {}
|
||||
}
|
||||
}
|
||||
|
||||
let ticks = (ticks & 0xffffff) as u32;
|
||||
if ticks > 1 {
|
||||
self.syst.set_reload(ticks - 1);
|
||||
self.syst.clear_current();
|
||||
self.syst.enable_counter();
|
||||
|
||||
while !self.syst.has_wrapped() {}
|
||||
}
|
||||
|
||||
self.syst.disable_counter();
|
||||
}
|
||||
|
||||
/// Delay using the Cortex-M systick for a certain duration, in ms.
|
||||
#[inline]
|
||||
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);
|
||||
ms -= 4294967;
|
||||
}
|
||||
self.delay_us(ms * 1_000);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
Delay::delay_ms(self, ms as u32);
|
||||
}
|
||||
}
|
||||
|
||||
impl DelayMs<u16> for Delay {
|
||||
#[inline(always)]
|
||||
fn delay_ms(&mut self, ms: u16) {
|
||||
Delay::delay_ms(self, u32::from(ms));
|
||||
}
|
||||
}
|
||||
|
||||
impl DelayMs<u8> for Delay {
|
||||
#[inline(always)]
|
||||
fn delay_ms(&mut self, ms: u8) {
|
||||
Delay::delay_ms(self, u32::from(ms));
|
||||
}
|
||||
}
|
||||
|
||||
impl DelayUs<u32> for Delay {
|
||||
#[inline]
|
||||
fn delay_us(&mut self, us: u32) {
|
||||
Delay::delay_us(self, us);
|
||||
}
|
||||
}
|
||||
|
||||
// This is a workaround to allow `delay_us(42)` construction without specifying a type.
|
||||
impl DelayUs<i32> for Delay {
|
||||
#[inline(always)]
|
||||
fn delay_us(&mut self, us: i32) {
|
||||
assert!(us >= 0);
|
||||
Delay::delay_us(self, us as u32);
|
||||
}
|
||||
}
|
||||
|
||||
impl DelayUs<u16> for Delay {
|
||||
#[inline(always)]
|
||||
fn delay_us(&mut self, us: u16) {
|
||||
Delay::delay_us(self, u32::from(us))
|
||||
}
|
||||
}
|
||||
|
||||
impl DelayUs<u8> for Delay {
|
||||
#[inline(always)]
|
||||
fn delay_us(&mut self, us: u8) {
|
||||
Delay::delay_us(self, u32::from(us))
|
||||
}
|
||||
}
|
||||
Vendored
+73
@@ -0,0 +1,73 @@
|
||||
//! Interrupts
|
||||
|
||||
pub use bare_metal::{CriticalSection, Mutex, Nr};
|
||||
|
||||
/// Trait for enums of external interrupt numbers.
|
||||
///
|
||||
/// This trait should be implemented by a peripheral access crate (PAC)
|
||||
/// on its enum of available external interrupts for a specific device.
|
||||
/// Each variant must convert to a u16 of its interrupt number,
|
||||
/// which is its exception number - 16.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This trait must only be implemented on enums of device interrupts. Each
|
||||
/// enum variant must represent a distinct value (no duplicates are permitted),
|
||||
/// and must always return the same value (do not change at runtime).
|
||||
///
|
||||
/// These requirements ensure safe nesting of critical sections.
|
||||
pub unsafe trait InterruptNumber: Copy {
|
||||
/// Return the interrupt number associated with this variant.
|
||||
///
|
||||
/// See trait documentation for safety requirements.
|
||||
fn number(self) -> u16;
|
||||
}
|
||||
|
||||
/// Implement InterruptNumber for the old bare_metal::Nr trait.
|
||||
/// This implementation is for backwards compatibility only and will be removed in cortex-m 0.8.
|
||||
unsafe impl<T: Nr + Copy> InterruptNumber for T {
|
||||
#[inline]
|
||||
fn number(self) -> u16 {
|
||||
self.nr() as u16
|
||||
}
|
||||
}
|
||||
|
||||
/// Disables all interrupts
|
||||
#[inline]
|
||||
pub fn disable() {
|
||||
call_asm!(__cpsid());
|
||||
}
|
||||
|
||||
/// Enables all the interrupts
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - Do not call this function inside an `interrupt::free` critical section
|
||||
#[inline]
|
||||
pub unsafe fn enable() {
|
||||
call_asm!(__cpsie());
|
||||
}
|
||||
|
||||
/// Execute closure `f` in an interrupt-free context.
|
||||
///
|
||||
/// This as also known as a "critical section".
|
||||
#[inline]
|
||||
pub fn free<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce(&CriticalSection) -> R,
|
||||
{
|
||||
let primask = crate::register::primask::read();
|
||||
|
||||
// disable interrupts
|
||||
disable();
|
||||
|
||||
let r = f(unsafe { &CriticalSection::new() });
|
||||
|
||||
// If the interrupts were active before our `disable` call, then re-enable
|
||||
// them. Otherwise, keep them disabled
|
||||
if primask.is_active() {
|
||||
unsafe { enable() }
|
||||
}
|
||||
|
||||
r
|
||||
}
|
||||
Vendored
+158
@@ -0,0 +1,158 @@
|
||||
//! Instrumentation Trace Macrocell
|
||||
//!
|
||||
//! **NOTE** This module is only available on ARMv7-M and newer.
|
||||
|
||||
use core::{fmt, ptr, slice};
|
||||
|
||||
use crate::peripheral::itm::Stim;
|
||||
|
||||
// NOTE assumes that `bytes` is 32-bit aligned
|
||||
unsafe fn write_words(stim: &mut Stim, bytes: &[u32]) {
|
||||
let mut p = bytes.as_ptr();
|
||||
for _ in 0..bytes.len() {
|
||||
while !stim.is_fifo_ready() {}
|
||||
stim.write_u32(ptr::read(p));
|
||||
p = p.offset(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes an aligned byte slice to the ITM.
|
||||
///
|
||||
/// `buffer` must be 4-byte aligned.
|
||||
unsafe fn write_aligned_impl(port: &mut Stim, buffer: &[u8]) {
|
||||
let len = buffer.len();
|
||||
|
||||
if len == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let split = len & !0b11;
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
write_words(
|
||||
port,
|
||||
slice::from_raw_parts(buffer.as_ptr() as *const u32, split >> 2),
|
||||
);
|
||||
|
||||
// 3 bytes or less left
|
||||
let mut left = len & 0b11;
|
||||
let mut ptr = buffer.as_ptr().add(split);
|
||||
|
||||
// at least 2 bytes left
|
||||
if left > 1 {
|
||||
while !port.is_fifo_ready() {}
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
port.write_u16(ptr::read(ptr as *const u16));
|
||||
|
||||
ptr = ptr.offset(2);
|
||||
left -= 2;
|
||||
}
|
||||
|
||||
// final byte
|
||||
if left == 1 {
|
||||
while !port.is_fifo_ready() {}
|
||||
port.write_u8(*ptr);
|
||||
}
|
||||
}
|
||||
|
||||
struct Port<'p>(&'p mut Stim);
|
||||
|
||||
impl<'p> fmt::Write for Port<'p> {
|
||||
#[inline]
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
write_all(self.0, s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type that aligns its contents on a 4-Byte boundary.
|
||||
///
|
||||
/// ITM transfers are most efficient when the data is 4-Byte-aligned. This type provides an easy
|
||||
/// way to accomplish and enforce such an alignment.
|
||||
#[repr(align(4))]
|
||||
pub struct Aligned<T: ?Sized>(pub T);
|
||||
|
||||
/// Writes `buffer` to an ITM port.
|
||||
#[allow(clippy::missing_inline_in_public_items)]
|
||||
pub fn write_all(port: &mut Stim, buffer: &[u8]) {
|
||||
unsafe {
|
||||
let mut len = buffer.len();
|
||||
let mut ptr = buffer.as_ptr();
|
||||
|
||||
if len == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
// 0x01 OR 0x03
|
||||
if ptr as usize % 2 == 1 {
|
||||
while !port.is_fifo_ready() {}
|
||||
port.write_u8(*ptr);
|
||||
|
||||
// 0x02 OR 0x04
|
||||
ptr = ptr.offset(1);
|
||||
len -= 1;
|
||||
}
|
||||
|
||||
// 0x02
|
||||
if ptr as usize % 4 == 2 {
|
||||
if len > 1 {
|
||||
// at least 2 bytes
|
||||
while !port.is_fifo_ready() {}
|
||||
|
||||
// We checked the alignment above, so this is safe
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
port.write_u16(ptr::read(ptr as *const u16));
|
||||
|
||||
// 0x04
|
||||
ptr = ptr.offset(2);
|
||||
len -= 2;
|
||||
} else {
|
||||
if len == 1 {
|
||||
// last byte
|
||||
while !port.is_fifo_ready() {}
|
||||
port.write_u8(*ptr);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The remaining data is 4-byte aligned, but might not be a multiple of 4 bytes
|
||||
write_aligned_impl(port, slice::from_raw_parts(ptr, len));
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a 4-byte aligned `buffer` to an ITM port.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use cortex_m::{itm::{self, Aligned}, peripheral::ITM};
|
||||
/// # let port = unsafe { &mut (*ITM::PTR).stim[0] };
|
||||
/// let mut buffer = Aligned([0; 14]);
|
||||
///
|
||||
/// buffer.0.copy_from_slice(b"Hello, world!\n");
|
||||
///
|
||||
/// itm::write_aligned(port, &buffer);
|
||||
///
|
||||
/// // Or equivalently
|
||||
/// itm::write_aligned(port, &Aligned(*b"Hello, world!\n"));
|
||||
/// ```
|
||||
#[allow(clippy::missing_inline_in_public_items)]
|
||||
pub fn write_aligned(port: &mut Stim, buffer: &Aligned<[u8]>) {
|
||||
unsafe { write_aligned_impl(port, &buffer.0) }
|
||||
}
|
||||
|
||||
/// Writes `fmt::Arguments` to the ITM `port`
|
||||
#[inline]
|
||||
pub fn write_fmt(port: &mut Stim, args: fmt::Arguments) {
|
||||
use core::fmt::Write;
|
||||
|
||||
Port(port).write_fmt(args).ok();
|
||||
}
|
||||
|
||||
/// Writes a string to the ITM `port`
|
||||
#[inline]
|
||||
pub fn write_str(port: &mut Stim, string: &str) {
|
||||
write_all(port, string.as_bytes())
|
||||
}
|
||||
Vendored
+112
@@ -0,0 +1,112 @@
|
||||
//! Low level access to Cortex-M processors
|
||||
//!
|
||||
//! This crate provides:
|
||||
//!
|
||||
//! - Access to core peripherals like NVIC, SCB and SysTick.
|
||||
//! - Access to core registers like CONTROL, MSP and PSR.
|
||||
//! - Interrupt manipulation mechanisms
|
||||
//! - Safe wrappers around Cortex-M specific instructions like `bkpt`
|
||||
//!
|
||||
//! # Optional features
|
||||
//!
|
||||
//! ## `inline-asm`
|
||||
//!
|
||||
//! When this feature is enabled the implementation of all the functions inside the `asm` and
|
||||
//! `register` modules use inline assembly (`asm!`) instead of external assembly (FFI into separate
|
||||
//! assembly files pre-compiled using `arm-none-eabi-gcc`). The advantages of enabling `inline-asm`
|
||||
//! are:
|
||||
//!
|
||||
//! - Reduced overhead. FFI eliminates the possibility of inlining so all operations include a
|
||||
//! function call overhead when `inline-asm` is not enabled.
|
||||
//!
|
||||
//! - Some of the `register` API only becomes available only when `inline-asm` is enabled. Check the
|
||||
//! API docs for details.
|
||||
//!
|
||||
//! The disadvantage is that `inline-asm` requires a Rust version at least 1.59 to use the `asm!()`
|
||||
//! macro. In the future 0.8 and above versions of `cortex-m`, this feature will always be enabled.
|
||||
//!
|
||||
//! ## `critical-section-single-core`
|
||||
//!
|
||||
//! This feature enables a [`critical-section`](https://github.com/rust-embedded/critical-section)
|
||||
//! implementation suitable for single-core targets, based on disabling interrupts globally.
|
||||
//!
|
||||
//! It is **unsound** to enable it on multi-core targets or for code running in unprivileged mode,
|
||||
//! and may cause functional problems in systems where some interrupts must be not be disabled
|
||||
//! or critical sections are managed as part of an RTOS. In these cases, you should use
|
||||
//! a target-specific implementation instead, typically provided by a HAL or RTOS crate.
|
||||
//!
|
||||
//! ## `cm7-r0p1`
|
||||
//!
|
||||
//! This feature enables workarounds for errata found on Cortex-M7 chips with revision r0p1. Some
|
||||
//! functions in this crate only work correctly on those chips if this Cargo feature is enabled
|
||||
//! (the functions are documented accordingly).
|
||||
//!
|
||||
//! ## `linker-plugin-lto`
|
||||
//!
|
||||
//! This feature links against prebuilt assembly blobs that are compatible with [Linker-Plugin LTO].
|
||||
//! This allows inlining assembly routines into the caller, even without the `inline-asm` feature,
|
||||
//! and works on stable Rust (but note the drawbacks below!).
|
||||
//!
|
||||
//! If you want to use this feature, you need to be aware of a few things:
|
||||
//!
|
||||
//! - You need to make sure that `-Clinker-plugin-lto` is passed to rustc. Please refer to the
|
||||
//! [Linker-Plugin LTO] documentation for details.
|
||||
//!
|
||||
//! - You have to use a Rust version whose LLVM version is compatible with the toolchain in
|
||||
//! `asm-toolchain`.
|
||||
//!
|
||||
//! - Due to a [Rust bug][rust-lang/rust#75940] in compiler versions **before 1.49**, this option
|
||||
//! does not work with optimization levels `s` and `z`.
|
||||
//!
|
||||
//! [Linker-Plugin LTO]: https://doc.rust-lang.org/stable/rustc/linker-plugin-lto.html
|
||||
//! [rust-lang/rust#75940]: https://github.com/rust-lang/rust/issues/75940
|
||||
//!
|
||||
//! # Minimum Supported Rust Version (MSRV)
|
||||
//!
|
||||
//! This crate is guaranteed to compile on stable Rust 1.38 and up. It *might*
|
||||
//! compile with older versions but that may change in any new patch release.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![no_std]
|
||||
#![allow(clippy::identity_op)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
// Prevent clippy from complaining about empty match expression that are used for cfg gating.
|
||||
#![allow(clippy::match_single_binding)]
|
||||
// This makes clippy warn about public functions which are not #[inline].
|
||||
//
|
||||
// Almost all functions in this crate result in trivial or even no assembly.
|
||||
// These functions should be #[inline].
|
||||
//
|
||||
// If you do add a function that's not supposed to be #[inline], you can add
|
||||
// #[allow(clippy::missing_inline_in_public_items)] in front of it to add an
|
||||
// exception to clippy's rules.
|
||||
//
|
||||
// This should be done in case of:
|
||||
// - A function containing non-trivial logic (such as itm::write_all); or
|
||||
// - A generated #[derive(Debug)] function (in which case the attribute needs
|
||||
// to be applied to the struct).
|
||||
#![deny(clippy::missing_inline_in_public_items)]
|
||||
// Don't warn about feature(asm) being stable on Rust >= 1.59.0
|
||||
#![allow(stable_features)]
|
||||
|
||||
extern crate bare_metal;
|
||||
extern crate volatile_register;
|
||||
|
||||
#[macro_use]
|
||||
mod call_asm;
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
pub mod asm;
|
||||
#[cfg(armv8m)]
|
||||
pub mod cmse;
|
||||
mod critical_section;
|
||||
pub mod delay;
|
||||
pub mod interrupt;
|
||||
#[cfg(all(not(armv6m), not(armv8m_base)))]
|
||||
pub mod itm;
|
||||
pub mod peripheral;
|
||||
pub mod prelude;
|
||||
pub mod register;
|
||||
|
||||
pub use crate::peripheral::Peripherals;
|
||||
Vendored
+114
@@ -0,0 +1,114 @@
|
||||
/// Macro for sending a formatted string through an ITM channel
|
||||
#[macro_export]
|
||||
macro_rules! iprint {
|
||||
($channel:expr, $s:expr) => {
|
||||
$crate::itm::write_str($channel, $s);
|
||||
};
|
||||
($channel:expr, $($arg:tt)*) => {
|
||||
$crate::itm::write_fmt($channel, format_args!($($arg)*));
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro for sending a formatted string through an ITM channel, with a newline.
|
||||
#[macro_export]
|
||||
macro_rules! iprintln {
|
||||
($channel:expr) => {
|
||||
$crate::itm::write_str($channel, "\n");
|
||||
};
|
||||
($channel:expr, $fmt:expr) => {
|
||||
$crate::itm::write_str($channel, concat!($fmt, "\n"));
|
||||
};
|
||||
($channel:expr, $fmt:expr, $($arg:tt)*) => {
|
||||
$crate::itm::write_fmt($channel, format_args!(concat!($fmt, "\n"), $($arg)*));
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro to create a mutable reference to a statically allocated value
|
||||
///
|
||||
/// This macro returns a value with type `Option<&'static mut $ty>`. `Some($expr)` will be returned
|
||||
/// the first time the macro is executed; further calls will return `None`. To avoid `unwrap`ping a
|
||||
/// `None` variant the caller must ensure that the macro is called from a function that's executed
|
||||
/// at most once in the whole lifetime of the program.
|
||||
///
|
||||
/// # Notes
|
||||
/// This macro is unsound on multi core systems.
|
||||
///
|
||||
/// For debuggability, you can set an explicit name for a singleton. This name only shows up the
|
||||
/// the debugger and is not referencable from other code. See example below.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ``` no_run
|
||||
/// use cortex_m::singleton;
|
||||
///
|
||||
/// fn main() {
|
||||
/// // OK if `main` is executed only once
|
||||
/// let x: &'static mut bool = singleton!(: bool = false).unwrap();
|
||||
///
|
||||
/// let y = alias();
|
||||
/// // BAD this second call to `alias` will definitively `panic!`
|
||||
/// let y_alias = alias();
|
||||
/// }
|
||||
///
|
||||
/// fn alias() -> &'static mut bool {
|
||||
/// singleton!(: bool = false).unwrap()
|
||||
/// }
|
||||
///
|
||||
/// fn singleton_with_name() {
|
||||
/// // A name only for debugging purposes
|
||||
/// singleton!(FOO_BUFFER: [u8; 1024] = [0u8; 1024]);
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! singleton {
|
||||
($name:ident: $ty:ty = $expr:expr) => {
|
||||
$crate::interrupt::free(|_| {
|
||||
// this is a tuple of a MaybeUninit and a bool because using an Option here is
|
||||
// problematic: Due to niche-optimization, an Option could end up producing a non-zero
|
||||
// initializer value which would move the entire static from `.bss` into `.data`...
|
||||
static mut $name: (::core::mem::MaybeUninit<$ty>, bool) =
|
||||
(::core::mem::MaybeUninit::uninit(), false);
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
let used = unsafe { $name.1 };
|
||||
if used {
|
||||
None
|
||||
} else {
|
||||
let expr = $expr;
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe {
|
||||
$name.1 = true;
|
||||
$name.0 = ::core::mem::MaybeUninit::new(expr);
|
||||
Some(&mut *$name.0.as_mut_ptr())
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
(: $ty:ty = $expr:expr) => {
|
||||
$crate::singleton!(VAR: $ty = $expr)
|
||||
};
|
||||
}
|
||||
|
||||
/// ``` compile_fail
|
||||
/// use cortex_m::singleton;
|
||||
///
|
||||
/// fn foo() {
|
||||
/// // check that the call to `uninitialized` requires unsafe
|
||||
/// singleton!(: u8 = std::mem::uninitialized());
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(dead_code)]
|
||||
const CFAIL: () = ();
|
||||
|
||||
/// ```
|
||||
/// #![deny(unsafe_code)]
|
||||
/// use cortex_m::singleton;
|
||||
///
|
||||
/// fn foo() {
|
||||
/// // check that calls to `singleton!` don't trip the `unsafe_code` lint
|
||||
/// singleton!(: u8 = 0);
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(dead_code)]
|
||||
const CPASS: () = ();
|
||||
Vendored
+93
@@ -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;
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
//! Cache and branch predictor maintenance operations
|
||||
//!
|
||||
//! *NOTE* Not available on Armv6-M.
|
||||
|
||||
use volatile_register::WO;
|
||||
|
||||
use crate::peripheral::CBP;
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// I-cache invalidate all to PoU
|
||||
pub iciallu: WO<u32>,
|
||||
reserved0: u32,
|
||||
/// I-cache invalidate by MVA to PoU
|
||||
pub icimvau: WO<u32>,
|
||||
/// D-cache invalidate by MVA to PoC
|
||||
pub dcimvac: WO<u32>,
|
||||
/// D-cache invalidate by set-way
|
||||
pub dcisw: WO<u32>,
|
||||
/// D-cache clean by MVA to PoU
|
||||
pub dccmvau: WO<u32>,
|
||||
/// D-cache clean by MVA to PoC
|
||||
pub dccmvac: WO<u32>,
|
||||
/// D-cache clean by set-way
|
||||
pub dccsw: WO<u32>,
|
||||
/// D-cache clean and invalidate by MVA to PoC
|
||||
pub dccimvac: WO<u32>,
|
||||
/// D-cache clean and invalidate by set-way
|
||||
pub dccisw: WO<u32>,
|
||||
/// Branch predictor invalidate all
|
||||
pub bpiall: WO<u32>,
|
||||
}
|
||||
|
||||
const CBP_SW_WAY_POS: u32 = 30;
|
||||
const CBP_SW_WAY_MASK: u32 = 0x3 << CBP_SW_WAY_POS;
|
||||
const CBP_SW_SET_POS: u32 = 5;
|
||||
const CBP_SW_SET_MASK: u32 = 0x1FF << CBP_SW_SET_POS;
|
||||
|
||||
impl CBP {
|
||||
/// I-cache invalidate all to PoU
|
||||
#[inline(always)]
|
||||
pub fn iciallu(&mut self) {
|
||||
unsafe { self.iciallu.write(0) };
|
||||
}
|
||||
|
||||
/// I-cache invalidate by MVA to PoU
|
||||
#[inline(always)]
|
||||
pub fn icimvau(&mut self, mva: u32) {
|
||||
unsafe { self.icimvau.write(mva) };
|
||||
}
|
||||
|
||||
/// D-cache invalidate by MVA to PoC
|
||||
#[inline(always)]
|
||||
pub unsafe fn dcimvac(&mut self, mva: u32) {
|
||||
self.dcimvac.write(mva);
|
||||
}
|
||||
|
||||
/// D-cache invalidate by set-way
|
||||
///
|
||||
/// `set` is masked to be between 0 and 3, and `way` between 0 and 511.
|
||||
#[inline(always)]
|
||||
pub unsafe fn dcisw(&mut self, set: u16, way: u16) {
|
||||
// The ARMv7-M Architecture Reference Manual, as of Revision E.b, says these set/way
|
||||
// operations have a register data format which depends on the implementation's
|
||||
// associativity and number of sets. Specifically the 'way' and 'set' fields have
|
||||
// offsets 32-log2(ASSOCIATIVITY) and log2(LINELEN) respectively.
|
||||
//
|
||||
// However, in Cortex-M7 devices, these offsets are fixed at 30 and 5, as per the Cortex-M7
|
||||
// Generic User Guide section 4.8.3. Since no other ARMv7-M implementations except the
|
||||
// Cortex-M7 have a DCACHE or ICACHE at all, it seems safe to do the same thing as the
|
||||
// CMSIS-Core implementation and use fixed values.
|
||||
self.dcisw.write(
|
||||
((u32::from(way) & (CBP_SW_WAY_MASK >> CBP_SW_WAY_POS)) << CBP_SW_WAY_POS)
|
||||
| ((u32::from(set) & (CBP_SW_SET_MASK >> CBP_SW_SET_POS)) << CBP_SW_SET_POS),
|
||||
);
|
||||
}
|
||||
|
||||
/// D-cache clean by MVA to PoU
|
||||
#[inline(always)]
|
||||
pub fn dccmvau(&mut self, mva: u32) {
|
||||
unsafe {
|
||||
self.dccmvau.write(mva);
|
||||
}
|
||||
}
|
||||
|
||||
/// D-cache clean by MVA to PoC
|
||||
#[inline(always)]
|
||||
pub fn dccmvac(&mut self, mva: u32) {
|
||||
unsafe {
|
||||
self.dccmvac.write(mva);
|
||||
}
|
||||
}
|
||||
|
||||
/// D-cache clean by set-way
|
||||
///
|
||||
/// `set` is masked to be between 0 and 3, and `way` between 0 and 511.
|
||||
#[inline(always)]
|
||||
pub fn dccsw(&mut self, set: u16, way: u16) {
|
||||
// See comment for dcisw() about the format here
|
||||
unsafe {
|
||||
self.dccsw.write(
|
||||
((u32::from(way) & (CBP_SW_WAY_MASK >> CBP_SW_WAY_POS)) << CBP_SW_WAY_POS)
|
||||
| ((u32::from(set) & (CBP_SW_SET_MASK >> CBP_SW_SET_POS)) << CBP_SW_SET_POS),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// D-cache clean and invalidate by MVA to PoC
|
||||
#[inline(always)]
|
||||
pub fn dccimvac(&mut self, mva: u32) {
|
||||
unsafe {
|
||||
self.dccimvac.write(mva);
|
||||
}
|
||||
}
|
||||
|
||||
/// D-cache clean and invalidate by set-way
|
||||
///
|
||||
/// `set` is masked to be between 0 and 3, and `way` between 0 and 511.
|
||||
#[inline(always)]
|
||||
pub fn dccisw(&mut self, set: u16, way: u16) {
|
||||
// See comment for dcisw() about the format here
|
||||
unsafe {
|
||||
self.dccisw.write(
|
||||
((u32::from(way) & (CBP_SW_WAY_MASK >> CBP_SW_WAY_POS)) << CBP_SW_WAY_POS)
|
||||
| ((u32::from(set) & (CBP_SW_SET_MASK >> CBP_SW_SET_POS)) << CBP_SW_SET_POS),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Branch predictor invalidate all
|
||||
#[inline(always)]
|
||||
pub fn bpiall(&mut self) {
|
||||
unsafe {
|
||||
self.bpiall.write(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
//! CPUID
|
||||
|
||||
use volatile_register::RO;
|
||||
#[cfg(not(armv6m))]
|
||||
use volatile_register::RW;
|
||||
|
||||
#[cfg(not(armv6m))]
|
||||
use crate::peripheral::CPUID;
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// CPUID base
|
||||
pub base: RO<u32>,
|
||||
|
||||
_reserved0: [u32; 15],
|
||||
|
||||
/// Processor Feature (not present on Cortex-M0 variants)
|
||||
#[cfg(not(armv6m))]
|
||||
pub pfr: [RO<u32>; 2],
|
||||
#[cfg(armv6m)]
|
||||
_reserved1: [u32; 2],
|
||||
|
||||
/// Debug Feature (not present on Cortex-M0 variants)
|
||||
#[cfg(not(armv6m))]
|
||||
pub dfr: RO<u32>,
|
||||
#[cfg(armv6m)]
|
||||
_reserved2: u32,
|
||||
|
||||
/// Auxiliary Feature (not present on Cortex-M0 variants)
|
||||
#[cfg(not(armv6m))]
|
||||
pub afr: RO<u32>,
|
||||
#[cfg(armv6m)]
|
||||
_reserved3: u32,
|
||||
|
||||
/// Memory Model Feature (not present on Cortex-M0 variants)
|
||||
#[cfg(not(armv6m))]
|
||||
pub mmfr: [RO<u32>; 4],
|
||||
#[cfg(armv6m)]
|
||||
_reserved4: [u32; 4],
|
||||
|
||||
/// Instruction Set Attribute (not present on Cortex-M0 variants)
|
||||
#[cfg(not(armv6m))]
|
||||
pub isar: [RO<u32>; 5],
|
||||
#[cfg(armv6m)]
|
||||
_reserved5: [u32; 5],
|
||||
|
||||
_reserved6: u32,
|
||||
|
||||
/// Cache Level ID (only present on Cortex-M7)
|
||||
#[cfg(not(armv6m))]
|
||||
pub clidr: RO<u32>,
|
||||
|
||||
/// Cache Type (only present on Cortex-M7)
|
||||
#[cfg(not(armv6m))]
|
||||
pub ctr: RO<u32>,
|
||||
|
||||
/// Cache Size ID (only present on Cortex-M7)
|
||||
#[cfg(not(armv6m))]
|
||||
pub ccsidr: RO<u32>,
|
||||
|
||||
/// Cache Size Selection (only present on Cortex-M7)
|
||||
#[cfg(not(armv6m))]
|
||||
pub csselr: RW<u32>,
|
||||
}
|
||||
|
||||
/// Type of cache to select on CSSELR writes.
|
||||
#[cfg(not(armv6m))]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum CsselrCacheType {
|
||||
/// Select DCache or unified cache
|
||||
DataOrUnified = 0,
|
||||
/// Select ICache
|
||||
Instruction = 1,
|
||||
}
|
||||
|
||||
#[cfg(not(armv6m))]
|
||||
impl CPUID {
|
||||
/// Selects the current CCSIDR
|
||||
///
|
||||
/// * `level`: the required cache level minus 1, e.g. 0 for L1, 1 for L2
|
||||
/// * `ind`: select instruction cache or data/unified cache
|
||||
///
|
||||
/// `level` is masked to be between 0 and 7.
|
||||
#[inline]
|
||||
pub fn select_cache(&mut self, level: u8, ind: CsselrCacheType) {
|
||||
const CSSELR_IND_POS: u32 = 0;
|
||||
const CSSELR_IND_MASK: u32 = 1 << CSSELR_IND_POS;
|
||||
const CSSELR_LEVEL_POS: u32 = 1;
|
||||
const CSSELR_LEVEL_MASK: u32 = 0x7 << CSSELR_LEVEL_POS;
|
||||
|
||||
unsafe {
|
||||
self.csselr.write(
|
||||
((u32::from(level) << CSSELR_LEVEL_POS) & CSSELR_LEVEL_MASK)
|
||||
| (((ind as u32) << CSSELR_IND_POS) & CSSELR_IND_MASK),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of sets and ways in the selected cache
|
||||
#[inline]
|
||||
pub fn cache_num_sets_ways(&mut self, level: u8, ind: CsselrCacheType) -> (u16, u16) {
|
||||
const CCSIDR_NUMSETS_POS: u32 = 13;
|
||||
const CCSIDR_NUMSETS_MASK: u32 = 0x7FFF << CCSIDR_NUMSETS_POS;
|
||||
const CCSIDR_ASSOCIATIVITY_POS: u32 = 3;
|
||||
const CCSIDR_ASSOCIATIVITY_MASK: u32 = 0x3FF << CCSIDR_ASSOCIATIVITY_POS;
|
||||
|
||||
self.select_cache(level, ind);
|
||||
crate::asm::dsb();
|
||||
let ccsidr = self.ccsidr.read();
|
||||
(
|
||||
(1 + ((ccsidr & CCSIDR_NUMSETS_MASK) >> CCSIDR_NUMSETS_POS)) as u16,
|
||||
(1 + ((ccsidr & CCSIDR_ASSOCIATIVITY_MASK) >> CCSIDR_ASSOCIATIVITY_POS)) as u16,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns log2 of the number of words in the smallest cache line of all the data cache and
|
||||
/// unified caches that are controlled by the processor.
|
||||
///
|
||||
/// This is the `DminLine` field of the CTR register.
|
||||
#[inline(always)]
|
||||
pub fn cache_dminline() -> u32 {
|
||||
const CTR_DMINLINE_POS: u32 = 16;
|
||||
const CTR_DMINLINE_MASK: u32 = 0xF << CTR_DMINLINE_POS;
|
||||
let ctr = unsafe { (*Self::PTR).ctr.read() };
|
||||
(ctr & CTR_DMINLINE_MASK) >> CTR_DMINLINE_POS
|
||||
}
|
||||
|
||||
/// Returns log2 of the number of words in the smallest cache line of all the instruction
|
||||
/// caches that are controlled by the processor.
|
||||
///
|
||||
/// This is the `IminLine` field of the CTR register.
|
||||
#[inline(always)]
|
||||
pub fn cache_iminline() -> u32 {
|
||||
const CTR_IMINLINE_POS: u32 = 0;
|
||||
const CTR_IMINLINE_MASK: u32 = 0xF << CTR_IMINLINE_POS;
|
||||
let ctr = unsafe { (*Self::PTR).ctr.read() };
|
||||
(ctr & CTR_IMINLINE_MASK) >> CTR_IMINLINE_POS
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
//! Debug Control Block
|
||||
|
||||
use volatile_register::{RW, WO};
|
||||
|
||||
use crate::peripheral::DCB;
|
||||
use core::ptr;
|
||||
|
||||
const DCB_DEMCR_TRCENA: u32 = 1 << 24;
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Debug Halting Control and Status
|
||||
pub dhcsr: RW<u32>,
|
||||
/// Debug Core Register Selector
|
||||
pub dcrsr: WO<u32>,
|
||||
/// Debug Core Register Data
|
||||
pub dcrdr: RW<u32>,
|
||||
/// Debug Exception and Monitor Control
|
||||
pub demcr: RW<u32>,
|
||||
}
|
||||
|
||||
impl DCB {
|
||||
/// Enables TRACE. This is for example required by the
|
||||
/// `peripheral::DWT` cycle counter to work properly.
|
||||
/// As by STM documentation, this flag is not reset on
|
||||
/// soft-reset, only on power reset.
|
||||
#[inline]
|
||||
pub fn enable_trace(&mut self) {
|
||||
// set bit 24 / TRCENA
|
||||
unsafe {
|
||||
self.demcr.modify(|w| w | DCB_DEMCR_TRCENA);
|
||||
}
|
||||
}
|
||||
|
||||
/// Disables TRACE. See `DCB::enable_trace()` for more details
|
||||
#[inline]
|
||||
pub fn disable_trace(&mut self) {
|
||||
// unset bit 24 / TRCENA
|
||||
unsafe {
|
||||
self.demcr.modify(|w| w & !DCB_DEMCR_TRCENA);
|
||||
}
|
||||
}
|
||||
|
||||
/// Is there a debugger attached? (see note)
|
||||
///
|
||||
/// Note: This function is [reported not to
|
||||
/// work](http://web.archive.org/web/20180821191012/https://community.nxp.com/thread/424925#comment-782843)
|
||||
/// on Cortex-M0 devices. Per the ARM v6-M Architecture Reference Manual, "Access to the DHCSR
|
||||
/// from software running on the processor is IMPLEMENTATION DEFINED". Indeed, from the
|
||||
/// [Cortex-M0+ r0p1 Technical Reference Manual](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0484c/BABJHEIG.html), "Note Software cannot access the debug registers."
|
||||
#[inline]
|
||||
pub fn is_debugger_attached() -> bool {
|
||||
unsafe {
|
||||
// do an 8-bit read of the 32-bit DHCSR register, and get the LSB
|
||||
let value = ptr::read_volatile(Self::PTR as *const u8);
|
||||
value & 0x1 == 1
|
||||
}
|
||||
}
|
||||
}
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
//! Data Watchpoint and Trace unit
|
||||
|
||||
#[cfg(not(armv6m))]
|
||||
use volatile_register::WO;
|
||||
use volatile_register::{RO, RW};
|
||||
|
||||
use crate::peripheral::DWT;
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Control
|
||||
pub ctrl: RW<u32>,
|
||||
/// Cycle Count
|
||||
#[cfg(not(armv6m))]
|
||||
pub cyccnt: RW<u32>,
|
||||
/// CPI Count
|
||||
#[cfg(not(armv6m))]
|
||||
pub cpicnt: RW<u32>,
|
||||
/// Exception Overhead Count
|
||||
#[cfg(not(armv6m))]
|
||||
pub exccnt: RW<u32>,
|
||||
/// Sleep Count
|
||||
#[cfg(not(armv6m))]
|
||||
pub sleepcnt: RW<u32>,
|
||||
/// LSU Count
|
||||
#[cfg(not(armv6m))]
|
||||
pub lsucnt: RW<u32>,
|
||||
/// Folded-instruction Count
|
||||
#[cfg(not(armv6m))]
|
||||
pub foldcnt: RW<u32>,
|
||||
/// Cortex-M0(+) does not have these parts
|
||||
#[cfg(armv6m)]
|
||||
reserved: [u32; 6],
|
||||
/// Program Counter Sample
|
||||
pub pcsr: RO<u32>,
|
||||
/// Comparators
|
||||
#[cfg(armv6m)]
|
||||
pub c: [Comparator; 2],
|
||||
#[cfg(not(armv6m))]
|
||||
/// Comparators
|
||||
pub c: [Comparator; 16],
|
||||
#[cfg(not(armv6m))]
|
||||
reserved: [u32; 932],
|
||||
/// Lock Access
|
||||
#[cfg(not(armv6m))]
|
||||
pub lar: WO<u32>,
|
||||
/// Lock Status
|
||||
#[cfg(not(armv6m))]
|
||||
pub lsr: RO<u32>,
|
||||
}
|
||||
|
||||
/// Comparator
|
||||
#[repr(C)]
|
||||
pub struct Comparator {
|
||||
/// Comparator
|
||||
pub comp: RW<u32>,
|
||||
/// Comparator Mask
|
||||
pub mask: RW<u32>,
|
||||
/// Comparator Function
|
||||
pub function: RW<u32>,
|
||||
reserved: u32,
|
||||
}
|
||||
|
||||
// DWT CTRL register fields
|
||||
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
|
||||
/// 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) {
|
||||
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() }
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn unlock() {
|
||||
// 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) }
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
//! Flash Patch and Breakpoint unit
|
||||
//!
|
||||
//! *NOTE* Not available on Armv6-M.
|
||||
|
||||
use volatile_register::{RO, RW, WO};
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Control
|
||||
pub ctrl: RW<u32>,
|
||||
/// Remap
|
||||
pub remap: RW<u32>,
|
||||
/// Comparator
|
||||
pub comp: [RW<u32>; 127],
|
||||
reserved: [u32; 875],
|
||||
/// Lock Access
|
||||
pub lar: WO<u32>,
|
||||
/// Lock Status
|
||||
pub lsr: RO<u32>,
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
//! Floating Point Unit
|
||||
//!
|
||||
//! *NOTE* Available only on targets with a Floating Point Unit (FPU) extension.
|
||||
|
||||
use volatile_register::{RO, RW};
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
reserved: u32,
|
||||
/// Floating Point Context Control
|
||||
pub fpccr: RW<u32>,
|
||||
/// Floating Point Context Address
|
||||
pub fpcar: RW<u32>,
|
||||
/// Floating Point Default Status Control
|
||||
pub fpdscr: RW<u32>,
|
||||
/// Media and FP Feature
|
||||
pub mvfr: [RO<u32>; 3],
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
//! Implementation Control Block
|
||||
|
||||
#[cfg(any(armv7m, armv8m, native))]
|
||||
use volatile_register::RO;
|
||||
use volatile_register::RW;
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Interrupt Controller Type Register
|
||||
///
|
||||
/// The bottom four bits of this register give the number of implemented
|
||||
/// interrupt lines, divided by 32. So a value of `0b0010` indicates 64
|
||||
/// interrupts.
|
||||
#[cfg(any(armv7m, armv8m, native))]
|
||||
pub ictr: RO<u32>,
|
||||
|
||||
/// The ICTR is not defined in the ARMv6-M Architecture Reference manual, so
|
||||
/// we replace it with this.
|
||||
#[cfg(not(any(armv7m, armv8m, native)))]
|
||||
_reserved: u32,
|
||||
|
||||
/// Auxiliary Control Register
|
||||
///
|
||||
/// This register is entirely implementation defined -- the standard gives
|
||||
/// it an address, but does not define its role or contents.
|
||||
pub actlr: RW<u32>,
|
||||
|
||||
/// Coprocessor Power Control Register
|
||||
#[cfg(armv8m)]
|
||||
pub cppwr: RW<u32>,
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
//! Instrumentation Trace Macrocell
|
||||
//!
|
||||
//! *NOTE* Not available on Armv6-M and Armv8-M Baseline.
|
||||
|
||||
use core::cell::UnsafeCell;
|
||||
use core::ptr;
|
||||
|
||||
use volatile_register::{RO, RW, WO};
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Stimulus Port
|
||||
pub stim: [Stim; 256],
|
||||
reserved0: [u32; 640],
|
||||
/// Trace Enable
|
||||
pub ter: [RW<u32>; 8],
|
||||
reserved1: [u32; 8],
|
||||
/// Trace Privilege
|
||||
pub tpr: RW<u32>,
|
||||
reserved2: [u32; 15],
|
||||
/// Trace Control
|
||||
pub tcr: RW<u32>,
|
||||
reserved3: [u32; 75],
|
||||
/// Lock Access
|
||||
pub lar: WO<u32>,
|
||||
/// Lock Status
|
||||
pub lsr: RO<u32>,
|
||||
}
|
||||
|
||||
/// Stimulus Port
|
||||
pub struct Stim {
|
||||
register: UnsafeCell<u32>,
|
||||
}
|
||||
|
||||
impl Stim {
|
||||
/// Writes an `u8` payload into the stimulus port
|
||||
#[inline]
|
||||
pub fn write_u8(&mut self, value: u8) {
|
||||
unsafe { ptr::write_volatile(self.register.get() as *mut u8, value) }
|
||||
}
|
||||
|
||||
/// Writes an `u16` payload into the stimulus port
|
||||
#[inline]
|
||||
pub fn write_u16(&mut self, value: u16) {
|
||||
unsafe { ptr::write_volatile(self.register.get() as *mut u16, value) }
|
||||
}
|
||||
|
||||
/// Writes an `u32` payload into the stimulus port
|
||||
#[inline]
|
||||
pub fn write_u32(&mut self, value: u32) {
|
||||
unsafe { ptr::write_volatile(self.register.get(), value) }
|
||||
}
|
||||
|
||||
/// Returns `true` if the stimulus port is ready to accept more data
|
||||
#[cfg(not(armv8m))]
|
||||
#[inline]
|
||||
pub fn is_fifo_ready(&self) -> bool {
|
||||
unsafe { ptr::read_volatile(self.register.get()) & 0b1 == 1 }
|
||||
}
|
||||
|
||||
/// Returns `true` if the stimulus port is ready to accept more data
|
||||
#[cfg(armv8m)]
|
||||
#[inline]
|
||||
pub fn is_fifo_ready(&self) -> bool {
|
||||
// ARMv8-M adds a disabled bit; we indicate that we are ready to
|
||||
// proceed with a stimulus write if the port is either ready (bit 0) or
|
||||
// disabled (bit 1).
|
||||
unsafe { ptr::read_volatile(self.register.get()) & 0b11 != 0 }
|
||||
}
|
||||
}
|
||||
+685
@@ -0,0 +1,685 @@
|
||||
//! 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 _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const self::ac::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
/// Cache and branch predictor maintenance operations
|
||||
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 _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const self::cbp::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(armv6m))]
|
||||
impl ops::Deref for CBP {
|
||||
type Target = self::cbp::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// CPUID
|
||||
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 _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const self::cpuid::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for CPUID {
|
||||
type Target = self::cpuid::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// Debug Control Block
|
||||
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 _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const dcb::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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 _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const dwt::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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 _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const fpb::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
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 _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const fpu::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
#[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.
|
||||
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 _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *mut icb::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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 _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *mut itm::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
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 _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const mpu::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for MPU {
|
||||
type Target = self::mpu::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// Nested Vector Interrupt Controller
|
||||
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 _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const nvic::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for NVIC {
|
||||
type Target = self::nvic::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// Security Attribution Unit
|
||||
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 _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const sau::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
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 _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const scb::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for SCB {
|
||||
type Target = self::scb::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// SysTick: System Timer
|
||||
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 _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const syst::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for SYST {
|
||||
type Target = self::syst::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// Trace Port Interface Unit
|
||||
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 _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const tpiu::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(armv6m))]
|
||||
impl ops::Deref for TPIU {
|
||||
type Target = self::tpiu::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
//! Memory Protection Unit
|
||||
|
||||
use volatile_register::{RO, RW};
|
||||
|
||||
/// Register block for ARMv7-M
|
||||
#[cfg(not(armv8m))]
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Type
|
||||
pub _type: RO<u32>,
|
||||
/// Control
|
||||
pub ctrl: RW<u32>,
|
||||
/// Region Number
|
||||
pub rnr: RW<u32>,
|
||||
/// Region Base Address
|
||||
pub rbar: RW<u32>,
|
||||
/// Region Attribute and Size
|
||||
pub rasr: RW<u32>,
|
||||
/// Alias 1 of RBAR
|
||||
pub rbar_a1: RW<u32>,
|
||||
/// Alias 1 of RASR
|
||||
pub rasr_a1: RW<u32>,
|
||||
/// Alias 2 of RBAR
|
||||
pub rbar_a2: RW<u32>,
|
||||
/// Alias 2 of RASR
|
||||
pub rasr_a2: RW<u32>,
|
||||
/// Alias 3 of RBAR
|
||||
pub rbar_a3: RW<u32>,
|
||||
/// Alias 3 of RASR
|
||||
pub rasr_a3: RW<u32>,
|
||||
}
|
||||
|
||||
/// Register block for ARMv8-M
|
||||
#[cfg(armv8m)]
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Type
|
||||
pub _type: RO<u32>,
|
||||
/// Control
|
||||
pub ctrl: RW<u32>,
|
||||
/// Region Number
|
||||
pub rnr: RW<u32>,
|
||||
/// Region Base Address
|
||||
pub rbar: RW<u32>,
|
||||
/// Region Limit Address
|
||||
pub rlar: RW<u32>,
|
||||
/// Alias 1 of RBAR
|
||||
pub rbar_a1: RW<u32>,
|
||||
/// Alias 1 of RLAR
|
||||
pub rlar_a1: RW<u32>,
|
||||
/// Alias 2 of RBAR
|
||||
pub rbar_a2: RW<u32>,
|
||||
/// Alias 2 of RLAR
|
||||
pub rlar_a2: RW<u32>,
|
||||
/// Alias 3 of RBAR
|
||||
pub rbar_a3: RW<u32>,
|
||||
/// Alias 3 of RLAR
|
||||
pub rlar_a3: RW<u32>,
|
||||
|
||||
// Reserved word at offset 0xBC
|
||||
_reserved: u32,
|
||||
|
||||
/// Memory Attribute Indirection register 0 and 1
|
||||
pub mair: [RW<u32>; 2],
|
||||
}
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
//! Nested Vector Interrupt Controller
|
||||
|
||||
use volatile_register::RW;
|
||||
#[cfg(not(armv6m))]
|
||||
use volatile_register::{RO, WO};
|
||||
|
||||
use crate::interrupt::InterruptNumber;
|
||||
use crate::peripheral::NVIC;
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Interrupt Set-Enable
|
||||
pub iser: [RW<u32>; 16],
|
||||
|
||||
_reserved0: [u32; 16],
|
||||
|
||||
/// Interrupt Clear-Enable
|
||||
pub icer: [RW<u32>; 16],
|
||||
|
||||
_reserved1: [u32; 16],
|
||||
|
||||
/// Interrupt Set-Pending
|
||||
pub ispr: [RW<u32>; 16],
|
||||
|
||||
_reserved2: [u32; 16],
|
||||
|
||||
/// Interrupt Clear-Pending
|
||||
pub icpr: [RW<u32>; 16],
|
||||
|
||||
_reserved3: [u32; 16],
|
||||
|
||||
/// Interrupt Active Bit (not present on Cortex-M0 variants)
|
||||
#[cfg(not(armv6m))]
|
||||
pub iabr: [RO<u32>; 16],
|
||||
#[cfg(armv6m)]
|
||||
_reserved4: [u32; 16],
|
||||
|
||||
_reserved5: [u32; 48],
|
||||
|
||||
/// Interrupt Priority
|
||||
///
|
||||
/// On ARMv7-M, 124 word-sized registers are available. Each of those
|
||||
/// contains of 4 interrupt priorities of 8 byte each.The architecture
|
||||
/// specifically allows accessing those along byte boundaries, so they are
|
||||
/// represented as 496 byte-sized registers, for convenience, and to allow
|
||||
/// atomic priority updates.
|
||||
///
|
||||
/// On ARMv6-M, the registers must only be accessed along word boundaries,
|
||||
/// so convenient byte-sized representation wouldn't work on that
|
||||
/// architecture.
|
||||
#[cfg(not(armv6m))]
|
||||
pub ipr: [RW<u8>; 496],
|
||||
|
||||
/// Interrupt Priority
|
||||
///
|
||||
/// On ARMv7-M, 124 word-sized registers are available. Each of those
|
||||
/// contains of 4 interrupt priorities of 8 byte each.The architecture
|
||||
/// specifically allows accessing those along byte boundaries, so they are
|
||||
/// represented as 496 byte-sized registers, for convenience, and to allow
|
||||
/// atomic priority updates.
|
||||
///
|
||||
/// On ARMv6-M, the registers must only be accessed along word boundaries,
|
||||
/// so convenient byte-sized representation wouldn't work on that
|
||||
/// architecture.
|
||||
#[cfg(armv6m)]
|
||||
pub ipr: [RW<u32>; 8],
|
||||
|
||||
#[cfg(not(armv6m))]
|
||||
_reserved6: [u32; 580],
|
||||
|
||||
/// Software Trigger Interrupt
|
||||
#[cfg(not(armv6m))]
|
||||
pub stir: WO<u32>,
|
||||
}
|
||||
|
||||
impl NVIC {
|
||||
/// Request an IRQ in software
|
||||
///
|
||||
/// Writing a value to the INTID field is the same as manually pending an interrupt by setting
|
||||
/// the corresponding interrupt bit in an Interrupt Set Pending Register. This is similar to
|
||||
/// [`NVIC::pend`].
|
||||
///
|
||||
/// This method is not available on ARMv6-M chips.
|
||||
///
|
||||
/// [`NVIC::pend`]: #method.pend
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn request<I>(&mut self, interrupt: I)
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
|
||||
unsafe {
|
||||
self.stir.write(u32::from(nr));
|
||||
}
|
||||
}
|
||||
|
||||
/// Disables `interrupt`
|
||||
#[inline]
|
||||
pub fn mask<I>(interrupt: I)
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
// NOTE(unsafe) this is a write to a stateless register
|
||||
unsafe { (*Self::PTR).icer[usize::from(nr / 32)].write(1 << (nr % 32)) }
|
||||
}
|
||||
|
||||
/// Enables `interrupt`
|
||||
///
|
||||
/// This function is `unsafe` because it can break mask-based critical sections
|
||||
#[inline]
|
||||
pub unsafe fn unmask<I>(interrupt: I)
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
// NOTE(ptr) this is a write to a stateless register
|
||||
(*Self::PTR).iser[usize::from(nr / 32)].write(1 << (nr % 32))
|
||||
}
|
||||
|
||||
/// Returns the NVIC priority of `interrupt`
|
||||
///
|
||||
/// *NOTE* NVIC encodes priority in the highest bits of a byte so values like `1` and `2` map
|
||||
/// to the same priority. Also for NVIC priorities, a lower value (e.g. `16`) has higher
|
||||
/// priority (urgency) than a larger value (e.g. `32`).
|
||||
#[inline]
|
||||
pub fn get_priority<I>(interrupt: I) -> u8
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
#[cfg(not(armv6m))]
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).ipr[usize::from(nr)].read() }
|
||||
}
|
||||
|
||||
#[cfg(armv6m)]
|
||||
{
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
let ipr_n = unsafe { (*Self::PTR).ipr[Self::ipr_index(interrupt)].read() };
|
||||
let prio = (ipr_n >> Self::ipr_shift(interrupt)) & 0x0000_00ff;
|
||||
prio as u8
|
||||
}
|
||||
}
|
||||
|
||||
/// Is `interrupt` active or pre-empted and stacked
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn is_active<I>(interrupt: I) -> bool
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
let mask = 1 << (nr % 32);
|
||||
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { ((*Self::PTR).iabr[usize::from(nr / 32)].read() & mask) == mask }
|
||||
}
|
||||
|
||||
/// Checks if `interrupt` is enabled
|
||||
#[inline]
|
||||
pub fn is_enabled<I>(interrupt: I) -> bool
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
let mask = 1 << (nr % 32);
|
||||
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { ((*Self::PTR).iser[usize::from(nr / 32)].read() & mask) == mask }
|
||||
}
|
||||
|
||||
/// Checks if `interrupt` is pending
|
||||
#[inline]
|
||||
pub fn is_pending<I>(interrupt: I) -> bool
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
let mask = 1 << (nr % 32);
|
||||
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { ((*Self::PTR).ispr[usize::from(nr / 32)].read() & mask) == mask }
|
||||
}
|
||||
|
||||
/// Forces `interrupt` into pending state
|
||||
#[inline]
|
||||
pub fn pend<I>(interrupt: I)
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
|
||||
// NOTE(unsafe) atomic stateless write; ICPR doesn't store any state
|
||||
unsafe { (*Self::PTR).ispr[usize::from(nr / 32)].write(1 << (nr % 32)) }
|
||||
}
|
||||
|
||||
/// Sets the "priority" of `interrupt` to `prio`
|
||||
///
|
||||
/// *NOTE* See [`get_priority`](struct.NVIC.html#method.get_priority) method for an explanation
|
||||
/// of how NVIC priorities work.
|
||||
///
|
||||
/// On ARMv6-M, updating an interrupt priority requires a read-modify-write operation. On
|
||||
/// ARMv7-M, the operation is performed in a single atomic write operation.
|
||||
///
|
||||
/// # Unsafety
|
||||
///
|
||||
/// Changing priority levels can break priority-based critical sections (see
|
||||
/// [`register::basepri`](crate::register::basepri)) and compromise memory safety.
|
||||
#[inline]
|
||||
pub unsafe fn set_priority<I>(&mut self, interrupt: I, prio: u8)
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
#[cfg(not(armv6m))]
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
self.ipr[usize::from(nr)].write(prio)
|
||||
}
|
||||
|
||||
#[cfg(armv6m)]
|
||||
{
|
||||
self.ipr[Self::ipr_index(interrupt)].modify(|value| {
|
||||
let mask = 0x0000_00ff << Self::ipr_shift(interrupt);
|
||||
let prio = u32::from(prio) << Self::ipr_shift(interrupt);
|
||||
|
||||
(value & !mask) | prio
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears `interrupt`'s pending state
|
||||
#[inline]
|
||||
pub fn unpend<I>(interrupt: I)
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
|
||||
// NOTE(unsafe) atomic stateless write; ICPR doesn't store any state
|
||||
unsafe { (*Self::PTR).icpr[usize::from(nr / 32)].write(1 << (nr % 32)) }
|
||||
}
|
||||
|
||||
#[cfg(armv6m)]
|
||||
#[inline]
|
||||
fn ipr_index<I>(interrupt: I) -> usize
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
usize::from(interrupt.number()) / 4
|
||||
}
|
||||
|
||||
#[cfg(armv6m)]
|
||||
#[inline]
|
||||
fn ipr_shift<I>(interrupt: I) -> usize
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
(usize::from(interrupt.number()) % 4) * 8
|
||||
}
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
//! Security Attribution Unit
|
||||
//!
|
||||
//! *NOTE* Available only on Armv8-M and Armv8.1-M, for the following Rust target triples:
|
||||
//! * `thumbv8m.base-none-eabi`
|
||||
//! * `thumbv8m.main-none-eabi`
|
||||
//! * `thumbv8m.main-none-eabihf`
|
||||
//!
|
||||
//! For reference please check the section B8.3 of the Armv8-M Architecture Reference Manual.
|
||||
|
||||
use crate::interrupt;
|
||||
use crate::peripheral::SAU;
|
||||
use bitfield::bitfield;
|
||||
use volatile_register::{RO, RW};
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Control Register
|
||||
pub ctrl: RW<Ctrl>,
|
||||
/// Type Register
|
||||
pub _type: RO<Type>,
|
||||
/// Region Number Register
|
||||
pub rnr: RW<Rnr>,
|
||||
/// Region Base Address Register
|
||||
pub rbar: RW<Rbar>,
|
||||
/// Region Limit Address Register
|
||||
pub rlar: RW<Rlar>,
|
||||
/// Secure Fault Status Register
|
||||
pub sfsr: RO<Sfsr>,
|
||||
/// Secure Fault Address Register
|
||||
pub sfar: RO<Sfar>,
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
/// Control Register description
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Ctrl(u32);
|
||||
get_enable, set_enable: 0;
|
||||
get_allns, set_allns: 1;
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
/// Type Register description
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Type(u32);
|
||||
u8;
|
||||
sregion, _: 7, 0;
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
/// Region Number Register description
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Rnr(u32);
|
||||
u8;
|
||||
get_region, set_region: 7, 0;
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
/// Region Base Address Register description
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Rbar(u32);
|
||||
u32;
|
||||
get_baddr, set_baddr: 31, 5;
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
/// Region Limit Address Register description
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Rlar(u32);
|
||||
u32;
|
||||
get_laddr, set_laddr: 31, 5;
|
||||
get_nsc, set_nsc: 1;
|
||||
get_enable, set_enable: 0;
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
/// Secure Fault Status Register description
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Sfsr(u32);
|
||||
invep, _: 0;
|
||||
invis, _: 1;
|
||||
inver, _: 2;
|
||||
auviol, _: 3;
|
||||
invtran, _: 4;
|
||||
lsperr, _: 5;
|
||||
sfarvalid, _: 6;
|
||||
lserr, _: 7;
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
/// Secure Fault Address Register description
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Sfar(u32);
|
||||
u32;
|
||||
address, _: 31, 0;
|
||||
}
|
||||
|
||||
/// Possible attribute of a SAU region.
|
||||
#[derive(Debug)]
|
||||
pub enum SauRegionAttribute {
|
||||
/// SAU region is Secure
|
||||
Secure,
|
||||
/// SAU region is Non-Secure Callable
|
||||
NonSecureCallable,
|
||||
/// SAU region is Non-Secure
|
||||
NonSecure,
|
||||
}
|
||||
|
||||
/// Description of a SAU region.
|
||||
#[derive(Debug)]
|
||||
pub struct SauRegion {
|
||||
/// First address of the region, its 5 least significant bits must be set to zero.
|
||||
pub base_address: u32,
|
||||
/// Last address of the region, its 5 least significant bits must be set to one.
|
||||
pub limit_address: u32,
|
||||
/// Attribute of the region.
|
||||
pub attribute: SauRegionAttribute,
|
||||
}
|
||||
|
||||
/// Possible error values returned by the SAU methods.
|
||||
#[derive(Debug)]
|
||||
pub enum SauError {
|
||||
/// The region number parameter to set or get a region must be between 0 and
|
||||
/// region_numbers() - 1.
|
||||
RegionNumberTooBig,
|
||||
/// Bits 0 to 4 of the base address of a SAU region must be set to zero.
|
||||
WrongBaseAddress,
|
||||
/// Bits 0 to 4 of the limit address of a SAU region must be set to one.
|
||||
WrongLimitAddress,
|
||||
}
|
||||
|
||||
impl SAU {
|
||||
/// Get the number of implemented SAU regions.
|
||||
#[inline]
|
||||
pub fn region_numbers(&self) -> u8 {
|
||||
self._type.read().sregion()
|
||||
}
|
||||
|
||||
/// Enable the SAU.
|
||||
#[inline]
|
||||
pub fn enable(&mut self) {
|
||||
unsafe {
|
||||
self.ctrl.modify(|mut ctrl| {
|
||||
ctrl.set_enable(true);
|
||||
ctrl
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a SAU region to a region number.
|
||||
/// SAU regions must be 32 bytes aligned and their sizes must be a multiple of 32 bytes. It
|
||||
/// means that the 5 least significant bits of the base address of a SAU region must be set to
|
||||
/// zero and the 5 least significant bits of the limit address must be set to one.
|
||||
/// The region number must be valid.
|
||||
/// This function is executed under a critical section to prevent having inconsistent results.
|
||||
#[inline]
|
||||
pub fn set_region(&mut self, region_number: u8, region: SauRegion) -> Result<(), SauError> {
|
||||
interrupt::free(|_| {
|
||||
let base_address = region.base_address;
|
||||
let limit_address = region.limit_address;
|
||||
let attribute = region.attribute;
|
||||
|
||||
if region_number >= self.region_numbers() {
|
||||
Err(SauError::RegionNumberTooBig)
|
||||
} else if base_address & 0x1F != 0 {
|
||||
Err(SauError::WrongBaseAddress)
|
||||
} else if limit_address & 0x1F != 0x1F {
|
||||
Err(SauError::WrongLimitAddress)
|
||||
} else {
|
||||
// All fields of these registers are going to be modified so we don't need to read them
|
||||
// before.
|
||||
let mut rnr = Rnr(0);
|
||||
let mut rbar = Rbar(0);
|
||||
let mut rlar = Rlar(0);
|
||||
|
||||
rnr.set_region(region_number);
|
||||
rbar.set_baddr(base_address >> 5);
|
||||
rlar.set_laddr(limit_address >> 5);
|
||||
|
||||
match attribute {
|
||||
SauRegionAttribute::Secure => {
|
||||
rlar.set_nsc(false);
|
||||
rlar.set_enable(false);
|
||||
}
|
||||
SauRegionAttribute::NonSecureCallable => {
|
||||
rlar.set_nsc(true);
|
||||
rlar.set_enable(true);
|
||||
}
|
||||
SauRegionAttribute::NonSecure => {
|
||||
rlar.set_nsc(false);
|
||||
rlar.set_enable(true);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
self.rnr.write(rnr);
|
||||
self.rbar.write(rbar);
|
||||
self.rlar.write(rlar);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a region from the SAU.
|
||||
/// The region number must be valid.
|
||||
/// This function is executed under a critical section to prevent having inconsistent results.
|
||||
#[inline]
|
||||
pub fn get_region(&mut self, region_number: u8) -> Result<SauRegion, SauError> {
|
||||
interrupt::free(|_| {
|
||||
if region_number >= self.region_numbers() {
|
||||
Err(SauError::RegionNumberTooBig)
|
||||
} else {
|
||||
unsafe {
|
||||
self.rnr.write(Rnr(region_number.into()));
|
||||
}
|
||||
|
||||
let rbar = self.rbar.read();
|
||||
let rlar = self.rlar.read();
|
||||
|
||||
let attribute = match (rlar.get_enable(), rlar.get_nsc()) {
|
||||
(false, _) => SauRegionAttribute::Secure,
|
||||
(true, false) => SauRegionAttribute::NonSecure,
|
||||
(true, true) => SauRegionAttribute::NonSecureCallable,
|
||||
};
|
||||
|
||||
Ok(SauRegion {
|
||||
base_address: rbar.get_baddr() << 5,
|
||||
limit_address: (rlar.get_laddr() << 5) | 0x1F,
|
||||
attribute,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+1109
File diff suppressed because it is too large.
Load diff
+185
@@ -0,0 +1,185 @@
|
||||
//! SysTick: System Timer
|
||||
|
||||
use volatile_register::{RO, RW};
|
||||
|
||||
use crate::peripheral::SYST;
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Control and Status
|
||||
pub csr: RW<u32>,
|
||||
/// Reload Value
|
||||
pub rvr: RW<u32>,
|
||||
/// Current Value
|
||||
pub cvr: RW<u32>,
|
||||
/// Calibration Value
|
||||
pub calib: RO<u32>,
|
||||
}
|
||||
|
||||
/// SysTick clock source
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum SystClkSource {
|
||||
/// Core-provided clock
|
||||
Core,
|
||||
/// External reference clock
|
||||
External,
|
||||
}
|
||||
|
||||
const SYST_COUNTER_MASK: u32 = 0x00ff_ffff;
|
||||
|
||||
const SYST_CSR_ENABLE: u32 = 1 << 0;
|
||||
const SYST_CSR_TICKINT: u32 = 1 << 1;
|
||||
const SYST_CSR_CLKSOURCE: u32 = 1 << 2;
|
||||
const SYST_CSR_COUNTFLAG: u32 = 1 << 16;
|
||||
|
||||
const SYST_CALIB_SKEW: u32 = 1 << 30;
|
||||
const SYST_CALIB_NOREF: u32 = 1 << 31;
|
||||
|
||||
impl SYST {
|
||||
/// Clears current value to 0
|
||||
///
|
||||
/// After calling `clear_current()`, the next call to `has_wrapped()` will return `false`.
|
||||
#[inline]
|
||||
pub fn clear_current(&mut self) {
|
||||
unsafe { self.cvr.write(0) }
|
||||
}
|
||||
|
||||
/// Disables counter
|
||||
#[inline]
|
||||
pub fn disable_counter(&mut self) {
|
||||
unsafe { self.csr.modify(|v| v & !SYST_CSR_ENABLE) }
|
||||
}
|
||||
|
||||
/// Disables SysTick interrupt
|
||||
#[inline]
|
||||
pub fn disable_interrupt(&mut self) {
|
||||
unsafe { self.csr.modify(|v| v & !SYST_CSR_TICKINT) }
|
||||
}
|
||||
|
||||
/// Enables counter
|
||||
///
|
||||
/// *NOTE* The reference manual indicates that:
|
||||
///
|
||||
/// "The SysTick counter reload and current value are undefined at reset, the correct
|
||||
/// initialization sequence for the SysTick counter is:
|
||||
///
|
||||
/// - Program reload value
|
||||
/// - Clear current value
|
||||
/// - Program Control and Status register"
|
||||
///
|
||||
/// The sequence translates to `self.set_reload(x); self.clear_current(); self.enable_counter()`
|
||||
#[inline]
|
||||
pub fn enable_counter(&mut self) {
|
||||
unsafe { self.csr.modify(|v| v | SYST_CSR_ENABLE) }
|
||||
}
|
||||
|
||||
/// Enables SysTick interrupt
|
||||
#[inline]
|
||||
pub fn enable_interrupt(&mut self) {
|
||||
unsafe { self.csr.modify(|v| v | SYST_CSR_TICKINT) }
|
||||
}
|
||||
|
||||
/// Gets clock source
|
||||
///
|
||||
/// *NOTE* This takes `&mut self` because the read operation is side effectful and can clear the
|
||||
/// bit that indicates that the timer has wrapped (cf. `SYST.has_wrapped`)
|
||||
#[inline]
|
||||
pub fn get_clock_source(&mut self) -> SystClkSource {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
if self.csr.read() & SYST_CSR_CLKSOURCE != 0 {
|
||||
SystClkSource::Core
|
||||
} else {
|
||||
SystClkSource::External
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets current value
|
||||
#[inline]
|
||||
pub fn get_current() -> u32 {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).cvr.read() }
|
||||
}
|
||||
|
||||
/// Gets reload value
|
||||
#[inline]
|
||||
pub fn get_reload() -> u32 {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).rvr.read() }
|
||||
}
|
||||
|
||||
/// Returns the reload value with which the counter would wrap once per 10
|
||||
/// ms
|
||||
///
|
||||
/// Returns `0` if the value is not known (e.g. because the clock can
|
||||
/// change dynamically).
|
||||
#[inline]
|
||||
pub fn get_ticks_per_10ms() -> u32 {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).calib.read() & SYST_COUNTER_MASK }
|
||||
}
|
||||
|
||||
/// Checks if an external reference clock is available
|
||||
#[inline]
|
||||
pub fn has_reference_clock() -> bool {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).calib.read() & SYST_CALIB_NOREF == 0 }
|
||||
}
|
||||
|
||||
/// Checks if the counter wrapped (underflowed) since the last check
|
||||
///
|
||||
/// *NOTE* This takes `&mut self` because the read operation is side effectful and will clear
|
||||
/// the bit of the read register.
|
||||
#[inline]
|
||||
pub fn has_wrapped(&mut self) -> bool {
|
||||
self.csr.read() & SYST_CSR_COUNTFLAG != 0
|
||||
}
|
||||
|
||||
/// Checks if counter is enabled
|
||||
///
|
||||
/// *NOTE* This takes `&mut self` because the read operation is side effectful and can clear the
|
||||
/// bit that indicates that the timer has wrapped (cf. `SYST.has_wrapped`)
|
||||
#[inline]
|
||||
pub fn is_counter_enabled(&mut self) -> bool {
|
||||
self.csr.read() & SYST_CSR_ENABLE != 0
|
||||
}
|
||||
|
||||
/// Checks if SysTick interrupt is enabled
|
||||
///
|
||||
/// *NOTE* This takes `&mut self` because the read operation is side effectful and can clear the
|
||||
/// bit that indicates that the timer has wrapped (cf. `SYST.has_wrapped`)
|
||||
#[inline]
|
||||
pub fn is_interrupt_enabled(&mut self) -> bool {
|
||||
self.csr.read() & SYST_CSR_TICKINT != 0
|
||||
}
|
||||
|
||||
/// Checks if the calibration value is precise
|
||||
///
|
||||
/// Returns `false` if using the reload value returned by
|
||||
/// `get_ticks_per_10ms()` may result in a period significantly deviating
|
||||
/// from 10 ms.
|
||||
#[inline]
|
||||
pub fn is_precise() -> bool {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).calib.read() & SYST_CALIB_SKEW == 0 }
|
||||
}
|
||||
|
||||
/// Sets clock source
|
||||
#[inline]
|
||||
pub fn set_clock_source(&mut self, clk_source: SystClkSource) {
|
||||
match clk_source {
|
||||
SystClkSource::External => unsafe { self.csr.modify(|v| v & !SYST_CSR_CLKSOURCE) },
|
||||
SystClkSource::Core => unsafe { self.csr.modify(|v| v | SYST_CSR_CLKSOURCE) },
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets reload value
|
||||
///
|
||||
/// Valid values are between `1` and `0x00ffffff`.
|
||||
///
|
||||
/// *NOTE* To make the timer wrap every `N` ticks set the reload value to `N - 1`
|
||||
#[inline]
|
||||
pub fn set_reload(&mut self, value: u32) {
|
||||
unsafe { self.rvr.write(value) }
|
||||
}
|
||||
}
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
#[test]
|
||||
fn cpuid() {
|
||||
let cpuid = unsafe { &*crate::peripheral::CPUID::PTR };
|
||||
|
||||
assert_eq!(address(&cpuid.base), 0xE000_ED00);
|
||||
assert_eq!(address(&cpuid.pfr), 0xE000_ED40);
|
||||
assert_eq!(address(&cpuid.dfr), 0xE000_ED48);
|
||||
assert_eq!(address(&cpuid.afr), 0xE000_ED4C);
|
||||
assert_eq!(address(&cpuid.mmfr), 0xE000_ED50);
|
||||
assert_eq!(address(&cpuid.isar), 0xE000_ED60);
|
||||
assert_eq!(address(&cpuid.clidr), 0xE000_ED78);
|
||||
assert_eq!(address(&cpuid.ctr), 0xE000_ED7C);
|
||||
assert_eq!(address(&cpuid.ccsidr), 0xE000_ED80);
|
||||
assert_eq!(address(&cpuid.csselr), 0xE000_ED84);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dcb() {
|
||||
let dcb = unsafe { &*crate::peripheral::DCB::PTR };
|
||||
|
||||
assert_eq!(address(&dcb.dhcsr), 0xE000_EDF0);
|
||||
assert_eq!(address(&dcb.dcrsr), 0xE000_EDF4);
|
||||
assert_eq!(address(&dcb.dcrdr), 0xE000_EDF8);
|
||||
assert_eq!(address(&dcb.demcr), 0xE000_EDFC);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dwt() {
|
||||
let dwt = unsafe { &*crate::peripheral::DWT::PTR };
|
||||
|
||||
assert_eq!(address(&dwt.ctrl), 0xE000_1000);
|
||||
#[cfg(not(armv6m))]
|
||||
assert_eq!(address(&dwt.cyccnt), 0xE000_1004);
|
||||
#[cfg(not(armv6m))]
|
||||
assert_eq!(address(&dwt.cpicnt), 0xE000_1008);
|
||||
#[cfg(not(armv6m))]
|
||||
assert_eq!(address(&dwt.exccnt), 0xE000_100C);
|
||||
#[cfg(not(armv6m))]
|
||||
assert_eq!(address(&dwt.sleepcnt), 0xE000_1010);
|
||||
#[cfg(not(armv6m))]
|
||||
assert_eq!(address(&dwt.lsucnt), 0xE000_1014);
|
||||
#[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);
|
||||
#[cfg(not(armv6m))]
|
||||
assert_eq!(address(&dwt.lar), 0xE000_1FB0);
|
||||
#[cfg(not(armv6m))]
|
||||
assert_eq!(address(&dwt.lsr), 0xE000_1FB4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fpb() {
|
||||
let fpb = unsafe { &*crate::peripheral::FPB::PTR };
|
||||
|
||||
assert_eq!(address(&fpb.ctrl), 0xE000_2000);
|
||||
assert_eq!(address(&fpb.remap), 0xE000_2004);
|
||||
assert_eq!(address(&fpb.comp), 0xE000_2008);
|
||||
assert_eq!(address(&fpb.comp[1]), 0xE000_200C);
|
||||
assert_eq!(address(&fpb.lar), 0xE000_2FB0);
|
||||
assert_eq!(address(&fpb.lsr), 0xE000_2FB4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fpu() {
|
||||
let fpu = unsafe { &*crate::peripheral::FPU::PTR };
|
||||
|
||||
assert_eq!(address(&fpu.fpccr), 0xE000_EF34);
|
||||
assert_eq!(address(&fpu.fpcar), 0xE000_EF38);
|
||||
assert_eq!(address(&fpu.fpdscr), 0xE000_EF3C);
|
||||
assert_eq!(address(&fpu.mvfr), 0xE000_EF40);
|
||||
assert_eq!(address(&fpu.mvfr[1]), 0xE000_EF44);
|
||||
assert_eq!(address(&fpu.mvfr[2]), 0xE000_EF48);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn itm() {
|
||||
let itm = unsafe { &*crate::peripheral::ITM::PTR };
|
||||
|
||||
assert_eq!(address(&itm.stim), 0xE000_0000);
|
||||
assert_eq!(address(&itm.ter), 0xE000_0E00);
|
||||
assert_eq!(address(&itm.tpr), 0xE000_0E40);
|
||||
assert_eq!(address(&itm.tcr), 0xE000_0E80);
|
||||
assert_eq!(address(&itm.lar), 0xE000_0FB0);
|
||||
assert_eq!(address(&itm.lsr), 0xE000_0FB4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mpu() {
|
||||
let mpu = unsafe { &*crate::peripheral::MPU::PTR };
|
||||
|
||||
assert_eq!(address(&mpu._type), 0xE000ED90);
|
||||
assert_eq!(address(&mpu.ctrl), 0xE000ED94);
|
||||
assert_eq!(address(&mpu.rnr), 0xE000ED98);
|
||||
assert_eq!(address(&mpu.rbar), 0xE000ED9C);
|
||||
assert_eq!(address(&mpu.rasr), 0xE000EDA0);
|
||||
assert_eq!(address(&mpu.rbar_a1), 0xE000EDA4);
|
||||
assert_eq!(address(&mpu.rasr_a1), 0xE000EDA8);
|
||||
assert_eq!(address(&mpu.rbar_a2), 0xE000EDAC);
|
||||
assert_eq!(address(&mpu.rasr_a2), 0xE000EDB0);
|
||||
assert_eq!(address(&mpu.rbar_a3), 0xE000EDB4);
|
||||
assert_eq!(address(&mpu.rasr_a3), 0xE000EDB8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nvic() {
|
||||
let nvic = unsafe { &*crate::peripheral::NVIC::PTR };
|
||||
|
||||
assert_eq!(address(&nvic.iser), 0xE000E100);
|
||||
assert_eq!(address(&nvic.icer), 0xE000E180);
|
||||
assert_eq!(address(&nvic.ispr), 0xE000E200);
|
||||
assert_eq!(address(&nvic.icpr), 0xE000E280);
|
||||
assert_eq!(address(&nvic.iabr), 0xE000E300);
|
||||
assert_eq!(address(&nvic.ipr), 0xE000E400);
|
||||
#[cfg(not(armv6m))]
|
||||
assert_eq!(address(&nvic.stir), 0xE000EF00);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scb() {
|
||||
let scb = unsafe { &*crate::peripheral::SCB::PTR };
|
||||
|
||||
assert_eq!(address(&scb.icsr), 0xE000_ED04);
|
||||
assert_eq!(address(&scb.vtor), 0xE000_ED08);
|
||||
assert_eq!(address(&scb.aircr), 0xE000_ED0C);
|
||||
assert_eq!(address(&scb.scr), 0xE000_ED10);
|
||||
assert_eq!(address(&scb.ccr), 0xE000_ED14);
|
||||
assert_eq!(address(&scb.shpr), 0xE000_ED18);
|
||||
assert_eq!(address(&scb.shcsr), 0xE000_ED24);
|
||||
assert_eq!(address(&scb.cfsr), 0xE000_ED28);
|
||||
assert_eq!(address(&scb.hfsr), 0xE000_ED2C);
|
||||
assert_eq!(address(&scb.dfsr), 0xE000_ED30);
|
||||
assert_eq!(address(&scb.mmfar), 0xE000_ED34);
|
||||
assert_eq!(address(&scb.bfar), 0xE000_ED38);
|
||||
assert_eq!(address(&scb.afsr), 0xE000_ED3C);
|
||||
assert_eq!(address(&scb.cpacr), 0xE000_ED88);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn syst() {
|
||||
let syst = unsafe { &*crate::peripheral::SYST::PTR };
|
||||
|
||||
assert_eq!(address(&syst.csr), 0xE000_E010);
|
||||
assert_eq!(address(&syst.rvr), 0xE000_E014);
|
||||
assert_eq!(address(&syst.cvr), 0xE000_E018);
|
||||
assert_eq!(address(&syst.calib), 0xE000_E01C);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tpiu() {
|
||||
let tpiu = unsafe { &*crate::peripheral::TPIU::PTR };
|
||||
|
||||
assert_eq!(address(&tpiu.sspsr), 0xE004_0000);
|
||||
assert_eq!(address(&tpiu.cspsr), 0xE004_0004);
|
||||
assert_eq!(address(&tpiu.acpr), 0xE004_0010);
|
||||
assert_eq!(address(&tpiu.sppr), 0xE004_00F0);
|
||||
assert_eq!(address(&tpiu.ffcr), 0xE004_0304);
|
||||
assert_eq!(address(&tpiu.lar), 0xE004_0FB0);
|
||||
assert_eq!(address(&tpiu.lsr), 0xE004_0FB4);
|
||||
assert_eq!(address(&tpiu._type), 0xE004_0FC8);
|
||||
}
|
||||
|
||||
fn address<T>(r: *const T) -> usize {
|
||||
r as usize
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
//! Trace Port Interface Unit;
|
||||
//!
|
||||
//! *NOTE* Not available on Armv6-M.
|
||||
|
||||
use volatile_register::{RO, RW, WO};
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Supported Parallel Port Sizes
|
||||
pub sspsr: RO<u32>,
|
||||
/// Current Parallel Port Size
|
||||
pub cspsr: RW<u32>,
|
||||
reserved0: [u32; 2],
|
||||
/// Asynchronous Clock Prescaler
|
||||
pub acpr: RW<u32>,
|
||||
reserved1: [u32; 55],
|
||||
/// Selected Pin Control
|
||||
pub sppr: RW<u32>,
|
||||
reserved2: [u32; 132],
|
||||
/// Formatter and Flush Control
|
||||
pub ffcr: RW<u32>,
|
||||
reserved3: [u32; 810],
|
||||
/// Lock Access
|
||||
pub lar: WO<u32>,
|
||||
/// Lock Status
|
||||
pub lsr: RO<u32>,
|
||||
reserved4: [u32; 4],
|
||||
/// TPIU Type
|
||||
pub _type: RO<u32>,
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
//! Prelude
|
||||
|
||||
pub use embedded_hal::prelude::*;
|
||||
Vendored
+54
@@ -0,0 +1,54 @@
|
||||
//! Application Program Status Register
|
||||
|
||||
/// Application Program Status Register
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Apsr {
|
||||
bits: u32,
|
||||
}
|
||||
|
||||
impl Apsr {
|
||||
/// Returns the contents of the register as raw bits
|
||||
#[inline]
|
||||
pub fn bits(self) -> u32 {
|
||||
self.bits
|
||||
}
|
||||
|
||||
/// DSP overflow and saturation flag
|
||||
#[inline]
|
||||
pub fn q(self) -> bool {
|
||||
self.bits & (1 << 27) == (1 << 27)
|
||||
}
|
||||
|
||||
/// Overflow flag
|
||||
#[inline]
|
||||
pub fn v(self) -> bool {
|
||||
self.bits & (1 << 28) == (1 << 28)
|
||||
}
|
||||
|
||||
/// Carry or borrow flag
|
||||
#[inline]
|
||||
pub fn c(self) -> bool {
|
||||
self.bits & (1 << 29) == (1 << 29)
|
||||
}
|
||||
|
||||
/// Zero flag
|
||||
#[inline]
|
||||
pub fn z(self) -> bool {
|
||||
self.bits & (1 << 30) == (1 << 30)
|
||||
}
|
||||
|
||||
/// Negative flag
|
||||
#[inline]
|
||||
pub fn n(self) -> bool {
|
||||
self.bits & (1 << 31) == (1 << 31)
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the CPU register
|
||||
///
|
||||
/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature.
|
||||
#[inline]
|
||||
pub fn read() -> Apsr {
|
||||
let bits: u32 = call_asm!(__apsr_r() -> u32);
|
||||
Apsr { bits }
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
//! Base Priority Mask Register
|
||||
|
||||
/// Reads the CPU register
|
||||
#[inline]
|
||||
pub fn read() -> u8 {
|
||||
call_asm!(__basepri_r() -> u8)
|
||||
}
|
||||
|
||||
/// Writes to the CPU register
|
||||
///
|
||||
/// **IMPORTANT** If you are using a Cortex-M7 device with revision r0p1 you MUST enable the
|
||||
/// `cm7-r0p1` Cargo feature or this function WILL misbehave.
|
||||
#[inline]
|
||||
pub unsafe fn write(basepri: u8) {
|
||||
#[cfg(feature = "cm7-r0p1")]
|
||||
{
|
||||
call_asm!(__basepri_w_cm7_r0p1(basepri: u8));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "cm7-r0p1"))]
|
||||
{
|
||||
call_asm!(__basepri_w(basepri: u8));
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
//! Base Priority Mask Register (conditional write)
|
||||
|
||||
/// Writes to BASEPRI *if*
|
||||
///
|
||||
/// - `basepri != 0` AND `basepri::read() == 0`, OR
|
||||
/// - `basepri != 0` AND `basepri < basepri::read()`
|
||||
///
|
||||
/// **IMPORTANT** If you are using a Cortex-M7 device with revision r0p1 you MUST enable the
|
||||
/// `cm7-r0p1` Cargo feature or this function WILL misbehave.
|
||||
#[inline]
|
||||
pub fn write(basepri: u8) {
|
||||
#[cfg(feature = "cm7-r0p1")]
|
||||
{
|
||||
call_asm!(__basepri_max_cm7_r0p1(basepri: u8));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "cm7-r0p1"))]
|
||||
{
|
||||
call_asm!(__basepri_max(basepri: u8));
|
||||
}
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
//! Control register
|
||||
|
||||
/// Control register
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Control {
|
||||
bits: u32,
|
||||
}
|
||||
|
||||
impl Control {
|
||||
/// Creates a `Control` value from raw bits.
|
||||
#[inline]
|
||||
pub fn from_bits(bits: u32) -> Self {
|
||||
Self { bits }
|
||||
}
|
||||
|
||||
/// Returns the contents of the register as raw bits
|
||||
#[inline]
|
||||
pub fn bits(self) -> u32 {
|
||||
self.bits
|
||||
}
|
||||
|
||||
/// Thread mode privilege level
|
||||
#[inline]
|
||||
pub fn npriv(self) -> Npriv {
|
||||
if self.bits & (1 << 0) == (1 << 0) {
|
||||
Npriv::Unprivileged
|
||||
} else {
|
||||
Npriv::Privileged
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the thread mode privilege level value (nPRIV).
|
||||
#[inline]
|
||||
pub fn set_npriv(&mut self, npriv: Npriv) {
|
||||
let mask = 1 << 0;
|
||||
match npriv {
|
||||
Npriv::Unprivileged => self.bits |= mask,
|
||||
Npriv::Privileged => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Currently active stack pointer
|
||||
#[inline]
|
||||
pub fn spsel(self) -> Spsel {
|
||||
if self.bits & (1 << 1) == (1 << 1) {
|
||||
Spsel::Psp
|
||||
} else {
|
||||
Spsel::Msp
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the SPSEL value.
|
||||
#[inline]
|
||||
pub fn set_spsel(&mut self, spsel: Spsel) {
|
||||
let mask = 1 << 1;
|
||||
match spsel {
|
||||
Spsel::Psp => self.bits |= mask,
|
||||
Spsel::Msp => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether context floating-point is currently active
|
||||
#[inline]
|
||||
pub fn fpca(self) -> Fpca {
|
||||
if self.bits & (1 << 2) == (1 << 2) {
|
||||
Fpca::Active
|
||||
} else {
|
||||
Fpca::NotActive
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the FPCA value.
|
||||
#[inline]
|
||||
pub fn set_fpca(&mut self, fpca: Fpca) {
|
||||
let mask = 1 << 2;
|
||||
match fpca {
|
||||
Fpca::Active => self.bits |= mask,
|
||||
Fpca::NotActive => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Thread mode privilege level
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Npriv {
|
||||
/// Privileged
|
||||
Privileged,
|
||||
/// Unprivileged
|
||||
Unprivileged,
|
||||
}
|
||||
|
||||
impl Npriv {
|
||||
/// Is in privileged thread mode?
|
||||
#[inline]
|
||||
pub fn is_privileged(self) -> bool {
|
||||
self == Npriv::Privileged
|
||||
}
|
||||
|
||||
/// Is in unprivileged thread mode?
|
||||
#[inline]
|
||||
pub fn is_unprivileged(self) -> bool {
|
||||
self == Npriv::Unprivileged
|
||||
}
|
||||
}
|
||||
|
||||
/// Currently active stack pointer
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Spsel {
|
||||
/// MSP is the current stack pointer
|
||||
Msp,
|
||||
/// PSP is the current stack pointer
|
||||
Psp,
|
||||
}
|
||||
|
||||
impl Spsel {
|
||||
/// Is MSP the current stack pointer?
|
||||
#[inline]
|
||||
pub fn is_msp(self) -> bool {
|
||||
self == Spsel::Msp
|
||||
}
|
||||
|
||||
/// Is PSP the current stack pointer?
|
||||
#[inline]
|
||||
pub fn is_psp(self) -> bool {
|
||||
self == Spsel::Psp
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether context floating-point is currently active
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Fpca {
|
||||
/// Floating-point context active.
|
||||
Active,
|
||||
/// No floating-point context active
|
||||
NotActive,
|
||||
}
|
||||
|
||||
impl Fpca {
|
||||
/// Is a floating-point context active?
|
||||
#[inline]
|
||||
pub fn is_active(self) -> bool {
|
||||
self == Fpca::Active
|
||||
}
|
||||
|
||||
/// Is a floating-point context not active?
|
||||
#[inline]
|
||||
pub fn is_not_active(self) -> bool {
|
||||
self == Fpca::NotActive
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the CPU register
|
||||
#[inline]
|
||||
pub fn read() -> Control {
|
||||
let bits: u32 = call_asm!(__control_r() -> u32);
|
||||
Control { bits }
|
||||
}
|
||||
|
||||
/// Writes to the CPU register.
|
||||
#[inline]
|
||||
pub unsafe fn write(control: Control) {
|
||||
let control = control.bits();
|
||||
call_asm!(__control_w(control: u32));
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
//! Fault Mask Register
|
||||
|
||||
/// All exceptions are ...
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Faultmask {
|
||||
/// Active
|
||||
Active,
|
||||
/// Inactive, expect for NMI
|
||||
Inactive,
|
||||
}
|
||||
|
||||
impl Faultmask {
|
||||
/// All exceptions are active
|
||||
#[inline]
|
||||
pub fn is_active(self) -> bool {
|
||||
self == Faultmask::Active
|
||||
}
|
||||
|
||||
/// All exceptions, except for NMI, are inactive
|
||||
#[inline]
|
||||
pub fn is_inactive(self) -> bool {
|
||||
self == Faultmask::Inactive
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the CPU register
|
||||
#[inline]
|
||||
pub fn read() -> Faultmask {
|
||||
let r: u32 = call_asm!(__faultmask_r() -> u32);
|
||||
if r & (1 << 0) == (1 << 0) {
|
||||
Faultmask::Inactive
|
||||
} else {
|
||||
Faultmask::Active
|
||||
}
|
||||
}
|
||||
+305
@@ -0,0 +1,305 @@
|
||||
//! Floating-point Status Control Register
|
||||
|
||||
/// Floating-point Status Control Register
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Fpscr {
|
||||
bits: u32,
|
||||
}
|
||||
|
||||
impl Fpscr {
|
||||
/// Creates a `Fspcr` value from raw bits.
|
||||
#[inline]
|
||||
pub fn from_bits(bits: u32) -> Self {
|
||||
Self { bits }
|
||||
}
|
||||
|
||||
/// Returns the contents of the register as raw bits
|
||||
#[inline]
|
||||
pub fn bits(self) -> u32 {
|
||||
self.bits
|
||||
}
|
||||
|
||||
/// Read the Negative condition code flag
|
||||
#[inline]
|
||||
pub fn n(self) -> bool {
|
||||
self.bits & (1 << 31) != 0
|
||||
}
|
||||
|
||||
/// Sets the Negative condition code flag
|
||||
#[inline]
|
||||
pub fn set_n(&mut self, n: bool) {
|
||||
let mask = 1 << 31;
|
||||
match n {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Zero condition code flag
|
||||
#[inline]
|
||||
pub fn z(self) -> bool {
|
||||
self.bits & (1 << 30) != 0
|
||||
}
|
||||
|
||||
/// Sets the Zero condition code flag
|
||||
#[inline]
|
||||
pub fn set_z(&mut self, z: bool) {
|
||||
let mask = 1 << 30;
|
||||
match z {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Carry condition code flag
|
||||
#[inline]
|
||||
pub fn c(self) -> bool {
|
||||
self.bits & (1 << 29) != 0
|
||||
}
|
||||
|
||||
/// Sets the Carry condition code flag
|
||||
#[inline]
|
||||
pub fn set_c(&mut self, c: bool) {
|
||||
let mask = 1 << 29;
|
||||
match c {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Overflow condition code flag
|
||||
#[inline]
|
||||
pub fn v(self) -> bool {
|
||||
self.bits & (1 << 28) != 0
|
||||
}
|
||||
|
||||
/// Sets the Zero condition code flag
|
||||
#[inline]
|
||||
pub fn set_v(&mut self, v: bool) {
|
||||
let mask = 1 << 28;
|
||||
match v {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Alternative Half Precision bit
|
||||
#[inline]
|
||||
pub fn ahp(self) -> bool {
|
||||
self.bits & (1 << 26) != 0
|
||||
}
|
||||
|
||||
/// Sets the Alternative Half Precision bit
|
||||
#[inline]
|
||||
pub fn set_ahp(&mut self, ahp: bool) {
|
||||
let mask = 1 << 26;
|
||||
match ahp {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Default NaN mode bit
|
||||
#[inline]
|
||||
pub fn dn(self) -> bool {
|
||||
self.bits & (1 << 25) != 0
|
||||
}
|
||||
|
||||
/// Sets the Default NaN mode bit
|
||||
#[inline]
|
||||
pub fn set_dn(&mut self, dn: bool) {
|
||||
let mask = 1 << 25;
|
||||
match dn {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Flush to Zero mode bit
|
||||
#[inline]
|
||||
pub fn fz(self) -> bool {
|
||||
self.bits & (1 << 24) != 0
|
||||
}
|
||||
|
||||
/// Sets the Flush to Zero mode bit
|
||||
#[inline]
|
||||
pub fn set_fz(&mut self, fz: bool) {
|
||||
let mask = 1 << 24;
|
||||
match fz {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Rounding Mode control field
|
||||
#[inline]
|
||||
pub fn rmode(self) -> RMode {
|
||||
match (self.bits & (3 << 22)) >> 22 {
|
||||
0 => RMode::Nearest,
|
||||
1 => RMode::PlusInfinity,
|
||||
2 => RMode::MinusInfinity,
|
||||
_ => RMode::Zero,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the Rounding Mode control field
|
||||
#[inline]
|
||||
pub fn set_rmode(&mut self, rmode: RMode) {
|
||||
let mask = 3 << 22;
|
||||
match rmode {
|
||||
RMode::Nearest => self.bits &= !mask,
|
||||
RMode::PlusInfinity => self.bits = (self.bits & !mask) | (1 << 22),
|
||||
RMode::MinusInfinity => self.bits = (self.bits & !mask) | (2 << 22),
|
||||
RMode::Zero => self.bits |= mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Input Denormal cumulative exception bit
|
||||
#[inline]
|
||||
pub fn idc(self) -> bool {
|
||||
self.bits & (1 << 7) != 0
|
||||
}
|
||||
|
||||
/// Sets the Input Denormal cumulative exception bit
|
||||
#[inline]
|
||||
pub fn set_idc(&mut self, idc: bool) {
|
||||
let mask = 1 << 7;
|
||||
match idc {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Inexact cumulative exception bit
|
||||
#[inline]
|
||||
pub fn ixc(self) -> bool {
|
||||
self.bits & (1 << 4) != 0
|
||||
}
|
||||
|
||||
/// Sets the Inexact cumulative exception bit
|
||||
#[inline]
|
||||
pub fn set_ixc(&mut self, ixc: bool) {
|
||||
let mask = 1 << 4;
|
||||
match ixc {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Underflow cumulative exception bit
|
||||
#[inline]
|
||||
pub fn ufc(self) -> bool {
|
||||
self.bits & (1 << 3) != 0
|
||||
}
|
||||
|
||||
/// Sets the Underflow cumulative exception bit
|
||||
#[inline]
|
||||
pub fn set_ufc(&mut self, ufc: bool) {
|
||||
let mask = 1 << 3;
|
||||
match ufc {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Overflow cumulative exception bit
|
||||
#[inline]
|
||||
pub fn ofc(self) -> bool {
|
||||
self.bits & (1 << 2) != 0
|
||||
}
|
||||
|
||||
/// Sets the Overflow cumulative exception bit
|
||||
#[inline]
|
||||
pub fn set_ofc(&mut self, ofc: bool) {
|
||||
let mask = 1 << 2;
|
||||
match ofc {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Division by Zero cumulative exception bit
|
||||
#[inline]
|
||||
pub fn dzc(self) -> bool {
|
||||
self.bits & (1 << 1) != 0
|
||||
}
|
||||
|
||||
/// Sets the Division by Zero cumulative exception bit
|
||||
#[inline]
|
||||
pub fn set_dzc(&mut self, dzc: bool) {
|
||||
let mask = 1 << 1;
|
||||
match dzc {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Invalid Operation cumulative exception bit
|
||||
#[inline]
|
||||
pub fn ioc(self) -> bool {
|
||||
self.bits & (1 << 0) != 0
|
||||
}
|
||||
|
||||
/// Sets the Invalid Operation cumulative exception bit
|
||||
#[inline]
|
||||
pub fn set_ioc(&mut self, ioc: bool) {
|
||||
let mask = 1 << 0;
|
||||
match ioc {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Rounding mode
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum RMode {
|
||||
/// Round to Nearest (RN) mode. This is the reset value.
|
||||
Nearest,
|
||||
/// Round towards Plus Infinity (RP) mode.
|
||||
PlusInfinity,
|
||||
/// Round towards Minus Infinity (RM) mode.
|
||||
MinusInfinity,
|
||||
/// Round towards Zero (RZ) mode.
|
||||
Zero,
|
||||
}
|
||||
|
||||
impl RMode {
|
||||
/// Is Nearest the current rounding mode?
|
||||
#[inline]
|
||||
pub fn is_nearest(self) -> bool {
|
||||
self == RMode::Nearest
|
||||
}
|
||||
|
||||
/// Is Plus Infinity the current rounding mode?
|
||||
#[inline]
|
||||
pub fn is_plus_infinity(self) -> bool {
|
||||
self == RMode::PlusInfinity
|
||||
}
|
||||
|
||||
/// Is Minus Infinity the current rounding mode?
|
||||
#[inline]
|
||||
pub fn is_minus_infinity(self) -> bool {
|
||||
self == RMode::MinusInfinity
|
||||
}
|
||||
|
||||
/// Is Zero the current rounding mode?
|
||||
#[inline]
|
||||
pub fn is_zero(self) -> bool {
|
||||
self == RMode::Zero
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the FPSCR register
|
||||
#[inline]
|
||||
pub fn read() -> Fpscr {
|
||||
let r: u32 = call_asm!(__fpscr_r() -> u32);
|
||||
Fpscr::from_bits(r)
|
||||
}
|
||||
|
||||
/// Set the value of the FPSCR register
|
||||
#[inline]
|
||||
pub unsafe fn write(fpscr: Fpscr) {
|
||||
let fpscr = fpscr.bits();
|
||||
call_asm!(__fpscr_w(fpscr: u32));
|
||||
}
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
//! Link register
|
||||
|
||||
/// Reads the CPU register
|
||||
///
|
||||
/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature.
|
||||
#[inline]
|
||||
pub fn read() -> u32 {
|
||||
call_asm!(__lr_r() -> u32)
|
||||
}
|
||||
|
||||
/// Writes `bits` to the CPU register
|
||||
///
|
||||
/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature.
|
||||
#[inline]
|
||||
pub unsafe fn write(bits: u32) {
|
||||
call_asm!(__lr_w(bits: u32));
|
||||
}
|
||||
Vendored
+68
@@ -0,0 +1,68 @@
|
||||
//! Processor core registers
|
||||
//!
|
||||
//! The following registers can only be accessed in PRIVILEGED mode:
|
||||
//!
|
||||
//! - BASEPRI
|
||||
//! - CONTROL
|
||||
//! - FAULTMASK
|
||||
//! - MSP
|
||||
//! - PRIMASK
|
||||
//!
|
||||
//! The rest of registers (see list below) can be accessed in either, PRIVILEGED
|
||||
//! or UNPRIVILEGED, mode.
|
||||
//!
|
||||
//! - APSR
|
||||
//! - LR
|
||||
//! - PC
|
||||
//! - PSP
|
||||
//!
|
||||
//! The following registers are NOT available on ARMv6-M devices
|
||||
//! (`thumbv6m-none-eabi`):
|
||||
//!
|
||||
//! - BASEPRI
|
||||
//! - FAULTMASK
|
||||
//!
|
||||
//! The following registers are only available for devices with an FPU:
|
||||
//!
|
||||
//! - FPSCR
|
||||
//!
|
||||
//! # References
|
||||
//!
|
||||
//! - Cortex-M* Devices Generic User Guide - Section 2.1.3 Core registers
|
||||
|
||||
#[cfg(all(not(armv6m), not(armv8m_base)))]
|
||||
pub mod basepri;
|
||||
|
||||
#[cfg(all(not(armv6m), not(armv8m_base)))]
|
||||
pub mod basepri_max;
|
||||
|
||||
pub mod control;
|
||||
|
||||
#[cfg(all(not(armv6m), not(armv8m_base)))]
|
||||
pub mod faultmask;
|
||||
|
||||
#[cfg(has_fpu)]
|
||||
pub mod fpscr;
|
||||
|
||||
pub mod msp;
|
||||
|
||||
pub mod primask;
|
||||
|
||||
pub mod psp;
|
||||
|
||||
#[cfg(armv8m_main)]
|
||||
pub mod msplim;
|
||||
|
||||
#[cfg(armv8m_main)]
|
||||
pub mod psplim;
|
||||
|
||||
// Accessing these registers requires inline assembly because their contents are tied to the current
|
||||
// stack frame
|
||||
#[cfg(feature = "inline-asm")]
|
||||
pub mod apsr;
|
||||
|
||||
#[cfg(feature = "inline-asm")]
|
||||
pub mod lr;
|
||||
|
||||
#[cfg(feature = "inline-asm")]
|
||||
pub mod pc;
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
//! Main Stack Pointer
|
||||
|
||||
/// Reads the CPU register
|
||||
#[inline]
|
||||
pub fn read() -> u32 {
|
||||
call_asm!(__msp_r() -> u32)
|
||||
}
|
||||
|
||||
/// Writes `bits` to the CPU register
|
||||
#[inline]
|
||||
#[deprecated = "calling this function invokes Undefined Behavior, consider asm::bootstrap as an alternative"]
|
||||
pub unsafe fn write(bits: u32) {
|
||||
call_asm!(__msp_w(bits: u32));
|
||||
}
|
||||
|
||||
/// Reads the Non-Secure CPU register from Secure state.
|
||||
///
|
||||
/// Executing this function in Non-Secure state will return zeroes.
|
||||
#[cfg(armv8m)]
|
||||
#[inline]
|
||||
pub fn read_ns() -> u32 {
|
||||
call_asm!(__msp_ns_r() -> u32)
|
||||
}
|
||||
|
||||
/// Writes `bits` to the Non-Secure CPU register from Secure state.
|
||||
///
|
||||
/// Executing this function in Non-Secure state will be ignored.
|
||||
#[cfg(armv8m)]
|
||||
#[inline]
|
||||
pub unsafe fn write_ns(bits: u32) {
|
||||
call_asm!(__msp_ns_w(bits: u32));
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
//! Main Stack Pointer Limit Register
|
||||
|
||||
/// Reads the CPU register
|
||||
#[inline]
|
||||
pub fn read() -> u32 {
|
||||
call_asm!(__msplim_r() -> u32)
|
||||
}
|
||||
|
||||
/// Writes `bits` to the CPU register
|
||||
#[inline]
|
||||
pub unsafe fn write(bits: u32) {
|
||||
call_asm!(__msplim_w(bits: u32))
|
||||
}
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
//! Program counter
|
||||
|
||||
/// Reads the CPU register
|
||||
///
|
||||
/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature.
|
||||
#[inline]
|
||||
pub fn read() -> u32 {
|
||||
call_asm!(__pc_r() -> u32)
|
||||
}
|
||||
|
||||
/// Writes `bits` to the CPU register
|
||||
///
|
||||
/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature.
|
||||
#[inline]
|
||||
pub unsafe fn write(bits: u32) {
|
||||
call_asm!(__pc_w(bits: u32));
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
//! Priority mask register
|
||||
|
||||
/// All exceptions with configurable priority are ...
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Primask {
|
||||
/// Active
|
||||
Active,
|
||||
/// Inactive
|
||||
Inactive,
|
||||
}
|
||||
|
||||
impl Primask {
|
||||
/// All exceptions with configurable priority are active
|
||||
#[inline]
|
||||
pub fn is_active(self) -> bool {
|
||||
self == Primask::Active
|
||||
}
|
||||
|
||||
/// All exceptions with configurable priority are inactive
|
||||
#[inline]
|
||||
pub fn is_inactive(self) -> bool {
|
||||
self == Primask::Inactive
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the CPU register
|
||||
#[inline]
|
||||
pub fn read() -> Primask {
|
||||
let r: u32 = call_asm!(__primask_r() -> u32);
|
||||
if r & (1 << 0) == (1 << 0) {
|
||||
Primask::Inactive
|
||||
} else {
|
||||
Primask::Active
|
||||
}
|
||||
}
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
//! Process Stack Pointer
|
||||
|
||||
/// Reads the CPU register
|
||||
#[inline]
|
||||
pub fn read() -> u32 {
|
||||
call_asm!(__psp_r() -> u32)
|
||||
}
|
||||
|
||||
/// Writes `bits` to the CPU register
|
||||
#[inline]
|
||||
pub unsafe fn write(bits: u32) {
|
||||
call_asm!(__psp_w(bits: u32))
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
//! Process Stack Pointer Limit Register
|
||||
|
||||
/// Reads the CPU register
|
||||
#[inline]
|
||||
pub fn read() -> u32 {
|
||||
call_asm!(__psplim_r() -> u32)
|
||||
}
|
||||
|
||||
/// Writes `bits` to the CPU register
|
||||
#[inline]
|
||||
pub unsafe fn write(bits: u32) {
|
||||
call_asm!(__psplim_w(bits: u32))
|
||||
}
|
||||
Reference in new issue
Block a user