diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index a7137d132..5ef2366d9 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -72,7 +72,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f1297385e91f061fcb6134bb25f51e12d8abff93" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-34c0188a682b32c32ff147d377e0629b1ebe8318" } vcell = "0.1.3" nb = "1.0.0" @@ -97,7 +97,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f1297385e91f061fcb6134bb25f51e12d8abff93", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-34c0188a682b32c32ff147d377e0629b1ebe8318", default-features = false, features = ["metadata"]} [features] default = ["rt"] diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs new file mode 100644 index 000000000..b77a3415b --- /dev/null +++ b/embassy-stm32/src/hsem/mod.rs @@ -0,0 +1,182 @@ +//! Hardware Semaphore (HSEM) + +// TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. +// Those MCUs have a different HSEM implementation (Secure semaphore lock support, +// Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), +// which is not yet supported by this code. +use embassy_hal_internal::{into_ref, PeripheralRef}; + +use crate::rcc::RccPeripheral; +use crate::{pac, Peripheral}; + +/// HSEM error. +#[derive(Debug)] +pub enum HsemError { + /// Locking the semaphore failed. + LockFailed, +} + +/// CPU core. +/// The enum values are identical to the bus master IDs / core Ids defined for each +/// chip family (i.e. stm32h747 see rm0399 table 95) +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] +#[repr(u8)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum CoreId { + #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] + /// Cortex-M7, core 1. + Core0 = 0x3, + + #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] + /// Cortex-M4, core 2. + Core1 = 0x1, + + #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))] + /// Cortex-M4, core 1 + Core0 = 0x4, + + #[cfg(any(stm32wb, stm32wl))] + /// Cortex-M0+, core 2. + Core1 = 0x8, +} + +/// Get the current core id +/// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core. +#[inline(always)] +pub fn get_current_coreid() -> CoreId { + let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; + match cpuid & 0x000000F0 { + #[cfg(any(stm32wb, stm32wl))] + 0x0 => CoreId::Core1, + + #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))] + 0x4 => CoreId::Core0, + + #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] + 0x4 => CoreId::Core1, + + #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] + 0x7 => CoreId::Core0, + _ => panic!("Unknown Cortex-M core"), + } +} + +/// Translates the core ID to an index into the interrupt registers. +#[inline(always)] +fn core_id_to_index(core: CoreId) -> usize { + match core { + CoreId::Core0 => 0, + #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))] + CoreId::Core1 => 1, + } +} + +/// HSEM driver +pub struct HardwareSemaphore<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> HardwareSemaphore<'d, T> { + /// Creates a new HardwareSemaphore instance. + pub fn new(peripheral: impl Peripheral

+ 'd) -> Self { + into_ref!(peripheral); + HardwareSemaphore { _peri: peripheral } + } + + /// Locks the semaphore. + /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to + /// check if the lock has been successful, carried out from the HSEM_Rx register. + pub fn two_step_lock(&mut self, sem_id: u8, process_id: u8) -> Result<(), HsemError> { + T::regs().r(sem_id as usize).write(|w| { + w.set_procid(process_id); + w.set_coreid(get_current_coreid() as u8); + w.set_lock(true); + }); + let reg = T::regs().r(sem_id as usize).read(); + match ( + reg.lock(), + reg.coreid() == get_current_coreid() as u8, + reg.procid() == process_id, + ) { + (true, true, true) => Ok(()), + _ => Err(HsemError::LockFailed), + } + } + + /// Locks the semaphore. + /// The 1-step procedure consists in a read to lock and check the semaphore in a single step, + /// carried out from the HSEM_RLRx register. + pub fn one_step_lock(&mut self, sem_id: u8) -> Result<(), HsemError> { + let reg = T::regs().rlr(sem_id as usize).read(); + match (reg.lock(), reg.coreid() == get_current_coreid() as u8, reg.procid()) { + (false, true, 0) => Ok(()), + _ => Err(HsemError::LockFailed), + } + } + + /// Unlocks the semaphore. + /// Unlocking a semaphore is a protected process, to prevent accidental clearing by a AHB bus + /// core ID or by a process not having the semaphore lock right. + pub fn unlock(&mut self, sem_id: u8, process_id: u8) { + T::regs().r(sem_id as usize).write(|w| { + w.set_procid(process_id); + w.set_coreid(get_current_coreid() as u8); + w.set_lock(false); + }); + } + + /// Unlocks all semaphores. + /// All semaphores locked by a COREID can be unlocked at once by using the HSEM_CR + /// register. Write COREID and correct KEY value in HSEM_CR. All locked semaphores with a + /// matching COREID are unlocked, and may generate an interrupt when enabled. + pub fn unlock_all(&mut self, key: u16, core_id: u8) { + T::regs().cr().write(|w| { + w.set_key(key); + w.set_coreid(core_id); + }); + } + + /// Checks if the semaphore is locked. + pub fn is_semaphore_locked(&self, sem_id: u8) -> bool { + T::regs().r(sem_id as usize).read().lock() + } + + /// Sets the clear (unlock) key + pub fn set_clear_key(&mut self, key: u16) { + T::regs().keyr().modify(|w| w.set_key(key)); + } + + /// Gets the clear (unlock) key + pub fn get_clear_key(&mut self) -> u16 { + T::regs().keyr().read().key() + } + + /// Sets the interrupt enable bit for the semaphore. + pub fn enable_interrupt(&mut self, core_id: CoreId, sem_x: usize, enable: bool) { + T::regs() + .ier(core_id_to_index(core_id)) + .modify(|w| w.set_ise(sem_x, enable)); + } + + /// Clears the interrupt flag for the semaphore. + pub fn clear_interrupt(&mut self, core_id: CoreId, sem_x: usize) { + T::regs() + .icr(core_id_to_index(core_id)) + .write(|w| w.set_isc(sem_x, false)); + } +} + +trait SealedInstance { + fn regs() -> pac::hsem::Hsem; +} + +/// HSEM instance trait. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + RccPeripheral + Send + 'static {} + +impl SealedInstance for crate::peripherals::HSEM { + fn regs() -> crate::pac::hsem::Hsem { + crate::pac::HSEM + } +} +impl Instance for crate::peripherals::HSEM {} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index b4b9d5d12..81ee60c1c 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -79,6 +79,8 @@ pub mod fmc; pub mod hash; #[cfg(hrtim)] pub mod hrtim; +#[cfg(hsem)] +pub mod hsem; #[cfg(i2c)] pub mod i2c; #[cfg(all(spi_v1, rcc_f4))]