embassy_stm32/hsem/
mod.rs

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