aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/pages/bootloader.adoc6
-rw-r--r--embassy-boot/CHANGELOG.md3
-rw-r--r--embassy-boot/Cargo.toml14
-rw-r--r--embassy-boot/src/boot_loader.rs12
-rw-r--r--embassy-boot/src/lib.rs4
-rw-r--r--embassy-stm32/CHANGELOG.md1
-rw-r--r--embassy-stm32/src/hsem/mod.rs252
-rw-r--r--tests/stm32/Cargo.toml10
-rw-r--r--tests/stm32/src/bin/hsem.rs47
9 files changed, 275 insertions, 74 deletions
diff --git a/docs/pages/bootloader.adoc b/docs/pages/bootloader.adoc
index b0f0331aa..c010b0622 100644
--- a/docs/pages/bootloader.adoc
+++ b/docs/pages/bootloader.adoc
@@ -43,14 +43,14 @@ Partition Size~dfu~= Partition Size~active~+ Page Size~active~
43+ 43+
44All values are specified in bytes. 44All values are specified in bytes.
45 45
46* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a magic field is written to instruct the bootloader that the partitions should be swapped. This partition must be able to store a magic field as well as the partition swap progress. The partition size given by: 46* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a magic field is written to instruct the bootloader that the partitions should be swapped. This partition must be able to store a magic field as well as the partition swap progress. The partition size is given by:
47+ 47+
48Partition Size~state~ = Write Size~state~ + (2 × Partition Size~active~ / Page Size~active~) 48Partition Size~state~ = (2 × Write Size~state~) + (4 × Write Size~state~ × Partition Size~active~ / Page Size~active~)
49+ 49+
50All values are specified in bytes. 50All values are specified in bytes.
51 51
52The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes. 52The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes.
53The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined. 53The BOOTLOADER_STATE partition must be big enough to store two words, plus four words per page in the ACTIVE partition.
54 54
55The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice. 55The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice.
56 56
diff --git a/embassy-boot/CHANGELOG.md b/embassy-boot/CHANGELOG.md
index 8d6395357..1d41043cb 100644
--- a/embassy-boot/CHANGELOG.md
+++ b/embassy-boot/CHANGELOG.md
@@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- Fixed documentation and assertion of STATE partition size requirements
12- Added documentation for package features
13
11## 0.6.1 - 2025-08-26 14## 0.6.1 - 2025-08-26
12 15
13- First release with changelog. 16- First release with changelog.
diff --git a/embassy-boot/Cargo.toml b/embassy-boot/Cargo.toml
index 8c5c1f633..754c6e5f1 100644
--- a/embassy-boot/Cargo.toml
+++ b/embassy-boot/Cargo.toml
@@ -26,6 +26,7 @@ features = ["defmt"]
26[dependencies] 26[dependencies]
27defmt = { version = "1.0.1", optional = true } 27defmt = { version = "1.0.1", optional = true }
28digest = "0.10" 28digest = "0.10"
29document-features = "0.2.7"
29log = { version = "0.4", optional = true } 30log = { version = "0.4", optional = true }
30ed25519-dalek = { version = "2", default-features = false, features = ["digest"], optional = true } 31ed25519-dalek = { version = "2", default-features = false, features = ["digest"], optional = true }
31embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } 32embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" }
@@ -45,11 +46,22 @@ critical-section = { version = "1.1.1", features = ["std"] }
45ed25519-dalek = { version = "2", default-features = false, features = ["std", "rand_core", "digest"] } 46ed25519-dalek = { version = "2", default-features = false, features = ["std", "rand_core", "digest"] }
46 47
47[features] 48[features]
49## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging
48defmt = ["dep:defmt"] 50defmt = ["dep:defmt"]
51## Use log for logging
49log = ["dep:log"] 52log = ["dep:log"]
53
54## Enable for devices that set erased flash bytes to `0x00` instead of the usual `0xFF`
55flash-erase-zero = []
56
57#! ## Firmware Signing
58#! Enable one of these features to allow verification of DFU signatures with
59#! `FirmwareUpdater::verify_and_mark_updated`.
60
61## Use the `ed25519-dalek` package to verify DFU signatures.
50ed25519-dalek = ["dep:ed25519-dalek", "_verify"] 62ed25519-dalek = ["dep:ed25519-dalek", "_verify"]
63## Use the `salty` package to verify DFU signatures.
51ed25519-salty = ["dep:salty", "_verify"] 64ed25519-salty = ["dep:salty", "_verify"]
52flash-erase-zero = []
53 65
54#Internal features 66#Internal features
55_verify = [] 67_verify = []
diff --git a/embassy-boot/src/boot_loader.rs b/embassy-boot/src/boot_loader.rs
index c38940d6e..a3a307051 100644
--- a/embassy-boot/src/boot_loader.rs
+++ b/embassy-boot/src/boot_loader.rs
@@ -135,10 +135,12 @@ pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> {
135 dfu: DFU, 135 dfu: DFU,
136 /// The state partition has the following format: 136 /// The state partition has the following format:
137 /// All ranges are in multiples of WRITE_SIZE bytes. 137 /// All ranges are in multiples of WRITE_SIZE bytes.
138 /// | Range | Description | 138 /// N = Active partition size divided by WRITE_SIZE.
139 /// | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | 139 /// | Range | Description |
140 /// | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. | 140 /// | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
141 /// | 2..2 + N | Progress index used while swapping or reverting 141 /// | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. |
142 /// | 2..(2 + 2N) | Progress index used while swapping |
143 /// | (2 + 2N)..(2 + 4N) | Progress index used while reverting
142 state: STATE, 144 state: STATE,
143} 145}
144 146
@@ -429,7 +431,7 @@ fn assert_partitions<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(
429 assert_eq!(dfu.capacity() as u32 % page_size, 0); 431 assert_eq!(dfu.capacity() as u32 % page_size, 0);
430 // DFU partition has to be bigger than ACTIVE partition to handle swap algorithm 432 // DFU partition has to be bigger than ACTIVE partition to handle swap algorithm
431 assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size); 433 assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size);
432 assert!(2 + 2 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32); 434 assert!(2 + 4 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32);
433} 435}
434 436
435#[cfg(test)] 437#[cfg(test)]
diff --git a/embassy-boot/src/lib.rs b/embassy-boot/src/lib.rs
index 7dc811f66..3e61d6036 100644
--- a/embassy-boot/src/lib.rs
+++ b/embassy-boot/src/lib.rs
@@ -3,6 +3,10 @@
3#![allow(unsafe_op_in_unsafe_fn)] 3#![allow(unsafe_op_in_unsafe_fn)]
4#![warn(missing_docs)] 4#![warn(missing_docs)]
5#![doc = include_str!("../README.md")] 5#![doc = include_str!("../README.md")]
6
7//! ## Feature flags
8#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
9
6mod fmt; 10mod fmt;
7 11
8mod boot_loader; 12mod boot_loader;
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index 7534f5b8e..9479ea7b1 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8## Unreleased - ReleaseDate 8## Unreleased - ReleaseDate
9 9
10- feat: add poll_for methods to exti 10- feat: add poll_for methods to exti
11- change: rework hsem and add HIL test for some chips.
11- change: stm32/eth: ethernet no longer has a hard dependency on station management, and station management can be used independently ([#4871](https://github.com/embassy-rs/embassy/pull/4871)) 12- change: stm32/eth: ethernet no longer has a hard dependency on station management, and station management can be used independently ([#4871](https://github.com/embassy-rs/embassy/pull/4871))
12- feat: allow embassy_executor::main for low power 13- feat: allow embassy_executor::main for low power
13- feat: Add waveform methods to ComplementaryPwm 14- feat: Add waveform methods to ComplementaryPwm
diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs
index 4d3a5d68d..a6b910a53 100644
--- a/embassy-stm32/src/hsem/mod.rs
+++ b/embassy-stm32/src/hsem/mod.rs
@@ -1,14 +1,20 @@
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
3use embassy_hal_internal::PeripheralType; 8use embassy_hal_internal::PeripheralType;
9use embassy_sync::waitqueue::AtomicWaker;
4 10
5// TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. 11// TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs.
6// Those MCUs have a different HSEM implementation (Secure semaphore lock support, 12// Those MCUs have a different HSEM implementation (Secure semaphore lock support,
7// Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), 13// Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute),
8// which is not yet supported by this code. 14// which is not yet supported by this code.
9use crate::Peri; 15use crate::Peri;
10use crate::pac;
11use crate::rcc::{self, RccPeripheral}; 16use crate::rcc::{self, RccPeripheral};
17use crate::{interrupt, pac};
12 18
13/// HSEM error. 19/// HSEM error.
14#[derive(Debug)] 20#[derive(Debug)]
@@ -41,63 +47,136 @@ pub enum CoreId {
41 Core1 = 0x8, 47 Core1 = 0x8,
42} 48}
43 49
44/// Get the current core id 50impl CoreId {
45/// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core. 51 /// Get the current core id
46#[inline(always)] 52 /// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core.
47pub fn get_current_coreid() -> CoreId { 53 pub fn current() -> Self {
48 let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; 54 let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() };
49 match (cpuid & 0x000000F0) >> 4 { 55 match (cpuid & 0x000000F0) >> 4 {
50 #[cfg(any(stm32wb, stm32wl))] 56 #[cfg(any(stm32wb, stm32wl))]
51 0x0 => CoreId::Core1, 57 0x0 => CoreId::Core1,
58
59 #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))]
60 0x4 => CoreId::Core0,
52 61
53 #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))] 62 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))]
54 0x4 => CoreId::Core0, 63 0x4 => CoreId::Core1,
55 64
56 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] 65 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))]
57 0x4 => CoreId::Core1, 66 0x7 => CoreId::Core0,
67 _ => panic!("Unknown Cortex-M core"),
68 }
69 }
58 70
59 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] 71 /// Translates the core ID to an index into the interrupt registers.
60 0x7 => CoreId::Core0, 72 pub fn to_index(&self) -> usize {
61 _ => panic!("Unknown Cortex-M core"), 73 match &self {
74 CoreId::Core0 => 0,
75 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))]
76 CoreId::Core1 => 1,
77 }
62 } 78 }
63} 79}
64 80
65/// Translates the core ID to an index into the interrupt registers. 81/// TX interrupt handler.
66#[inline(always)] 82pub struct HardwareSemaphoreInterruptHandler<T: Instance> {
67fn core_id_to_index(core: CoreId) -> usize { 83 _phantom: PhantomData<T>,
68 match core { 84}
69 CoreId::Core0 => 0, 85
70 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))] 86impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for HardwareSemaphoreInterruptHandler<T> {
71 CoreId::Core1 => 1, 87 unsafe fn on_interrupt() {
88 let core_id = CoreId::current();
89
90 for number in 0..5 {
91 if T::regs().isr(core_id.to_index()).read().isf(number as usize) {
92 T::regs()
93 .icr(core_id.to_index())
94 .write(|w| w.set_isc(number as usize, true));
95
96 T::regs()
97 .ier(core_id.to_index())
98 .modify(|w| w.set_ise(number as usize, false));
99
100 T::state().waker_for(number).wake();
101 }
102 }
72 } 103 }
73} 104}
74 105
75/// HSEM driver 106/// Hardware semaphore mutex
76pub struct HardwareSemaphore<'d, T: Instance> { 107pub struct HardwareSemaphoreMutex<'a, T: Instance> {
77 _peri: Peri<'d, T>, 108 index: u8,
109 process_id: u8,
110 _lifetime: PhantomData<&'a mut T>,
78} 111}
79 112
80impl<'d, T: Instance> HardwareSemaphore<'d, T> { 113impl<'a, T: Instance> Drop for HardwareSemaphoreMutex<'a, T> {
81 /// Creates a new HardwareSemaphore instance. 114 fn drop(&mut self) {
82 pub fn new(peripheral: Peri<'d, T>) -> Self { 115 HardwareSemaphoreChannel::<'a, T> {
83 rcc::enable_and_reset::<T>(); 116 index: self.index,
117 _lifetime: PhantomData,
118 }
119 .unlock(self.process_id);
120 }
121}
122
123/// Hardware semaphore channel
124pub struct HardwareSemaphoreChannel<'a, T: Instance> {
125 index: u8,
126 _lifetime: PhantomData<&'a mut T>,
127}
128
129impl<'a, T: Instance> HardwareSemaphoreChannel<'a, T> {
130 pub(self) const fn new(number: u8) -> Self {
131 core::assert!(number > 0 && number <= 6);
132
133 Self {
134 index: number - 1,
135 _lifetime: PhantomData,
136 }
137 }
138
139 /// Locks the semaphore.
140 /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to
141 /// check if the lock has been successful, carried out from the HSEM_Rx register.
142 pub async fn lock(&mut self, process_id: u8) -> HardwareSemaphoreMutex<'a, T> {
143 let core_id = CoreId::current();
144
145 poll_fn(|cx| {
146 T::state().waker_for(self.index).register(cx.waker());
147
148 compiler_fence(Ordering::SeqCst);
84 149
85 HardwareSemaphore { _peri: peripheral } 150 T::regs()
151 .ier(core_id.to_index())
152 .modify(|w| w.set_ise(self.index as usize, true));
153
154 if self.two_step_lock(process_id).is_ok() {
155 Poll::Ready(HardwareSemaphoreMutex {
156 index: self.index,
157 process_id: process_id,
158 _lifetime: PhantomData,
159 })
160 } else {
161 Poll::Pending
162 }
163 })
164 .await
86 } 165 }
87 166
88 /// Locks the semaphore. 167 /// Locks the semaphore.
89 /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to 168 /// 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. 169 /// 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> { 170 pub fn two_step_lock(&mut self, process_id: u8) -> Result<(), HsemError> {
92 T::regs().r(sem_id as usize).write(|w| { 171 T::regs().r(self.index as usize).write(|w| {
93 w.set_procid(process_id); 172 w.set_procid(process_id);
94 w.set_coreid(get_current_coreid() as u8); 173 w.set_coreid(CoreId::current() as u8);
95 w.set_lock(true); 174 w.set_lock(true);
96 }); 175 });
97 let reg = T::regs().r(sem_id as usize).read(); 176 let reg = T::regs().r(self.index as usize).read();
98 match ( 177 match (
99 reg.lock(), 178 reg.lock(),
100 reg.coreid() == get_current_coreid() as u8, 179 reg.coreid() == CoreId::current() as u8,
101 reg.procid() == process_id, 180 reg.procid() == process_id,
102 ) { 181 ) {
103 (true, true, true) => Ok(()), 182 (true, true, true) => Ok(()),
@@ -108,9 +187,9 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> {
108 /// Locks the semaphore. 187 /// Locks the semaphore.
109 /// The 1-step procedure consists in a read to lock and check the semaphore in a single step, 188 /// 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. 189 /// carried out from the HSEM_RLRx register.
111 pub fn one_step_lock(&mut self, sem_id: u8) -> Result<(), HsemError> { 190 pub fn one_step_lock(&mut self) -> Result<(), HsemError> {
112 let reg = T::regs().rlr(sem_id as usize).read(); 191 let reg = T::regs().rlr(self.index as usize).read();
113 match (reg.lock(), reg.coreid() == get_current_coreid() as u8, reg.procid()) { 192 match (reg.lock(), reg.coreid() == CoreId::current() as u8, reg.procid()) {
114 (false, true, 0) => Ok(()), 193 (false, true, 0) => Ok(()),
115 _ => Err(HsemError::LockFailed), 194 _ => Err(HsemError::LockFailed),
116 } 195 }
@@ -119,14 +198,53 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> {
119 /// Unlocks the semaphore. 198 /// Unlocks the semaphore.
120 /// Unlocking a semaphore is a protected process, to prevent accidental clearing by a AHB bus 199 /// 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. 200 /// core ID or by a process not having the semaphore lock right.
122 pub fn unlock(&mut self, sem_id: u8, process_id: u8) { 201 pub fn unlock(&mut self, process_id: u8) {
123 T::regs().r(sem_id as usize).write(|w| { 202 T::regs().r(self.index as usize).write(|w| {
124 w.set_procid(process_id); 203 w.set_procid(process_id);
125 w.set_coreid(get_current_coreid() as u8); 204 w.set_coreid(CoreId::current() as u8);
126 w.set_lock(false); 205 w.set_lock(false);
127 }); 206 });
128 } 207 }
129 208
209 /// Checks if the semaphore is locked.
210 pub fn is_semaphore_locked(&self) -> bool {
211 T::regs().r(self.index as usize).read().lock()
212 }
213}
214
215/// HSEM driver
216pub struct HardwareSemaphore<T: Instance> {
217 _type: PhantomData<T>,
218}
219
220impl<T: Instance> HardwareSemaphore<T> {
221 /// Creates a new HardwareSemaphore instance.
222 pub fn new<'d>(
223 _peripheral: Peri<'d, T>,
224 _irq: impl interrupt::typelevel::Binding<T::Interrupt, HardwareSemaphoreInterruptHandler<T>> + 'd,
225 ) -> Self {
226 rcc::enable_and_reset::<T>();
227
228 HardwareSemaphore { _type: PhantomData }
229 }
230
231 /// Get a single channel, and keep the global struct
232 pub const fn channel_for<'a>(&'a mut self, number: u8) -> HardwareSemaphoreChannel<'a, T> {
233 HardwareSemaphoreChannel::new(number)
234 }
235
236 /// Split the global struct into channels
237 pub const fn split<'a>(self) -> [HardwareSemaphoreChannel<'a, T>; 6] {
238 [
239 HardwareSemaphoreChannel::new(1),
240 HardwareSemaphoreChannel::new(2),
241 HardwareSemaphoreChannel::new(3),
242 HardwareSemaphoreChannel::new(4),
243 HardwareSemaphoreChannel::new(5),
244 HardwareSemaphoreChannel::new(6),
245 ]
246 }
247
130 /// Unlocks all semaphores. 248 /// Unlocks all semaphores.
131 /// All semaphores locked by a COREID can be unlocked at once by using the HSEM_CR 249 /// 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 250 /// register. Write COREID and correct KEY value in HSEM_CR. All locked semaphores with a
@@ -138,11 +256,6 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> {
138 }); 256 });
139 } 257 }
140 258
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 259 /// Sets the clear (unlock) key
147 pub fn set_clear_key(&mut self, key: u16) { 260 pub fn set_clear_key(&mut self, key: u16) {
148 T::regs().keyr().modify(|w| w.set_key(key)); 261 T::regs().keyr().modify(|w| w.set_key(key));
@@ -152,38 +265,51 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> {
152 pub fn get_clear_key(&mut self) -> u16 { 265 pub fn get_clear_key(&mut self) -> u16 {
153 T::regs().keyr().read().key() 266 T::regs().keyr().read().key()
154 } 267 }
268}
155 269
156 /// Sets the interrupt enable bit for the semaphore. 270struct State {
157 pub fn enable_interrupt(&mut self, core_id: CoreId, sem_x: usize, enable: bool) { 271 wakers: [AtomicWaker; 6],
158 T::regs() 272}
159 .ier(core_id_to_index(core_id))
160 .modify(|w| w.set_ise(sem_x, enable));
161 }
162 273
163 /// Gets the interrupt flag for the semaphore. 274impl State {
164 pub fn is_interrupt_active(&mut self, core_id: CoreId, sem_x: usize) -> bool { 275 const fn new() -> Self {
165 T::regs().isr(core_id_to_index(core_id)).read().isf(sem_x) 276 Self {
277 wakers: [const { AtomicWaker::new() }; 6],
278 }
166 } 279 }
167 280
168 /// Clears the interrupt flag for the semaphore. 281 const fn waker_for(&self, number: u8) -> &AtomicWaker {
169 pub fn clear_interrupt(&mut self, core_id: CoreId, sem_x: usize) { 282 &self.wakers[number as usize]
170 T::regs()
171 .icr(core_id_to_index(core_id))
172 .write(|w| w.set_isc(sem_x, false));
173 } 283 }
174} 284}
175 285
176trait SealedInstance { 286trait SealedInstance {
177 fn regs() -> pac::hsem::Hsem; 287 fn regs() -> pac::hsem::Hsem;
288 fn state() -> &'static State;
178} 289}
179 290
180/// HSEM instance trait. 291/// HSEM instance trait.
181#[allow(private_bounds)] 292#[allow(private_bounds)]
182pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {} 293pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {
294 /// Interrupt for this peripheral.
295 type Interrupt: interrupt::typelevel::Interrupt;
296}
183 297
184impl SealedInstance for crate::peripherals::HSEM { 298impl SealedInstance for crate::peripherals::HSEM {
185 fn regs() -> crate::pac::hsem::Hsem { 299 fn regs() -> crate::pac::hsem::Hsem {
186 crate::pac::HSEM 300 crate::pac::HSEM
187 } 301 }
302
303 fn state() -> &'static State {
304 static STATE: State = State::new();
305 &STATE
306 }
188} 307}
189impl Instance for crate::peripherals::HSEM {} 308
309foreach_interrupt!(
310 ($inst:ident, hsem, $block:ident, $signal_name:ident, $irq:ident) => {
311 impl Instance for crate::peripherals::$inst {
312 type Interrupt = crate::interrupt::typelevel::$irq;
313 }
314 };
315);
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml
index b92b47be2..f5684d4df 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -31,9 +31,9 @@ stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng", "dual-
31stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash", "dual-bank"] 31stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash", "dual-bank"]
32stm32u585ai = ["embassy-stm32/stm32u585ai", "spi-v345", "chrono", "rng", "hash", "cordic"] 32stm32u585ai = ["embassy-stm32/stm32u585ai", "spi-v345", "chrono", "rng", "hash", "cordic"]
33stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "spi-v345", "chrono", "rng", "hash"] # FIXME: cordic test cause it crash 33stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "spi-v345", "chrono", "rng", "hash"] # FIXME: cordic test cause it crash
34stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"] 34stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng", "hsem"]
35stm32wba52cg = ["embassy-stm32/stm32wba52cg", "spi-v345", "chrono", "rng", "hash"] 35stm32wba52cg = ["embassy-stm32/stm32wba52cg", "spi-v345", "chrono", "rng", "hash"]
36stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"] 36stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono", "hsem"]
37stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"] 37stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"]
38stm32h503rb = ["embassy-stm32/stm32h503rb", "spi-v345", "rng", "stop"] 38stm32h503rb = ["embassy-stm32/stm32h503rb", "spi-v345", "rng", "stop"]
39stm32h7s3l8 = ["embassy-stm32/stm32h7s3l8", "spi-v345", "rng", "cordic", "hash-v34"] # TODO: fdcan crashes, cryp dma hangs. 39stm32h7s3l8 = ["embassy-stm32/stm32h7s3l8", "spi-v345", "rng", "cordic", "hash-v34"] # TODO: fdcan crashes, cryp dma hangs.
@@ -58,6 +58,7 @@ not-gpdma = []
58dac = [] 58dac = []
59ucpd = [] 59ucpd = []
60cordic = ["dep:num-traits"] 60cordic = ["dep:num-traits"]
61hsem = []
61dual-bank = ["embassy-stm32/dual-bank"] 62dual-bank = ["embassy-stm32/dual-bank"]
62single-bank = ["embassy-stm32/single-bank"] 63single-bank = ["embassy-stm32/single-bank"]
63eeprom = [] 64eeprom = []
@@ -224,6 +225,11 @@ name = "wpan_mac"
224path = "src/bin/wpan_mac.rs" 225path = "src/bin/wpan_mac.rs"
225required-features = [ "mac",] 226required-features = [ "mac",]
226 227
228[[bin]]
229name = "hsem"
230path = "src/bin/hsem.rs"
231required-features = [ "hsem",]
232
227# END TESTS 233# END TESTS
228 234
229[profile.dev] 235[profile.dev]
diff --git a/tests/stm32/src/bin/hsem.rs b/tests/stm32/src/bin/hsem.rs
new file mode 100644
index 000000000..19648997c
--- /dev/null
+++ b/tests/stm32/src/bin/hsem.rs
@@ -0,0 +1,47 @@
1// required-features: hsem
2#![no_std]
3#![no_main]
4
5#[path = "../common.rs"]
6mod common;
7
8use common::*;
9use embassy_executor::Spawner;
10use embassy_stm32::bind_interrupts;
11use embassy_stm32::hsem::{HardwareSemaphore, HardwareSemaphoreInterruptHandler};
12use embassy_stm32::peripherals::HSEM;
13
14bind_interrupts!(struct Irqs{
15 HSEM => HardwareSemaphoreInterruptHandler<HSEM>;
16});
17
18#[embassy_executor::main]
19async fn main(_spawner: Spawner) {
20 let p: embassy_stm32::Peripherals = init();
21
22 let hsem = HardwareSemaphore::new(p.HSEM, Irqs);
23
24 // if hsem.channel_for(SemaphoreNumber::Channel5).is_semaphore_locked() {
25 // defmt::panic!("Semaphore 5 already locked!")
26 // }
27 //
28 // hsem.channel_for(SemaphoreNumber::Channel5).one_step_lock().unwrap();
29 // hsem.channel_for(SemaphoreNumber::Channel1).two_step_lock(0).unwrap();
30 //
31 // hsem.channel_for(SemaphoreNumber::Channel5).unlock(0);
32
33 let [_channel1, _channel2, _channel3, _channel4, mut channel5, _channel6] = hsem.split();
34
35 info!("Locking channel 5");
36
37 let mutex = channel5.lock(0).await;
38
39 info!("Locked channel 5");
40
41 drop(mutex);
42
43 info!("Unlocked channel 5");
44
45 info!("Test OK");
46 cortex_m::asm::bkpt();
47}