Merge #601
601: [part 1/n] Change macrotables to build.rs codegen r=lulf a=Dirbaio This PR replaces the "macrotables" (the macros like `stm32_data::peripherals!`) with a `const METADATA`. Macrotables had some problems: - Hard to debug - Somewhat footgunny (typo the "pattern" and then nothing matches and the macro now expands to nothing, silently!) - Limited power - Can't count, so we had to add a [special macrotable for that](f50f3f0a73/embassy-stm32/src/dma/bdma.rs (L26)). - Can't remove duplicates, so we had to fallback to [Rust code in build.rs](f50f3f0a73/embassy-stm32/build.rs (L105-L145)) - Can't include the results as a listto another macro, so again [build.rs](https://github.com/embassy-rs/embassy/blob/master/embassy-stm32/build.rs#L100-L101). They work fine for the 95% of cases, but for the remaining 5% we need Rust code in build.rs. So we might as well do everything with Rust code, so everything is consistent. The new approach generates a `const METADATA: Metadata = Metadata { ... }` with [these structs](https://github.com/embassy-rs/embassy/blob/unmacrotablize/stm32-metapac-gen/src/assets/metadata.rs) in `stm32-metapac`. `build.rs` can then read that and generate whatever code. Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
This commit is contained in:
		| @@ -36,7 +36,9 @@ seq-macro = "0.2.2" | ||||
| cfg-if = "1.0.0" | ||||
|  | ||||
| [build-dependencies] | ||||
| stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false } | ||||
| proc-macro2 = "1.0.36" | ||||
| quote = "1.0.15" | ||||
| stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]} | ||||
|  | ||||
| [features] | ||||
| sdmmc-rs = ["embedded-sdmmc"] | ||||
|   | ||||
| @@ -1,8 +1,10 @@ | ||||
| use std::collections::HashMap; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| use std::collections::HashSet; | ||||
| use std::env; | ||||
| use std::fmt::Write; | ||||
| use std::fs; | ||||
| use std::path::PathBuf; | ||||
| use stm32_metapac::metadata::METADATA; | ||||
|  | ||||
| fn main() { | ||||
|     let chip_name = match env::vars() | ||||
| @@ -18,67 +20,50 @@ fn main() { | ||||
|     .unwrap() | ||||
|     .to_ascii_lowercase(); | ||||
|  | ||||
|     struct Peripheral { | ||||
|         kind: String, | ||||
|         name: String, | ||||
|         version: String, | ||||
|     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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let mut peripheral_version_mapping = HashMap::<String, String>::new(); | ||||
|     stm32_metapac::peripheral_versions!( | ||||
|         ($peri:ident, $version:ident) => { | ||||
|             peripheral_version_mapping.insert(stringify!($peri).to_string(), stringify!($version).to_string()); | ||||
|             println!("cargo:rustc-cfg={}", stringify!($peri)); | ||||
|             println!("cargo:rustc-cfg={}_{}", stringify!($peri), stringify!($version)); | ||||
|         }; | ||||
|     ); | ||||
|  | ||||
|     let mut peripherals: Vec<Peripheral> = Vec::new(); | ||||
|     stm32_metapac::peripherals!( | ||||
|         ($kind:ident, $name:ident) => { | ||||
|             peripherals.push(Peripheral{ | ||||
|                 kind: stringify!($kind).to_string(), | ||||
|                 name: stringify!($name).to_string(), | ||||
|                 version: peripheral_version_mapping[&stringify!($kind).to_ascii_lowercase()].clone() | ||||
|             }); | ||||
|         }; | ||||
|     ); | ||||
|  | ||||
|     // ======== | ||||
|     // Generate singletons | ||||
|  | ||||
|     let mut singletons: Vec<String> = Vec::new(); | ||||
|     for p in peripherals { | ||||
|         match p.kind.as_str() { | ||||
|             // 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)); | ||||
|     for p in METADATA.peripherals { | ||||
|         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" => {} | ||||
|                 // 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 p.version == "h7" { | ||||
|                     singletons.push("MCO1".to_string()); | ||||
|                     singletons.push("MCO2".to_string()); | ||||
|                 // 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 == "h7" { | ||||
|                         singletons.push("MCO1".to_string()); | ||||
|                         singletons.push("MCO2".to_string()); | ||||
|                     } | ||||
|                     singletons.push(p.name.to_string()); | ||||
|                 } | ||||
|                 singletons.push(p.name.clone()); | ||||
|             } | ||||
|             //"dbgmcu" => {} | ||||
|             //"syscfg" => {} | ||||
|             //"dma" => {} | ||||
|             //"bdma" => {} | ||||
|             //"dmamux" => {} | ||||
|                 //"dbgmcu" => {} | ||||
|                 //"syscfg" => {} | ||||
|                 //"dma" => {} | ||||
|                 //"bdma" => {} | ||||
|                 //"dmamux" => {} | ||||
|  | ||||
|             // For other peripherals, one singleton per peri | ||||
|             _ => singletons.push(p.name.clone()), | ||||
|                 // For other peripherals, one singleton per peri | ||||
|                 _ => singletons.push(p.name.to_string()), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -88,60 +73,161 @@ fn main() { | ||||
|     } | ||||
|  | ||||
|     // One singleton per DMA channel | ||||
|     stm32_metapac::dma_channels! { | ||||
|         ($channel_peri:ident, $dma_peri:ident, $version:ident, $channel_num:expr, $ignore:tt) => { | ||||
|             singletons.push(stringify!($channel_peri).to_string()); | ||||
|         }; | ||||
|     for c in METADATA.dma_channels { | ||||
|         singletons.push(c.name.to_string()); | ||||
|     } | ||||
|  | ||||
|     let mut generated = String::new(); | ||||
|     write!( | ||||
|         &mut generated, | ||||
|         "embassy_hal_common::peripherals!({});\n", | ||||
|         singletons.join(",") | ||||
|     ) | ||||
|     .unwrap(); | ||||
|     let mut g = TokenStream::new(); | ||||
|  | ||||
|     let singleton_tokens: Vec<_> = singletons.iter().map(|s| format_ident!("{}", s)).collect(); | ||||
|     g.extend(quote! { | ||||
|         embassy_hal_common::peripherals!(#(#singleton_tokens),*); | ||||
|     }); | ||||
|  | ||||
|     // ======== | ||||
|     // Generate interrupt declarations | ||||
|  | ||||
|     let mut irqs = Vec::new(); | ||||
|     for irq in METADATA.interrupts { | ||||
|         irqs.push(format_ident!("{}", irq.name)); | ||||
|     } | ||||
|  | ||||
|     g.extend(quote! { | ||||
|         pub mod interrupt { | ||||
|             use crate::pac::Interrupt as InterruptEnum; | ||||
|             #( | ||||
|                 embassy::interrupt::declare!(#irqs); | ||||
|             )* | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     // ======== | ||||
|     // Generate DMA IRQs. | ||||
|     // This can't be done with macrotables alone because in many chips, one irq is shared between many | ||||
|     // channels, so we have to deduplicate them. | ||||
|  | ||||
|     #[allow(unused_mut)] | ||||
|     let mut dma_irqs: Vec<String> = Vec::new(); | ||||
|     #[allow(unused_mut)] | ||||
|     let mut bdma_irqs: Vec<String> = Vec::new(); | ||||
|     let mut dma_irqs: HashSet<&str> = HashSet::new(); | ||||
|     let mut bdma_irqs: HashSet<&str> = HashSet::new(); | ||||
|  | ||||
|     stm32_metapac::interrupts! { | ||||
|         ($peri:ident, dma, $block:ident, $signal_name:ident, $irq:ident) => { | ||||
|             dma_irqs.push(stringify!($irq).to_string()); | ||||
|         }; | ||||
|         ($peri:ident, bdma, $block:ident, $signal_name:ident, $irq:ident) => { | ||||
|             bdma_irqs.push(stringify!($irq).to_string()); | ||||
|         }; | ||||
|     for p in METADATA.peripherals { | ||||
|         if let Some(r) = &p.registers { | ||||
|             match r.kind { | ||||
|                 "dma" => { | ||||
|                     for irq in p.interrupts { | ||||
|                         dma_irqs.insert(irq.interrupt); | ||||
|                     } | ||||
|                 } | ||||
|                 "bdma" => { | ||||
|                     for irq in p.interrupts { | ||||
|                         bdma_irqs.insert(irq.interrupt); | ||||
|                     } | ||||
|                 } | ||||
|                 _ => {} | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     dma_irqs.sort(); | ||||
|     dma_irqs.dedup(); | ||||
|     bdma_irqs.sort(); | ||||
|     bdma_irqs.dedup(); | ||||
|     let tokens: Vec<_> = dma_irqs.iter().map(|s| format_ident!("{}", s)).collect(); | ||||
|     g.extend(quote! { | ||||
|         #( | ||||
|             #[crate::interrupt] | ||||
|             unsafe fn #tokens () { | ||||
|                 crate::dma::dma::on_irq(); | ||||
|             } | ||||
|         )* | ||||
|     }); | ||||
|  | ||||
|     for irq in dma_irqs { | ||||
|         write!( | ||||
|             &mut generated, | ||||
|             "#[crate::interrupt] unsafe fn {} () {{ crate::dma::dma::on_irq(); }}\n", | ||||
|             irq | ||||
|         ) | ||||
|         .unwrap(); | ||||
|     let tokens: Vec<_> = bdma_irqs.iter().map(|s| format_ident!("{}", s)).collect(); | ||||
|     g.extend(quote! { | ||||
|         #( | ||||
|             #[crate::interrupt] | ||||
|             unsafe fn #tokens () { | ||||
|                 crate::dma::bdma::on_irq(); | ||||
|             } | ||||
|         )* | ||||
|     }); | ||||
|  | ||||
|     // ======== | ||||
|     // 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! { | ||||
|                         critical_section::with(|_| unsafe { | ||||
|                             crate::pac::RCC.#rst_reg().modify(|w| w.#set_rst_field(true)); | ||||
|                             crate::pac::RCC.#rst_reg().modify(|w| w.#set_rst_field(false)); | ||||
|                         }); | ||||
|                     } | ||||
|                 } | ||||
|                 None => TokenStream::new(), | ||||
|             }; | ||||
|  | ||||
|             let pname = format_ident!("{}", p.name); | ||||
|             let clk = format_ident!("{}", rcc.clock.to_ascii_lowercase()); | ||||
|             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! { | ||||
|                 impl crate::rcc::sealed::RccPeripheral for peripherals::#pname { | ||||
|                     fn frequency() -> crate::time::Hertz { | ||||
|                         critical_section::with(|_| unsafe { | ||||
|                             crate::rcc::get_freqs().#clk | ||||
|                         }) | ||||
|                     } | ||||
|                     fn enable() { | ||||
|                         critical_section::with(|_| unsafe { | ||||
|                             crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true)) | ||||
|                         }) | ||||
|                     } | ||||
|                     fn disable() { | ||||
|                         critical_section::with(|_| unsafe { | ||||
|                             crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false)); | ||||
|                         }) | ||||
|                     } | ||||
|                     fn reset() { | ||||
|                         #rst | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 impl crate::rcc::RccPeripheral for peripherals::#pname {} | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     for irq in bdma_irqs { | ||||
|         write!( | ||||
|             &mut generated, | ||||
|             "#[crate::interrupt] unsafe fn {} () {{ crate::dma::bdma::on_irq(); }}\n", | ||||
|             irq | ||||
|         ) | ||||
|         .unwrap(); | ||||
|     // ======== | ||||
|     // Generate fns to enable GPIO, DMA in RCC | ||||
|  | ||||
|     for kind in ["dma", "bdma", "dmamux", "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 | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     // ======== | ||||
| @@ -149,7 +235,7 @@ fn main() { | ||||
|  | ||||
|     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, &generated).unwrap(); | ||||
|     fs::write(out_file, g.to_string()).unwrap(); | ||||
|  | ||||
|     // ======== | ||||
|     // Multicore | ||||
|   | ||||
| @@ -9,7 +9,6 @@ use embassy::waitqueue::AtomicWaker; | ||||
| use crate::dma::Request; | ||||
| use crate::pac; | ||||
| use crate::pac::bdma::vals; | ||||
| use crate::rcc::sealed::RccPeripheral; | ||||
|  | ||||
| use super::{Word, WordSize}; | ||||
|  | ||||
| @@ -77,11 +76,7 @@ pub(crate) unsafe fn init() { | ||||
|             crate::interrupt::$irq::steal().enable(); | ||||
|         }; | ||||
|     } | ||||
|     pac::peripherals! { | ||||
|         (bdma, $peri:ident) => { | ||||
|             crate::peripherals::$peri::enable(); | ||||
|         }; | ||||
|     } | ||||
|     crate::generated::init_bdma(); | ||||
| } | ||||
|  | ||||
| pac::dma_channels! { | ||||
|   | ||||
| @@ -7,7 +7,6 @@ use embassy::waitqueue::AtomicWaker; | ||||
| use crate::interrupt; | ||||
| use crate::pac; | ||||
| use crate::pac::dma::{regs, vals}; | ||||
| use crate::rcc::sealed::RccPeripheral; | ||||
|  | ||||
| use super::{Request, Word, WordSize}; | ||||
|  | ||||
| @@ -74,11 +73,7 @@ pub(crate) unsafe fn init() { | ||||
|             interrupt::$irq::steal().enable(); | ||||
|         }; | ||||
|     } | ||||
|     pac::peripherals! { | ||||
|         (dma, $peri:ident) => { | ||||
|             crate::peripherals::$peri::enable(); | ||||
|         }; | ||||
|     } | ||||
|     crate::generated::init_dma(); | ||||
| } | ||||
|  | ||||
| pac::dma_channels! { | ||||
|   | ||||
| @@ -49,11 +49,5 @@ pac::dma_channels! { | ||||
|  | ||||
| /// safety: must be called only once | ||||
| pub(crate) unsafe fn init() { | ||||
|     crate::pac::peripheral_rcc! { | ||||
|         ($name:ident, dmamux, DMAMUX, $clock:ident, ($reg:ident, $field:ident, $set_field:ident), $rst:tt) => { | ||||
|             crate::pac::RCC.$reg().modify(|reg| { | ||||
|                 reg.$set_field(true); | ||||
|             }); | ||||
|         }; | ||||
|     } | ||||
|     crate::generated::init_dmamux(); | ||||
| } | ||||
|   | ||||
| @@ -608,13 +608,7 @@ crate::pac::pins!( | ||||
| ); | ||||
|  | ||||
| pub(crate) unsafe fn init() { | ||||
|     crate::pac::peripheral_rcc! { | ||||
|         ($name:ident, gpio, GPIO, $clock:ident, ($reg:ident, $field:ident, $set_field:ident), $rst:tt) => { | ||||
|             crate::pac::RCC.$reg().modify(|reg| { | ||||
|                 reg.$set_field(true); | ||||
|             }); | ||||
|         }; | ||||
|     } | ||||
|     crate::generated::init_gpio(); | ||||
| } | ||||
|  | ||||
| mod eh02 { | ||||
|   | ||||
| @@ -3,11 +3,4 @@ pub use critical_section::CriticalSection; | ||||
| pub use embassy::interrupt::{take, Interrupt}; | ||||
| pub use embassy_hal_common::interrupt::Priority4 as Priority; | ||||
|  | ||||
| use crate::pac::Interrupt as InterruptEnum; | ||||
| use embassy::interrupt::declare; | ||||
|  | ||||
| crate::pac::interrupts!( | ||||
|     ($name:ident) => { | ||||
|         declare!($name); | ||||
|     }; | ||||
| ); | ||||
| pub use crate::generated::interrupt::*; | ||||
|   | ||||
| @@ -65,8 +65,6 @@ mod generated { | ||||
|     #![allow(unused_imports)] | ||||
|     #![allow(non_snake_case)] | ||||
|  | ||||
|     use crate::interrupt; | ||||
|  | ||||
|     include!(concat!(env!("OUT_DIR"), "/generated.rs")); | ||||
| } | ||||
| pub use embassy_macros::interrupt; | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| #![macro_use] | ||||
|  | ||||
| use crate::peripherals; | ||||
| use crate::time::Hertz; | ||||
| use core::mem::MaybeUninit; | ||||
|  | ||||
| @@ -104,66 +103,3 @@ pub(crate) mod sealed { | ||||
| } | ||||
|  | ||||
| pub trait RccPeripheral: sealed::RccPeripheral + 'static {} | ||||
|  | ||||
| crate::pac::peripheral_rcc!( | ||||
|     ($inst:ident, gpio, GPIO, $clk:ident, $en:tt, $rst:tt) => {}; | ||||
|     ($inst:ident, $module:ident, $block:ident, $clk:ident, ($en_reg:ident, $en_field:ident, $en_set_field:ident), ($rst_reg:ident, $rst_field:ident, $rst_set_field:ident)) => { | ||||
|         impl sealed::RccPeripheral for peripherals::$inst { | ||||
|             fn frequency() -> crate::time::Hertz { | ||||
|                 critical_section::with(|_| { | ||||
|                     unsafe { get_freqs().$clk } | ||||
|                 }) | ||||
|             } | ||||
|             fn enable() { | ||||
|                 critical_section::with(|_| { | ||||
|                     unsafe { | ||||
|                         crate::pac::RCC.$en_reg().modify(|w| w.$en_set_field(true)); | ||||
|                     } | ||||
|                 }) | ||||
|             } | ||||
|             fn disable() { | ||||
|                 critical_section::with(|_| { | ||||
|                     unsafe { | ||||
|                         crate::pac::RCC.$en_reg().modify(|w| w.$en_set_field(false)); | ||||
|                     } | ||||
|                 }) | ||||
|             } | ||||
|             fn reset() { | ||||
|                 critical_section::with(|_| { | ||||
|                     unsafe { | ||||
|                         crate::pac::RCC.$rst_reg().modify(|w| w.$rst_set_field(true)); | ||||
|                         crate::pac::RCC.$rst_reg().modify(|w| w.$rst_set_field(false)); | ||||
|                     } | ||||
|                 }) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         impl RccPeripheral for peripherals::$inst {} | ||||
|     }; | ||||
|     ($inst:ident, $module:ident, $block:ident, $clk:ident, ($en_reg:ident, $en_field:ident, $en_set_field:ident), _) => { | ||||
|         impl sealed::RccPeripheral for peripherals::$inst { | ||||
|             fn frequency() -> crate::time::Hertz { | ||||
|                 critical_section::with(|_| { | ||||
|                     unsafe { get_freqs().$clk } | ||||
|                 }) | ||||
|             } | ||||
|             fn enable() { | ||||
|                 critical_section::with(|_| { | ||||
|                     unsafe { | ||||
|                         crate::pac::RCC.$en_reg().modify(|w| w.$en_set_field(true)); | ||||
|                     } | ||||
|                 }) | ||||
|             } | ||||
|             fn disable() { | ||||
|                 critical_section::with(|_| { | ||||
|                     unsafe { | ||||
|                         crate::pac::RCC.$en_reg().modify(|w| w.$en_set_field(false)); | ||||
|                     } | ||||
|                 }) | ||||
|             } | ||||
|             fn reset() {} | ||||
|         } | ||||
|  | ||||
|         impl RccPeripheral for peripherals::$inst {} | ||||
|     }; | ||||
| ); | ||||
|   | ||||
							
								
								
									
										97
									
								
								stm32-metapac-gen/src/assets/metadata.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								stm32-metapac-gen/src/assets/metadata.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| #[derive(Debug, Eq, PartialEq, Clone)] | ||||
| pub struct Metadata { | ||||
|     pub name: &'static str, | ||||
|     pub family: &'static str, | ||||
|     pub line: &'static str, | ||||
|     pub memory: &'static [MemoryRegion], | ||||
|     pub peripherals: &'static [Peripheral], | ||||
|     pub interrupts: &'static [Interrupt], | ||||
|     pub dma_channels: &'static [DmaChannel], | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone)] | ||||
| pub struct MemoryRegion { | ||||
|     pub name: &'static str, | ||||
|     pub kind: MemoryRegionKind, | ||||
|     pub address: u32, | ||||
|     pub size: u32, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone)] | ||||
| pub enum MemoryRegionKind { | ||||
|     Flash, | ||||
|     Ram, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone)] | ||||
| pub struct Interrupt { | ||||
|     pub name: &'static str, | ||||
|     pub number: u32, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone)] | ||||
| pub struct Package { | ||||
|     pub name: &'static str, | ||||
|     pub package: &'static str, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone)] | ||||
| pub struct Peripheral { | ||||
|     pub name: &'static str, | ||||
|     pub address: u64, | ||||
|     pub registers: Option<PeripheralRegisters>, | ||||
|     pub rcc: Option<PeripheralRcc>, | ||||
|     pub pins: &'static [PeripheralPin], | ||||
|     pub dma_channels: &'static [PeripheralDmaChannel], | ||||
|     pub interrupts: &'static [PeripheralInterrupt], | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone)] | ||||
| pub struct PeripheralRegisters { | ||||
|     pub kind: &'static str, | ||||
|     pub version: &'static str, | ||||
|     pub block: &'static str, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone)] | ||||
| pub struct PeripheralInterrupt { | ||||
|     pub signal: &'static str, | ||||
|     pub interrupt: &'static str, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone)] | ||||
| pub struct PeripheralRcc { | ||||
|     pub clock: &'static str, | ||||
|     pub enable: Option<PeripheralRccRegister>, | ||||
|     pub reset: Option<PeripheralRccRegister>, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone)] | ||||
| pub struct PeripheralRccRegister { | ||||
|     pub register: &'static str, | ||||
|     pub field: &'static str, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone)] | ||||
| pub struct PeripheralPin { | ||||
|     pub pin: &'static str, | ||||
|     pub signal: &'static str, | ||||
|     pub af: Option<&'static str>, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone)] | ||||
| pub struct DmaChannel { | ||||
|     pub name: &'static str, | ||||
|     pub dma: &'static str, | ||||
|     pub channel: u32, | ||||
|     pub dmamux: Option<&'static str>, | ||||
|     pub dmamux_channel: Option<u32>, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone)] | ||||
| pub struct PeripheralDmaChannel { | ||||
|     pub signal: &'static str, | ||||
|     pub channel: Option<&'static str>, | ||||
|     pub dmamux: Option<&'static str>, | ||||
|     pub request: Option<u32>, | ||||
| } | ||||
| @@ -16,6 +16,17 @@ use chiptool::{generate, ir, transform}; | ||||
| mod data; | ||||
| use data::*; | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone)] | ||||
| struct Metadata<'a> { | ||||
|     name: &'a str, | ||||
|     family: &'a str, | ||||
|     line: &'a str, | ||||
|     memory: &'a [MemoryRegion], | ||||
|     peripherals: &'a [Peripheral], | ||||
|     interrupts: &'a [Interrupt], | ||||
|     dma_channels: &'a [DmaChannel], | ||||
| } | ||||
|  | ||||
| fn make_peripheral_counts(out: &mut String, data: &BTreeMap<String, u8>) { | ||||
|     write!( | ||||
|         out, | ||||
| @@ -115,7 +126,6 @@ pub fn gen_chip( | ||||
|     let mut interrupt_table: Vec<Vec<String>> = Vec::new(); | ||||
|     let mut peripherals_table: Vec<Vec<String>> = Vec::new(); | ||||
|     let mut peripheral_pins_table: Vec<Vec<String>> = Vec::new(); | ||||
|     let mut peripheral_rcc_table: Vec<Vec<String>> = Vec::new(); | ||||
|     let mut dma_channels_table: Vec<Vec<String>> = Vec::new(); | ||||
|     let mut peripheral_dma_channels_table: Vec<Vec<String>> = Vec::new(); | ||||
|     let mut peripheral_counts: BTreeMap<String, u8> = BTreeMap::new(); | ||||
| @@ -253,34 +263,6 @@ pub fn gen_chip( | ||||
|                 } | ||||
|                 _ => {} | ||||
|             } | ||||
|  | ||||
|             if let Some(rcc) = &p.rcc { | ||||
|                 let mut clock = rcc.clock.to_ascii_lowercase(); | ||||
|                 if p.name.starts_with("TIM") { | ||||
|                     clock = format!("{}_tim", clock) | ||||
|                 } | ||||
|  | ||||
|                 let mut row = Vec::new(); | ||||
|                 row.push(p.name.clone()); | ||||
|                 row.push(bi.kind.clone()); | ||||
|                 row.push(bi.block.clone()); | ||||
|                 row.push(clock); | ||||
|  | ||||
|                 for reg in [&rcc.enable, &rcc.reset] { | ||||
|                     if let Some(reg) = reg { | ||||
|                         row.push(format!( | ||||
|                             "({}, {}, set_{})", | ||||
|                             reg.register.to_ascii_lowercase(), | ||||
|                             reg.field.to_ascii_lowercase(), | ||||
|                             reg.field.to_ascii_lowercase() | ||||
|                         )); | ||||
|                     } else { | ||||
|                         row.push("_".to_string()) | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 peripheral_rcc_table.push(row); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         dev.peripherals.push(ir_peri); | ||||
| @@ -384,12 +366,7 @@ pub fn gen_chip( | ||||
|     let mut device_x = String::new(); | ||||
|  | ||||
|     for irq in &core.interrupts { | ||||
|         write!( | ||||
|             &mut device_x, | ||||
|             "PROVIDE({} = DefaultHandler);\n", | ||||
|             irq.name.to_ascii_uppercase() | ||||
|         ) | ||||
|         .unwrap(); | ||||
|         write!(&mut device_x, "PROVIDE({} = DefaultHandler);\n", irq.name).unwrap(); | ||||
|     } | ||||
|  | ||||
|     // ============================== | ||||
| @@ -397,6 +374,7 @@ pub fn gen_chip( | ||||
|  | ||||
|     let mut data = String::new(); | ||||
|  | ||||
|     write!(&mut data, "#[cfg(feature=\"metadata\")] pub mod metadata;").unwrap(); | ||||
|     write!(&mut data, "#[cfg(feature=\"pac\")] mod pac;").unwrap(); | ||||
|     write!(&mut data, "#[cfg(feature=\"pac\")] pub use pac::*; ").unwrap(); | ||||
|  | ||||
| @@ -415,7 +393,6 @@ pub fn gen_chip( | ||||
|         "peripheral_dma_channels", | ||||
|         &peripheral_dma_channels_table, | ||||
|     ); | ||||
|     make_table(&mut data, "peripheral_rcc", &peripheral_rcc_table); | ||||
|     make_table(&mut data, "dma_channels", &dma_channels_table); | ||||
|     make_table(&mut data, "dbgmcu", &dbgmcu_table); | ||||
|     make_peripheral_counts(&mut data, &peripheral_counts); | ||||
| @@ -424,6 +401,38 @@ pub fn gen_chip( | ||||
|     let mut file = File::create(chip_dir.join("mod.rs")).unwrap(); | ||||
|     file.write_all(data.as_bytes()).unwrap(); | ||||
|  | ||||
|     // ============================== | ||||
|     // generate metadata.rs | ||||
|  | ||||
|     let metadata = Metadata { | ||||
|         name: &chip.name, | ||||
|         family: &chip.family, | ||||
|         line: &chip.line, | ||||
|         memory: &chip.memory, | ||||
|         peripherals: &core.peripherals, | ||||
|         interrupts: &core.interrupts, | ||||
|         dma_channels: &core.dma_channels, | ||||
|     }; | ||||
|     let metadata = format!("{:#?}", metadata); | ||||
|     let metadata = metadata.replace("[\n", "&[\n"); | ||||
|     let metadata = metadata.replace("[],\n", "&[],\n"); | ||||
|  | ||||
|     let mut data = String::new(); | ||||
|  | ||||
|     write!( | ||||
|         &mut data, | ||||
|         " | ||||
|             include!(\"../../metadata.rs\"); | ||||
|             use MemoryRegionKind::*; | ||||
|             pub const METADATA: Metadata = {};     | ||||
|         ", | ||||
|         metadata | ||||
|     ) | ||||
|     .unwrap(); | ||||
|  | ||||
|     let mut file = File::create(chip_dir.join("metadata.rs")).unwrap(); | ||||
|     file.write_all(data.as_bytes()).unwrap(); | ||||
|  | ||||
|     // ============================== | ||||
|     // generate device.x | ||||
|  | ||||
| @@ -462,7 +471,21 @@ pub fn gen(options: Options) { | ||||
|     for chip_name in &options.chips { | ||||
|         println!("Generating {}...", chip_name); | ||||
|  | ||||
|         let chip = load_chip(&options, chip_name); | ||||
|         let mut chip = load_chip(&options, chip_name); | ||||
|  | ||||
|         // Cleanup | ||||
|         for core in &mut chip.cores { | ||||
|             for irq in &mut core.interrupts { | ||||
|                 irq.name = irq.name.to_ascii_uppercase(); | ||||
|             } | ||||
|             for p in &mut core.peripherals { | ||||
|                 for irq in &mut p.interrupts { | ||||
|                     irq.interrupt = irq.interrupt.to_ascii_uppercase(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Generate | ||||
|         for (core_index, core) in chip.cores.iter().enumerate() { | ||||
|             let chip_core_name = match chip.cores.len() { | ||||
|                 1 => chip_name.clone(), | ||||
| @@ -557,6 +580,13 @@ pub fn gen(options: Options) { | ||||
|     ) | ||||
|     .unwrap(); | ||||
|  | ||||
|     // Generate src/metadata.rs | ||||
|     fs::write( | ||||
|         options.out_dir.join("src").join("metadata.rs"), | ||||
|         include_bytes!("assets/metadata.rs"), | ||||
|     ) | ||||
|     .unwrap(); | ||||
|  | ||||
|     // Generate Cargo.toml | ||||
|     const BUILDDEP_BEGIN: &[u8] = b"# BEGIN BUILD DEPENDENCIES"; | ||||
|     const BUILDDEP_END: &[u8] = b"# END BUILD DEPENDENCIES"; | ||||
|   | ||||
| @@ -19,10 +19,14 @@ regex = "1.5.4" | ||||
| default = ["pac"] | ||||
|  | ||||
| # Build the actual PAC. Set by default. | ||||
| # If not set, only the macrotables will be generated. You may want to not set it | ||||
| # if you're using stm32-metapac from a build.rs script to use the macros. | ||||
| # If you just want the metadata, unset it with `default-features = false`.  | ||||
| pac = [] | ||||
|  | ||||
| # Build the chip metadata. | ||||
| # If set, a const `stm32_metapac::METADATA` will be exported, containing all the  | ||||
| # metadata for the currently selected chip. | ||||
| metadata = [] | ||||
|  | ||||
| rt = ["cortex-m-rt/device"] | ||||
| memory-x = [] | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user