diff options
| author | Dion Dokter <[email protected]> | 2024-07-08 16:54:06 +0200 |
|---|---|---|
| committer | Dion Dokter <[email protected]> | 2024-07-08 16:54:06 +0200 |
| commit | 203297b56912c05d2dd6a009ffeb433fb2ffbea6 (patch) | |
| tree | fa1708215925ad861d68dc8454069c11ae5c861c /embassy-stm32/src | |
| parent | b1ea90a87e5ce6b16bbc155ad30d6d3473a998bb (diff) | |
Make clocks repr C.
Add shared data.
Modify freq functions to use shared data.
Modify examples to use new init/
Diffstat (limited to 'embassy-stm32/src')
| -rw-r--r-- | embassy-stm32/src/lib.rs | 116 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/mod.rs | 27 | ||||
| -rw-r--r-- | embassy-stm32/src/rtc/mod.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/time.rs | 36 |
4 files changed, 180 insertions, 1 deletions
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 95f59360a..8f001f03b 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -273,7 +273,123 @@ impl Default for Config { | |||
| 273 | /// This returns the peripheral singletons that can be used for creating drivers. | 273 | /// This returns the peripheral singletons that can be used for creating drivers. |
| 274 | /// | 274 | /// |
| 275 | /// This should only be called once at startup, otherwise it panics. | 275 | /// This should only be called once at startup, otherwise it panics. |
| 276 | #[cfg(not(feature = "_dual-core"))] | ||
| 276 | pub fn init(config: Config) -> Peripherals { | 277 | pub fn init(config: Config) -> Peripherals { |
| 278 | init_hw(config) | ||
| 279 | } | ||
| 280 | |||
| 281 | #[cfg(feature = "_dual-core")] | ||
| 282 | mod dual_core { | ||
| 283 | use rcc::Clocks; | ||
| 284 | |||
| 285 | use super::*; | ||
| 286 | use core::{ | ||
| 287 | mem::MaybeUninit, | ||
| 288 | sync::atomic::{AtomicUsize, Ordering}, | ||
| 289 | }; | ||
| 290 | |||
| 291 | /// Object containing data that embassy needs to share between cores. | ||
| 292 | /// | ||
| 293 | /// It cannot be initialized by the user. The intended use is: | ||
| 294 | /// | ||
| 295 | /// ``` | ||
| 296 | /// #[link_section = ".ram_d3"] | ||
| 297 | /// static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | ||
| 298 | /// | ||
| 299 | /// init_secondary(&SHARED_DATA); | ||
| 300 | /// ``` | ||
| 301 | /// | ||
| 302 | /// This static must be placed in the same position for both cores. How and where this is done is left to the user. | ||
| 303 | pub struct SharedData { | ||
| 304 | init_flag: AtomicUsize, | ||
| 305 | clocks: MaybeUninit<Clocks>, | ||
| 306 | } | ||
| 307 | |||
| 308 | const INIT_DONE_FLAG: usize = 0xca11ab1e; | ||
| 309 | |||
| 310 | /// Initialize the `embassy-stm32` HAL with the provided configuration. | ||
| 311 | /// This function does the actual initialization of the hardware, in contrast to [init_secondary] or [try_init_secondary]. | ||
| 312 | /// Any core can do the init, but it's important only one core does it. | ||
| 313 | /// | ||
| 314 | /// This returns the peripheral singletons that can be used for creating drivers. | ||
| 315 | /// | ||
| 316 | /// This should only be called once at startup, otherwise it panics. | ||
| 317 | /// | ||
| 318 | /// The `shared_data` is used to coordinate the init with the second core. Read the [SharedData] docs | ||
| 319 | /// for more information on its requirements. | ||
| 320 | pub fn init_primary(config: Config, shared_data: &'static MaybeUninit<SharedData>) -> Peripherals { | ||
| 321 | let shared_data = unsafe { shared_data.assume_init_ref() }; | ||
| 322 | |||
| 323 | rcc::set_freqs_ptr(&shared_data.clocks); | ||
| 324 | let p = init_hw(config); | ||
| 325 | |||
| 326 | shared_data.init_flag.store(INIT_DONE_FLAG, Ordering::SeqCst); | ||
| 327 | |||
| 328 | p | ||
| 329 | } | ||
| 330 | |||
| 331 | /// Try to initialize the `embassy-stm32` HAL based on the init done by the other core using [init_primary]. | ||
| 332 | /// | ||
| 333 | /// This returns the peripheral singletons that can be used for creating drivers if the other core is done with its init. | ||
| 334 | /// If the other core is not done yet, this will return `None`. | ||
| 335 | /// | ||
| 336 | /// This should only be called once at startup, otherwise it may panic. | ||
| 337 | /// | ||
| 338 | /// The `shared_data` is used to coordinate the init with the second core. Read the [SharedData] docs | ||
| 339 | /// for more information on its requirements. | ||
| 340 | pub fn try_init_secondary(shared_data: &'static MaybeUninit<SharedData>) -> Option<Peripherals> { | ||
| 341 | let shared_data = unsafe { shared_data.assume_init_ref() }; | ||
| 342 | |||
| 343 | if shared_data | ||
| 344 | .init_flag | ||
| 345 | .compare_exchange(INIT_DONE_FLAG, 0, Ordering::SeqCst, Ordering::SeqCst) | ||
| 346 | .is_err() | ||
| 347 | { | ||
| 348 | return None; | ||
| 349 | } | ||
| 350 | |||
| 351 | Some(init_secondary_hw(shared_data)) | ||
| 352 | } | ||
| 353 | |||
| 354 | /// Initialize the `embassy-stm32` HAL based on the init done by the other core using [init_primary]. | ||
| 355 | /// | ||
| 356 | /// This returns the peripheral singletons that can be used for creating drivers when the other core is done with its init. | ||
| 357 | /// If the other core is not done yet, this will spinloop wait on it. | ||
| 358 | /// | ||
| 359 | /// This should only be called once at startup, otherwise it may panic. | ||
| 360 | /// | ||
| 361 | /// The `shared_data` is used to coordinate the init with the second core. Read the [SharedData] docs | ||
| 362 | /// for more information on its requirements. | ||
| 363 | pub fn init_secondary(shared_data: &'static MaybeUninit<SharedData>) -> Peripherals { | ||
| 364 | let shared_data = unsafe { shared_data.assume_init_ref() }; | ||
| 365 | |||
| 366 | while shared_data | ||
| 367 | .init_flag | ||
| 368 | .compare_exchange(INIT_DONE_FLAG, 0, Ordering::SeqCst, Ordering::SeqCst) | ||
| 369 | .is_err() | ||
| 370 | {} | ||
| 371 | |||
| 372 | init_secondary_hw(shared_data) | ||
| 373 | } | ||
| 374 | |||
| 375 | fn init_secondary_hw(shared_data: &'static SharedData) -> Peripherals { | ||
| 376 | rcc::set_freqs_ptr(&shared_data.clocks); | ||
| 377 | |||
| 378 | // We use different timers on the different cores, so we have to still initialize one here | ||
| 379 | critical_section::with(|cs| { | ||
| 380 | // must be after rcc init | ||
| 381 | #[cfg(feature = "_time-driver")] | ||
| 382 | time_driver::init(cs); | ||
| 383 | }); | ||
| 384 | |||
| 385 | Peripherals::take() | ||
| 386 | } | ||
| 387 | } | ||
| 388 | |||
| 389 | #[cfg(feature = "_dual-core")] | ||
| 390 | pub use dual_core::*; | ||
| 391 | |||
| 392 | fn init_hw(config: Config) -> Peripherals { | ||
| 277 | critical_section::with(|cs| { | 393 | critical_section::with(|cs| { |
| 278 | let p = Peripherals::take_with_cs(cs); | 394 | let p = Peripherals::take_with_cs(cs); |
| 279 | 395 | ||
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 024c63cf5..587231b0c 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -48,11 +48,22 @@ pub(crate) static mut REFCOUNT_STOP1: u32 = 0; | |||
| 48 | /// May be read without a critical section | 48 | /// May be read without a critical section |
| 49 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; | 49 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; |
| 50 | 50 | ||
| 51 | #[cfg(not(feature = "_dual-core"))] | ||
| 51 | /// Frozen clock frequencies | 52 | /// Frozen clock frequencies |
| 52 | /// | 53 | /// |
| 53 | /// The existence of this value indicates that the clock configuration can no longer be changed | 54 | /// The existence of this value indicates that the clock configuration can no longer be changed |
| 54 | static mut CLOCK_FREQS: MaybeUninit<Clocks> = MaybeUninit::uninit(); | 55 | static mut CLOCK_FREQS: MaybeUninit<Clocks> = MaybeUninit::uninit(); |
| 55 | 56 | ||
| 57 | #[cfg(feature = "_dual-core")] | ||
| 58 | static CLOCK_FREQS_PTR: core::sync::atomic::AtomicPtr<MaybeUninit<Clocks>> = | ||
| 59 | core::sync::atomic::AtomicPtr::new(core::ptr::null_mut()); | ||
| 60 | |||
| 61 | #[cfg(feature = "_dual-core")] | ||
| 62 | pub(crate) fn set_freqs_ptr(freqs: &'static MaybeUninit<Clocks>) { | ||
| 63 | CLOCK_FREQS_PTR.store(freqs as *const _ as *mut _, core::sync::atomic::Ordering::SeqCst); | ||
| 64 | } | ||
| 65 | |||
| 66 | #[cfg(not(feature = "_dual-core"))] | ||
| 56 | /// Sets the clock frequencies | 67 | /// Sets the clock frequencies |
| 57 | /// | 68 | /// |
| 58 | /// Safety: Sets a mutable global. | 69 | /// Safety: Sets a mutable global. |
| @@ -61,11 +72,27 @@ pub(crate) unsafe fn set_freqs(freqs: Clocks) { | |||
| 61 | CLOCK_FREQS = MaybeUninit::new(freqs); | 72 | CLOCK_FREQS = MaybeUninit::new(freqs); |
| 62 | } | 73 | } |
| 63 | 74 | ||
| 75 | #[cfg(feature = "_dual-core")] | ||
| 76 | /// Sets the clock frequencies | ||
| 77 | /// | ||
| 78 | /// Safety: Sets a mutable global. | ||
| 79 | pub(crate) unsafe fn set_freqs(freqs: Clocks) { | ||
| 80 | debug!("rcc: {:?}", freqs); | ||
| 81 | CLOCK_FREQS_PTR.load(core::sync::atomic::Ordering::SeqCst).write(MaybeUninit::new(freqs)); | ||
| 82 | } | ||
| 83 | |||
| 84 | #[cfg(not(feature = "_dual-core"))] | ||
| 64 | /// Safety: Reads a mutable global. | 85 | /// Safety: Reads a mutable global. |
| 65 | pub(crate) unsafe fn get_freqs() -> &'static Clocks { | 86 | pub(crate) unsafe fn get_freqs() -> &'static Clocks { |
| 66 | CLOCK_FREQS.assume_init_ref() | 87 | CLOCK_FREQS.assume_init_ref() |
| 67 | } | 88 | } |
| 68 | 89 | ||
| 90 | #[cfg(feature = "_dual-core")] | ||
| 91 | /// Safety: Reads a mutable global. | ||
| 92 | pub(crate) unsafe fn get_freqs() -> &'static Clocks { | ||
| 93 | unwrap!(CLOCK_FREQS_PTR.load(core::sync::atomic::Ordering::SeqCst).as_ref()).assume_init_ref() | ||
| 94 | } | ||
| 95 | |||
| 69 | pub(crate) trait SealedRccPeripheral { | 96 | pub(crate) trait SealedRccPeripheral { |
| 70 | fn frequency() -> Hertz; | 97 | fn frequency() -> Hertz; |
| 71 | const RCC_INFO: RccInfo; | 98 | const RCC_INFO: RccInfo; |
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index a7f70b153..fe57cfe66 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs | |||
| @@ -168,7 +168,7 @@ impl Rtc { | |||
| 168 | 168 | ||
| 169 | fn frequency() -> Hertz { | 169 | fn frequency() -> Hertz { |
| 170 | let freqs = unsafe { crate::rcc::get_freqs() }; | 170 | let freqs = unsafe { crate::rcc::get_freqs() }; |
| 171 | freqs.rtc.unwrap() | 171 | freqs.rtc.to_hertz().unwrap() |
| 172 | } | 172 | } |
| 173 | 173 | ||
| 174 | /// Acquire a [`RtcTimeProvider`] instance. | 174 | /// Acquire a [`RtcTimeProvider`] instance. |
diff --git a/embassy-stm32/src/time.rs b/embassy-stm32/src/time.rs index 17690aefc..d7337191d 100644 --- a/embassy-stm32/src/time.rs +++ b/embassy-stm32/src/time.rs | |||
| @@ -87,3 +87,39 @@ impl Div<Hertz> for Hertz { | |||
| 87 | self.0 / rhs.0 | 87 | self.0 / rhs.0 |
| 88 | } | 88 | } |
| 89 | } | 89 | } |
| 90 | |||
| 91 | #[repr(C)] | ||
| 92 | #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug, Default)] | ||
| 93 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 94 | /// A variant on [Hertz] that acts as an `Option<Hertz>` that is smaller and repr C. | ||
| 95 | /// | ||
| 96 | /// An `Option<Hertz>` can be `.into()`'d into this type and back. | ||
| 97 | /// The only restriction is that that [Hertz] cannot have the value 0 since that's | ||
| 98 | /// seen as the `None` variant. | ||
| 99 | pub struct MaybeHertz(u32); | ||
| 100 | |||
| 101 | impl MaybeHertz { | ||
| 102 | /// Same as calling the `.into()` function, but without type inference. | ||
| 103 | pub fn to_hertz(self) -> Option<Hertz> { | ||
| 104 | self.into() | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | impl From<Option<Hertz>> for MaybeHertz { | ||
| 109 | fn from(value: Option<Hertz>) -> Self { | ||
| 110 | match value { | ||
| 111 | Some(Hertz(0)) => panic!("Hertz cannot be 0"), | ||
| 112 | Some(Hertz(val)) => Self(val), | ||
| 113 | None => Self(0), | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | impl From<MaybeHertz> for Option<Hertz> { | ||
| 119 | fn from(value: MaybeHertz) -> Self { | ||
| 120 | match value { | ||
| 121 | MaybeHertz(0) => None, | ||
| 122 | MaybeHertz(val) => Some(Hertz(val)), | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
