Skip to content

Commit

Permalink
rtic-monotonics: Fix stm32-metapac use
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nilfit committed Oct 3, 2023
1 parent 3143b7e commit 0effee4
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 36 deletions.
4 changes: 4 additions & 0 deletions rtic-monotonics/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion rtic-monotonics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand Down Expand Up @@ -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.
Expand Down
175 changes: 165 additions & 10 deletions rtic-monotonics/build.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,177 @@
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),
}
}
39 changes: 14 additions & 25 deletions rtic-monotonics/src/stm32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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);

0 comments on commit 0effee4

Please sign in to comment.