|
1 |
| -//! Code that implements the `critical-section` traits on Cortex-R or Cortex-A when only a single |
2 |
| -//! core is used. |
| 1 | +//! Code that implements the `critical-section` traits on Cortex-R or Cortex-A |
3 | 2 | //!
|
4 |
| -//! Only valid if you have a single core. |
| 3 | +//! We have single-core and multi-core versions. Select with the |
| 4 | +//! `critical-section-single-core` and `critical-section-multi-core` features. |
5 | 5 |
|
6 |
| -use core::sync::atomic; |
| 6 | +#[cfg(feature = "critical-section-single-core")] |
| 7 | +mod single_core { |
| 8 | + struct SingleCoreCriticalSection; |
7 | 9 |
|
8 |
| -struct SingleCoreCriticalSection; |
9 |
| -critical_section::set_impl!(SingleCoreCriticalSection); |
| 10 | + critical_section::set_impl!(SingleCoreCriticalSection); |
10 | 11 |
|
11 |
| -unsafe impl critical_section::Impl for SingleCoreCriticalSection { |
12 |
| - unsafe fn acquire() -> critical_section::RawRestoreState { |
13 |
| - // the i bit means "masked" |
14 |
| - let was_active = !crate::register::Cpsr::read().i(); |
15 |
| - crate::interrupt::disable(); |
16 |
| - atomic::compiler_fence(atomic::Ordering::SeqCst); |
17 |
| - was_active |
| 12 | + /// Indicates the critical section was entered with interrupts on |
| 13 | + pub const INT_ON: u8 = 0; |
| 14 | + |
| 15 | + /// Indicates the critical section was entered with interrupts off |
| 16 | + pub const INT_OFF: u8 = 1; |
| 17 | + |
| 18 | + #[cfg(feature = "critical-section-single-core")] |
| 19 | + unsafe impl critical_section::Impl for SingleCoreCriticalSection { |
| 20 | + unsafe fn acquire() -> critical_section::RawRestoreState { |
| 21 | + use core::sync::atomic; |
| 22 | + // the i bit means "masked" |
| 23 | + let was_active = !crate::register::Cpsr::read().i(); |
| 24 | + crate::interrupt::disable(); |
| 25 | + atomic::compiler_fence(atomic::Ordering::SeqCst); |
| 26 | + if was_active { |
| 27 | + INT_ON |
| 28 | + } else { |
| 29 | + INT_OFF |
| 30 | + } |
| 31 | + } |
| 32 | + |
| 33 | + unsafe fn release(was_active: critical_section::RawRestoreState) { |
| 34 | + use core::sync::atomic; |
| 35 | + // Only re-enable interrupts if they were enabled before the critical section. |
| 36 | + if was_active == INT_ON { |
| 37 | + atomic::compiler_fence(atomic::Ordering::SeqCst); |
| 38 | + // Safety: This is OK because we're releasing a lock that was |
| 39 | + // entered with interrupts enabled |
| 40 | + unsafe { |
| 41 | + crate::interrupt::enable(); |
| 42 | + } |
| 43 | + } |
| 44 | + } |
18 | 45 | }
|
| 46 | +} |
| 47 | + |
| 48 | +#[cfg(feature = "critical-section-multi-core")] |
| 49 | +mod multi_core { |
| 50 | + struct MultiCoreCriticalSection; |
| 51 | + |
| 52 | + critical_section::set_impl!(MultiCoreCriticalSection); |
| 53 | + |
| 54 | + /// The default value for our spin-lock |
| 55 | + pub const UNLOCKED: u32 = 0xFFFF_FFFF; |
| 56 | + |
| 57 | + /// Indicates the critical section was entered with interrupts on, and the spin-lock unlocked |
| 58 | + pub const INT_ON_UNLOCKED: u8 = 0; |
| 59 | + |
| 60 | + /// Indicates the critical section was entered with interrupts off, and the spin-lock locked (by us) |
| 61 | + pub const INT_OFF_LOCKED: u8 = 1; |
| 62 | + |
| 63 | + /// Indicates the critical section was entered with interrupts off, and the spin-lock unlocked |
| 64 | + pub const INT_OFF_UNLOCKED: u8 = 2; |
| 65 | + |
| 66 | + pub static CORE_SPIN_LOCK: core::sync::atomic::AtomicU32 = |
| 67 | + core::sync::atomic::AtomicU32::new(UNLOCKED); |
| 68 | + unsafe impl critical_section::Impl for MultiCoreCriticalSection { |
| 69 | + unsafe fn acquire() -> critical_section::RawRestoreState { |
| 70 | + use core::sync::atomic; |
| 71 | + |
| 72 | + // the i bit means "masked" |
| 73 | + let was_active = !crate::register::Cpsr::read().i(); |
| 74 | + crate::interrupt::disable(); |
| 75 | + |
| 76 | + let core_id = crate::asm::core_id(); |
| 77 | + |
| 78 | + let locked_already = loop { |
| 79 | + match CORE_SPIN_LOCK.compare_exchange( |
| 80 | + UNLOCKED, |
| 81 | + core_id, |
| 82 | + atomic::Ordering::Acquire, |
| 83 | + atomic::Ordering::Relaxed, |
| 84 | + ) { |
| 85 | + Ok(_) => { |
| 86 | + // we got the lock |
| 87 | + break false; |
| 88 | + } |
| 89 | + Err(n) if n == core_id => { |
| 90 | + // we already held the lock |
| 91 | + break true; |
| 92 | + } |
| 93 | + Err(_) => { |
| 94 | + // someone else holds the lock |
| 95 | + core::hint::spin_loop(); |
| 96 | + } |
| 97 | + } |
| 98 | + }; |
| 99 | + |
| 100 | + atomic::compiler_fence(atomic::Ordering::SeqCst); |
| 101 | + |
| 102 | + match (was_active, locked_already) { |
| 103 | + (true, true) => { |
| 104 | + panic!("Invalid CS state?!"); |
| 105 | + } |
| 106 | + (true, false) => { |
| 107 | + // we need to turn interrupts on, and release the lock |
| 108 | + INT_ON_UNLOCKED |
| 109 | + } |
| 110 | + (false, false) => { |
| 111 | + // we need release the lock |
| 112 | + INT_OFF_UNLOCKED |
| 113 | + } |
| 114 | + (false, true) => { |
| 115 | + // we need to do nothing |
| 116 | + INT_OFF_LOCKED |
| 117 | + } |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + unsafe fn release(was_active: critical_section::RawRestoreState) { |
| 122 | + use core::sync::atomic; |
19 | 123 |
|
20 |
| - unsafe fn release(was_active: critical_section::RawRestoreState) { |
21 |
| - // Only re-enable interrupts if they were enabled before the critical section. |
22 |
| - if was_active { |
23 | 124 | atomic::compiler_fence(atomic::Ordering::SeqCst);
|
24 |
| - // Safety: This is OK because we're releasing a lock that was |
25 |
| - // entered with interrupts enabled |
26 |
| - unsafe { |
27 |
| - crate::interrupt::enable(); |
| 125 | + match was_active { |
| 126 | + INT_OFF_LOCKED => { |
| 127 | + // do nothing |
| 128 | + } |
| 129 | + INT_OFF_UNLOCKED => { |
| 130 | + // the spin-lock was unlocked before, so unlock it |
| 131 | + CORE_SPIN_LOCK.store(UNLOCKED, atomic::Ordering::Release); |
| 132 | + } |
| 133 | + INT_ON_UNLOCKED => { |
| 134 | + // the spin-lock was unlocked before, so unlock it |
| 135 | + CORE_SPIN_LOCK.store(UNLOCKED, atomic::Ordering::Release); |
| 136 | + // Safety: This is OK because we're releasing a lock that was |
| 137 | + // entered with interrupts enabled |
| 138 | + unsafe { |
| 139 | + crate::interrupt::enable(); |
| 140 | + } |
| 141 | + } |
| 142 | + _ => { |
| 143 | + unreachable!() |
| 144 | + } |
28 | 145 | }
|
29 | 146 | }
|
30 | 147 | }
|
|
0 commit comments