From 8824202c5ac8a6afb3a328c90259b69d472b33e2 Mon Sep 17 00:00:00 2001 From: Nils Fitinghoff Date: Mon, 2 Oct 2023 16:17:39 +0200 Subject: [PATCH] rtic-monotonics: Fix stm32-metapac use Previously, the stm32 monotonics only compiled for some chip families. For example, stm32g081kb worked, but not stm32f407*. The stm32-metapac does not directly unify peripheral names between the many stm32 families, but provides tools for build scripts to generate code that uses the right names for the selected chip. Use that mechanism instead of targeting a specific family. --- rtic-monotonics/CHANGELOG.md | 4 + rtic-monotonics/Cargo.toml | 9 +- rtic-monotonics/build.rs | 178 +++++++++++++++++++++++++++++++++-- rtic-monotonics/src/stm32.rs | 39 +++----- 4 files changed, 194 insertions(+), 36 deletions(-) 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);