diff options
Diffstat (limited to 'embassy-nxp/build.rs')
| -rw-r--r-- | embassy-nxp/build.rs | 353 |
1 files changed, 326 insertions, 27 deletions
diff --git a/embassy-nxp/build.rs b/embassy-nxp/build.rs index f3c062c87..f53c29161 100644 --- a/embassy-nxp/build.rs +++ b/embassy-nxp/build.rs | |||
| @@ -4,10 +4,12 @@ use std::process::Command; | |||
| 4 | use std::{env, fs}; | 4 | use std::{env, fs}; |
| 5 | 5 | ||
| 6 | use cfg_aliases::cfg_aliases; | 6 | use cfg_aliases::cfg_aliases; |
| 7 | #[cfg(feature = "_rt1xxx")] | ||
| 8 | use nxp_pac::metadata; | 7 | use nxp_pac::metadata; |
| 8 | use nxp_pac::metadata::{METADATA, Peripheral}; | ||
| 9 | #[allow(unused)] | 9 | #[allow(unused)] |
| 10 | use proc_macro2::TokenStream; | 10 | use proc_macro2::TokenStream; |
| 11 | use proc_macro2::{Ident, Literal, Span}; | ||
| 12 | use quote::format_ident; | ||
| 11 | #[allow(unused)] | 13 | #[allow(unused)] |
| 12 | use quote::quote; | 14 | use quote::quote; |
| 13 | 15 | ||
| @@ -31,56 +33,188 @@ fn main() { | |||
| 31 | .unwrap() | 33 | .unwrap() |
| 32 | .to_ascii_lowercase(); | 34 | .to_ascii_lowercase(); |
| 33 | 35 | ||
| 36 | let singletons = singletons(&mut cfgs); | ||
| 37 | |||
| 34 | cfg_aliases! { | 38 | cfg_aliases! { |
| 35 | rt1xxx: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, | 39 | rt1xxx: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, |
| 36 | gpio1: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, | ||
| 37 | gpio2: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, | ||
| 38 | gpio3: { feature = "mimxrt1062" }, | ||
| 39 | gpio4: { feature = "mimxrt1062" }, | ||
| 40 | gpio5: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, | ||
| 41 | } | 40 | } |
| 42 | 41 | ||
| 43 | eprintln!("chip: {chip_name}"); | 42 | eprintln!("chip: {chip_name}"); |
| 44 | 43 | ||
| 45 | generate_code(); | 44 | generate_code(&mut cfgs, &singletons); |
| 46 | } | 45 | } |
| 47 | 46 | ||
| 48 | #[cfg(feature = "_rt1xxx")] | 47 | /// A peripheral singleton returned by `embassy_nxp::init`. |
| 49 | fn generate_iomuxc() -> TokenStream { | 48 | struct Singleton { |
| 50 | use proc_macro2::{Ident, Span}; | 49 | name: String, |
| 51 | 50 | ||
| 52 | let pads = metadata::iomuxc::IOMUXC_REGISTERS.iter().map(|registers| { | 51 | /// A cfg guard which indicates whether the `Peripherals` struct will give the user this singleton. |
| 53 | let name = Ident::new(®isters.name, Span::call_site()); | 52 | cfg: Option<TokenStream>, |
| 54 | let address = registers.pad_ctl; | 53 | } |
| 55 | 54 | ||
| 56 | quote! { | 55 | fn singletons(cfgs: &mut common::CfgSet) -> Vec<Singleton> { |
| 57 | pub const #name: u32 = #address; | 56 | let mut singletons = Vec::new(); |
| 57 | |||
| 58 | for peripheral in METADATA.peripherals { | ||
| 59 | // GPIO and DMA are generated in a 2nd pass. | ||
| 60 | let skip_singleton = if peripheral.name.starts_with("GPIO") || peripheral.name.starts_with("DMA") { | ||
| 61 | true | ||
| 62 | } else { | ||
| 63 | false | ||
| 64 | }; | ||
| 65 | |||
| 66 | if !skip_singleton { | ||
| 67 | singletons.push(Singleton { | ||
| 68 | name: peripheral.name.into(), | ||
| 69 | cfg: None, | ||
| 70 | }); | ||
| 58 | } | 71 | } |
| 59 | }); | 72 | } |
| 60 | 73 | ||
| 61 | let muxes = metadata::iomuxc::IOMUXC_REGISTERS.iter().map(|registers| { | 74 | cfgs.declare_all(&[ |
| 62 | let name = Ident::new(®isters.name, Span::call_site()); | 75 | "gpio1", |
| 63 | let address = registers.mux_ctl; | 76 | "gpio1_hi", |
| 77 | "gpio2", | ||
| 78 | "gpio2_hi", | ||
| 79 | "gpio3", | ||
| 80 | "gpio3_hi", | ||
| 81 | "gpio4", | ||
| 82 | "gpio4_hi", | ||
| 83 | "gpio5", | ||
| 84 | "gpio5_hi", | ||
| 85 | "gpio10", | ||
| 86 | "gpio10_hi", | ||
| 87 | ]); | ||
| 64 | 88 | ||
| 65 | quote! { | 89 | for peripheral in METADATA.peripherals.iter().filter(|p| p.name.starts_with("GPIO")) { |
| 66 | pub const #name: u32 = #address; | 90 | let number = peripheral.name.strip_prefix("GPIO").unwrap(); |
| 91 | assert!(number.parse::<u8>().is_ok()); | ||
| 92 | cfgs.enable(format!("gpio{}", number)); | ||
| 93 | |||
| 94 | for signal in peripheral.signals.iter() { | ||
| 95 | let pin_number = signal.name.parse::<u8>().unwrap(); | ||
| 96 | |||
| 97 | if pin_number > 15 { | ||
| 98 | cfgs.enable(format!("gpio{}_hi", number)); | ||
| 99 | } | ||
| 100 | |||
| 101 | // GPIO signals only defined a single signal, on a single pin. | ||
| 102 | assert_eq!(signal.pins.len(), 1); | ||
| 103 | |||
| 104 | singletons.push(Singleton { | ||
| 105 | name: signal.pins[0].pin.into(), | ||
| 106 | cfg: None, | ||
| 107 | }); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | for peripheral in METADATA.peripherals.iter().filter(|p| p.name.starts_with("DMA")) { | ||
| 112 | let instance = peripheral.name.strip_prefix("DMA").unwrap(); | ||
| 113 | assert!(instance.parse::<u8>().is_ok()); | ||
| 114 | |||
| 115 | for signal in peripheral.signals.iter() { | ||
| 116 | let channel_number = signal.name.parse::<u8>().unwrap(); | ||
| 117 | let name = format!("DMA{instance}_CH{channel_number}"); | ||
| 118 | |||
| 119 | // DMA has no pins. | ||
| 120 | assert!(signal.pins.is_empty()); | ||
| 121 | |||
| 122 | singletons.push(Singleton { name, cfg: None }); | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | for peripheral in METADATA.peripherals.iter().filter(|p| p.name.starts_with("SCT")) { | ||
| 127 | let instance = peripheral.name.strip_prefix("SCT").unwrap(); | ||
| 128 | assert!(instance.parse::<u8>().is_ok()); | ||
| 129 | |||
| 130 | for signal in peripheral.signals.iter() { | ||
| 131 | if !signal.name.starts_with("OUT") { | ||
| 132 | continue; | ||
| 133 | } | ||
| 134 | |||
| 135 | let channel_number = signal.name.strip_prefix("OUT").unwrap().parse::<u8>().unwrap(); | ||
| 136 | let name = format!("SCT{instance}_OUT{channel_number}"); | ||
| 137 | |||
| 138 | singletons.push(Singleton { name, cfg: None }); | ||
| 67 | } | 139 | } |
| 140 | } | ||
| 141 | |||
| 142 | singletons | ||
| 143 | } | ||
| 144 | |||
| 145 | #[cfg(feature = "_rt1xxx")] | ||
| 146 | fn generate_iomuxc() -> TokenStream { | ||
| 147 | let iomuxc_pad_impls = metadata::METADATA | ||
| 148 | .pins | ||
| 149 | .iter() | ||
| 150 | .filter(|p| p.iomuxc.as_ref().filter(|i| i.mux.is_some()).is_some()) | ||
| 151 | .map(|pin| { | ||
| 152 | let Some(ref iomuxc) = pin.iomuxc else { | ||
| 153 | panic!("Pin {} has no IOMUXC definitions", pin.name); | ||
| 154 | }; | ||
| 155 | |||
| 156 | let name = Ident::new(pin.name, Span::call_site()); | ||
| 157 | let mux = iomuxc.mux.unwrap(); | ||
| 158 | let pad = iomuxc.pad; | ||
| 159 | |||
| 160 | quote! { | ||
| 161 | impl_iomuxc_pad!(#name, #pad, #mux); | ||
| 162 | } | ||
| 163 | }); | ||
| 164 | |||
| 165 | let base_match_arms = metadata::METADATA | ||
| 166 | .peripherals | ||
| 167 | .iter() | ||
| 168 | .filter(|p| p.name.starts_with("GPIO")) | ||
| 169 | .map(|peripheral| { | ||
| 170 | peripheral.signals.iter().map(|signal| { | ||
| 171 | // All GPIO signals have a single pin. | ||
| 172 | let pin = &signal.pins[0]; | ||
| 173 | let instance = peripheral.name.strip_prefix("GPIO").unwrap(); | ||
| 174 | let bank_match = format_ident!("Gpio{}", instance); | ||
| 175 | let pin_number = signal.name.parse::<u8>().unwrap(); | ||
| 176 | let pin_ident = Ident::new(pin.pin, Span::call_site()); | ||
| 177 | |||
| 178 | quote! { | ||
| 179 | (Bank::#bank_match, #pin_number) => <crate::peripherals::#pin_ident as crate::iomuxc::SealedPad> | ||
| 180 | } | ||
| 181 | }) | ||
| 182 | }) | ||
| 183 | .flatten() | ||
| 184 | .collect::<Vec<_>>(); | ||
| 185 | |||
| 186 | let pad_match_arms = base_match_arms.iter().map(|arm| { | ||
| 187 | quote! { #arm::PAD } | ||
| 188 | }); | ||
| 189 | |||
| 190 | let mux_match_arms = base_match_arms.iter().map(|arm| { | ||
| 191 | quote! { #arm::MUX } | ||
| 68 | }); | 192 | }); |
| 69 | 193 | ||
| 70 | quote! { | 194 | quote! { |
| 71 | pub mod iomuxc { | 195 | #(#iomuxc_pad_impls)* |
| 72 | pub mod pads { | 196 | |
| 73 | #(#pads)* | 197 | pub(crate) fn iomuxc_pad(bank: crate::gpio::Bank, pin: u8) -> *mut () { |
| 198 | use crate::gpio::Bank; | ||
| 199 | |||
| 200 | match (bank, pin) { | ||
| 201 | #(#pad_match_arms),*, | ||
| 202 | _ => unreachable!() | ||
| 74 | } | 203 | } |
| 204 | } | ||
| 205 | |||
| 206 | pub(crate) fn iomuxc_mux(bank: crate::gpio::Bank, pin: u8) -> Option<*mut ()> { | ||
| 207 | use crate::gpio::Bank; | ||
| 75 | 208 | ||
| 76 | pub mod muxes { | 209 | match (bank, pin) { |
| 77 | #(#muxes)* | 210 | #(#mux_match_arms),*, |
| 211 | _ => unreachable!() | ||
| 78 | } | 212 | } |
| 79 | } | 213 | } |
| 80 | } | 214 | } |
| 81 | } | 215 | } |
| 82 | 216 | ||
| 83 | fn generate_code() { | 217 | fn generate_code(cfgs: &mut common::CfgSet, singletons: &[Singleton]) { |
| 84 | #[allow(unused)] | 218 | #[allow(unused)] |
| 85 | use std::fmt::Write; | 219 | use std::fmt::Write; |
| 86 | 220 | ||
| @@ -88,14 +222,179 @@ fn generate_code() { | |||
| 88 | #[allow(unused_mut)] | 222 | #[allow(unused_mut)] |
| 89 | let mut output = String::new(); | 223 | let mut output = String::new(); |
| 90 | 224 | ||
| 225 | writeln!(&mut output, "{}", peripherals(singletons)).unwrap(); | ||
| 226 | |||
| 91 | #[cfg(feature = "_rt1xxx")] | 227 | #[cfg(feature = "_rt1xxx")] |
| 92 | writeln!(&mut output, "{}", generate_iomuxc()).unwrap(); | 228 | writeln!(&mut output, "{}", generate_iomuxc()).unwrap(); |
| 93 | 229 | ||
| 230 | writeln!(&mut output, "{}", interrupts()).unwrap(); | ||
| 231 | writeln!(&mut output, "{}", impl_peripherals(cfgs, singletons)).unwrap(); | ||
| 232 | |||
| 94 | let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); | 233 | let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); |
| 95 | fs::write(&out_file, output).unwrap(); | 234 | fs::write(&out_file, output).unwrap(); |
| 96 | rustfmt(&out_file); | 235 | rustfmt(&out_file); |
| 97 | } | 236 | } |
| 98 | 237 | ||
| 238 | fn interrupts() -> TokenStream { | ||
| 239 | let interrupts = METADATA.interrupts.iter().map(|interrupt| format_ident!("{interrupt}")); | ||
| 240 | |||
| 241 | quote! { | ||
| 242 | embassy_hal_internal::interrupt_mod!(#(#interrupts),*); | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | fn peripherals(singletons: &[Singleton]) -> TokenStream { | ||
| 247 | let defs = singletons.iter().map(|s| { | ||
| 248 | let ident = Ident::new(&s.name, Span::call_site()); | ||
| 249 | quote! { #ident } | ||
| 250 | }); | ||
| 251 | |||
| 252 | let peripherals = singletons.iter().map(|s| { | ||
| 253 | let ident = Ident::new(&s.name, Span::call_site()); | ||
| 254 | let cfg = s.cfg.clone().unwrap_or_else(|| quote! {}); | ||
| 255 | quote! { | ||
| 256 | #cfg | ||
| 257 | #ident | ||
| 258 | } | ||
| 259 | }); | ||
| 260 | |||
| 261 | quote! { | ||
| 262 | embassy_hal_internal::peripherals_definition!(#(#defs),*); | ||
| 263 | embassy_hal_internal::peripherals_struct!(#(#peripherals),*); | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | fn impl_gpio_pin(impls: &mut Vec<TokenStream>, peripheral: &Peripheral) { | ||
| 268 | let instance = peripheral.name.strip_prefix("GPIO").unwrap(); | ||
| 269 | let bank = format_ident!("Gpio{}", instance); | ||
| 270 | // let pin = | ||
| 271 | |||
| 272 | for signal in peripheral.signals.iter() { | ||
| 273 | let pin_number = signal.name.parse::<u8>().unwrap(); | ||
| 274 | let pin = Ident::new(signal.pins[0].pin, Span::call_site()); | ||
| 275 | |||
| 276 | impls.push(quote! { | ||
| 277 | impl_pin!(#pin, #bank, #pin_number); | ||
| 278 | }); | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | fn impl_dma_channel(impls: &mut Vec<TokenStream>, peripheral: &Peripheral) { | ||
| 283 | let instance = Ident::new(peripheral.name, Span::call_site()); | ||
| 284 | |||
| 285 | for signal in peripheral.signals.iter() { | ||
| 286 | let channel_number = signal.name.parse::<u8>().unwrap(); | ||
| 287 | let channel_name = format_ident!("{instance}_CH{channel_number}"); | ||
| 288 | |||
| 289 | impls.push(quote! { | ||
| 290 | impl_dma_channel!(#instance, #channel_name, #channel_number); | ||
| 291 | }); | ||
| 292 | } | ||
| 293 | } | ||
| 294 | |||
| 295 | fn impl_usart(impls: &mut Vec<TokenStream>, peripheral: &Peripheral) { | ||
| 296 | let instance = Ident::new(peripheral.name, Span::call_site()); | ||
| 297 | let flexcomm = Ident::new( | ||
| 298 | peripheral.flexcomm.expect("LPC55 must specify FLEXCOMM instance"), | ||
| 299 | Span::call_site(), | ||
| 300 | ); | ||
| 301 | let number = Literal::u8_unsuffixed(peripheral.name.strip_prefix("USART").unwrap().parse::<u8>().unwrap()); | ||
| 302 | |||
| 303 | impls.push(quote! { | ||
| 304 | impl_usart_instance!(#instance, #flexcomm, #number); | ||
| 305 | }); | ||
| 306 | |||
| 307 | for signal in peripheral.signals { | ||
| 308 | let r#macro = match signal.name { | ||
| 309 | "TXD" => format_ident!("impl_usart_txd_pin"), | ||
| 310 | "RXD" => format_ident!("impl_usart_rxd_pin"), | ||
| 311 | _ => unreachable!(), | ||
| 312 | }; | ||
| 313 | |||
| 314 | for pin in signal.pins { | ||
| 315 | let alt = format_ident!("ALT{}", pin.alt); | ||
| 316 | let pin = format_ident!("{}", pin.pin); | ||
| 317 | |||
| 318 | impls.push(quote! { | ||
| 319 | #r#macro!(#pin, #instance, #alt); | ||
| 320 | }); | ||
| 321 | } | ||
| 322 | } | ||
| 323 | |||
| 324 | for dma_mux in peripheral.dma_muxing { | ||
| 325 | assert_eq!(dma_mux.mux, "DMA0", "TODO: USART for more than LPC55"); | ||
| 326 | |||
| 327 | let r#macro = match dma_mux.signal { | ||
| 328 | "TX" => format_ident!("impl_usart_tx_channel"), | ||
| 329 | "RX" => format_ident!("impl_usart_rx_channel"), | ||
| 330 | _ => unreachable!(), | ||
| 331 | }; | ||
| 332 | |||
| 333 | let channel = format_ident!("DMA0_CH{}", dma_mux.request); | ||
| 334 | |||
| 335 | impls.push(quote! { | ||
| 336 | #r#macro!(#instance, #channel); | ||
| 337 | }); | ||
| 338 | } | ||
| 339 | } | ||
| 340 | |||
| 341 | fn impl_sct(impls: &mut Vec<TokenStream>, peripheral: &Peripheral) { | ||
| 342 | let instance = Ident::new(peripheral.name, Span::call_site()); | ||
| 343 | |||
| 344 | impls.push(quote! { | ||
| 345 | impl_sct_instance!(#instance); | ||
| 346 | }); | ||
| 347 | |||
| 348 | for signal in peripheral.signals.iter() { | ||
| 349 | if signal.name.starts_with("OUT") { | ||
| 350 | let channel_number = signal.name.strip_prefix("OUT").unwrap().parse::<u8>().unwrap(); | ||
| 351 | |||
| 352 | let channel_name = format_ident!("{instance}_OUT{channel_number}"); | ||
| 353 | |||
| 354 | impls.push(quote! { | ||
| 355 | impl_sct_output_instance!(#instance, #channel_name, #channel_number); | ||
| 356 | }); | ||
| 357 | |||
| 358 | if signal.name.starts_with("OUT") { | ||
| 359 | for pin in signal.pins { | ||
| 360 | let pin_name = format_ident!("{}", pin.pin); | ||
| 361 | let alt = format_ident!("ALT{}", pin.alt); | ||
| 362 | |||
| 363 | impls.push(quote! { | ||
| 364 | impl_sct_output_pin!(#instance, #channel_name, #pin_name, #alt); | ||
| 365 | }); | ||
| 366 | } | ||
| 367 | } | ||
| 368 | } | ||
| 369 | } | ||
| 370 | } | ||
| 371 | |||
| 372 | fn impl_peripherals(_cfgs: &mut common::CfgSet, _singletons: &[Singleton]) -> TokenStream { | ||
| 373 | let mut impls = Vec::new(); | ||
| 374 | |||
| 375 | for peripheral in metadata::METADATA.peripherals.iter() { | ||
| 376 | if peripheral.name.starts_with("GPIO") { | ||
| 377 | impl_gpio_pin(&mut impls, peripheral); | ||
| 378 | } | ||
| 379 | |||
| 380 | if peripheral.name.starts_with("DMA") { | ||
| 381 | impl_dma_channel(&mut impls, peripheral); | ||
| 382 | } | ||
| 383 | |||
| 384 | if peripheral.name.starts_with("USART") { | ||
| 385 | impl_usart(&mut impls, peripheral); | ||
| 386 | } | ||
| 387 | |||
| 388 | if peripheral.name.starts_with("SCT") { | ||
| 389 | impl_sct(&mut impls, peripheral); | ||
| 390 | } | ||
| 391 | } | ||
| 392 | |||
| 393 | quote! { | ||
| 394 | #(#impls)* | ||
| 395 | } | ||
| 396 | } | ||
| 397 | |||
| 99 | /// rustfmt a given path. | 398 | /// rustfmt a given path. |
| 100 | /// Failures are logged to stderr and ignored. | 399 | /// Failures are logged to stderr and ignored. |
| 101 | fn rustfmt(path: impl AsRef<Path>) { | 400 | fn rustfmt(path: impl AsRef<Path>) { |
