mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-25 16:23:10 +00:00
2b7e76efe9
This commit addresses #3256 by disabling dma NVIC interrupt enablement at startup. Instead, per-channel NVIC interrupt enablement is now done with the rest of the dma channel configuration. This ensures that each core will only handle the interrupts of the DMA channels that it uses.
1801 lines
69 KiB
Rust
1801 lines
69 KiB
Rust
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
|
use std::fmt::Write as _;
|
|
use std::io::Write;
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::Command;
|
|
use std::{env, fs};
|
|
|
|
use proc_macro2::{Ident, TokenStream};
|
|
use quote::{format_ident, quote};
|
|
use stm32_metapac::metadata::ir::BitOffset;
|
|
use stm32_metapac::metadata::{
|
|
MemoryRegionKind, PeripheralRccKernelClock, PeripheralRccRegister, PeripheralRegisters, StopMode, ALL_CHIPS,
|
|
ALL_PERIPHERAL_VERSIONS, METADATA,
|
|
};
|
|
|
|
#[path = "./build_common.rs"]
|
|
mod common;
|
|
|
|
fn main() {
|
|
let mut cfgs = common::CfgSet::new();
|
|
common::set_target_cfgs(&mut cfgs);
|
|
|
|
let chip_name = match env::vars()
|
|
.map(|(a, _)| a)
|
|
.filter(|x| x.starts_with("CARGO_FEATURE_STM32"))
|
|
.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();
|
|
|
|
eprintln!("chip: {chip_name}");
|
|
|
|
for p in METADATA.peripherals {
|
|
if let Some(r) = &p.registers {
|
|
cfgs.enable(r.kind);
|
|
cfgs.enable(format!("{}_{}", r.kind, r.version));
|
|
}
|
|
}
|
|
|
|
for &(kind, versions) in ALL_PERIPHERAL_VERSIONS.iter() {
|
|
cfgs.declare(kind);
|
|
for &version in versions.iter() {
|
|
cfgs.declare(format!("{}_{}", kind, version));
|
|
}
|
|
}
|
|
|
|
// ========
|
|
// Generate singletons
|
|
|
|
let mut singletons: Vec<String> = Vec::new();
|
|
for p in METADATA.peripherals {
|
|
if let Some(r) = &p.registers {
|
|
if r.kind == "adccommon" || r.kind == "sai" || r.kind == "ucpd" {
|
|
// TODO: should we emit this for all peripherals? if so, we will need a list of all
|
|
// possible peripherals across all chips, so that we can declare the configs
|
|
// (replacing the hard-coded list of `peri_*` cfgs below)
|
|
cfgs.enable(format!("peri_{}", p.name.to_ascii_lowercase()));
|
|
}
|
|
|
|
match r.kind {
|
|
// Generate singletons per pin, not per port
|
|
"gpio" => {
|
|
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" => {
|
|
for pin in p.pins {
|
|
if pin.signal.starts_with("MCO") {
|
|
let name = pin.signal.replace('_', "").to_string();
|
|
if !singletons.contains(&name) {
|
|
cfgs.enable(name.to_ascii_lowercase());
|
|
singletons.push(name);
|
|
}
|
|
}
|
|
}
|
|
singletons.push(p.name.to_string());
|
|
}
|
|
//"dbgmcu" => {}
|
|
//"syscfg" => {}
|
|
//"dma" => {}
|
|
//"bdma" => {}
|
|
//"dmamux" => {}
|
|
|
|
// For other peripherals, one singleton per peri
|
|
_ => singletons.push(p.name.to_string()),
|
|
}
|
|
}
|
|
}
|
|
|
|
cfgs.declare_all(&[
|
|
"peri_adc1_common",
|
|
"peri_adc3_common",
|
|
"peri_adc12_common",
|
|
"peri_adc34_common",
|
|
"peri_sai1",
|
|
"peri_sai2",
|
|
"peri_sai3",
|
|
"peri_sai4",
|
|
"peri_ucpd1",
|
|
"peri_ucpd2",
|
|
]);
|
|
cfgs.declare_all(&["mco", "mco1", "mco2"]);
|
|
|
|
// One singleton per EXTI line
|
|
for pin_num in 0..16 {
|
|
singletons.push(format!("EXTI{}", pin_num));
|
|
}
|
|
|
|
// One singleton per DMA channel
|
|
for c in METADATA.dma_channels {
|
|
singletons.push(c.name.to_string());
|
|
}
|
|
|
|
let mut pin_set = std::collections::HashSet::new();
|
|
for p in METADATA.peripherals {
|
|
for pin in p.pins {
|
|
pin_set.insert(pin.pin);
|
|
}
|
|
}
|
|
|
|
struct SplitFeature {
|
|
feature_name: String,
|
|
pin_name_with_c: String,
|
|
#[cfg(feature = "_split-pins-enabled")]
|
|
pin_name_without_c: String,
|
|
}
|
|
|
|
// Extra analog switch pins available on most H7 chips
|
|
let split_features: Vec<SplitFeature> = vec![
|
|
#[cfg(feature = "split-pa0")]
|
|
SplitFeature {
|
|
feature_name: "split-pa0".to_string(),
|
|
pin_name_with_c: "PA0_C".to_string(),
|
|
pin_name_without_c: "PA0".to_string(),
|
|
},
|
|
#[cfg(feature = "split-pa1")]
|
|
SplitFeature {
|
|
feature_name: "split-pa1".to_string(),
|
|
pin_name_with_c: "PA1_C".to_string(),
|
|
pin_name_without_c: "PA1".to_string(),
|
|
},
|
|
#[cfg(feature = "split-pc2")]
|
|
SplitFeature {
|
|
feature_name: "split-pc2".to_string(),
|
|
pin_name_with_c: "PC2_C".to_string(),
|
|
pin_name_without_c: "PC2".to_string(),
|
|
},
|
|
#[cfg(feature = "split-pc3")]
|
|
SplitFeature {
|
|
feature_name: "split-pc3".to_string(),
|
|
pin_name_with_c: "PC3_C".to_string(),
|
|
pin_name_without_c: "PC3".to_string(),
|
|
},
|
|
];
|
|
|
|
for split_feature in &split_features {
|
|
if pin_set.contains(split_feature.pin_name_with_c.as_str()) {
|
|
singletons.push(split_feature.pin_name_with_c.clone());
|
|
} else {
|
|
panic!(
|
|
"'{}' feature invalid for this chip! No pin '{}' found.\n
|
|
Found pins: {:#?}",
|
|
split_feature.feature_name, split_feature.pin_name_with_c, pin_set
|
|
)
|
|
}
|
|
}
|
|
|
|
// ========
|
|
// Handle time-driver-XXXX features.
|
|
|
|
let time_driver = match env::vars()
|
|
.map(|(a, _)| a)
|
|
.filter(|x| x.starts_with("CARGO_FEATURE_TIME_DRIVER_"))
|
|
.get_one()
|
|
{
|
|
Ok(x) => Some(
|
|
x.strip_prefix("CARGO_FEATURE_TIME_DRIVER_")
|
|
.unwrap()
|
|
.to_ascii_lowercase(),
|
|
),
|
|
Err(GetOneError::None) => None,
|
|
Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"),
|
|
};
|
|
|
|
let time_driver_singleton = match time_driver.as_ref().map(|x| x.as_ref()) {
|
|
None => "",
|
|
Some("tim1") => "TIM1",
|
|
Some("tim2") => "TIM2",
|
|
Some("tim3") => "TIM3",
|
|
Some("tim4") => "TIM4",
|
|
Some("tim5") => "TIM5",
|
|
Some("tim8") => "TIM8",
|
|
Some("tim9") => "TIM9",
|
|
Some("tim12") => "TIM12",
|
|
Some("tim15") => "TIM15",
|
|
Some("tim20") => "TIM20",
|
|
Some("tim21") => "TIM21",
|
|
Some("tim22") => "TIM22",
|
|
Some("tim23") => "TIM23",
|
|
Some("tim24") => "TIM24",
|
|
Some("any") => {
|
|
// Order of TIM candidators:
|
|
// 1. 2CH -> 2CH_CMP -> GP16 -> GP32 -> ADV
|
|
// 2. In same catagory: larger TIM number first
|
|
[
|
|
"TIM22", "TIM21", "TIM12", "TIM9", // 2CH
|
|
"TIM15", // 2CH_CMP
|
|
"TIM19", "TIM4", "TIM3", // GP16
|
|
"TIM24", "TIM23", "TIM5", "TIM2", // GP32
|
|
"TIM20", "TIM8", "TIM1", //ADV
|
|
]
|
|
.iter()
|
|
.find(|tim| singletons.contains(&tim.to_string())).expect("time-driver-any requested, but the chip doesn't have TIM1, TIM2, TIM3, TIM4, TIM5, TIM8, TIM9, TIM12, TIM15, TIM20, TIM21, TIM22, TIM23 or TIM24.")
|
|
}
|
|
_ => panic!("unknown time_driver {:?}", time_driver),
|
|
};
|
|
|
|
if !time_driver_singleton.is_empty() {
|
|
cfgs.enable(format!("time_driver_{}", time_driver_singleton.to_lowercase()));
|
|
}
|
|
for tim in [
|
|
"tim1", "tim2", "tim3", "tim4", "tim5", "tim8", "tim9", "tim12", "tim15", "tim20", "tim21", "tim22", "tim23",
|
|
"tim24",
|
|
] {
|
|
cfgs.declare(format!("time_driver_{}", tim));
|
|
}
|
|
|
|
// ========
|
|
// Write singletons
|
|
|
|
let mut g = TokenStream::new();
|
|
|
|
let singleton_tokens: Vec<_> = singletons.iter().map(|s| format_ident!("{}", s)).collect();
|
|
|
|
g.extend(quote! {
|
|
embassy_hal_internal::peripherals_definition!(#(#singleton_tokens),*);
|
|
});
|
|
|
|
let singleton_tokens: Vec<_> = singletons
|
|
.iter()
|
|
.filter(|s| *s != &time_driver_singleton.to_string())
|
|
.map(|s| format_ident!("{}", s))
|
|
.collect();
|
|
|
|
g.extend(quote! {
|
|
embassy_hal_internal::peripherals_struct!(#(#singleton_tokens),*);
|
|
});
|
|
|
|
// ========
|
|
// Generate interrupt declarations
|
|
|
|
let mut irqs = Vec::new();
|
|
for irq in METADATA.interrupts {
|
|
irqs.push(format_ident!("{}", irq.name));
|
|
}
|
|
|
|
g.extend(quote! {
|
|
embassy_hal_internal::interrupt_mod!(
|
|
#(
|
|
#irqs,
|
|
)*
|
|
);
|
|
});
|
|
|
|
// ========
|
|
// Generate FLASH regions
|
|
let mut flash_regions = TokenStream::new();
|
|
let flash_memory_regions: Vec<_> = METADATA
|
|
.memory
|
|
.iter()
|
|
.filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some())
|
|
.collect();
|
|
for region in flash_memory_regions.iter() {
|
|
let region_name = format_ident!("{}", get_flash_region_name(region.name));
|
|
let bank_variant = format_ident!(
|
|
"{}",
|
|
if region.name.starts_with("BANK_1") {
|
|
"Bank1"
|
|
} else if region.name.starts_with("BANK_2") {
|
|
"Bank2"
|
|
} else {
|
|
continue;
|
|
}
|
|
);
|
|
let base = region.address;
|
|
let size = region.size;
|
|
let settings = region.settings.as_ref().unwrap();
|
|
let erase_size = settings.erase_size;
|
|
let write_size = settings.write_size;
|
|
let erase_value = settings.erase_value;
|
|
|
|
flash_regions.extend(quote! {
|
|
pub const #region_name: crate::flash::FlashRegion = crate::flash::FlashRegion {
|
|
bank: crate::flash::FlashBank::#bank_variant,
|
|
base: #base,
|
|
size: #size,
|
|
erase_size: #erase_size,
|
|
write_size: #write_size,
|
|
erase_value: #erase_value,
|
|
_ensure_internal: (),
|
|
};
|
|
});
|
|
|
|
let region_type = format_ident!("{}", get_flash_region_type_name(region.name));
|
|
flash_regions.extend(quote! {
|
|
#[cfg(flash)]
|
|
pub struct #region_type<'d, MODE = crate::flash::Async>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_internal::PeripheralRef<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData<MODE>);
|
|
});
|
|
}
|
|
|
|
let (fields, (inits, region_names)): (Vec<TokenStream>, (Vec<TokenStream>, Vec<Ident>)) = flash_memory_regions
|
|
.iter()
|
|
.map(|f| {
|
|
let region_name = get_flash_region_name(f.name);
|
|
let field_name = format_ident!("{}", region_name.to_lowercase());
|
|
let field_type = format_ident!("{}", get_flash_region_type_name(f.name));
|
|
let field = quote! {
|
|
pub #field_name: #field_type<'d, MODE>
|
|
};
|
|
let region_name = format_ident!("{}", region_name);
|
|
let init = quote! {
|
|
#field_name: #field_type(&#region_name, unsafe { p.clone_unchecked()}, core::marker::PhantomData)
|
|
};
|
|
|
|
(field, (init, region_name))
|
|
})
|
|
.unzip();
|
|
|
|
let regions_len = flash_memory_regions.len();
|
|
flash_regions.extend(quote! {
|
|
#[cfg(flash)]
|
|
pub struct FlashLayout<'d, MODE = crate::flash::Async> {
|
|
#(#fields),*,
|
|
_mode: core::marker::PhantomData<MODE>,
|
|
}
|
|
|
|
#[cfg(flash)]
|
|
impl<'d, MODE> FlashLayout<'d, MODE> {
|
|
pub(crate) fn new(p: embassy_hal_internal::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self {
|
|
Self {
|
|
#(#inits),*,
|
|
_mode: core::marker::PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub const FLASH_REGIONS: [&crate::flash::FlashRegion; #regions_len] = [
|
|
#(&#region_names),*
|
|
];
|
|
});
|
|
|
|
let max_erase_size = flash_memory_regions
|
|
.iter()
|
|
.map(|region| region.settings.as_ref().unwrap().erase_size)
|
|
.max()
|
|
.unwrap();
|
|
|
|
g.extend(quote! { pub const MAX_ERASE_SIZE: usize = #max_erase_size as usize; });
|
|
|
|
g.extend(quote! { pub mod flash_regions { #flash_regions } });
|
|
|
|
// ========
|
|
// Extract the rcc registers
|
|
|
|
let rcc_registers = METADATA
|
|
.peripherals
|
|
.iter()
|
|
.filter_map(|p| p.registers.as_ref())
|
|
.find(|r| r.kind == "rcc")
|
|
.unwrap();
|
|
let rcc_block = rcc_registers.ir.blocks.iter().find(|b| b.name == "Rcc").unwrap();
|
|
|
|
// ========
|
|
// Generate RccPeripheral impls
|
|
|
|
// count how many times each xxENR field is used, to enable refcounting if used more than once.
|
|
let mut rcc_field_count: HashMap<_, usize> = HashMap::new();
|
|
for p in METADATA.peripherals {
|
|
if let Some(rcc) = &p.rcc {
|
|
let en = rcc.enable.as_ref().unwrap();
|
|
*rcc_field_count.entry((en.register, en.field)).or_insert(0) += 1;
|
|
}
|
|
}
|
|
|
|
struct ClockGen<'a> {
|
|
rcc_registers: &'a PeripheralRegisters,
|
|
chained_muxes: HashMap<&'a str, &'a PeripheralRccRegister>,
|
|
|
|
clock_names: BTreeSet<String>,
|
|
muxes: BTreeSet<(Ident, Ident, Ident)>,
|
|
}
|
|
|
|
let mut clock_gen = ClockGen {
|
|
rcc_registers,
|
|
chained_muxes: HashMap::new(),
|
|
|
|
clock_names: BTreeSet::new(),
|
|
muxes: BTreeSet::new(),
|
|
};
|
|
if chip_name.starts_with("stm32h5") {
|
|
clock_gen.chained_muxes.insert(
|
|
"PER",
|
|
&PeripheralRccRegister {
|
|
register: "CCIPR5",
|
|
field: "PERSEL",
|
|
},
|
|
);
|
|
}
|
|
|
|
if chip_name.starts_with("stm32h7r") || chip_name.starts_with("stm32h7s") {
|
|
clock_gen.chained_muxes.insert(
|
|
"PER",
|
|
&PeripheralRccRegister {
|
|
register: "AHBPERCKSELR",
|
|
field: "PERSEL",
|
|
},
|
|
);
|
|
} else if chip_name.starts_with("stm32h7") {
|
|
clock_gen.chained_muxes.insert(
|
|
"PER",
|
|
&PeripheralRccRegister {
|
|
register: "D1CCIPR",
|
|
field: "PERSEL",
|
|
},
|
|
);
|
|
}
|
|
if chip_name.starts_with("stm32u5") {
|
|
clock_gen.chained_muxes.insert(
|
|
"ICLK",
|
|
&PeripheralRccRegister {
|
|
register: "CCIPR1",
|
|
field: "ICLKSEL",
|
|
},
|
|
);
|
|
}
|
|
if chip_name.starts_with("stm32wb") && !chip_name.starts_with("stm32wba") {
|
|
clock_gen.chained_muxes.insert(
|
|
"CLK48",
|
|
&PeripheralRccRegister {
|
|
register: "CCIPR",
|
|
field: "CLK48SEL",
|
|
},
|
|
);
|
|
}
|
|
if chip_name.starts_with("stm32f7") {
|
|
clock_gen.chained_muxes.insert(
|
|
"CLK48",
|
|
&PeripheralRccRegister {
|
|
register: "DCKCFGR2",
|
|
field: "CLK48SEL",
|
|
},
|
|
);
|
|
}
|
|
if chip_name.starts_with("stm32f4") && !chip_name.starts_with("stm32f410") {
|
|
clock_gen.chained_muxes.insert(
|
|
"CLK48",
|
|
&PeripheralRccRegister {
|
|
register: "DCKCFGR",
|
|
field: "CLK48SEL",
|
|
},
|
|
);
|
|
}
|
|
|
|
impl<'a> ClockGen<'a> {
|
|
fn gen_clock(&mut self, peripheral: &str, name: &str) -> TokenStream {
|
|
let clock_name = format_ident!("{}", name.to_ascii_lowercase());
|
|
self.clock_names.insert(name.to_ascii_lowercase());
|
|
quote!(unsafe {
|
|
unwrap!(
|
|
crate::rcc::get_freqs().#clock_name.to_hertz(),
|
|
"peripheral '{}' is configured to use the '{}' clock, which is not running. \
|
|
Either enable it in 'config.rcc' or change 'config.rcc.mux' to use another clock",
|
|
#peripheral,
|
|
#name
|
|
)
|
|
})
|
|
}
|
|
|
|
fn gen_mux(&mut self, peripheral: &str, mux: &PeripheralRccRegister) -> TokenStream {
|
|
let ir = &self.rcc_registers.ir;
|
|
let fieldset_name = mux.register.to_ascii_lowercase();
|
|
let fieldset = ir
|
|
.fieldsets
|
|
.iter()
|
|
.find(|i| i.name.eq_ignore_ascii_case(&fieldset_name))
|
|
.unwrap();
|
|
let field_name = mux.field.to_ascii_lowercase();
|
|
let field = fieldset.fields.iter().find(|i| i.name == field_name).unwrap();
|
|
let enum_name = field.enumm.unwrap();
|
|
let enumm = ir.enums.iter().find(|i| i.name == enum_name).unwrap();
|
|
|
|
let fieldset_name = format_ident!("{}", fieldset_name);
|
|
let field_name = format_ident!("{}", field_name);
|
|
let enum_name = format_ident!("{}", enum_name);
|
|
|
|
self.muxes
|
|
.insert((fieldset_name.clone(), field_name.clone(), enum_name.clone()));
|
|
|
|
let mut match_arms = TokenStream::new();
|
|
|
|
for v in enumm.variants.iter().filter(|v| v.name != "DISABLE") {
|
|
let variant_name = format_ident!("{}", v.name);
|
|
let expr = if let Some(mux) = self.chained_muxes.get(&v.name) {
|
|
self.gen_mux(peripheral, mux)
|
|
} else {
|
|
self.gen_clock(peripheral, v.name)
|
|
};
|
|
match_arms.extend(quote! {
|
|
crate::pac::rcc::vals::#enum_name::#variant_name => #expr,
|
|
});
|
|
}
|
|
|
|
quote! {
|
|
match crate::pac::RCC.#fieldset_name().read().#field_name() {
|
|
#match_arms
|
|
#[allow(unreachable_patterns)]
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let mut refcount_idxs = HashMap::new();
|
|
|
|
for p in METADATA.peripherals {
|
|
if !singletons.contains(&p.name.to_string()) {
|
|
continue;
|
|
}
|
|
|
|
if let Some(rcc) = &p.rcc {
|
|
let rst_reg = rcc.reset.as_ref();
|
|
let en_reg = rcc.enable.as_ref().unwrap();
|
|
let pname = format_ident!("{}", p.name);
|
|
|
|
let get_offset_and_bit = |reg: &PeripheralRccRegister| -> TokenStream {
|
|
let reg_offset = rcc_block
|
|
.items
|
|
.iter()
|
|
.find(|i| i.name.eq_ignore_ascii_case(reg.register))
|
|
.unwrap()
|
|
.byte_offset;
|
|
let reg_offset: u8 = (reg_offset / 4).try_into().unwrap();
|
|
|
|
let bit_offset = &rcc_registers
|
|
.ir
|
|
.fieldsets
|
|
.iter()
|
|
.find(|i| i.name.eq_ignore_ascii_case(reg.register))
|
|
.unwrap()
|
|
.fields
|
|
.iter()
|
|
.find(|i| i.name.eq_ignore_ascii_case(reg.field))
|
|
.unwrap()
|
|
.bit_offset;
|
|
let BitOffset::Regular(bit_offset) = bit_offset else {
|
|
panic!("cursed bit offset")
|
|
};
|
|
let bit_offset: u8 = bit_offset.offset.try_into().unwrap();
|
|
|
|
quote! { (#reg_offset, #bit_offset) }
|
|
};
|
|
|
|
let reset_offset_and_bit = match rst_reg {
|
|
Some(rst_reg) => {
|
|
let reset_offset_and_bit = get_offset_and_bit(rst_reg);
|
|
quote! { Some(#reset_offset_and_bit) }
|
|
}
|
|
None => quote! { None },
|
|
};
|
|
let enable_offset_and_bit = get_offset_and_bit(en_reg);
|
|
|
|
let needs_refcount = *rcc_field_count.get(&(en_reg.register, en_reg.field)).unwrap() > 1;
|
|
let refcount_idx = if needs_refcount {
|
|
let next_refcount_idx = refcount_idxs.len() as u8;
|
|
let refcount_idx = *refcount_idxs
|
|
.entry((en_reg.register, en_reg.field))
|
|
.or_insert(next_refcount_idx);
|
|
quote! { Some(#refcount_idx) }
|
|
} else {
|
|
quote! { None }
|
|
};
|
|
|
|
let clock_frequency = match &rcc.kernel_clock {
|
|
PeripheralRccKernelClock::Mux(mux) => clock_gen.gen_mux(p.name, mux),
|
|
PeripheralRccKernelClock::Clock(clock) => clock_gen.gen_clock(p.name, clock),
|
|
};
|
|
|
|
// A refcount leak can result if the same field is shared by peripherals with different stop modes
|
|
// This condition should be checked in stm32-data
|
|
let stop_mode = match rcc.stop_mode {
|
|
StopMode::Standby => quote! { crate::rcc::StopMode::Standby },
|
|
StopMode::Stop2 => quote! { crate::rcc::StopMode::Stop2 },
|
|
StopMode::Stop1 => quote! { crate::rcc::StopMode::Stop1 },
|
|
};
|
|
|
|
g.extend(quote! {
|
|
impl crate::rcc::SealedRccPeripheral for peripherals::#pname {
|
|
fn frequency() -> crate::time::Hertz {
|
|
#clock_frequency
|
|
}
|
|
|
|
const RCC_INFO: crate::rcc::RccInfo = unsafe {
|
|
crate::rcc::RccInfo::new(
|
|
#reset_offset_and_bit,
|
|
#enable_offset_and_bit,
|
|
#refcount_idx,
|
|
#[cfg(feature = "low-power")]
|
|
#stop_mode,
|
|
)
|
|
};
|
|
}
|
|
|
|
impl crate::rcc::RccPeripheral for peripherals::#pname {}
|
|
});
|
|
}
|
|
}
|
|
|
|
g.extend({
|
|
let refcounts_len = refcount_idxs.len();
|
|
let refcount_zeros: TokenStream = refcount_idxs.iter().map(|_| quote! { 0u8, }).collect();
|
|
quote! {
|
|
pub(crate) static mut REFCOUNTS: [u8; #refcounts_len] = [#refcount_zeros];
|
|
}
|
|
});
|
|
|
|
let struct_fields: Vec<_> = clock_gen
|
|
.muxes
|
|
.iter()
|
|
.map(|(_fieldset, fieldname, enum_name)| {
|
|
quote! {
|
|
pub #fieldname: #enum_name
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
let mut inits = TokenStream::new();
|
|
for fieldset in clock_gen
|
|
.muxes
|
|
.iter()
|
|
.map(|(f, _, _)| f)
|
|
.collect::<BTreeSet<_>>()
|
|
.into_iter()
|
|
{
|
|
let setters: Vec<_> = clock_gen
|
|
.muxes
|
|
.iter()
|
|
.filter(|(f, _, _)| f == fieldset)
|
|
.map(|(_, fieldname, _)| {
|
|
let setter = format_ident!("set_{}", fieldname);
|
|
quote! {
|
|
w.#setter(self.#fieldname);
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
inits.extend(quote! {
|
|
crate::pac::RCC.#fieldset().modify(|w| {
|
|
#(#setters)*
|
|
});
|
|
})
|
|
}
|
|
|
|
let enum_names: BTreeSet<_> = clock_gen.muxes.iter().map(|(_, _, enum_name)| enum_name).collect();
|
|
|
|
g.extend(quote! {
|
|
pub mod mux {
|
|
#(pub use crate::pac::rcc::vals::#enum_names as #enum_names; )*
|
|
|
|
#[derive(Clone, Copy)]
|
|
#[non_exhaustive]
|
|
pub struct ClockMux {
|
|
#( #struct_fields, )*
|
|
}
|
|
|
|
impl ClockMux {
|
|
pub(crate) const fn default() -> Self {
|
|
// safety: zero value is valid for all PAC enums.
|
|
unsafe { ::core::mem::zeroed() }
|
|
}
|
|
}
|
|
|
|
impl Default for ClockMux {
|
|
fn default() -> Self {
|
|
Self::default()
|
|
}
|
|
}
|
|
|
|
impl ClockMux {
|
|
pub(crate) fn init(&self) {
|
|
#inits
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Generate RCC
|
|
clock_gen.clock_names.insert("sys".to_string());
|
|
clock_gen.clock_names.insert("rtc".to_string());
|
|
let clock_idents: Vec<_> = clock_gen.clock_names.iter().map(|n| format_ident!("{}", n)).collect();
|
|
g.extend(quote! {
|
|
#[derive(Clone, Copy, Debug)]
|
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
#[repr(C)]
|
|
pub struct Clocks {
|
|
#(
|
|
pub #clock_idents: crate::time::MaybeHertz,
|
|
)*
|
|
}
|
|
});
|
|
|
|
let clocks_macro = quote!(
|
|
macro_rules! set_clocks {
|
|
($($(#[$m:meta])* $k:ident: $v:expr,)*) => {
|
|
{
|
|
#[allow(unused)]
|
|
struct Temp {
|
|
$($(#[$m])* $k: Option<crate::time::Hertz>,)*
|
|
}
|
|
let all = Temp {
|
|
$($(#[$m])* $k: $v,)*
|
|
};
|
|
crate::rcc::set_freqs(crate::rcc::Clocks {
|
|
#( #clock_idents: all.#clock_idents.into(), )*
|
|
});
|
|
}
|
|
};
|
|
}
|
|
);
|
|
|
|
// ========
|
|
// Generate fns to enable GPIO, DMA in RCC
|
|
|
|
for kind in ["dma", "bdma", "dmamux", "gpdma", "gpio"] {
|
|
let mut gg = TokenStream::new();
|
|
|
|
for p in METADATA.peripherals {
|
|
if p.registers.is_some() && p.registers.as_ref().unwrap().kind == kind {
|
|
if let Some(rcc) = &p.rcc {
|
|
let en = rcc.enable.as_ref().unwrap();
|
|
let en_reg = format_ident!("{}", en.register.to_ascii_lowercase());
|
|
let set_en_field = format_ident!("set_{}", en.field.to_ascii_lowercase());
|
|
|
|
gg.extend(quote! {
|
|
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true));
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
let fname = format_ident!("init_{}", kind);
|
|
g.extend(quote! {
|
|
pub unsafe fn #fname(){
|
|
#gg
|
|
}
|
|
})
|
|
}
|
|
|
|
// ========
|
|
// Generate pin_trait_impl!
|
|
|
|
#[rustfmt::skip]
|
|
let signals: HashMap<_, _> = [
|
|
// (kind, signal) => trait
|
|
(("ucpd", "CC1"), quote!(crate::ucpd::Cc1Pin)),
|
|
(("ucpd", "CC2"), quote!(crate::ucpd::Cc2Pin)),
|
|
(("usart", "TX"), quote!(crate::usart::TxPin)),
|
|
(("usart", "RX"), quote!(crate::usart::RxPin)),
|
|
(("usart", "CTS"), quote!(crate::usart::CtsPin)),
|
|
(("usart", "RTS"), quote!(crate::usart::RtsPin)),
|
|
(("usart", "CK"), quote!(crate::usart::CkPin)),
|
|
(("usart", "DE"), quote!(crate::usart::DePin)),
|
|
(("lpuart", "TX"), quote!(crate::usart::TxPin)),
|
|
(("lpuart", "RX"), quote!(crate::usart::RxPin)),
|
|
(("lpuart", "CTS"), quote!(crate::usart::CtsPin)),
|
|
(("lpuart", "RTS"), quote!(crate::usart::RtsPin)),
|
|
(("lpuart", "CK"), quote!(crate::usart::CkPin)),
|
|
(("lpuart", "DE"), quote!(crate::usart::DePin)),
|
|
(("sai", "SCK_A"), quote!(crate::sai::SckPin<A>)),
|
|
(("sai", "SCK_B"), quote!(crate::sai::SckPin<B>)),
|
|
(("sai", "FS_A"), quote!(crate::sai::FsPin<A>)),
|
|
(("sai", "FS_B"), quote!(crate::sai::FsPin<B>)),
|
|
(("sai", "SD_A"), quote!(crate::sai::SdPin<A>)),
|
|
(("sai", "SD_B"), quote!(crate::sai::SdPin<B>)),
|
|
(("sai", "MCLK_A"), quote!(crate::sai::MclkPin<A>)),
|
|
(("sai", "MCLK_B"), quote!(crate::sai::MclkPin<B>)),
|
|
(("sai", "WS"), quote!(crate::sai::WsPin)),
|
|
(("spi", "SCK"), quote!(crate::spi::SckPin)),
|
|
(("spi", "MOSI"), quote!(crate::spi::MosiPin)),
|
|
(("spi", "MISO"), quote!(crate::spi::MisoPin)),
|
|
(("spi", "NSS"), quote!(crate::spi::CsPin)),
|
|
(("spi", "I2S_MCK"), quote!(crate::spi::MckPin)),
|
|
(("spi", "I2S_CK"), quote!(crate::spi::CkPin)),
|
|
(("spi", "I2S_WS"), quote!(crate::spi::WsPin)),
|
|
(("i2c", "SDA"), quote!(crate::i2c::SdaPin)),
|
|
(("i2c", "SCL"), quote!(crate::i2c::SclPin)),
|
|
(("rcc", "MCO_1"), quote!(crate::rcc::McoPin)),
|
|
(("rcc", "MCO_2"), quote!(crate::rcc::McoPin)),
|
|
(("rcc", "MCO"), quote!(crate::rcc::McoPin)),
|
|
(("dcmi", "D0"), quote!(crate::dcmi::D0Pin)),
|
|
(("dcmi", "D1"), quote!(crate::dcmi::D1Pin)),
|
|
(("dcmi", "D2"), quote!(crate::dcmi::D2Pin)),
|
|
(("dcmi", "D3"), quote!(crate::dcmi::D3Pin)),
|
|
(("dcmi", "D4"), quote!(crate::dcmi::D4Pin)),
|
|
(("dcmi", "D5"), quote!(crate::dcmi::D5Pin)),
|
|
(("dcmi", "D6"), quote!(crate::dcmi::D6Pin)),
|
|
(("dcmi", "D7"), quote!(crate::dcmi::D7Pin)),
|
|
(("dcmi", "D8"), quote!(crate::dcmi::D8Pin)),
|
|
(("dcmi", "D9"), quote!(crate::dcmi::D9Pin)),
|
|
(("dcmi", "D10"), quote!(crate::dcmi::D10Pin)),
|
|
(("dcmi", "D11"), quote!(crate::dcmi::D11Pin)),
|
|
(("dcmi", "D12"), quote!(crate::dcmi::D12Pin)),
|
|
(("dcmi", "D13"), quote!(crate::dcmi::D13Pin)),
|
|
(("dcmi", "HSYNC"), quote!(crate::dcmi::HSyncPin)),
|
|
(("dcmi", "VSYNC"), quote!(crate::dcmi::VSyncPin)),
|
|
(("dcmi", "PIXCLK"), quote!(crate::dcmi::PixClkPin)),
|
|
(("dsihost", "TE"), quote!(crate::dsihost::TePin)),
|
|
(("ltdc", "CLK"), quote!(crate::ltdc::ClkPin)),
|
|
(("ltdc", "HSYNC"), quote!(crate::ltdc::HsyncPin)),
|
|
(("ltdc", "VSYNC"), quote!(crate::ltdc::VsyncPin)),
|
|
(("ltdc", "DE"), quote!(crate::ltdc::DePin)),
|
|
(("ltdc", "R0"), quote!(crate::ltdc::R0Pin)),
|
|
(("ltdc", "R1"), quote!(crate::ltdc::R1Pin)),
|
|
(("ltdc", "R2"), quote!(crate::ltdc::R2Pin)),
|
|
(("ltdc", "R3"), quote!(crate::ltdc::R3Pin)),
|
|
(("ltdc", "R4"), quote!(crate::ltdc::R4Pin)),
|
|
(("ltdc", "R5"), quote!(crate::ltdc::R5Pin)),
|
|
(("ltdc", "R6"), quote!(crate::ltdc::R6Pin)),
|
|
(("ltdc", "R7"), quote!(crate::ltdc::R7Pin)),
|
|
(("ltdc", "G0"), quote!(crate::ltdc::G0Pin)),
|
|
(("ltdc", "G1"), quote!(crate::ltdc::G1Pin)),
|
|
(("ltdc", "G2"), quote!(crate::ltdc::G2Pin)),
|
|
(("ltdc", "G3"), quote!(crate::ltdc::G3Pin)),
|
|
(("ltdc", "G4"), quote!(crate::ltdc::G4Pin)),
|
|
(("ltdc", "G5"), quote!(crate::ltdc::G5Pin)),
|
|
(("ltdc", "G6"), quote!(crate::ltdc::G6Pin)),
|
|
(("ltdc", "G7"), quote!(crate::ltdc::G7Pin)),
|
|
(("ltdc", "B0"), quote!(crate::ltdc::B0Pin)),
|
|
(("ltdc", "B1"), quote!(crate::ltdc::B1Pin)),
|
|
(("ltdc", "B2"), quote!(crate::ltdc::B2Pin)),
|
|
(("ltdc", "B3"), quote!(crate::ltdc::B3Pin)),
|
|
(("ltdc", "B4"), quote!(crate::ltdc::B4Pin)),
|
|
(("ltdc", "B5"), quote!(crate::ltdc::B5Pin)),
|
|
(("ltdc", "B6"), quote!(crate::ltdc::B6Pin)),
|
|
(("ltdc", "B7"), quote!(crate::ltdc::B7Pin)),
|
|
(("usb", "DP"), quote!(crate::usb::DpPin)),
|
|
(("usb", "DM"), quote!(crate::usb::DmPin)),
|
|
(("otg", "DP"), quote!(crate::usb::DpPin)),
|
|
(("otg", "DM"), quote!(crate::usb::DmPin)),
|
|
(("otg", "ULPI_CK"), quote!(crate::usb::UlpiClkPin)),
|
|
(("otg", "ULPI_DIR"), quote!(crate::usb::UlpiDirPin)),
|
|
(("otg", "ULPI_NXT"), quote!(crate::usb::UlpiNxtPin)),
|
|
(("otg", "ULPI_STP"), quote!(crate::usb::UlpiStpPin)),
|
|
(("otg", "ULPI_D0"), quote!(crate::usb::UlpiD0Pin)),
|
|
(("otg", "ULPI_D1"), quote!(crate::usb::UlpiD1Pin)),
|
|
(("otg", "ULPI_D2"), quote!(crate::usb::UlpiD2Pin)),
|
|
(("otg", "ULPI_D3"), quote!(crate::usb::UlpiD3Pin)),
|
|
(("otg", "ULPI_D4"), quote!(crate::usb::UlpiD4Pin)),
|
|
(("otg", "ULPI_D5"), quote!(crate::usb::UlpiD5Pin)),
|
|
(("otg", "ULPI_D6"), quote!(crate::usb::UlpiD6Pin)),
|
|
(("otg", "ULPI_D7"), quote!(crate::usb::UlpiD7Pin)),
|
|
(("can", "TX"), quote!(crate::can::TxPin)),
|
|
(("can", "RX"), quote!(crate::can::RxPin)),
|
|
(("eth", "REF_CLK"), quote!(crate::eth::RefClkPin)),
|
|
(("eth", "RX_CLK"), quote!(crate::eth::RXClkPin)),
|
|
(("eth", "TX_CLK"), quote!(crate::eth::TXClkPin)),
|
|
(("eth", "MDIO"), quote!(crate::eth::MDIOPin)),
|
|
(("eth", "MDC"), quote!(crate::eth::MDCPin)),
|
|
(("eth", "CRS_DV"), quote!(crate::eth::CRSPin)),
|
|
(("eth", "RX_DV"), quote!(crate::eth::RXDVPin)),
|
|
(("eth", "RXD0"), quote!(crate::eth::RXD0Pin)),
|
|
(("eth", "RXD1"), quote!(crate::eth::RXD1Pin)),
|
|
(("eth", "RXD2"), quote!(crate::eth::RXD2Pin)),
|
|
(("eth", "RXD3"), quote!(crate::eth::RXD3Pin)),
|
|
(("eth", "TXD0"), quote!(crate::eth::TXD0Pin)),
|
|
(("eth", "TXD1"), quote!(crate::eth::TXD1Pin)),
|
|
(("eth", "TXD2"), quote!(crate::eth::TXD2Pin)),
|
|
(("eth", "TXD3"), quote!(crate::eth::TXD3Pin)),
|
|
(("eth", "TX_EN"), quote!(crate::eth::TXEnPin)),
|
|
(("fmc", "A0"), quote!(crate::fmc::A0Pin)),
|
|
(("fmc", "A1"), quote!(crate::fmc::A1Pin)),
|
|
(("fmc", "A2"), quote!(crate::fmc::A2Pin)),
|
|
(("fmc", "A3"), quote!(crate::fmc::A3Pin)),
|
|
(("fmc", "A4"), quote!(crate::fmc::A4Pin)),
|
|
(("fmc", "A5"), quote!(crate::fmc::A5Pin)),
|
|
(("fmc", "A6"), quote!(crate::fmc::A6Pin)),
|
|
(("fmc", "A7"), quote!(crate::fmc::A7Pin)),
|
|
(("fmc", "A8"), quote!(crate::fmc::A8Pin)),
|
|
(("fmc", "A9"), quote!(crate::fmc::A9Pin)),
|
|
(("fmc", "A10"), quote!(crate::fmc::A10Pin)),
|
|
(("fmc", "A11"), quote!(crate::fmc::A11Pin)),
|
|
(("fmc", "A12"), quote!(crate::fmc::A12Pin)),
|
|
(("fmc", "A13"), quote!(crate::fmc::A13Pin)),
|
|
(("fmc", "A14"), quote!(crate::fmc::A14Pin)),
|
|
(("fmc", "A15"), quote!(crate::fmc::A15Pin)),
|
|
(("fmc", "A16"), quote!(crate::fmc::A16Pin)),
|
|
(("fmc", "A17"), quote!(crate::fmc::A17Pin)),
|
|
(("fmc", "A18"), quote!(crate::fmc::A18Pin)),
|
|
(("fmc", "A19"), quote!(crate::fmc::A19Pin)),
|
|
(("fmc", "A20"), quote!(crate::fmc::A20Pin)),
|
|
(("fmc", "A21"), quote!(crate::fmc::A21Pin)),
|
|
(("fmc", "A22"), quote!(crate::fmc::A22Pin)),
|
|
(("fmc", "A23"), quote!(crate::fmc::A23Pin)),
|
|
(("fmc", "A24"), quote!(crate::fmc::A24Pin)),
|
|
(("fmc", "A25"), quote!(crate::fmc::A25Pin)),
|
|
(("fmc", "D0"), quote!(crate::fmc::D0Pin)),
|
|
(("fmc", "D1"), quote!(crate::fmc::D1Pin)),
|
|
(("fmc", "D2"), quote!(crate::fmc::D2Pin)),
|
|
(("fmc", "D3"), quote!(crate::fmc::D3Pin)),
|
|
(("fmc", "D4"), quote!(crate::fmc::D4Pin)),
|
|
(("fmc", "D5"), quote!(crate::fmc::D5Pin)),
|
|
(("fmc", "D6"), quote!(crate::fmc::D6Pin)),
|
|
(("fmc", "D7"), quote!(crate::fmc::D7Pin)),
|
|
(("fmc", "D8"), quote!(crate::fmc::D8Pin)),
|
|
(("fmc", "D9"), quote!(crate::fmc::D9Pin)),
|
|
(("fmc", "D10"), quote!(crate::fmc::D10Pin)),
|
|
(("fmc", "D11"), quote!(crate::fmc::D11Pin)),
|
|
(("fmc", "D12"), quote!(crate::fmc::D12Pin)),
|
|
(("fmc", "D13"), quote!(crate::fmc::D13Pin)),
|
|
(("fmc", "D14"), quote!(crate::fmc::D14Pin)),
|
|
(("fmc", "D15"), quote!(crate::fmc::D15Pin)),
|
|
(("fmc", "D16"), quote!(crate::fmc::D16Pin)),
|
|
(("fmc", "D17"), quote!(crate::fmc::D17Pin)),
|
|
(("fmc", "D18"), quote!(crate::fmc::D18Pin)),
|
|
(("fmc", "D19"), quote!(crate::fmc::D19Pin)),
|
|
(("fmc", "D20"), quote!(crate::fmc::D20Pin)),
|
|
(("fmc", "D21"), quote!(crate::fmc::D21Pin)),
|
|
(("fmc", "D22"), quote!(crate::fmc::D22Pin)),
|
|
(("fmc", "D23"), quote!(crate::fmc::D23Pin)),
|
|
(("fmc", "D24"), quote!(crate::fmc::D24Pin)),
|
|
(("fmc", "D25"), quote!(crate::fmc::D25Pin)),
|
|
(("fmc", "D26"), quote!(crate::fmc::D26Pin)),
|
|
(("fmc", "D27"), quote!(crate::fmc::D27Pin)),
|
|
(("fmc", "D28"), quote!(crate::fmc::D28Pin)),
|
|
(("fmc", "D29"), quote!(crate::fmc::D29Pin)),
|
|
(("fmc", "D30"), quote!(crate::fmc::D30Pin)),
|
|
(("fmc", "D31"), quote!(crate::fmc::D31Pin)),
|
|
(("fmc", "DA0"), quote!(crate::fmc::DA0Pin)),
|
|
(("fmc", "DA1"), quote!(crate::fmc::DA1Pin)),
|
|
(("fmc", "DA2"), quote!(crate::fmc::DA2Pin)),
|
|
(("fmc", "DA3"), quote!(crate::fmc::DA3Pin)),
|
|
(("fmc", "DA4"), quote!(crate::fmc::DA4Pin)),
|
|
(("fmc", "DA5"), quote!(crate::fmc::DA5Pin)),
|
|
(("fmc", "DA6"), quote!(crate::fmc::DA6Pin)),
|
|
(("fmc", "DA7"), quote!(crate::fmc::DA7Pin)),
|
|
(("fmc", "DA8"), quote!(crate::fmc::DA8Pin)),
|
|
(("fmc", "DA9"), quote!(crate::fmc::DA9Pin)),
|
|
(("fmc", "DA10"), quote!(crate::fmc::DA10Pin)),
|
|
(("fmc", "DA11"), quote!(crate::fmc::DA11Pin)),
|
|
(("fmc", "DA12"), quote!(crate::fmc::DA12Pin)),
|
|
(("fmc", "DA13"), quote!(crate::fmc::DA13Pin)),
|
|
(("fmc", "DA14"), quote!(crate::fmc::DA14Pin)),
|
|
(("fmc", "DA15"), quote!(crate::fmc::DA15Pin)),
|
|
(("fmc", "SDNWE"), quote!(crate::fmc::SDNWEPin)),
|
|
(("fmc", "SDNCAS"), quote!(crate::fmc::SDNCASPin)),
|
|
(("fmc", "SDNRAS"), quote!(crate::fmc::SDNRASPin)),
|
|
(("fmc", "SDNE0"), quote!(crate::fmc::SDNE0Pin)),
|
|
(("fmc", "SDNE1"), quote!(crate::fmc::SDNE1Pin)),
|
|
(("fmc", "SDCKE0"), quote!(crate::fmc::SDCKE0Pin)),
|
|
(("fmc", "SDCKE1"), quote!(crate::fmc::SDCKE1Pin)),
|
|
(("fmc", "SDCLK"), quote!(crate::fmc::SDCLKPin)),
|
|
(("fmc", "NBL0"), quote!(crate::fmc::NBL0Pin)),
|
|
(("fmc", "NBL1"), quote!(crate::fmc::NBL1Pin)),
|
|
(("fmc", "NBL2"), quote!(crate::fmc::NBL2Pin)),
|
|
(("fmc", "NBL3"), quote!(crate::fmc::NBL3Pin)),
|
|
(("fmc", "INT"), quote!(crate::fmc::INTPin)),
|
|
(("fmc", "NL"), quote!(crate::fmc::NLPin)),
|
|
(("fmc", "NWAIT"), quote!(crate::fmc::NWaitPin)),
|
|
(("fmc", "NE1"), quote!(crate::fmc::NE1Pin)),
|
|
(("fmc", "NE2"), quote!(crate::fmc::NE2Pin)),
|
|
(("fmc", "NE3"), quote!(crate::fmc::NE3Pin)),
|
|
(("fmc", "NE4"), quote!(crate::fmc::NE4Pin)),
|
|
(("fmc", "NCE"), quote!(crate::fmc::NCEPin)),
|
|
(("fmc", "NOE"), quote!(crate::fmc::NOEPin)),
|
|
(("fmc", "NWE"), quote!(crate::fmc::NWEPin)),
|
|
(("fmc", "CLK"), quote!(crate::fmc::ClkPin)),
|
|
(("fmc", "BA0"), quote!(crate::fmc::BA0Pin)),
|
|
(("fmc", "BA1"), quote!(crate::fmc::BA1Pin)),
|
|
(("timer", "CH1"), quote!(crate::timer::Channel1Pin)),
|
|
(("timer", "CH1N"), quote!(crate::timer::Channel1ComplementaryPin)),
|
|
(("timer", "CH2"), quote!(crate::timer::Channel2Pin)),
|
|
(("timer", "CH2N"), quote!(crate::timer::Channel2ComplementaryPin)),
|
|
(("timer", "CH3"), quote!(crate::timer::Channel3Pin)),
|
|
(("timer", "CH3N"), quote!(crate::timer::Channel3ComplementaryPin)),
|
|
(("timer", "CH4"), quote!(crate::timer::Channel4Pin)),
|
|
(("timer", "CH4N"), quote!(crate::timer::Channel4ComplementaryPin)),
|
|
(("timer", "ETR"), quote!(crate::timer::ExternalTriggerPin)),
|
|
(("timer", "BKIN"), quote!(crate::timer::BreakInputPin)),
|
|
(("timer", "BKIN_COMP1"), quote!(crate::timer::BreakInputComparator1Pin)),
|
|
(("timer", "BKIN_COMP2"), quote!(crate::timer::BreakInputComparator2Pin)),
|
|
(("timer", "BKIN2"), quote!(crate::timer::BreakInput2Pin)),
|
|
(("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInput2Comparator1Pin)),
|
|
(("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInput2Comparator2Pin)),
|
|
(("hrtim", "CHA1"), quote!(crate::hrtim::ChannelAPin)),
|
|
(("hrtim", "CHA2"), quote!(crate::hrtim::ChannelAComplementaryPin)),
|
|
(("hrtim", "CHB1"), quote!(crate::hrtim::ChannelBPin)),
|
|
(("hrtim", "CHB2"), quote!(crate::hrtim::ChannelBComplementaryPin)),
|
|
(("hrtim", "CHC1"), quote!(crate::hrtim::ChannelCPin)),
|
|
(("hrtim", "CHC2"), quote!(crate::hrtim::ChannelCComplementaryPin)),
|
|
(("hrtim", "CHD1"), quote!(crate::hrtim::ChannelDPin)),
|
|
(("hrtim", "CHD2"), quote!(crate::hrtim::ChannelDComplementaryPin)),
|
|
(("hrtim", "CHE1"), quote!(crate::hrtim::ChannelEPin)),
|
|
(("hrtim", "CHE2"), quote!(crate::hrtim::ChannelEComplementaryPin)),
|
|
(("hrtim", "CHF1"), quote!(crate::hrtim::ChannelFPin)),
|
|
(("hrtim", "CHF2"), quote!(crate::hrtim::ChannelFComplementaryPin)),
|
|
(("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)),
|
|
(("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)),
|
|
(("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)),
|
|
(("sdmmc", "D1"), quote!(crate::sdmmc::D1Pin)),
|
|
(("sdmmc", "D2"), quote!(crate::sdmmc::D2Pin)),
|
|
(("sdmmc", "D3"), quote!(crate::sdmmc::D3Pin)),
|
|
(("sdmmc", "D4"), quote!(crate::sdmmc::D4Pin)),
|
|
(("sdmmc", "D5"), quote!(crate::sdmmc::D5Pin)),
|
|
(("sdmmc", "D6"), quote!(crate::sdmmc::D6Pin)),
|
|
(("sdmmc", "D6"), quote!(crate::sdmmc::D7Pin)),
|
|
(("sdmmc", "D8"), quote!(crate::sdmmc::D8Pin)),
|
|
(("quadspi", "BK1_IO0"), quote!(crate::qspi::BK1D0Pin)),
|
|
(("quadspi", "BK1_IO1"), quote!(crate::qspi::BK1D1Pin)),
|
|
(("quadspi", "BK1_IO2"), quote!(crate::qspi::BK1D2Pin)),
|
|
(("quadspi", "BK1_IO3"), quote!(crate::qspi::BK1D3Pin)),
|
|
(("quadspi", "BK1_NCS"), quote!(crate::qspi::BK1NSSPin)),
|
|
(("quadspi", "BK2_IO0"), quote!(crate::qspi::BK2D0Pin)),
|
|
(("quadspi", "BK2_IO1"), quote!(crate::qspi::BK2D1Pin)),
|
|
(("quadspi", "BK2_IO2"), quote!(crate::qspi::BK2D2Pin)),
|
|
(("quadspi", "BK2_IO3"), quote!(crate::qspi::BK2D3Pin)),
|
|
(("quadspi", "BK2_NCS"), quote!(crate::qspi::BK2NSSPin)),
|
|
(("quadspi", "CLK"), quote!(crate::qspi::SckPin)),
|
|
(("octospi", "IO0"), quote!(crate::ospi::D0Pin)),
|
|
(("octospi", "IO1"), quote!(crate::ospi::D1Pin)),
|
|
(("octospi", "IO2"), quote!(crate::ospi::D2Pin)),
|
|
(("octospi", "IO3"), quote!(crate::ospi::D3Pin)),
|
|
(("octospi", "IO4"), quote!(crate::ospi::D4Pin)),
|
|
(("octospi", "IO5"), quote!(crate::ospi::D5Pin)),
|
|
(("octospi", "IO6"), quote!(crate::ospi::D6Pin)),
|
|
(("octospi", "IO7"), quote!(crate::ospi::D7Pin)),
|
|
(("octospi", "DQS"), quote!(crate::ospi::DQSPin)),
|
|
(("octospi", "NCS"), quote!(crate::ospi::NSSPin)),
|
|
(("octospi", "CLK"), quote!(crate::ospi::SckPin)),
|
|
(("octospi", "NCLK"), quote!(crate::ospi::NckPin)),
|
|
(("tsc", "G1_IO1"), quote!(crate::tsc::G1IO1Pin)),
|
|
(("tsc", "G1_IO2"), quote!(crate::tsc::G1IO2Pin)),
|
|
(("tsc", "G1_IO3"), quote!(crate::tsc::G1IO3Pin)),
|
|
(("tsc", "G1_IO4"), quote!(crate::tsc::G1IO4Pin)),
|
|
(("tsc", "G2_IO1"), quote!(crate::tsc::G2IO1Pin)),
|
|
(("tsc", "G2_IO2"), quote!(crate::tsc::G2IO2Pin)),
|
|
(("tsc", "G2_IO3"), quote!(crate::tsc::G2IO3Pin)),
|
|
(("tsc", "G2_IO4"), quote!(crate::tsc::G2IO4Pin)),
|
|
(("tsc", "G3_IO1"), quote!(crate::tsc::G3IO1Pin)),
|
|
(("tsc", "G3_IO2"), quote!(crate::tsc::G3IO2Pin)),
|
|
(("tsc", "G3_IO3"), quote!(crate::tsc::G3IO3Pin)),
|
|
(("tsc", "G3_IO4"), quote!(crate::tsc::G3IO4Pin)),
|
|
(("tsc", "G4_IO1"), quote!(crate::tsc::G4IO1Pin)),
|
|
(("tsc", "G4_IO2"), quote!(crate::tsc::G4IO2Pin)),
|
|
(("tsc", "G4_IO3"), quote!(crate::tsc::G4IO3Pin)),
|
|
(("tsc", "G4_IO4"), quote!(crate::tsc::G4IO4Pin)),
|
|
(("tsc", "G5_IO1"), quote!(crate::tsc::G5IO1Pin)),
|
|
(("tsc", "G5_IO2"), quote!(crate::tsc::G5IO2Pin)),
|
|
(("tsc", "G5_IO3"), quote!(crate::tsc::G5IO3Pin)),
|
|
(("tsc", "G5_IO4"), quote!(crate::tsc::G5IO4Pin)),
|
|
(("tsc", "G6_IO1"), quote!(crate::tsc::G6IO1Pin)),
|
|
(("tsc", "G6_IO2"), quote!(crate::tsc::G6IO2Pin)),
|
|
(("tsc", "G6_IO3"), quote!(crate::tsc::G6IO3Pin)),
|
|
(("tsc", "G6_IO4"), quote!(crate::tsc::G6IO4Pin)),
|
|
(("tsc", "G7_IO1"), quote!(crate::tsc::G7IO1Pin)),
|
|
(("tsc", "G7_IO2"), quote!(crate::tsc::G7IO2Pin)),
|
|
(("tsc", "G7_IO3"), quote!(crate::tsc::G7IO3Pin)),
|
|
(("tsc", "G7_IO4"), quote!(crate::tsc::G7IO4Pin)),
|
|
(("tsc", "G8_IO1"), quote!(crate::tsc::G8IO1Pin)),
|
|
(("tsc", "G8_IO2"), quote!(crate::tsc::G8IO2Pin)),
|
|
(("tsc", "G8_IO3"), quote!(crate::tsc::G8IO3Pin)),
|
|
(("tsc", "G8_IO4"), quote!(crate::tsc::G8IO4Pin)),
|
|
].into();
|
|
|
|
for p in METADATA.peripherals {
|
|
if let Some(regs) = &p.registers {
|
|
for pin in p.pins {
|
|
let key = (regs.kind, pin.signal);
|
|
if let Some(tr) = signals.get(&key) {
|
|
let mut peri = format_ident!("{}", p.name);
|
|
|
|
let pin_name = {
|
|
// If we encounter a _C pin but the split_feature for this pin is not enabled, skip it
|
|
if pin.pin.ends_with("_C") && !split_features.iter().any(|x| x.pin_name_with_c == pin.pin) {
|
|
continue;
|
|
}
|
|
|
|
format_ident!("{}", pin.pin)
|
|
};
|
|
|
|
let af = pin.af.unwrap_or(0);
|
|
|
|
// MCO is special
|
|
if pin.signal.starts_with("MCO") {
|
|
peri = format_ident!("{}", pin.signal.replace('_', ""));
|
|
}
|
|
|
|
g.extend(quote! {
|
|
pin_trait_impl!(#tr, #peri, #pin_name, #af);
|
|
})
|
|
}
|
|
|
|
// ADC is special
|
|
if regs.kind == "adc" {
|
|
if p.rcc.is_none() {
|
|
continue;
|
|
}
|
|
|
|
let peri = format_ident!("{}", p.name);
|
|
let pin_name = {
|
|
// If we encounter a _C pin but the split_feature for this pin is not enabled, skip it
|
|
if pin.pin.ends_with("_C") && !split_features.iter().any(|x| x.pin_name_with_c == pin.pin) {
|
|
continue;
|
|
}
|
|
format_ident!("{}", pin.pin)
|
|
};
|
|
|
|
// H7 has differential voltage measurements
|
|
let ch: Option<u8> = if pin.signal.starts_with("INP") {
|
|
Some(pin.signal.strip_prefix("INP").unwrap().parse().unwrap())
|
|
} else if pin.signal.starts_with("INN") {
|
|
// TODO handle in the future when embassy supports differential measurements
|
|
None
|
|
} else if pin.signal.starts_with("IN") && pin.signal.ends_with('b') {
|
|
// we number STM32L1 ADC bank 1 as 0..=31, bank 2 as 32..=63
|
|
let signal = pin.signal.strip_prefix("IN").unwrap().strip_suffix('b').unwrap();
|
|
Some(32u8 + signal.parse::<u8>().unwrap())
|
|
} else if pin.signal.starts_with("IN") {
|
|
Some(pin.signal.strip_prefix("IN").unwrap().parse().unwrap())
|
|
} else {
|
|
None
|
|
};
|
|
if let Some(ch) = ch {
|
|
g.extend(quote! {
|
|
impl_adc_pin!( #peri, #pin_name, #ch);
|
|
})
|
|
}
|
|
}
|
|
|
|
if regs.kind == "opamp" {
|
|
if pin.signal.starts_with("VP") {
|
|
// Impl NonInvertingPin for the VP* signals (VP0, VP1, VP2, etc)
|
|
let peri = format_ident!("{}", p.name);
|
|
let pin_name = format_ident!("{}", pin.pin);
|
|
let ch: u8 = pin.signal.strip_prefix("VP").unwrap().parse().unwrap();
|
|
|
|
g.extend(quote! {
|
|
impl_opamp_vp_pin!( #peri, #pin_name, #ch);
|
|
})
|
|
} else if pin.signal == "VOUT" {
|
|
// Impl OutputPin for the VOUT pin
|
|
let peri = format_ident!("{}", p.name);
|
|
let pin_name = format_ident!("{}", pin.pin);
|
|
g.extend(quote! {
|
|
impl_opamp_vout_pin!( #peri, #pin_name );
|
|
})
|
|
}
|
|
}
|
|
|
|
// DAC is special
|
|
if regs.kind == "dac" {
|
|
let peri = format_ident!("{}", p.name);
|
|
let pin_name = format_ident!("{}", pin.pin);
|
|
let ch: u8 = pin.signal.strip_prefix("OUT").unwrap().parse().unwrap();
|
|
|
|
g.extend(quote! {
|
|
impl_dac_pin!( #peri, #pin_name, #ch);
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ========
|
|
// Generate dma_trait_impl!
|
|
|
|
let signals: HashMap<_, _> = [
|
|
// (kind, signal) => trait
|
|
(("adc", "ADC"), quote!(crate::adc::RxDma)),
|
|
(("adc", "ADC1"), quote!(crate::adc::RxDma)),
|
|
(("adc", "ADC2"), quote!(crate::adc::RxDma)),
|
|
(("adc", "ADC3"), quote!(crate::adc::RxDma)),
|
|
(("adc", "ADC4"), quote!(crate::adc::RxDma)),
|
|
(("ucpd", "RX"), quote!(crate::ucpd::RxDma)),
|
|
(("ucpd", "TX"), quote!(crate::ucpd::TxDma)),
|
|
(("usart", "RX"), quote!(crate::usart::RxDma)),
|
|
(("usart", "TX"), quote!(crate::usart::TxDma)),
|
|
(("lpuart", "RX"), quote!(crate::usart::RxDma)),
|
|
(("lpuart", "TX"), quote!(crate::usart::TxDma)),
|
|
(("sai", "A"), quote!(crate::sai::Dma<A>)),
|
|
(("sai", "B"), quote!(crate::sai::Dma<B>)),
|
|
(("spi", "RX"), quote!(crate::spi::RxDma)),
|
|
(("spi", "TX"), quote!(crate::spi::TxDma)),
|
|
(("i2c", "RX"), quote!(crate::i2c::RxDma)),
|
|
(("i2c", "TX"), quote!(crate::i2c::TxDma)),
|
|
(("dcmi", "DCMI"), quote!(crate::dcmi::FrameDma)),
|
|
(("dcmi", "PSSI"), quote!(crate::dcmi::FrameDma)),
|
|
// SDMMCv1 uses the same channel for both directions, so just implement for RX
|
|
(("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)),
|
|
(("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)),
|
|
(("octospi", "OCTOSPI1"), quote!(crate::ospi::OctoDma)),
|
|
(("dac", "CH1"), quote!(crate::dac::DacDma1)),
|
|
(("dac", "CH2"), quote!(crate::dac::DacDma2)),
|
|
(("timer", "UP"), quote!(crate::timer::UpDma)),
|
|
(("hash", "IN"), quote!(crate::hash::Dma)),
|
|
(("cryp", "IN"), quote!(crate::cryp::DmaIn)),
|
|
(("cryp", "OUT"), quote!(crate::cryp::DmaOut)),
|
|
(("timer", "CH1"), quote!(crate::timer::Ch1Dma)),
|
|
(("timer", "CH2"), quote!(crate::timer::Ch2Dma)),
|
|
(("timer", "CH3"), quote!(crate::timer::Ch3Dma)),
|
|
(("timer", "CH4"), quote!(crate::timer::Ch4Dma)),
|
|
(("cordic", "WRITE"), quote!(crate::cordic::WriteDma)), // FIXME: stm32u5a crash on Cordic driver
|
|
(("cordic", "READ"), quote!(crate::cordic::ReadDma)), // FIXME: stm32u5a crash on Cordic driver
|
|
]
|
|
.into();
|
|
|
|
for p in METADATA.peripherals {
|
|
if let Some(regs) = &p.registers {
|
|
// FIXME: stm32u5a crash on Cordic driver
|
|
if chip_name.starts_with("stm32u5a") && regs.kind == "cordic" {
|
|
continue;
|
|
}
|
|
|
|
let mut dupe = HashSet::new();
|
|
for ch in p.dma_channels {
|
|
if let Some(tr) = signals.get(&(regs.kind, ch.signal)) {
|
|
let peri = format_ident!("{}", p.name);
|
|
|
|
let channels = if let Some(channel) = &ch.channel {
|
|
// Chip with DMA/BDMA, without DMAMUX
|
|
vec![*channel]
|
|
} else if let Some(dmamux) = &ch.dmamux {
|
|
// Chip with DMAMUX
|
|
METADATA
|
|
.dma_channels
|
|
.iter()
|
|
.filter(|ch| ch.dmamux == Some(*dmamux))
|
|
.map(|ch| ch.name)
|
|
.collect()
|
|
} else if let Some(dma) = &ch.dma {
|
|
// Chip with GPDMA
|
|
METADATA
|
|
.dma_channels
|
|
.iter()
|
|
.filter(|ch| ch.dma == *dma)
|
|
.map(|ch| ch.name)
|
|
.collect()
|
|
} else {
|
|
unreachable!();
|
|
};
|
|
|
|
for channel in channels {
|
|
// Some chips have multiple request numbers for the same (peri, signal, channel) combos.
|
|
// Ignore the dupes, picking the first one. Otherwise this causes conflicting trait impls
|
|
let key = (ch.signal, channel.to_string());
|
|
if !dupe.insert(key) {
|
|
continue;
|
|
}
|
|
|
|
let request = if let Some(request) = ch.request {
|
|
let request = request as u8;
|
|
quote!(#request)
|
|
} else {
|
|
quote!(())
|
|
};
|
|
|
|
let channel = format_ident!("{}", channel);
|
|
g.extend(quote! {
|
|
dma_trait_impl!(#tr, #peri, #channel, #request);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ========
|
|
// Generate Div/Mul impls for RCC prescalers/dividers/multipliers.
|
|
for e in rcc_registers.ir.enums {
|
|
fn is_rcc_name(e: &str) -> bool {
|
|
match e {
|
|
"Pllp" | "Pllq" | "Pllr" | "Pllm" | "Plln" => true,
|
|
"Timpre" | "Pllrclkpre" => false,
|
|
e if e.ends_with("pre") || e.ends_with("pres") || e.ends_with("div") || e.ends_with("mul") => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
struct Frac {
|
|
num: u32,
|
|
denom: u32,
|
|
}
|
|
|
|
impl Frac {
|
|
fn simplify(self) -> Self {
|
|
let d = gcd(self.num, self.denom);
|
|
Self {
|
|
num: self.num / d,
|
|
denom: self.denom / d,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn gcd(a: u32, b: u32) -> u32 {
|
|
if b == 0 {
|
|
return a;
|
|
}
|
|
gcd(b, a % b)
|
|
}
|
|
|
|
fn parse_num(n: &str) -> Result<Frac, ()> {
|
|
for prefix in ["DIV", "MUL"] {
|
|
if let Some(n) = n.strip_prefix(prefix) {
|
|
let exponent = n.find('_').map(|e| n.len() - 1 - e).unwrap_or(0) as u32;
|
|
let mantissa = n.replace('_', "").parse().map_err(|_| ())?;
|
|
let f = Frac {
|
|
num: mantissa,
|
|
denom: 10u32.pow(exponent),
|
|
};
|
|
return Ok(f.simplify());
|
|
}
|
|
}
|
|
Err(())
|
|
}
|
|
|
|
if is_rcc_name(e.name) {
|
|
let enum_name = format_ident!("{}", e.name);
|
|
let mut muls = Vec::new();
|
|
let mut divs = Vec::new();
|
|
for v in e.variants {
|
|
let Ok(val) = parse_num(v.name) else {
|
|
panic!("could not parse mul/div. enum={} variant={}", e.name, v.name)
|
|
};
|
|
let variant_name = format_ident!("{}", v.name);
|
|
let variant = quote!(crate::pac::rcc::vals::#enum_name::#variant_name);
|
|
let num = val.num;
|
|
let denom = val.denom;
|
|
muls.push(quote!(#variant => self * #num / #denom,));
|
|
divs.push(quote!(#variant => self * #denom / #num,));
|
|
}
|
|
|
|
g.extend(quote! {
|
|
impl core::ops::Div<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz {
|
|
type Output = crate::time::Hertz;
|
|
fn div(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output {
|
|
match rhs {
|
|
#(#divs)*
|
|
#[allow(unreachable_patterns)]
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
impl core::ops::Mul<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz {
|
|
type Output = crate::time::Hertz;
|
|
fn mul(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output {
|
|
match rhs {
|
|
#(#muls)*
|
|
#[allow(unreachable_patterns)]
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// ========
|
|
// Write peripheral_interrupts module.
|
|
let mut mt = TokenStream::new();
|
|
for p in METADATA.peripherals {
|
|
let mut pt = TokenStream::new();
|
|
|
|
for irq in p.interrupts {
|
|
let iname = format_ident!("{}", irq.interrupt);
|
|
let sname = format_ident!("{}", irq.signal);
|
|
pt.extend(quote!(pub type #sname = crate::interrupt::typelevel::#iname;));
|
|
}
|
|
|
|
let pname = format_ident!("{}", p.name);
|
|
mt.extend(quote!(pub mod #pname { #pt }));
|
|
}
|
|
g.extend(quote!(#[allow(non_camel_case_types)] pub mod peripheral_interrupts { #mt }));
|
|
|
|
// ========
|
|
// Write foreach_foo! macrotables
|
|
|
|
let mut flash_regions_table: Vec<Vec<String>> = Vec::new();
|
|
let mut interrupts_table: Vec<Vec<String>> = Vec::new();
|
|
let mut peripherals_table: Vec<Vec<String>> = Vec::new();
|
|
let mut pins_table: Vec<Vec<String>> = Vec::new();
|
|
let mut adc_table: Vec<Vec<String>> = Vec::new();
|
|
|
|
for m in METADATA
|
|
.memory
|
|
.iter()
|
|
.filter(|m| m.kind == MemoryRegionKind::Flash && m.settings.is_some())
|
|
{
|
|
let settings = m.settings.as_ref().unwrap();
|
|
let row = vec![
|
|
get_flash_region_type_name(m.name),
|
|
settings.write_size.to_string(),
|
|
settings.erase_size.to_string(),
|
|
];
|
|
flash_regions_table.push(row);
|
|
}
|
|
|
|
let gpio_base = METADATA.peripherals.iter().find(|p| p.name == "GPIOA").unwrap().address as u32;
|
|
let gpio_stride = 0x400;
|
|
|
|
for p in METADATA.peripherals {
|
|
if let Some(regs) = &p.registers {
|
|
if regs.kind == "gpio" {
|
|
let port_letter = p.name.chars().nth(4).unwrap();
|
|
assert_eq!(0, (p.address as u32 - gpio_base) % gpio_stride);
|
|
let port_num = (p.address as u32 - gpio_base) / gpio_stride;
|
|
|
|
for pin_num in 0u32..16 {
|
|
let pin_name = format!("P{}{}", port_letter, pin_num);
|
|
|
|
pins_table.push(vec![
|
|
pin_name.clone(),
|
|
p.name.to_string(),
|
|
port_num.to_string(),
|
|
pin_num.to_string(),
|
|
format!("EXTI{}", pin_num),
|
|
]);
|
|
|
|
// If we have the split pins, we need to do a little extra work:
|
|
// Add the "_C" variant to the table. The solution is not optimal, though.
|
|
// Adding them only when the corresponding GPIOx also appears.
|
|
// This should avoid unintended side-effects as much as possible.
|
|
#[cfg(feature = "_split-pins-enabled")]
|
|
for split_feature in &split_features {
|
|
if split_feature.pin_name_without_c == pin_name {
|
|
pins_table.push(vec![
|
|
split_feature.pin_name_with_c.to_string(),
|
|
p.name.to_string(),
|
|
port_num.to_string(),
|
|
pin_num.to_string(),
|
|
format!("EXTI{}", pin_num),
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if regs.kind == "adc" {
|
|
let adc_num = p.name.strip_prefix("ADC").unwrap();
|
|
let mut adc_common = None;
|
|
for p2 in METADATA.peripherals {
|
|
if let Some(common_nums) = p2.name.strip_prefix("ADC").and_then(|s| s.strip_suffix("_COMMON")) {
|
|
if common_nums.contains(adc_num) {
|
|
adc_common = Some(p2);
|
|
}
|
|
}
|
|
}
|
|
let adc_common = adc_common.map(|p| p.name).unwrap_or("none");
|
|
let row = vec![p.name.to_string(), adc_common.to_string(), "adc".to_string()];
|
|
adc_table.push(row);
|
|
}
|
|
|
|
for irq in p.interrupts {
|
|
let row = vec![
|
|
p.name.to_string(),
|
|
regs.kind.to_string(),
|
|
regs.block.to_string(),
|
|
irq.signal.to_string(),
|
|
irq.interrupt.to_ascii_uppercase(),
|
|
];
|
|
interrupts_table.push(row)
|
|
}
|
|
|
|
let row = vec![regs.kind.to_string(), p.name.to_string()];
|
|
peripherals_table.push(row);
|
|
}
|
|
}
|
|
|
|
let mut dmas = TokenStream::new();
|
|
let has_dmamux = METADATA
|
|
.peripherals
|
|
.iter()
|
|
.flat_map(|p| &p.registers)
|
|
.any(|p| p.kind == "dmamux");
|
|
|
|
let mut dma_irqs: BTreeMap<&str, Vec<String>> = BTreeMap::new();
|
|
|
|
for p in METADATA.peripherals {
|
|
if let Some(r) = &p.registers {
|
|
if r.kind == "dma" || r.kind == "bdma" || r.kind == "gpdma" || r.kind == "lpdma" {
|
|
for irq in p.interrupts {
|
|
let ch_name = format!("{}_{}", p.name, irq.signal);
|
|
let ch = METADATA.dma_channels.iter().find(|c| c.name == ch_name).unwrap();
|
|
|
|
// Some H7 chips have BDMA1 hardcoded for DFSDM, ie no DMAMUX. It's unsupported, skip it.
|
|
if has_dmamux && ch.dmamux.is_none() {
|
|
continue;
|
|
}
|
|
|
|
dma_irqs.entry(irq.interrupt).or_default().push(ch_name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "_dual-core")]
|
|
let mut dma_ch_to_irq: BTreeMap<&str, Vec<String>> = BTreeMap::new();
|
|
|
|
#[cfg(feature = "_dual-core")]
|
|
for (irq, channels) in &dma_irqs {
|
|
for channel in channels {
|
|
dma_ch_to_irq.entry(channel).or_default().push(irq.to_string());
|
|
}
|
|
}
|
|
|
|
for (ch_idx, ch) in METADATA.dma_channels.iter().enumerate() {
|
|
// Some H7 chips have BDMA1 hardcoded for DFSDM, ie no DMAMUX. It's unsupported, skip it.
|
|
if has_dmamux && ch.dmamux.is_none() {
|
|
continue;
|
|
}
|
|
|
|
let name = format_ident!("{}", ch.name);
|
|
let idx = ch_idx as u8;
|
|
#[cfg(feature = "_dual-core")]
|
|
let irq = {
|
|
let irq_name = if let Some(x) = &dma_ch_to_irq.get(ch.name) {
|
|
format_ident!("{}", x.get(0).unwrap())
|
|
} else {
|
|
panic!("failed to find dma interrupt")
|
|
};
|
|
quote!(crate::pac::Interrupt::#irq_name)
|
|
};
|
|
|
|
g.extend(quote!(dma_channel_impl!(#name, #idx);));
|
|
|
|
let dma = format_ident!("{}", ch.dma);
|
|
let ch_num = ch.channel as usize;
|
|
|
|
let dma_peri = METADATA.peripherals.iter().find(|p| p.name == ch.dma).unwrap();
|
|
let bi = dma_peri.registers.as_ref().unwrap();
|
|
|
|
let dma_info = match bi.kind {
|
|
"dma" => quote!(crate::dma::DmaInfo::Dma(crate::pac::#dma)),
|
|
"bdma" => quote!(crate::dma::DmaInfo::Bdma(crate::pac::#dma)),
|
|
"gpdma" => quote!(crate::pac::#dma),
|
|
"lpdma" => quote!(unsafe { crate::pac::gpdma::Gpdma::from_ptr(crate::pac::#dma.as_ptr())}),
|
|
_ => panic!("bad dma channel kind {}", bi.kind),
|
|
};
|
|
|
|
let dmamux = match &ch.dmamux {
|
|
Some(dmamux) => {
|
|
let dmamux = format_ident!("{}", dmamux);
|
|
let num = ch.dmamux_channel.unwrap() as usize;
|
|
quote! {
|
|
dmamux: crate::dma::DmamuxInfo {
|
|
mux: crate::pac::#dmamux,
|
|
num: #num,
|
|
},
|
|
}
|
|
}
|
|
None => quote!(),
|
|
};
|
|
|
|
#[cfg(not(feature = "_dual-core"))]
|
|
dmas.extend(quote! {
|
|
crate::dma::ChannelInfo {
|
|
dma: #dma_info,
|
|
num: #ch_num,
|
|
#dmamux
|
|
},
|
|
});
|
|
#[cfg(feature = "_dual-core")]
|
|
dmas.extend(quote! {
|
|
crate::dma::ChannelInfo {
|
|
dma: #dma_info,
|
|
num: #ch_num,
|
|
irq: #irq,
|
|
#dmamux
|
|
},
|
|
});
|
|
}
|
|
|
|
// ========
|
|
// Generate DMA IRQs.
|
|
|
|
let dma_irqs: TokenStream = dma_irqs
|
|
.iter()
|
|
.map(|(irq, channels)| {
|
|
let irq = format_ident!("{}", irq);
|
|
|
|
let channels = channels.iter().map(|c| format_ident!("{}", c));
|
|
|
|
quote! {
|
|
#[cfg(feature = "rt")]
|
|
#[crate::interrupt]
|
|
unsafe fn #irq () {
|
|
#(
|
|
<crate::peripherals::#channels as crate::dma::ChannelInterrupt>::on_irq();
|
|
)*
|
|
}
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
g.extend(dma_irqs);
|
|
|
|
g.extend(quote! {
|
|
pub(crate) const DMA_CHANNELS: &[crate::dma::ChannelInfo] = &[#dmas];
|
|
});
|
|
|
|
for irq in METADATA.interrupts {
|
|
let name = irq.name.to_ascii_uppercase();
|
|
interrupts_table.push(vec![name.clone()]);
|
|
if name.contains("EXTI") {
|
|
interrupts_table.push(vec!["EXTI".to_string(), name.clone()]);
|
|
}
|
|
}
|
|
|
|
let mut m = clocks_macro.to_string();
|
|
|
|
// DO NOT ADD more macros like these.
|
|
// These turned to be a bad idea!
|
|
// Instead, make build.rs generate the final code.
|
|
make_table(&mut m, "foreach_flash_region", &flash_regions_table);
|
|
make_table(&mut m, "foreach_interrupt", &interrupts_table);
|
|
make_table(&mut m, "foreach_peripheral", &peripherals_table);
|
|
make_table(&mut m, "foreach_pin", &pins_table);
|
|
make_table(&mut m, "foreach_adc", &adc_table);
|
|
|
|
let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
|
let out_file = out_dir.join("_macros.rs").to_string_lossy().to_string();
|
|
fs::write(&out_file, m).unwrap();
|
|
rustfmt(&out_file);
|
|
|
|
// ========
|
|
// Write generated.rs
|
|
|
|
let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string();
|
|
fs::write(&out_file, g.to_string()).unwrap();
|
|
rustfmt(&out_file);
|
|
|
|
// ========
|
|
// Configs for multicore and for targeting groups of chips
|
|
|
|
fn get_chip_cfgs(chip_name: &str) -> Vec<String> {
|
|
let mut cfgs = Vec::new();
|
|
|
|
// Multicore
|
|
|
|
let mut s = chip_name.split('_');
|
|
let mut chip_name: String = s.next().unwrap().to_string();
|
|
let core_name = if let Some(c) = s.next() {
|
|
if !c.starts_with("CM") {
|
|
chip_name.push('_');
|
|
chip_name.push_str(c);
|
|
None
|
|
} else {
|
|
Some(c)
|
|
}
|
|
} else {
|
|
None
|
|
};
|
|
|
|
if let Some(core) = core_name {
|
|
cfgs.push(format!("{}_{}", &chip_name[..chip_name.len() - 2], core));
|
|
}
|
|
|
|
// Configs for targeting groups of chips
|
|
if &chip_name[..8] == "stm32wba" {
|
|
cfgs.push(chip_name[..8].to_owned()); // stm32wba
|
|
cfgs.push(chip_name[..10].to_owned()); // stm32wba52
|
|
cfgs.push(format!("package_{}", &chip_name[10..11]));
|
|
cfgs.push(format!("flashsize_{}", &chip_name[11..12]));
|
|
} else {
|
|
if &chip_name[..8] == "stm32h7r" || &chip_name[..8] == "stm32h7s" {
|
|
cfgs.push("stm32h7rs".to_owned());
|
|
} else {
|
|
cfgs.push(chip_name[..7].to_owned()); // stm32f4
|
|
}
|
|
cfgs.push(chip_name[..9].to_owned()); // stm32f429
|
|
cfgs.push(format!("{}x", &chip_name[..8])); // stm32f42x
|
|
cfgs.push(format!("{}x{}", &chip_name[..7], &chip_name[8..9])); // stm32f4x9
|
|
cfgs.push(format!("package_{}", &chip_name[9..10]));
|
|
cfgs.push(format!("flashsize_{}", &chip_name[10..11]));
|
|
}
|
|
|
|
// Mark the L4+ chips as they have many differences to regular L4.
|
|
if &chip_name[..7] == "stm32l4" {
|
|
if "pqrs".contains(&chip_name[7..8]) {
|
|
cfgs.push("stm32l4_plus".to_owned());
|
|
} else {
|
|
cfgs.push("stm32l4_nonplus".to_owned());
|
|
}
|
|
}
|
|
|
|
cfgs
|
|
}
|
|
|
|
cfgs.enable_all(&get_chip_cfgs(&chip_name));
|
|
for &chip_name in ALL_CHIPS.iter() {
|
|
cfgs.declare_all(&get_chip_cfgs(&chip_name.to_ascii_lowercase()));
|
|
}
|
|
|
|
println!("cargo:rerun-if-changed=build.rs");
|
|
}
|
|
|
|
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),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
fn make_table(out: &mut String, name: &str, data: &Vec<Vec<String>>) {
|
|
write!(
|
|
out,
|
|
"#[allow(unused)]
|
|
macro_rules! {} {{
|
|
($($pat:tt => $code:tt;)*) => {{
|
|
macro_rules! __{}_inner {{
|
|
$(($pat) => $code;)*
|
|
($_:tt) => {{}}
|
|
}}
|
|
",
|
|
name, name
|
|
)
|
|
.unwrap();
|
|
|
|
for row in data {
|
|
writeln!(out, " __{}_inner!(({}));", name, row.join(",")).unwrap();
|
|
}
|
|
|
|
write!(
|
|
out,
|
|
" }};
|
|
}}"
|
|
)
|
|
.unwrap();
|
|
}
|
|
|
|
fn get_flash_region_name(name: &str) -> String {
|
|
let name = name.replace("BANK_", "BANK").replace("REGION_", "REGION");
|
|
if name.contains("REGION") {
|
|
name
|
|
} else {
|
|
name + "_REGION"
|
|
}
|
|
}
|
|
|
|
fn get_flash_region_type_name(name: &str) -> String {
|
|
get_flash_region_name(name)
|
|
.replace("BANK", "Bank")
|
|
.replace("REGION", "Region")
|
|
.replace('_', "")
|
|
}
|
|
|
|
/// rustfmt a given path.
|
|
/// Failures are logged to stderr and ignored.
|
|
fn rustfmt(path: impl AsRef<Path>) {
|
|
let path = path.as_ref();
|
|
match Command::new("rustfmt").args([path]).output() {
|
|
Err(e) => {
|
|
eprintln!("failed to exec rustfmt {:?}: {:?}", path, e);
|
|
}
|
|
Ok(out) => {
|
|
if !out.status.success() {
|
|
eprintln!("rustfmt {:?} failed:", path);
|
|
eprintln!("=== STDOUT:");
|
|
std::io::stderr().write_all(&out.stdout).unwrap();
|
|
eprintln!("=== STDERR:");
|
|
std::io::stderr().write_all(&out.stderr).unwrap();
|
|
}
|
|
}
|
|
}
|
|
}
|