rtic/export/cortex_basepri.rs
1use super::cortex_logical2hw;
2use cortex_m::register::{basepri, basepri_max};
3pub use cortex_m::{
4 asm::wfi,
5 interrupt,
6 peripheral::{scb::SystemHandler, DWT, SCB, SYST},
7 Peripherals,
8};
9
10#[cfg(not(any(feature = "thumbv7-backend", feature = "thumbv8main-backend")))]
11compile_error!(
12 "Building for Cortex-M with basepri, but 'thumbv7-backend' or 'thumbv8main-backend' backend not selected"
13);
14
15#[inline(always)]
16pub fn run<F>(priority: u8, f: F)
17where
18 F: FnOnce(),
19{
20 if priority == 1 {
21 // If the priority of this interrupt is `1` then BASEPRI can only be `0`
22 f();
23 unsafe { basepri::write(0) }
24 } else {
25 let initial = basepri::read();
26 f();
27 unsafe { basepri::write(initial) }
28 }
29}
30
31/// Lock implementation using BASEPRI and global Critical Section (CS)
32///
33/// # Safety
34///
35/// The system ceiling is raised from current to ceiling
36/// by either
37/// - raising the BASEPRI to the ceiling value, or
38/// - disable all interrupts in case we want to
39/// mask interrupts with maximum priority
40///
41/// Dereferencing a raw pointer inside CS
42///
43/// The priority.set/priority.get can safely be outside the CS
44/// as being a context local cell (not affected by preemptions).
45/// It is merely used in order to omit masking in case current
46/// priority is current priority >= ceiling.
47///
48/// Lock Efficiency:
49/// Experiments validate (sub)-zero cost for CS implementation
50/// (Sub)-zero as:
51/// - Either zero OH (lock optimized out), or
52/// - Amounting to an optimal assembly implementation
53/// - The BASEPRI value is folded to a constant at compile time
54/// - CS entry, single assembly instruction to write BASEPRI
55/// - CS exit, single assembly instruction to write BASEPRI
56/// - priority.set/get optimized out (their effect not)
57/// - On par or better than any handwritten implementation of SRP
58///
59/// Limitations:
60/// The current implementation reads/writes BASEPRI once
61/// even in some edge cases where this may be omitted.
62/// Total OH of per task is max 2 clock cycles, negligible in practice
63/// but can in theory be fixed.
64///
65#[inline(always)]
66pub unsafe fn lock<T, R>(
67 ptr: *mut T,
68 ceiling: u8,
69 nvic_prio_bits: u8,
70 f: impl FnOnce(&mut T) -> R,
71) -> R {
72 if ceiling == (1 << nvic_prio_bits) {
73 critical_section::with(|_| f(&mut *ptr))
74 } else {
75 let current = basepri::read();
76 basepri_max::write(cortex_logical2hw(ceiling, nvic_prio_bits));
77 let r = f(&mut *ptr);
78 basepri::write(current);
79 r
80 }
81}