aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/hsem/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-stm32/src/hsem/mod.rs')
-rw-r--r--embassy-stm32/src/hsem/mod.rs288
1 files changed, 225 insertions, 63 deletions
diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs
index 573a1851d..b5fa3c897 100644
--- a/embassy-stm32/src/hsem/mod.rs
+++ b/embassy-stm32/src/hsem/mod.rs
@@ -1,14 +1,22 @@
1//! Hardware Semaphore (HSEM) 1//! Hardware Semaphore (HSEM)
2 2
3use core::future::poll_fn;
4use core::marker::PhantomData;
5use core::sync::atomic::{Ordering, compiler_fence};
6use core::task::Poll;
7
8#[cfg(all(stm32wb, feature = "low-power"))]
9use critical_section::CriticalSection;
3use embassy_hal_internal::PeripheralType; 10use embassy_hal_internal::PeripheralType;
11use embassy_sync::waitqueue::AtomicWaker;
4 12
5use crate::pac;
6use crate::rcc::{self, RccPeripheral};
7// TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. 13// TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs.
8// Those MCUs have a different HSEM implementation (Secure semaphore lock support, 14// Those MCUs have a different HSEM implementation (Secure semaphore lock support,
9// Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), 15// Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute),
10// which is not yet supported by this code. 16// which is not yet supported by this code.
11use crate::Peri; 17use crate::Peri;
18use crate::rcc::{self, RccPeripheral};
19use crate::{interrupt, pac};
12 20
13/// HSEM error. 21/// HSEM error.
14#[derive(Debug)] 22#[derive(Debug)]
@@ -41,63 +49,153 @@ pub enum CoreId {
41 Core1 = 0x8, 49 Core1 = 0x8,
42} 50}
43 51
44/// Get the current core id 52impl CoreId {
45/// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core. 53 /// Get the current core id
46#[inline(always)] 54 /// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core.
47pub fn get_current_coreid() -> CoreId { 55 pub fn current() -> Self {
48 let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; 56 let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() };
49 match (cpuid & 0x000000F0) >> 4 { 57 match (cpuid & 0x000000F0) >> 4 {
50 #[cfg(any(stm32wb, stm32wl))] 58 #[cfg(any(stm32wb, stm32wl))]
51 0x0 => CoreId::Core1, 59 0x0 => CoreId::Core1,
52 60
53 #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))] 61 #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))]
54 0x4 => CoreId::Core0, 62 0x4 => CoreId::Core0,
55 63
56 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] 64 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))]
57 0x4 => CoreId::Core1, 65 0x4 => CoreId::Core1,
58 66
59 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] 67 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))]
60 0x7 => CoreId::Core0, 68 0x7 => CoreId::Core0,
61 _ => panic!("Unknown Cortex-M core"), 69 _ => panic!("Unknown Cortex-M core"),
70 }
71 }
72
73 /// Translates the core ID to an index into the interrupt registers.
74 pub fn to_index(&self) -> usize {
75 match &self {
76 CoreId::Core0 => 0,
77 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))]
78 CoreId::Core1 => 1,
79 }
62 } 80 }
63} 81}
64 82
65/// Translates the core ID to an index into the interrupt registers. 83#[cfg(not(all(stm32wb, feature = "low-power")))]
66#[inline(always)] 84const PUB_CHANNELS: usize = 6;
67fn core_id_to_index(core: CoreId) -> usize { 85
68 match core { 86#[cfg(all(stm32wb, feature = "low-power"))]
69 CoreId::Core0 => 0, 87const PUB_CHANNELS: usize = 4;
70 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))] 88
71 CoreId::Core1 => 1, 89/// TX interrupt handler.
90pub struct HardwareSemaphoreInterruptHandler<T: Instance> {
91 _phantom: PhantomData<T>,
92}
93
94impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for HardwareSemaphoreInterruptHandler<T> {
95 unsafe fn on_interrupt() {
96 let core_id = CoreId::current();
97
98 for number in 0..5 {
99 if T::regs().isr(core_id.to_index()).read().isf(number as usize) {
100 T::regs()
101 .icr(core_id.to_index())
102 .write(|w| w.set_isc(number as usize, true));
103
104 T::regs()
105 .ier(core_id.to_index())
106 .modify(|w| w.set_ise(number as usize, false));
107
108 T::state().waker_for(number).wake();
109 }
110 }
72 } 111 }
73} 112}
74 113
75/// HSEM driver 114/// Hardware semaphore mutex
76pub struct HardwareSemaphore<'d, T: Instance> { 115pub struct HardwareSemaphoreMutex<'a, T: Instance> {
77 _peri: Peri<'d, T>, 116 index: u8,
117 process_id: u8,
118 _lifetime: PhantomData<&'a mut T>,
78} 119}
79 120
80impl<'d, T: Instance> HardwareSemaphore<'d, T> { 121impl<'a, T: Instance> Drop for HardwareSemaphoreMutex<'a, T> {
81 /// Creates a new HardwareSemaphore instance. 122 fn drop(&mut self) {
82 pub fn new(peripheral: Peri<'d, T>) -> Self { 123 HardwareSemaphoreChannel::<'a, T> {
83 rcc::enable_and_reset::<T>(); 124 index: self.index,
125 _lifetime: PhantomData,
126 }
127 .unlock(self.process_id);
128 }
129}
130
131/// Hardware semaphore channel
132pub struct HardwareSemaphoreChannel<'a, T: Instance> {
133 index: u8,
134 _lifetime: PhantomData<&'a mut T>,
135}
136
137impl<'a, T: Instance> HardwareSemaphoreChannel<'a, T> {
138 pub(crate) const fn new(number: u8) -> Self {
139 core::assert!(number > 0 && number <= 6);
140
141 Self {
142 index: number - 1,
143 _lifetime: PhantomData,
144 }
145 }
146
147 /// Locks the semaphore.
148 /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to
149 /// check if the lock has been successful, carried out from the HSEM_Rx register.
150 pub async fn lock(&mut self, process_id: u8) -> HardwareSemaphoreMutex<'a, T> {
151 let _scoped_block_stop = T::RCC_INFO.block_stop();
152 let core_id = CoreId::current();
153
154 poll_fn(|cx| {
155 T::state().waker_for(self.index).register(cx.waker());
156
157 compiler_fence(Ordering::SeqCst);
158
159 T::regs()
160 .ier(core_id.to_index())
161 .modify(|w| w.set_ise(self.index as usize, true));
84 162
85 HardwareSemaphore { _peri: peripheral } 163 match self.try_lock(process_id) {
164 Some(mutex) => Poll::Ready(mutex),
165 None => Poll::Pending,
166 }
167 })
168 .await
169 }
170
171 /// Try to lock the semaphor
172 /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to
173 /// check if the lock has been successful, carried out from the HSEM_Rx register.
174 pub fn try_lock(&mut self, process_id: u8) -> Option<HardwareSemaphoreMutex<'a, T>> {
175 if self.two_step_lock(process_id).is_ok() {
176 Some(HardwareSemaphoreMutex {
177 index: self.index,
178 process_id: process_id,
179 _lifetime: PhantomData,
180 })
181 } else {
182 None
183 }
86 } 184 }
87 185
88 /// Locks the semaphore. 186 /// Locks the semaphore.
89 /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to 187 /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to
90 /// check if the lock has been successful, carried out from the HSEM_Rx register. 188 /// check if the lock has been successful, carried out from the HSEM_Rx register.
91 pub fn two_step_lock(&mut self, sem_id: u8, process_id: u8) -> Result<(), HsemError> { 189 pub fn two_step_lock(&mut self, process_id: u8) -> Result<(), HsemError> {
92 T::regs().r(sem_id as usize).write(|w| { 190 T::regs().r(self.index as usize).write(|w| {
93 w.set_procid(process_id); 191 w.set_procid(process_id);
94 w.set_coreid(get_current_coreid() as u8); 192 w.set_coreid(CoreId::current() as u8);
95 w.set_lock(true); 193 w.set_lock(true);
96 }); 194 });
97 let reg = T::regs().r(sem_id as usize).read(); 195 let reg = T::regs().r(self.index as usize).read();
98 match ( 196 match (
99 reg.lock(), 197 reg.lock(),
100 reg.coreid() == get_current_coreid() as u8, 198 reg.coreid() == CoreId::current() as u8,
101 reg.procid() == process_id, 199 reg.procid() == process_id,
102 ) { 200 ) {
103 (true, true, true) => Ok(()), 201 (true, true, true) => Ok(()),
@@ -108,9 +206,9 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> {
108 /// Locks the semaphore. 206 /// Locks the semaphore.
109 /// The 1-step procedure consists in a read to lock and check the semaphore in a single step, 207 /// The 1-step procedure consists in a read to lock and check the semaphore in a single step,
110 /// carried out from the HSEM_RLRx register. 208 /// carried out from the HSEM_RLRx register.
111 pub fn one_step_lock(&mut self, sem_id: u8) -> Result<(), HsemError> { 209 pub fn one_step_lock(&mut self) -> Result<(), HsemError> {
112 let reg = T::regs().rlr(sem_id as usize).read(); 210 let reg = T::regs().rlr(self.index as usize).read();
113 match (reg.lock(), reg.coreid() == get_current_coreid() as u8, reg.procid()) { 211 match (reg.lock(), reg.coreid() == CoreId::current() as u8, reg.procid()) {
114 (false, true, 0) => Ok(()), 212 (false, true, 0) => Ok(()),
115 _ => Err(HsemError::LockFailed), 213 _ => Err(HsemError::LockFailed),
116 } 214 }
@@ -119,14 +217,60 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> {
119 /// Unlocks the semaphore. 217 /// Unlocks the semaphore.
120 /// Unlocking a semaphore is a protected process, to prevent accidental clearing by a AHB bus 218 /// Unlocking a semaphore is a protected process, to prevent accidental clearing by a AHB bus
121 /// core ID or by a process not having the semaphore lock right. 219 /// core ID or by a process not having the semaphore lock right.
122 pub fn unlock(&mut self, sem_id: u8, process_id: u8) { 220 pub fn unlock(&mut self, process_id: u8) {
123 T::regs().r(sem_id as usize).write(|w| { 221 T::regs().r(self.index as usize).write(|w| {
124 w.set_procid(process_id); 222 w.set_procid(process_id);
125 w.set_coreid(get_current_coreid() as u8); 223 w.set_coreid(CoreId::current() as u8);
126 w.set_lock(false); 224 w.set_lock(false);
127 }); 225 });
128 } 226 }
129 227
228 /// Return the channel number
229 pub const fn channel(&self) -> u8 {
230 self.index + 1
231 }
232}
233
234/// HSEM driver
235pub struct HardwareSemaphore<T: Instance> {
236 _type: PhantomData<T>,
237}
238
239impl<T: Instance> HardwareSemaphore<T> {
240 /// Creates a new HardwareSemaphore instance.
241 pub fn new<'d>(
242 _peripheral: Peri<'d, T>,
243 _irq: impl interrupt::typelevel::Binding<T::Interrupt, HardwareSemaphoreInterruptHandler<T>> + 'd,
244 ) -> Self {
245 rcc::enable_and_reset_without_stop::<T>();
246
247 HardwareSemaphore { _type: PhantomData }
248 }
249
250 /// Get a single channel, and keep the global struct
251 pub const fn channel_for<'a>(&'a mut self, number: u8) -> HardwareSemaphoreChannel<'a, T> {
252 #[cfg(all(stm32wb, feature = "low-power"))]
253 core::assert!(number != 3 && number != 4);
254
255 HardwareSemaphoreChannel::new(number)
256 }
257
258 /// Split the global struct into channels
259 ///
260 /// If using low-power mode, channels 3 and 4 will not be returned
261 pub const fn split<'a>(self) -> [HardwareSemaphoreChannel<'a, T>; PUB_CHANNELS] {
262 [
263 HardwareSemaphoreChannel::new(1),
264 HardwareSemaphoreChannel::new(2),
265 #[cfg(not(all(stm32wb, feature = "low-power")))]
266 HardwareSemaphoreChannel::new(3),
267 #[cfg(not(all(stm32wb, feature = "low-power")))]
268 HardwareSemaphoreChannel::new(4),
269 HardwareSemaphoreChannel::new(5),
270 HardwareSemaphoreChannel::new(6),
271 ]
272 }
273
130 /// Unlocks all semaphores. 274 /// Unlocks all semaphores.
131 /// All semaphores locked by a COREID can be unlocked at once by using the HSEM_CR 275 /// All semaphores locked by a COREID can be unlocked at once by using the HSEM_CR
132 /// register. Write COREID and correct KEY value in HSEM_CR. All locked semaphores with a 276 /// register. Write COREID and correct KEY value in HSEM_CR. All locked semaphores with a
@@ -138,11 +282,6 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> {
138 }); 282 });
139 } 283 }
140 284
141 /// Checks if the semaphore is locked.
142 pub fn is_semaphore_locked(&self, sem_id: u8) -> bool {
143 T::regs().r(sem_id as usize).read().lock()
144 }
145
146 /// Sets the clear (unlock) key 285 /// Sets the clear (unlock) key
147 pub fn set_clear_key(&mut self, key: u16) { 286 pub fn set_clear_key(&mut self, key: u16) {
148 T::regs().keyr().modify(|w| w.set_key(key)); 287 T::regs().keyr().modify(|w| w.set_key(key));
@@ -152,38 +291,61 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> {
152 pub fn get_clear_key(&mut self) -> u16 { 291 pub fn get_clear_key(&mut self) -> u16 {
153 T::regs().keyr().read().key() 292 T::regs().keyr().read().key()
154 } 293 }
294}
155 295
156 /// Sets the interrupt enable bit for the semaphore. 296#[cfg(all(stm32wb, feature = "low-power"))]
157 pub fn enable_interrupt(&mut self, core_id: CoreId, sem_x: usize, enable: bool) { 297pub(crate) fn init_hsem(cs: CriticalSection) {
158 T::regs() 298 rcc::enable_and_reset_with_cs::<crate::peripherals::HSEM>(cs);
159 .ier(core_id_to_index(core_id)) 299
160 .modify(|w| w.set_ise(sem_x, enable)); 300 unsafe {
301 crate::rcc::REFCOUNT_STOP1 = 0;
302 crate::rcc::REFCOUNT_STOP2 = 0;
161 } 303 }
304}
162 305
163 /// Gets the interrupt flag for the semaphore. 306struct State {
164 pub fn is_interrupt_active(&mut self, core_id: CoreId, sem_x: usize) -> bool { 307 wakers: [AtomicWaker; 6],
165 T::regs().isr(core_id_to_index(core_id)).read().isf(sem_x) 308}
309
310impl State {
311 const fn new() -> Self {
312 Self {
313 wakers: [const { AtomicWaker::new() }; 6],
314 }
166 } 315 }
167 316
168 /// Clears the interrupt flag for the semaphore. 317 const fn waker_for(&self, index: u8) -> &AtomicWaker {
169 pub fn clear_interrupt(&mut self, core_id: CoreId, sem_x: usize) { 318 &self.wakers[index as usize]
170 T::regs()
171 .icr(core_id_to_index(core_id))
172 .write(|w| w.set_isc(sem_x, false));
173 } 319 }
174} 320}
175 321
176trait SealedInstance { 322trait SealedInstance {
177 fn regs() -> pac::hsem::Hsem; 323 fn regs() -> pac::hsem::Hsem;
324 fn state() -> &'static State;
178} 325}
179 326
180/// HSEM instance trait. 327/// HSEM instance trait.
181#[allow(private_bounds)] 328#[allow(private_bounds)]
182pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {} 329pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {
330 /// Interrupt for this peripheral.
331 type Interrupt: interrupt::typelevel::Interrupt;
332}
183 333
184impl SealedInstance for crate::peripherals::HSEM { 334impl SealedInstance for crate::peripherals::HSEM {
185 fn regs() -> crate::pac::hsem::Hsem { 335 fn regs() -> crate::pac::hsem::Hsem {
186 crate::pac::HSEM 336 crate::pac::HSEM
187 } 337 }
338
339 fn state() -> &'static State {
340 static STATE: State = State::new();
341 &STATE
342 }
188} 343}
189impl Instance for crate::peripherals::HSEM {} 344
345foreach_interrupt!(
346 ($inst:ident, hsem, $block:ident, $signal_name:ident, $irq:ident) => {
347 impl Instance for crate::peripherals::$inst {
348 type Interrupt = crate::interrupt::typelevel::$irq;
349 }
350 };
351);