aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2025-11-19 13:20:17 -0600
committerxoviat <[email protected]>2025-11-19 13:20:17 -0600
commit141685f6f7ff5e78fb08d3aefb67be5d16485ceb (patch)
treec1bcc573f050b5353db6f4561f2957cac288f3f4
parent0ee68ed648ef96f001247409a9bacd2dc5cfbb30 (diff)
hsem: add hardware test and rework
-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
4 files changed, 245 insertions, 65 deletions
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index b6caf8f65..597cbb192 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7 7
8## Unreleased - ReleaseDate 8## Unreleased - ReleaseDate
9 9
10- change: rework hsem and add HIL test for some chips.
10- 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)) 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))
11- feat: allow embassy_executor::main for low power 12- feat: allow embassy_executor::main for low power
12- feat: Add waveform methods to ComplementaryPwm 13- 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}