diff options
Diffstat (limited to 'embassy-stm32/src/hsem/mod.rs')
| -rw-r--r-- | embassy-stm32/src/hsem/mod.rs | 288 |
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 | ||
| 3 | use core::future::poll_fn; | ||
| 4 | use core::marker::PhantomData; | ||
| 5 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 6 | use core::task::Poll; | ||
| 7 | |||
| 8 | #[cfg(all(stm32wb, feature = "low-power"))] | ||
| 9 | use critical_section::CriticalSection; | ||
| 3 | use embassy_hal_internal::PeripheralType; | 10 | use embassy_hal_internal::PeripheralType; |
| 11 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 4 | 12 | ||
| 5 | use crate::pac; | ||
| 6 | use 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. |
| 11 | use crate::Peri; | 17 | use crate::Peri; |
| 18 | use crate::rcc::{self, RccPeripheral}; | ||
| 19 | use 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 | 52 | impl 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. |
| 47 | pub 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)] | 84 | const PUB_CHANNELS: usize = 6; |
| 67 | fn core_id_to_index(core: CoreId) -> usize { | 85 | |
| 68 | match core { | 86 | #[cfg(all(stm32wb, feature = "low-power"))] |
| 69 | CoreId::Core0 => 0, | 87 | const PUB_CHANNELS: usize = 4; |
| 70 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))] | 88 | |
| 71 | CoreId::Core1 => 1, | 89 | /// TX interrupt handler. |
| 90 | pub struct HardwareSemaphoreInterruptHandler<T: Instance> { | ||
| 91 | _phantom: PhantomData<T>, | ||
| 92 | } | ||
| 93 | |||
| 94 | impl<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 |
| 76 | pub struct HardwareSemaphore<'d, T: Instance> { | 115 | pub 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 | ||
| 80 | impl<'d, T: Instance> HardwareSemaphore<'d, T> { | 121 | impl<'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 | ||
| 132 | pub struct HardwareSemaphoreChannel<'a, T: Instance> { | ||
| 133 | index: u8, | ||
| 134 | _lifetime: PhantomData<&'a mut T>, | ||
| 135 | } | ||
| 136 | |||
| 137 | impl<'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 | ||
| 235 | pub struct HardwareSemaphore<T: Instance> { | ||
| 236 | _type: PhantomData<T>, | ||
| 237 | } | ||
| 238 | |||
| 239 | impl<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) { | 297 | pub(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. | 306 | struct 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 | |||
| 310 | impl 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 | ||
| 176 | trait SealedInstance { | 322 | trait 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)] |
| 182 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {} | 329 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static { |
| 330 | /// Interrupt for this peripheral. | ||
| 331 | type Interrupt: interrupt::typelevel::Interrupt; | ||
| 332 | } | ||
| 183 | 333 | ||
| 184 | impl SealedInstance for crate::peripherals::HSEM { | 334 | impl 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 | } |
| 189 | impl Instance for crate::peripherals::HSEM {} | 344 | |
| 345 | foreach_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 | ); | ||
