stm32: avoid creating many tiny critical sections in init.

Saves 292 bytes on stm32f0 bilnky with max optimizations (from 3132 to 2840).
This commit is contained in:
Dario Nieuwenhuis 2023-10-12 00:34:47 +02:00
parent 66e399b5c6
commit 97ca0e77bf
14 changed files with 183 additions and 149 deletions

View File

@ -4,6 +4,7 @@ use core::sync::atomic::{compiler_fence, Ordering};
use cortex_m::interrupt::InterruptNumber; use cortex_m::interrupt::InterruptNumber;
use cortex_m::peripheral::NVIC; use cortex_m::peripheral::NVIC;
use critical_section::CriticalSection;
/// Generate a standard `mod interrupt` for a HAL. /// Generate a standard `mod interrupt` for a HAL.
#[macro_export] #[macro_export]
@ -91,6 +92,12 @@ macro_rules! interrupt_mod {
fn set_priority(prio: crate::interrupt::Priority) { fn set_priority(prio: crate::interrupt::Priority) {
Self::IRQ.set_priority(prio) Self::IRQ.set_priority(prio)
} }
/// Set the interrupt priority with an already-acquired critical section
#[inline]
fn set_priority_with_cs(cs: critical_section::CriticalSection, prio: crate::interrupt::Priority) {
Self::IRQ.set_priority_with_cs(cs, prio)
}
} }
$( $(
@ -195,10 +202,29 @@ pub unsafe trait InterruptExt: InterruptNumber + Copy {
/// Set the interrupt priority. /// Set the interrupt priority.
#[inline] #[inline]
fn set_priority(self, prio: Priority) { fn set_priority(self, prio: Priority) {
critical_section::with(|_| unsafe { unsafe {
let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(()); let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(());
nvic.set_priority(self, prio.into())
}) // On thumbv6, set_priority must do a RMW to change 8bit in a 32bit reg.
#[cfg(armv6m)]
critical_section::with(|_| nvic.set_priority(self, prio.into()));
// On thumbv7+, set_priority does an atomic 8bit write, so no CS needed.
#[cfg(not(armv6m))]
nvic.set_priority(self, prio.into());
}
}
/// Set the interrupt priority with an already-acquired critical section
///
/// Equivalent to `set_priority`, except you pass a `CriticalSection` to prove
/// you've already acquired a critical section. This prevents acquiring another
/// one, which saves code size.
#[inline]
fn set_priority_with_cs(self, _cs: CriticalSection, prio: Priority) {
unsafe {
let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(());
nvic.set_priority(self, prio.into());
}
} }
} }

View File

@ -48,17 +48,23 @@ macro_rules! peripherals_struct {
///Returns all the peripherals *once* ///Returns all the peripherals *once*
#[inline] #[inline]
pub(crate) fn take() -> Self { pub(crate) fn take() -> Self {
critical_section::with(Self::take_with_cs)
}
///Returns all the peripherals *once*
#[inline]
pub(crate) fn take_with_cs(_cs: critical_section::CriticalSection) -> Self {
#[no_mangle] #[no_mangle]
static mut _EMBASSY_DEVICE_PERIPHERALS: bool = false; static mut _EMBASSY_DEVICE_PERIPHERALS: bool = false;
critical_section::with(|_| unsafe { // safety: OK because we're inside a CS.
unsafe {
if _EMBASSY_DEVICE_PERIPHERALS { if _EMBASSY_DEVICE_PERIPHERALS {
panic!("init called more than once!") panic!("init called more than once!")
} }
_EMBASSY_DEVICE_PERIPHERALS = true; _EMBASSY_DEVICE_PERIPHERALS = true;
Self::steal() Self::steal()
}) }
} }
} }

View File

@ -554,23 +554,19 @@ fn main() {
fn frequency() -> crate::time::Hertz { fn frequency() -> crate::time::Hertz {
#clock_frequency #clock_frequency
} }
fn enable_and_reset() { fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) {
critical_section::with(|_cs| {
#before_enable #before_enable
#[cfg(feature = "low-power")] #[cfg(feature = "low-power")]
crate::rcc::clock_refcount_add(_cs); crate::rcc::clock_refcount_add(_cs);
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true)); crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true));
#after_enable #after_enable
#rst #rst
})
} }
fn disable() { fn disable_with_cs(_cs: critical_section::CriticalSection) {
critical_section::with(|_cs| {
#before_disable #before_disable
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false)); crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false));
#[cfg(feature = "low-power")] #[cfg(feature = "low-power")]
crate::rcc::clock_refcount_sub(_cs); crate::rcc::clock_refcount_sub(_cs);
})
} }
} }

View File

@ -567,18 +567,14 @@ foreach_peripheral!(
critical_section::with(|_| unsafe { crate::rcc::get_freqs().apb1 }) critical_section::with(|_| unsafe { crate::rcc::get_freqs().apb1 })
} }
fn enable_and_reset() { fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) {
critical_section::with(|_| {
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true)); crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true));
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false)); crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false));
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true)); crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true));
})
} }
fn disable() { fn disable_with_cs(_cs: critical_section::CriticalSection) {
critical_section::with(|_| {
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false)) crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false))
})
} }
} }

View File

@ -77,10 +77,10 @@ impl State {
static STATE: State = State::new(); static STATE: State = State::new();
/// safety: must be called only once /// safety: must be called only once
pub(crate) unsafe fn init(irq_priority: Priority) { pub(crate) unsafe fn init(cs: critical_section::CriticalSection, irq_priority: Priority) {
foreach_interrupt! { foreach_interrupt! {
($peri:ident, bdma, $block:ident, $signal_name:ident, $irq:ident) => { ($peri:ident, bdma, $block:ident, $signal_name:ident, $irq:ident) => {
crate::interrupt::typelevel::$irq::set_priority(irq_priority); crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, irq_priority);
crate::interrupt::typelevel::$irq::enable(); crate::interrupt::typelevel::$irq::enable();
}; };
} }

View File

@ -154,10 +154,10 @@ impl State {
static STATE: State = State::new(); static STATE: State = State::new();
/// safety: must be called only once /// safety: must be called only once
pub(crate) unsafe fn init(irq_priority: Priority) { pub(crate) unsafe fn init(cs: critical_section::CriticalSection, irq_priority: Priority) {
foreach_interrupt! { foreach_interrupt! {
($peri:ident, dma, $block:ident, $signal_name:ident, $irq:ident) => { ($peri:ident, dma, $block:ident, $signal_name:ident, $irq:ident) => {
interrupt::typelevel::$irq::set_priority(irq_priority); interrupt::typelevel::$irq::set_priority_with_cs(cs, irq_priority);
interrupt::typelevel::$irq::enable(); interrupt::typelevel::$irq::enable();
}; };
} }

View File

@ -47,6 +47,6 @@ foreach_dma_channel! {
} }
/// safety: must be called only once /// safety: must be called only once
pub(crate) unsafe fn init() { pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) {
crate::_generated::init_dmamux(); crate::_generated::init_dmamux();
} }

View File

@ -53,10 +53,10 @@ impl State {
static STATE: State = State::new(); static STATE: State = State::new();
/// safety: must be called only once /// safety: must be called only once
pub(crate) unsafe fn init(irq_priority: Priority) { pub(crate) unsafe fn init(cs: critical_section::CriticalSection, irq_priority: Priority) {
foreach_interrupt! { foreach_interrupt! {
($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => { ($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => {
crate::interrupt::typelevel::$irq::set_priority(irq_priority); crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, irq_priority);
crate::interrupt::typelevel::$irq::enable(); crate::interrupt::typelevel::$irq::enable();
}; };
} }

View File

@ -56,16 +56,17 @@ pub(crate) fn slice_ptr_parts_mut<T>(slice: *mut [T]) -> (usize, usize) {
// safety: must be called only once at startup // safety: must be called only once at startup
pub(crate) unsafe fn init( pub(crate) unsafe fn init(
cs: critical_section::CriticalSection,
#[cfg(bdma)] bdma_priority: Priority, #[cfg(bdma)] bdma_priority: Priority,
#[cfg(dma)] dma_priority: Priority, #[cfg(dma)] dma_priority: Priority,
#[cfg(gpdma)] gpdma_priority: Priority, #[cfg(gpdma)] gpdma_priority: Priority,
) { ) {
#[cfg(bdma)] #[cfg(bdma)]
bdma::init(bdma_priority); bdma::init(cs, bdma_priority);
#[cfg(dma)] #[cfg(dma)]
dma::init(dma_priority); dma::init(cs, dma_priority);
#[cfg(gpdma)] #[cfg(gpdma)]
gpdma::init(gpdma_priority); gpdma::init(cs, gpdma_priority);
#[cfg(dmamux)] #[cfg(dmamux)]
dmamux::init(); dmamux::init(cs);
} }

View File

@ -367,7 +367,7 @@ macro_rules! enable_irq {
} }
/// safety: must be called only once /// safety: must be called only once
pub(crate) unsafe fn init() { pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) {
use crate::interrupt::typelevel::Interrupt; use crate::interrupt::typelevel::Interrupt;
foreach_exti_irq!(enable_irq); foreach_exti_irq!(enable_irq);

View File

@ -1,6 +1,7 @@
#![macro_use] #![macro_use]
use core::convert::Infallible; use core::convert::Infallible;
use critical_section::CriticalSection;
use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef};
use crate::pac::gpio::{self, vals}; use crate::pac::gpio::{self, vals};
@ -757,9 +758,9 @@ foreach_pin!(
}; };
); );
pub(crate) unsafe fn init() { pub(crate) unsafe fn init(_cs: CriticalSection) {
#[cfg(afio)] #[cfg(afio)]
<crate::peripherals::AFIO as crate::rcc::sealed::RccPeripheral>::enable_and_reset(); <crate::peripherals::AFIO as crate::rcc::sealed::RccPeripheral>::enable_and_reset_with_cs(_cs);
crate::_generated::init_gpio(); crate::_generated::init_gpio();
} }

View File

@ -155,7 +155,8 @@ impl Default for Config {
/// Initialize embassy. /// Initialize embassy.
pub fn init(config: Config) -> Peripherals { pub fn init(config: Config) -> Peripherals {
let p = Peripherals::take(); critical_section::with(|cs| {
let p = Peripherals::take_with_cs(cs);
#[cfg(dbgmcu)] #[cfg(dbgmcu)]
if config.enable_debug_during_sleep { if config.enable_debug_during_sleep {
@ -186,11 +187,11 @@ pub fn init(config: Config) -> Peripherals {
} }
#[cfg(not(any(stm32f1, stm32wb, stm32wl)))] #[cfg(not(any(stm32f1, stm32wb, stm32wl)))]
peripherals::SYSCFG::enable_and_reset(); peripherals::SYSCFG::enable_and_reset_with_cs(cs);
#[cfg(not(any(stm32h5, stm32h7, stm32wb, stm32wl)))] #[cfg(not(any(stm32h5, stm32h7, stm32wb, stm32wl)))]
peripherals::PWR::enable_and_reset(); peripherals::PWR::enable_and_reset_with_cs(cs);
#[cfg(not(any(stm32f2, stm32f4, stm32f7, stm32l0, stm32h5, stm32h7)))] #[cfg(not(any(stm32f2, stm32f4, stm32f7, stm32l0, stm32h5, stm32h7)))]
peripherals::FLASH::enable_and_reset(); peripherals::FLASH::enable_and_reset_with_cs(cs);
unsafe { unsafe {
#[cfg(feature = "_split-pins-enabled")] #[cfg(feature = "_split-pins-enabled")]
@ -205,8 +206,9 @@ pub fn init(config: Config) -> Peripherals {
pmcr.set_pc3so(true); pmcr.set_pc3so(true);
}); });
gpio::init(); gpio::init(cs);
dma::init( dma::init(
cs,
#[cfg(bdma)] #[cfg(bdma)]
config.bdma_interrupt_priority, config.bdma_interrupt_priority,
#[cfg(dma)] #[cfg(dma)]
@ -215,21 +217,20 @@ pub fn init(config: Config) -> Peripherals {
config.gpdma_interrupt_priority, config.gpdma_interrupt_priority,
); );
#[cfg(feature = "exti")] #[cfg(feature = "exti")]
exti::init(); exti::init(cs);
rcc::init(config.rcc); rcc::init(config.rcc);
// must be after rcc init // must be after rcc init
#[cfg(feature = "_time-driver")] #[cfg(feature = "_time-driver")]
time_driver::init(); time_driver::init(cs);
#[cfg(feature = "low-power")] #[cfg(feature = "low-power")]
while !crate::rcc::low_power_ready() { while !crate::rcc::low_power_ready() {
critical_section::with(|cs| {
crate::rcc::clock_refcount_sub(cs); crate::rcc::clock_refcount_sub(cs);
});
} }
} }
p p
})
} }

View File

@ -229,10 +229,19 @@ pub mod low_level {
} }
pub(crate) mod sealed { pub(crate) mod sealed {
use critical_section::CriticalSection;
pub trait RccPeripheral { pub trait RccPeripheral {
fn frequency() -> crate::time::Hertz; fn frequency() -> crate::time::Hertz;
fn enable_and_reset(); fn enable_and_reset_with_cs(cs: CriticalSection);
fn disable(); fn disable_with_cs(cs: CriticalSection);
fn enable_and_reset() {
critical_section::with(|cs| Self::enable_and_reset_with_cs(cs))
}
fn disable() {
critical_section::with(|cs| Self::disable_with_cs(cs))
}
} }
} }

View File

@ -152,14 +152,13 @@ embassy_time::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
}); });
impl RtcDriver { impl RtcDriver {
fn init(&'static self) { fn init(&'static self, cs: critical_section::CriticalSection) {
let r = T::regs_gp16(); let r = T::regs_gp16();
<T as RccPeripheral>::enable_and_reset(); <T as RccPeripheral>::enable_and_reset_with_cs(cs);
let timer_freq = T::frequency(); let timer_freq = T::frequency();
critical_section::with(|_| {
r.cr1().modify(|w| w.set_cen(false)); r.cr1().modify(|w| w.set_cen(false));
r.cnt().write(|w| w.set_cnt(0)); r.cnt().write(|w| w.set_cnt(0));
@ -190,7 +189,6 @@ impl RtcDriver {
unsafe { <T as BasicInstance>::Interrupt::enable() }; unsafe { <T as BasicInstance>::Interrupt::enable() };
r.cr1().modify(|w| w.set_cen(true)); r.cr1().modify(|w| w.set_cen(true));
})
} }
fn on_interrupt(&self) { fn on_interrupt(&self) {
@ -462,6 +460,6 @@ pub(crate) fn get_driver() -> &'static RtcDriver {
&DRIVER &DRIVER
} }
pub(crate) fn init() { pub(crate) fn init(cs: CriticalSection) {
DRIVER.init() DRIVER.init(cs)
} }