aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp/src
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2025-04-07 23:13:35 +0000
committerGitHub <[email protected]>2025-04-07 23:13:35 +0000
commita23e971d313e8098b8d3a13f3ddf7629aa79a98c (patch)
treed45c6783176caa1131fcbb022e62433ed7f73674 /embassy-rp/src
parentdb3179cfb4be8596adbc601b27056bce03781407 (diff)
parent313328c09a52ba6f8f30105a3a9f98a76a5aef69 (diff)
Merge pull request #4017 from shilga/SpinlockMutex
embassy-rp: Spinlock mutex implementation
Diffstat (limited to 'embassy-rp/src')
-rw-r--r--embassy-rp/src/critical_section_impl.rs43
-rw-r--r--embassy-rp/src/lib.rs2
-rw-r--r--embassy-rp/src/spinlock.rs75
-rw-r--r--embassy-rp/src/spinlock_mutex.rs93
4 files changed, 171 insertions, 42 deletions
diff --git a/embassy-rp/src/critical_section_impl.rs b/embassy-rp/src/critical_section_impl.rs
index d233e6fab..2e4e8f716 100644
--- a/embassy-rp/src/critical_section_impl.rs
+++ b/embassy-rp/src/critical_section_impl.rs
@@ -1,6 +1,7 @@
1use core::sync::atomic::{AtomicU8, Ordering}; 1use core::sync::atomic::{AtomicU8, Ordering};
2 2
3use crate::pac; 3use crate::pac;
4use crate::spinlock::Spinlock;
4 5
5struct RpSpinlockCs; 6struct RpSpinlockCs;
6critical_section::set_impl!(RpSpinlockCs); 7critical_section::set_impl!(RpSpinlockCs);
@@ -92,46 +93,4 @@ impl RpSpinlockCs {
92 } 93 }
93} 94}
94 95
95pub struct Spinlock<const N: usize>(core::marker::PhantomData<()>)
96where
97 Spinlock<N>: SpinlockValid;
98
99impl<const N: usize> Spinlock<N>
100where
101 Spinlock<N>: SpinlockValid,
102{
103 /// Try to claim the spinlock. Will return `Some(Self)` if the lock is obtained, and `None` if the lock is
104 /// already in use somewhere else.
105 pub fn try_claim() -> Option<Self> {
106 let lock = pac::SIO.spinlock(N).read();
107 if lock > 0 {
108 Some(Self(core::marker::PhantomData))
109 } else {
110 None
111 }
112 }
113
114 /// Clear a locked spin-lock.
115 ///
116 /// # Safety
117 ///
118 /// Only call this function if you hold the spin-lock.
119 pub unsafe fn release() {
120 // Write (any value): release the lock
121 pac::SIO.spinlock(N).write_value(1);
122 }
123}
124
125impl<const N: usize> Drop for Spinlock<N>
126where
127 Spinlock<N>: SpinlockValid,
128{
129 fn drop(&mut self) {
130 // This is safe because we own the object, and hence hold the lock.
131 unsafe { Self::release() }
132 }
133}
134
135pub(crate) type Spinlock31 = Spinlock<31>; 96pub(crate) type Spinlock31 = Spinlock<31>;
136pub trait SpinlockValid {}
137impl SpinlockValid for Spinlock<31> {}
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index 35099d07b..f549446bc 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -41,6 +41,8 @@ pub mod rom_data;
41#[cfg(feature = "rp2040")] 41#[cfg(feature = "rp2040")]
42pub mod rtc; 42pub mod rtc;
43pub mod spi; 43pub mod spi;
44mod spinlock;
45pub mod spinlock_mutex;
44#[cfg(feature = "time-driver")] 46#[cfg(feature = "time-driver")]
45pub mod time_driver; 47pub mod time_driver;
46#[cfg(feature = "_rp235x")] 48#[cfg(feature = "_rp235x")]
diff --git a/embassy-rp/src/spinlock.rs b/embassy-rp/src/spinlock.rs
new file mode 100644
index 000000000..7effd2ae0
--- /dev/null
+++ b/embassy-rp/src/spinlock.rs
@@ -0,0 +1,75 @@
1use crate::pac;
2
3pub struct Spinlock<const N: usize>(core::marker::PhantomData<()>)
4where
5 Spinlock<N>: SpinlockValid;
6
7impl<const N: usize> Spinlock<N>
8where
9 Spinlock<N>: SpinlockValid,
10{
11 /// Try to claim the spinlock. Will return `Some(Self)` if the lock is obtained, and `None` if the lock is
12 /// already in use somewhere else.
13 pub fn try_claim() -> Option<Self> {
14 let lock = pac::SIO.spinlock(N).read();
15 if lock > 0 {
16 Some(Self(core::marker::PhantomData))
17 } else {
18 None
19 }
20 }
21
22 /// Clear a locked spin-lock.
23 ///
24 /// # Safety
25 ///
26 /// Only call this function if you hold the spin-lock.
27 pub unsafe fn release() {
28 // Write (any value): release the lock
29 pac::SIO.spinlock(N).write_value(1);
30 }
31}
32
33impl<const N: usize> Drop for Spinlock<N>
34where
35 Spinlock<N>: SpinlockValid,
36{
37 fn drop(&mut self) {
38 // This is safe because we own the object, and hence hold the lock.
39 unsafe { Self::release() }
40 }
41}
42
43pub trait SpinlockValid {}
44impl SpinlockValid for Spinlock<0> {}
45impl SpinlockValid for Spinlock<1> {}
46impl SpinlockValid for Spinlock<2> {}
47impl SpinlockValid for Spinlock<3> {}
48impl SpinlockValid for Spinlock<4> {}
49impl SpinlockValid for Spinlock<5> {}
50impl SpinlockValid for Spinlock<6> {}
51impl SpinlockValid for Spinlock<7> {}
52impl SpinlockValid for Spinlock<8> {}
53impl SpinlockValid for Spinlock<9> {}
54impl SpinlockValid for Spinlock<10> {}
55impl SpinlockValid for Spinlock<11> {}
56impl SpinlockValid for Spinlock<12> {}
57impl SpinlockValid for Spinlock<13> {}
58impl SpinlockValid for Spinlock<14> {}
59impl SpinlockValid for Spinlock<15> {}
60impl SpinlockValid for Spinlock<16> {}
61impl SpinlockValid for Spinlock<17> {}
62impl SpinlockValid for Spinlock<18> {}
63impl SpinlockValid for Spinlock<19> {}
64impl SpinlockValid for Spinlock<20> {}
65impl SpinlockValid for Spinlock<21> {}
66impl SpinlockValid for Spinlock<22> {}
67impl SpinlockValid for Spinlock<23> {}
68impl SpinlockValid for Spinlock<24> {}
69impl SpinlockValid for Spinlock<25> {}
70impl SpinlockValid for Spinlock<26> {}
71impl SpinlockValid for Spinlock<27> {}
72impl SpinlockValid for Spinlock<28> {}
73impl SpinlockValid for Spinlock<29> {}
74impl SpinlockValid for Spinlock<30> {}
75impl SpinlockValid for Spinlock<31> {}
diff --git a/embassy-rp/src/spinlock_mutex.rs b/embassy-rp/src/spinlock_mutex.rs
new file mode 100644
index 000000000..85174cf86
--- /dev/null
+++ b/embassy-rp/src/spinlock_mutex.rs
@@ -0,0 +1,93 @@
1//! Mutex implementation utilizing an hardware spinlock
2
3use core::marker::PhantomData;
4use core::sync::atomic::Ordering;
5
6use embassy_sync::blocking_mutex::raw::RawMutex;
7
8use crate::spinlock::{Spinlock, SpinlockValid};
9
10/// A mutex that allows borrowing data across executors and interrupts by utilizing an hardware spinlock
11///
12/// # Safety
13///
14/// This mutex is safe to share between different executors and interrupts.
15pub struct SpinlockRawMutex<const N: usize> {
16 _phantom: PhantomData<()>,
17}
18unsafe impl<const N: usize> Send for SpinlockRawMutex<N> {}
19unsafe impl<const N: usize> Sync for SpinlockRawMutex<N> {}
20
21impl<const N: usize> SpinlockRawMutex<N> {
22 /// Create a new `SpinlockRawMutex`.
23 pub const fn new() -> Self {
24 Self { _phantom: PhantomData }
25 }
26}
27
28unsafe impl<const N: usize> RawMutex for SpinlockRawMutex<N>
29where
30 Spinlock<N>: SpinlockValid,
31{
32 const INIT: Self = Self::new();
33
34 fn lock<R>(&self, f: impl FnOnce() -> R) -> R {
35 // Store the initial interrupt state in stack variable
36 let interrupts_active = cortex_m::register::primask::read().is_active();
37
38 // Spin until we get the lock
39 loop {
40 // Need to disable interrupts to ensure that we will not deadlock
41 // if an interrupt or higher prio locks the spinlock after we acquire the lock
42 cortex_m::interrupt::disable();
43 // Ensure the compiler doesn't re-order accesses and violate safety here
44 core::sync::atomic::compiler_fence(Ordering::SeqCst);
45 if let Some(lock) = Spinlock::<N>::try_claim() {
46 // We just acquired the lock.
47 // 1. Forget it, so we don't immediately unlock
48 core::mem::forget(lock);
49 break;
50 }
51 // We didn't get the lock, enable interrupts if they were enabled before we started
52 if interrupts_active {
53 // safety: interrupts are only enabled, if they had been enabled before
54 unsafe {
55 cortex_m::interrupt::enable();
56 }
57 }
58 }
59
60 let retval = f();
61
62 // Ensure the compiler doesn't re-order accesses and violate safety here
63 core::sync::atomic::compiler_fence(Ordering::SeqCst);
64 // Release the spinlock to allow others to lock mutex again
65 // safety: this point is only reached a spinlock was acquired before
66 unsafe {
67 Spinlock::<N>::release();
68 }
69
70 // Re-enable interrupts if they were enabled before the mutex was locked
71 if interrupts_active {
72 // safety: interrupts are only enabled, if they had been enabled before
73 unsafe {
74 cortex_m::interrupt::enable();
75 }
76 }
77
78 retval
79 }
80}
81
82pub mod blocking_mutex {
83 //! Mutex implementation utilizing an hardware spinlock
84 use embassy_sync::blocking_mutex::Mutex;
85
86 use crate::spinlock_mutex::SpinlockRawMutex;
87 /// A mutex that allows borrowing data across executors and interrupts by utilizing an hardware spinlock.
88 ///
89 /// # Safety
90 ///
91 /// This mutex is safe to share between different executors and interrupts.
92 pub type SpinlockMutex<const N: usize, T> = Mutex<SpinlockRawMutex<N>, T>;
93}