From 2b7e76efe9916170cba69da964d53c19a246ae45 Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Sat, 17 Aug 2024 00:26:33 +0300 Subject: [PATCH] Fix dma nvic issues on dual core lines This commit addresses #3256 by disabling dma NVIC interrupt enablement at startup. Instead, per-channel NVIC interrupt enablement is now done with the rest of the dma channel configuration. This ensures that each core will only handle the interrupts of the DMA channels that it uses. --- embassy-stm32/build.rs | 70 +++++++++++++++------- embassy-stm32/src/dma/dma_bdma.rs | 9 +++ embassy-stm32/src/dma/gpdma.rs | 9 +++ embassy-stm32/src/lib.rs | 20 ++++++- embassy-stm32/src/rcc/bd.rs | 2 + embassy-stm32/src/rcc/c0.rs | 1 + embassy-stm32/src/rcc/f013.rs | 1 + embassy-stm32/src/rcc/f247.rs | 2 + embassy-stm32/src/rcc/g0.rs | 2 + embassy-stm32/src/rcc/g4.rs | 2 + embassy-stm32/src/rcc/h.rs | 3 +- embassy-stm32/src/rcc/l.rs | 1 + embassy-stm32/src/rcc/u5.rs | 1 + embassy-stm32/src/rcc/wba.rs | 1 + examples/boot/application/stm32wl/memory.x | 6 +- examples/stm32wl/memory.x | 6 +- 16 files changed, 108 insertions(+), 28 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index d8a7ea0e60..1984a14208 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1494,6 +1494,36 @@ fn main() { .flat_map(|p| &p.registers) .any(|p| p.kind == "dmamux"); + let mut dma_irqs: BTreeMap<&str, Vec> = BTreeMap::new(); + + for p in METADATA.peripherals { + if let Some(r) = &p.registers { + if r.kind == "dma" || r.kind == "bdma" || r.kind == "gpdma" || r.kind == "lpdma" { + for irq in p.interrupts { + let ch_name = format!("{}_{}", p.name, irq.signal); + let ch = METADATA.dma_channels.iter().find(|c| c.name == ch_name).unwrap(); + + // Some H7 chips have BDMA1 hardcoded for DFSDM, ie no DMAMUX. It's unsupported, skip it. + if has_dmamux && ch.dmamux.is_none() { + continue; + } + + dma_irqs.entry(irq.interrupt).or_default().push(ch_name); + } + } + } + } + + #[cfg(feature = "_dual-core")] + let mut dma_ch_to_irq: BTreeMap<&str, Vec> = BTreeMap::new(); + + #[cfg(feature = "_dual-core")] + for (irq, channels) in &dma_irqs { + for channel in channels { + dma_ch_to_irq.entry(channel).or_default().push(irq.to_string()); + } + } + for (ch_idx, ch) in METADATA.dma_channels.iter().enumerate() { // Some H7 chips have BDMA1 hardcoded for DFSDM, ie no DMAMUX. It's unsupported, skip it. if has_dmamux && ch.dmamux.is_none() { @@ -1502,6 +1532,16 @@ fn main() { let name = format_ident!("{}", ch.name); let idx = ch_idx as u8; + #[cfg(feature = "_dual-core")] + let irq = { + let irq_name = if let Some(x) = &dma_ch_to_irq.get(ch.name) { + format_ident!("{}", x.get(0).unwrap()) + } else { + panic!("failed to find dma interrupt") + }; + quote!(crate::pac::Interrupt::#irq_name) + }; + g.extend(quote!(dma_channel_impl!(#name, #idx);)); let dma = format_ident!("{}", ch.dma); @@ -1532,6 +1572,7 @@ fn main() { None => quote!(), }; + #[cfg(not(feature = "_dual-core"))] dmas.extend(quote! { crate::dma::ChannelInfo { dma: #dma_info, @@ -1539,31 +1580,20 @@ fn main() { #dmamux }, }); + #[cfg(feature = "_dual-core")] + dmas.extend(quote! { + crate::dma::ChannelInfo { + dma: #dma_info, + num: #ch_num, + irq: #irq, + #dmamux + }, + }); } // ======== // Generate DMA IRQs. - let mut dma_irqs: BTreeMap<&str, Vec> = BTreeMap::new(); - - for p in METADATA.peripherals { - if let Some(r) = &p.registers { - if r.kind == "dma" || r.kind == "bdma" || r.kind == "gpdma" { - for irq in p.interrupts { - let ch_name = format!("{}_{}", p.name, irq.signal); - let ch = METADATA.dma_channels.iter().find(|c| c.name == ch_name).unwrap(); - - // Some H7 chips have BDMA1 hardcoded for DFSDM, ie no DMAMUX. It's unsupported, skip it. - if has_dmamux && ch.dmamux.is_none() { - continue; - } - - dma_irqs.entry(irq.interrupt).or_default().push(ch_name); - } - } - } - } - let dma_irqs: TokenStream = dma_irqs .iter() .map(|(irq, channels)| { diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index 8a6aa53a0b..8e2964f94c 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs @@ -15,6 +15,8 @@ use crate::{interrupt, pac}; pub(crate) struct ChannelInfo { pub(crate) dma: DmaInfo, pub(crate) num: usize, + #[cfg(feature = "_dual-core")] + pub(crate) irq: pac::Interrupt, #[cfg(dmamux)] pub(crate) dmamux: super::DmamuxInfo, } @@ -259,10 +261,12 @@ pub(crate) unsafe fn init( foreach_interrupt! { ($peri:ident, dma, $block:ident, $signal_name:ident, $irq:ident) => { crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, dma_priority); + #[cfg(not(feature = "_dual-core"))] crate::interrupt::typelevel::$irq::enable(); }; ($peri:ident, bdma, $block:ident, $signal_name:ident, $irq:ident) => { crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, bdma_priority); + #[cfg(not(feature = "_dual-core"))] crate::interrupt::typelevel::$irq::enable(); }; } @@ -341,6 +345,11 @@ impl AnyChannel { options: TransferOptions, ) { let info = self.info(); + #[cfg(feature = "_dual-core")] + { + use embassy_hal_internal::interrupt::InterruptExt as _; + info.irq.enable(); + } #[cfg(dmamux)] super::dmamux::configure_dmamux(&info.dmamux, _request); diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 13d5d15be8..f9d66ca863 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -18,6 +18,8 @@ use crate::pac::gpdma::vals; pub(crate) struct ChannelInfo { pub(crate) dma: pac::gpdma::Gpdma, pub(crate) num: usize, + #[cfg(feature = "_dual-core")] + pub(crate) irq: pac::Interrupt, } /// GPDMA transfer options. @@ -57,6 +59,7 @@ pub(crate) unsafe fn init(cs: critical_section::CriticalSection, irq_priority: P foreach_interrupt! { ($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => { crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, irq_priority); + #[cfg(not(feature = "_dual-core"))] crate::interrupt::typelevel::$irq::enable(); }; } @@ -67,6 +70,12 @@ impl AnyChannel { /// Safety: Must be called with a matching set of parameters for a valid dma channel pub(crate) unsafe fn on_irq(&self) { let info = self.info(); + #[cfg(feature = "_dual-core")] + { + use embassy_hal_internal::interrupt::InterruptExt as _; + info.irq.enable(); + } + let state = &STATE[self.id as usize]; let ch = info.dma.ch(info.num); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 12ebbae2d5..98695e7381 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -197,6 +197,7 @@ pub use crate::pac::NVIC_PRIO_BITS; /// `embassy-stm32` global configuration. #[non_exhaustive] +#[derive(Clone, Copy)] pub struct Config { /// RCC config. pub rcc: rcc::Config, @@ -303,6 +304,7 @@ mod dual_core { pub struct SharedData { init_flag: AtomicUsize, clocks: UnsafeCell>, + config: UnsafeCell>, } unsafe impl Sync for SharedData {} @@ -325,6 +327,8 @@ mod dual_core { rcc::set_freqs_ptr(shared_data.clocks.get()); let p = init_hw(config); + unsafe { *shared_data.config.get() }.write(config); + shared_data.init_flag.store(INIT_DONE_FLAG, Ordering::SeqCst); p @@ -372,9 +376,23 @@ mod dual_core { fn init_secondary_hw(shared_data: &'static SharedData) -> Peripherals { rcc::set_freqs_ptr(shared_data.clocks.get()); + let config = unsafe { (*shared_data.config.get()).assume_init() }; + // We use different timers on the different cores, so we have to still initialize one here - #[cfg(feature = "_time-driver")] critical_section::with(|cs| { + unsafe { + dma::init( + cs, + #[cfg(bdma)] + config.bdma_interrupt_priority, + #[cfg(dma)] + config.dma_interrupt_priority, + #[cfg(gpdma)] + config.gpdma_interrupt_priority, + ) + } + + #[cfg(feature = "_time-driver")] // must be after rcc init time_driver::init(cs); }); diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs index 4e9c18594c..9ccca8a2aa 100644 --- a/embassy-stm32/src/rcc/bd.rs +++ b/embassy-stm32/src/rcc/bd.rs @@ -16,6 +16,7 @@ pub enum LseMode { Bypass, } +#[derive(Clone, Copy)] pub struct LseConfig { pub frequency: Hertz, pub mode: LseMode, @@ -80,6 +81,7 @@ fn bdcr() -> Reg { return crate::pac::RCC.csr1(); } +#[derive(Clone, Copy)] pub struct LsConfig { pub rtc: RtcClockSource, pub lsi: bool, diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index 5adf37941d..6712aedc4c 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs @@ -37,6 +37,7 @@ pub struct Hsi { /// Clocks configutation #[non_exhaustive] +#[derive(Clone, Copy)] pub struct Config { /// HSI Configuration pub hsi: Option, diff --git a/embassy-stm32/src/rcc/f013.rs b/embassy-stm32/src/rcc/f013.rs index 63dc27bdd1..60577b2134 100644 --- a/embassy-stm32/src/rcc/f013.rs +++ b/embassy-stm32/src/rcc/f013.rs @@ -76,6 +76,7 @@ pub enum HrtimClockSource { /// Clocks configutation #[non_exhaustive] +#[derive(Clone, Copy)] pub struct Config { pub hsi: bool, pub hse: Option, diff --git a/embassy-stm32/src/rcc/f247.rs b/embassy-stm32/src/rcc/f247.rs index 61f687d30d..58056301ae 100644 --- a/embassy-stm32/src/rcc/f247.rs +++ b/embassy-stm32/src/rcc/f247.rs @@ -63,6 +63,7 @@ pub struct Pll { /// Used to calculate flash waitstates. See /// RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock frequency #[cfg(stm32f2)] +#[derive(Clone, Copy)] pub enum VoltageScale { /// 2.7 to 3.6 V Range0, @@ -76,6 +77,7 @@ pub enum VoltageScale { /// Configuration of the core clocks #[non_exhaustive] +#[derive(Clone, Copy)] pub struct Config { pub hsi: bool, pub hse: Option, diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs index c2fa0ca39e..c53c83b0e9 100644 --- a/embassy-stm32/src/rcc/g0.rs +++ b/embassy-stm32/src/rcc/g0.rs @@ -33,6 +33,7 @@ pub struct Hse { /// Use this struct to configure the PLL source, input frequency, multiplication factor, and output /// dividers. Be sure to keep check the datasheet for your specific part for the appropriate /// frequency ranges for each of these settings. +#[derive(Clone, Copy)] pub struct Pll { /// PLL Source clock selection. pub source: PllSource, @@ -55,6 +56,7 @@ pub struct Pll { /// Clocks configutation #[non_exhaustive] +#[derive(Clone, Copy)] pub struct Config { /// HSI Enable pub hsi: bool, diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index c261c0feda..16561f9083 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -32,6 +32,7 @@ pub struct Hse { /// Use this struct to configure the PLL source, input frequency, multiplication factor, and output /// dividers. Be sure to keep check the datasheet for your specific part for the appropriate /// frequency ranges for each of these settings. +#[derive(Clone, Copy)] pub struct Pll { /// PLL Source clock selection. pub source: PllSource, @@ -54,6 +55,7 @@ pub struct Pll { /// Clocks configutation #[non_exhaustive] +#[derive(Clone, Copy)] pub struct Config { /// HSI Enable pub hsi: bool, diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index e3c7dd1584..376a0b454a 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs @@ -120,7 +120,7 @@ impl From for Timpre { /// Power supply configuration /// See RM0433 Rev 4 7.4 #[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468, pwr_h7rs))] -#[derive(PartialEq)] +#[derive(Clone, Copy, PartialEq)] pub enum SupplyConfig { /// Default power supply configuration. /// V CORE Power Domains are supplied from the LDO according to VOS. @@ -180,6 +180,7 @@ pub enum SMPSSupplyVoltage { /// Configuration of the core clocks #[non_exhaustive] +#[derive(Clone, Copy)] pub struct Config { pub hsi: Option, pub hse: Option, diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs index e9266c65b7..6120d33beb 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs @@ -30,6 +30,7 @@ pub struct Hse { } /// Clocks configuration +#[derive(Clone, Copy)] pub struct Config { // base clock sources pub msi: Option, diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index d6331f5125..28545ca51b 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -59,6 +59,7 @@ pub struct Pll { pub divr: Option, } +#[derive(Clone, Copy)] pub struct Config { // base clock sources pub msi: Option, diff --git a/embassy-stm32/src/rcc/wba.rs b/embassy-stm32/src/rcc/wba.rs index 8e1779d7c4..1fee648d41 100644 --- a/embassy-stm32/src/rcc/wba.rs +++ b/embassy-stm32/src/rcc/wba.rs @@ -15,6 +15,7 @@ pub struct Hse { } /// Clocks configuration +#[derive(Clone, Copy)] pub struct Config { // base clock sources pub hsi: bool, diff --git a/examples/boot/application/stm32wl/memory.x b/examples/boot/application/stm32wl/memory.x index 5af1723f5e..20109e37e1 100644 --- a/examples/boot/application/stm32wl/memory.x +++ b/examples/boot/application/stm32wl/memory.x @@ -5,8 +5,8 @@ MEMORY BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K FLASH : ORIGIN = 0x08008000, LENGTH = 64K DFU : ORIGIN = 0x08018000, LENGTH = 68K - SHARED_RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64 - RAM (rwx) : ORIGIN = 0x20000040, LENGTH = 32K - 64 + SHARED_RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128 + RAM (rwx) : ORIGIN = 0x20000080, LENGTH = 32K - 128 } __bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER); @@ -21,4 +21,4 @@ SECTIONS { *(.shared_data) } > SHARED_RAM -} \ No newline at end of file +} diff --git a/examples/stm32wl/memory.x b/examples/stm32wl/memory.x index 0298caa4b4..4590867a87 100644 --- a/examples/stm32wl/memory.x +++ b/examples/stm32wl/memory.x @@ -2,8 +2,8 @@ MEMORY { /* NOTE 1 K = 1 KiBi = 1024 bytes */ FLASH : ORIGIN = 0x08000000, LENGTH = 256K - SHARED_RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64 - RAM (rwx) : ORIGIN = 0x20000040, LENGTH = 64K - 64 + SHARED_RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128 + RAM (rwx) : ORIGIN = 0x20000080, LENGTH = 64K - 128 } SECTIONS @@ -12,4 +12,4 @@ SECTIONS { *(.shared_data) } > SHARED_RAM -} \ No newline at end of file +}