embassy_stm32/hsem/
mod.rs

1//! Hardware Semaphore (HSEM)
2
3use embassy_hal_internal::PeripheralType;
4
5use crate::pac;
6use crate::rcc::{self, RccPeripheral};
7// 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,
9// Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute),
10// which is not yet supported by this code.
11use crate::Peri;
12
13/// HSEM error.
14#[derive(Debug)]
15pub enum HsemError {
16    /// Locking the semaphore failed.
17    LockFailed,
18}
19
20/// CPU core.
21/// The enum values are identical to the bus master IDs / core Ids defined for each
22/// chip family (i.e. stm32h747 see rm0399 table 95)
23#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
24#[repr(u8)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub enum CoreId {
27    #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))]
28    /// Cortex-M7, core 1.
29    Core0 = 0x3,
30
31    #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))]
32    /// Cortex-M4, core 2.
33    Core1 = 0x1,
34
35    #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))]
36    /// Cortex-M4, core 1
37    Core0 = 0x4,
38
39    #[cfg(any(stm32wb, stm32wl))]
40    /// Cortex-M0+, core 2.
41    Core1 = 0x8,
42}
43
44/// Get the current core id
45/// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core.
46#[inline(always)]
47pub fn get_current_coreid() -> CoreId {
48    let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() };
49    match (cpuid & 0x000000F0) >> 4 {
50        #[cfg(any(stm32wb, stm32wl))]
51        0x0 => CoreId::Core1,
52
53        #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))]
54        0x4 => CoreId::Core0,
55
56        #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))]
57        0x4 => CoreId::Core1,
58
59        #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))]
60        0x7 => CoreId::Core0,
61        _ => panic!("Unknown Cortex-M core"),
62    }
63}
64
65/// Translates the core ID to an index into the interrupt registers.
66#[inline(always)]
67fn core_id_to_index(core: CoreId) -> usize {
68    match core {
69        CoreId::Core0 => 0,
70        #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))]
71        CoreId::Core1 => 1,
72    }
73}
74
75/// HSEM driver
76pub struct HardwareSemaphore<'d, T: Instance> {
77    _peri: Peri<'d, T>,
78}
79
80impl<'d, T: Instance> HardwareSemaphore<'d, T> {
81    /// Creates a new HardwareSemaphore instance.
82    pub fn new(peripheral: Peri<'d, T>) -> Self {
83        rcc::enable_and_reset::<T>();
84
85        HardwareSemaphore { _peri: peripheral }
86    }
87
88    /// Locks the semaphore.
89    /// 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.
91    pub fn two_step_lock(&mut self, sem_id: u8, process_id: u8) -> Result<(), HsemError> {
92        T::regs().r(sem_id as usize).write(|w| {
93            w.set_procid(process_id);
94            w.set_coreid(get_current_coreid() as u8);
95            w.set_lock(true);
96        });
97        let reg = T::regs().r(sem_id as usize).read();
98        match (
99            reg.lock(),
100            reg.coreid() == get_current_coreid() as u8,
101            reg.procid() == process_id,
102        ) {
103            (true, true, true) => Ok(()),
104            _ => Err(HsemError::LockFailed),
105        }
106    }
107
108    /// Locks the semaphore.
109    /// 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.
111    pub fn one_step_lock(&mut self, sem_id: u8) -> Result<(), HsemError> {
112        let reg = T::regs().rlr(sem_id as usize).read();
113        match (reg.lock(), reg.coreid() == get_current_coreid() as u8, reg.procid()) {
114            (false, true, 0) => Ok(()),
115            _ => Err(HsemError::LockFailed),
116        }
117    }
118
119    /// Unlocks the semaphore.
120    /// 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.
122    pub fn unlock(&mut self, sem_id: u8, process_id: u8) {
123        T::regs().r(sem_id as usize).write(|w| {
124            w.set_procid(process_id);
125            w.set_coreid(get_current_coreid() as u8);
126            w.set_lock(false);
127        });
128    }
129
130    /// Unlocks all semaphores.
131    /// 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
133    /// matching COREID are unlocked, and may generate an interrupt when enabled.
134    pub fn unlock_all(&mut self, key: u16, core_id: u8) {
135        T::regs().cr().write(|w| {
136            w.set_key(key);
137            w.set_coreid(core_id);
138        });
139    }
140
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
147    pub fn set_clear_key(&mut self, key: u16) {
148        T::regs().keyr().modify(|w| w.set_key(key));
149    }
150
151    /// Gets the clear (unlock) key
152    pub fn get_clear_key(&mut self) -> u16 {
153        T::regs().keyr().read().key()
154    }
155
156    /// Sets the interrupt enable bit for the semaphore.
157    pub fn enable_interrupt(&mut self, core_id: CoreId, sem_x: usize, enable: bool) {
158        T::regs()
159            .ier(core_id_to_index(core_id))
160            .modify(|w| w.set_ise(sem_x, enable));
161    }
162
163    /// Gets the interrupt flag for the semaphore.
164    pub fn is_interrupt_active(&mut self, core_id: CoreId, sem_x: usize) -> bool {
165        T::regs().isr(core_id_to_index(core_id)).read().isf(sem_x)
166    }
167
168    /// Clears the interrupt flag for the semaphore.
169    pub fn clear_interrupt(&mut self, core_id: CoreId, sem_x: usize) {
170        T::regs()
171            .icr(core_id_to_index(core_id))
172            .write(|w| w.set_isc(sem_x, false));
173    }
174}
175
176trait SealedInstance {
177    fn regs() -> pac::hsem::Hsem;
178}
179
180/// HSEM instance trait.
181#[allow(private_bounds)]
182pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {}
183
184impl SealedInstance for crate::peripherals::HSEM {
185    fn regs() -> crate::pac::hsem::Hsem {
186        crate::pac::HSEM
187    }
188}
189impl Instance for crate::peripherals::HSEM {}