aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rtic-monotonics/CHANGELOG.md4
-rw-r--r--rtic-monotonics/Cargo.toml9
-rw-r--r--rtic-monotonics/build.rs178
-rw-r--r--rtic-monotonics/src/stm32.rs39
4 files changed, 194 insertions, 36 deletions
diff --git a/rtic-monotonics/CHANGELOG.md b/rtic-monotonics/CHANGELOG.md
index 6b5761c7..af3136ca 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 e8197db6..1d9138d3 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 a2ed5707..50a3660a 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();
+
+ // 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);
+ }
+ }
+
+ // ========
+ // Generate singletons
+
+ let mut singletons: Vec<String> = 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()),
+ }
+ }
+ }
- match stm32_chip.len() {
- 0 => {
- // Not using stm32.
+ let mut g = TokenStream::new();
+
+ // ========
+ // Generate RccPeripheral impls
+
+ for p in METADATA.peripherals {
+ if !singletons.contains(&p.name.to_string()) {
+ continue;
}
- 1 => {
- // Allows to just use #[cfg(stm32)] if one of the stm32 chips is used.
- println!("cargo:rustc-cfg=stm32");
+
+ 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<Self::Item, GetOneError>;
+}
+
+impl<T: Iterator> IteratorExt for T {
+ fn get_one(mut self) -> Result<Self::Item, GetOneError> {
+ 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 77d36e4c..736ca788 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);
}
}
@@ -299,31 +300,19 @@ 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);