diff --git a/rtic-monotonics/CHANGELOG.md b/rtic-monotonics/CHANGELOG.md index 6b5761c722..af3136ca93 100644 --- a/rtic-monotonics/CHANGELOG.md +++ b/rtic-monotonics/CHANGELOG.md @@ -7,6 +7,10 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ## Unreleased +### Fixed + +- Fix STM32 support for other chip families + ## v1.2.0 - 2023-09-19 ### Added diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml index e8197db64f..1d9138d3e1 100644 --- a/rtic-monotonics/Cargo.toml +++ b/rtic-monotonics/Cargo.toml @@ -43,7 +43,12 @@ nrf5340-net-pac = { version = "0.12.2", optional = true } nrf9160-pac = { version = "0.12.2", optional = true } # STM32 -stm32-metapac = { version = "14.0.0", features = ["metadata"], optional = true } +stm32-metapac = { version = "14.0.0", optional = true } + +[build-dependencies] +proc-macro2 = { version = "1.0.36", optional = true } +quote = { version = "1.0.15", optional = true } +stm32-metapac = { version = "14.0.0", default-features = false, features = ["metadata"], optional = true } [features] default = [] @@ -78,6 +83,8 @@ stm32_tim5 = [] stm32_tim12 = [] stm32_tim15 = [] +stm32-metapac = ["dep:stm32-metapac", "dep:quote", "dep:proc-macro2"] + # Maintainers: this `stm32-metapac` feature list is taken from: # https://github.com/embassy-rs/embassy/blob/2e6f4237f2410aa18c9866a5a1a5ed1f3bec8a4e/embassy-stm32/Cargo.toml#L143 # It should be updated if `stm32-metapac` version changes because it might contain new chip definitions. diff --git a/rtic-monotonics/build.rs b/rtic-monotonics/build.rs index a2ed570798..50a3660a24 100644 --- a/rtic-monotonics/build.rs +++ b/rtic-monotonics/build.rs @@ -1,22 +1,180 @@ fn main() { - // feature=["stm32g081kb"] etc. - let stm32_chip: Vec<_> = std::env::vars() + #[cfg(feature = "stm32-metapac")] + stm32(); + + println!("cargo:rerun-if-changed=build.rs"); +} + +#[cfg(feature = "stm32-metapac")] +fn stm32() { + use std::path::PathBuf; + use std::{env, fs}; + + use proc_macro2::TokenStream; + use quote::{format_ident, quote}; + + use stm32_metapac::metadata::METADATA; + let chip_name = match env::vars() .map(|(a, _)| a) .filter(|x| { !x.starts_with("CARGO_FEATURE_STM32_METAPAC") && !x.starts_with("CARGO_FEATURE_STM32_TIM") && x.starts_with("CARGO_FEATURE_STM32") }) - .collect(); + .get_one() + { + Ok(x) => x, + Err(GetOneError::None) => panic!("No stm32xx Cargo feature enabled"), + Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"), + } + .strip_prefix("CARGO_FEATURE_") + .unwrap() + .to_ascii_lowercase(); - match stm32_chip.len() { - 0 => { - // Not using stm32. + // Allows to just use #[cfg(stm32)] if one of the stm32 chips is used. + println!("cargo:rustc-cfg=stm32"); + + for p in METADATA.peripherals { + if let Some(r) = &p.registers { + println!("cargo:rustc-cfg={}", r.kind); + println!("cargo:rustc-cfg={}_{}", r.kind, r.version); } - 1 => { - // Allows to just use #[cfg(stm32)] if one of the stm32 chips is used. - println!("cargo:rustc-cfg=stm32"); + } + + // ======== + // Generate singletons + + let mut singletons: Vec = Vec::new(); + for p in METADATA.peripherals { + if !p.name.contains("TIM") { + continue; + } + if let Some(r) = &p.registers { + match r.kind { + // Generate singletons per pin, not per port + "gpio" => { + println!("{}", p.name); + let port_letter = p.name.strip_prefix("GPIO").unwrap(); + for pin_num in 0..16 { + singletons.push(format!("P{}{}", port_letter, pin_num)); + } + } + + // No singleton for these, the HAL handles them specially. + "exti" => {} + + // We *shouldn't* have singletons for these, but the HAL currently requires + // singletons, for using with RccPeripheral to enable/disable clocks to them. + "rcc" => { + if r.version.starts_with("h5") + || r.version.starts_with("h7") + || r.version.starts_with("f4") + { + singletons.push("MCO1".to_string()); + singletons.push("MCO2".to_string()); + } + if r.version.starts_with("l4") { + singletons.push("MCO".to_string()); + } + singletons.push(p.name.to_string()); + } + //"dbgmcu" => {} + //"syscfg" => {} + //"dma" => {} + //"bdma" => {} + //"dmamux" => {} + + // For other peripherals, one singleton per peri + _ => singletons.push(p.name.to_string()), + } + } + } + + let mut g = TokenStream::new(); + + // ======== + // Generate RccPeripheral impls + + for p in METADATA.peripherals { + if !singletons.contains(&p.name.to_string()) { + continue; + } + + if let Some(rcc) = &p.rcc { + let en = rcc.enable.as_ref().unwrap(); + + let rst = match &rcc.reset { + Some(rst) => { + let rst_reg = format_ident!("{}", rst.register.to_ascii_lowercase()); + let set_rst_field = format_ident!("set_{}", rst.field.to_ascii_lowercase()); + quote! { + stm32_metapac::RCC.#rst_reg().modify(|w| w.#set_rst_field(true)); + stm32_metapac::RCC.#rst_reg().modify(|w| w.#set_rst_field(false)); + } + } + None => TokenStream::new(), + }; + + let after_enable = if chip_name.starts_with("stm32f2") { + // Errata: ES0005 - 2.1.11 Delay after an RCC peripheral clock enabling + quote! { + cortex_m::asm::dsb(); + } + } else { + TokenStream::new() + }; + + let pname = format_ident!("{}", p.name); + let en_reg = format_ident!("{}", en.register.to_ascii_lowercase()); + let set_en_field = format_ident!("set_{}", en.field.to_ascii_lowercase()); + + g.extend(quote! { + #[doc(hidden)] + pub mod #pname { + pub fn enable() { + stm32_metapac::RCC.#en_reg().modify(|w| w.#set_en_field(true)); + #after_enable + } + pub fn reset() { + #rst + } + } + }); + } + } + + // ======== + // Generate NVIC impl + let prio_bits = METADATA.nvic_priority_bits; + g.extend(quote! { + pub const NVIC_PRIO_BITS: u8 = #prio_bits; + }); + + // ======== + // Write generated.rs + + let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); + fs::write(out_file, g.to_string()).unwrap(); +} + +enum GetOneError { + None, + Multiple, +} + +trait IteratorExt: Iterator { + fn get_one(self) -> Result; +} + +impl IteratorExt for T { + fn get_one(mut self) -> Result { + match self.next() { + None => Err(GetOneError::None), + Some(res) => match self.next() { + Some(_) => Err(GetOneError::Multiple), + None => Ok(res), + }, } - _ => panic!("multiple stm32xx definitions {:?}", stm32_chip), } } diff --git a/rtic-monotonics/src/stm32.rs b/rtic-monotonics/src/stm32.rs index 77d36e4c40..736ca788c3 100644 --- a/rtic-monotonics/src/stm32.rs +++ b/rtic-monotonics/src/stm32.rs @@ -37,9 +37,16 @@ use crate::{Monotonic, TimeoutError, TimerQueue}; use atomic_polyfill::{AtomicU64, Ordering}; pub use fugit::{self, ExtU64}; -use pac::metadata::METADATA; use stm32_metapac as pac; +mod _generated { + #![allow(dead_code)] + #![allow(unused_imports)] + #![allow(non_snake_case)] + + include!(concat!(env!("OUT_DIR"), "/_generated.rs")); +} + const TIMER_HZ: u32 = 1_000_000; #[doc(hidden)] @@ -114,17 +121,6 @@ macro_rules! create_stm32_tim15_monotonic_token { }}; } -// Creates `enable_timer()` function which enables timer in RCC. -macro_rules! enable_timer { - ($apbenrX:ident, $set_timXen:ident, $apbrstrX:ident, $set_timXrst:ident) => { - fn enable_timer() { - pac::RCC.$apbenrX().modify(|r| r.$set_timXen(true)); - pac::RCC.$apbrstrX().modify(|r| r.$set_timXrst(true)); - pac::RCC.$apbrstrX().modify(|r| r.$set_timXrst(false)); - } - }; -} - macro_rules! make_timer { ($mono_name:ident, $timer:ident, $bits:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { /// Monotonic timer queue implementation. @@ -139,6 +135,11 @@ macro_rules! make_timer { static $overflow: AtomicU64 = AtomicU64::new(0); static $tq: TimerQueue<$mono_name> = TimerQueue::new(); + fn enable_timer() { + _generated::$timer::enable(); + _generated::$timer::reset(); + } + impl $mono_name { /// Starts the monotonic timer. /// - `tim_clock_hz`: `TIMx` peripheral clock frequency. @@ -173,7 +174,7 @@ macro_rules! make_timer { // plus we are not using any external shared resources so we won't impact // basepri/source masking based critical sections. unsafe { - crate::set_monotonic_prio(METADATA.nvic_priority_bits.unwrap(), pac::Interrupt::$timer); + crate::set_monotonic_prio(_generated::NVIC_PRIO_BITS, pac::Interrupt::$timer); cortex_m::peripheral::NVIC::unmask(pac::Interrupt::$timer); } } @@ -298,32 +299,20 @@ macro_rules! make_timer { }; } -#[cfg(feature = "stm32_tim2")] -enable_timer!(apbenr1, set_tim2en, apbrstr1, set_tim2rst); #[cfg(feature = "stm32_tim2")] make_timer!(Tim2, TIM2, u32, TIMER2_OVERFLOWS, TIMER2_TQ); -#[cfg(feature = "stm32_tim3")] -enable_timer!(apbenr1, set_tim3en, apbrstr1, set_tim3rst); #[cfg(feature = "stm32_tim3")] make_timer!(Tim3, TIM3, u16, TIMER3_OVERFLOWS, TIMER3_TQ); -#[cfg(feature = "stm32_tim4")] -enable_timer!(apbenr1, set_tim4en, apbrstr1, set_tim4rst); #[cfg(feature = "stm32_tim4")] make_timer!(Tim4, TIM4, u16, TIMER4_OVERFLOWS, TIMER4_TQ); -#[cfg(feature = "stm32_tim5")] -enable_timer!(apbenr1, set_tim5en, apbrstr1, set_tim5rst); #[cfg(feature = "stm32_tim5")] make_timer!(Tim5, TIM5, u16, TIMER5_OVERFLOWS, TIMER5_TQ); -#[cfg(feature = "stm32_tim12")] -enable_timer!(apb1enr, set_tim12en, apb1rstr, set_tim12rst); #[cfg(feature = "stm32_tim12")] make_timer!(Tim12, TIM12, u16, TIMER12_OVERFLOWS, TIMER12_TQ); -#[cfg(feature = "stm32_tim15")] -enable_timer!(apbenr2, set_tim15en, apbrstr2, set_tim15rst); #[cfg(feature = "stm32_tim15")] make_timer!(Tim15, TIM15, u16, TIMER15_OVERFLOWS, TIMER15_TQ);