968 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			968 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use std::cmp::Ordering;
 | |
| use std::collections::{BTreeSet, HashMap};
 | |
| use std::fmt::Write;
 | |
| use std::io::Write as _;
 | |
| use std::path::{Path, PathBuf};
 | |
| use std::process::Command;
 | |
| use std::sync::LazyLock;
 | |
| use std::{env, fs};
 | |
| 
 | |
| use common::CfgSet;
 | |
| use mspm0_metapac::metadata::{ALL_CHIPS, METADATA};
 | |
| use proc_macro2::{Ident, Literal, Span, TokenStream};
 | |
| use quote::{format_ident, quote};
 | |
| 
 | |
| #[path = "./build_common.rs"]
 | |
| mod common;
 | |
| 
 | |
| fn main() {
 | |
|     generate_code();
 | |
|     interrupt_group_linker_magic();
 | |
| }
 | |
| 
 | |
| fn generate_code() {
 | |
|     let mut cfgs = common::CfgSet::new();
 | |
|     common::set_target_cfgs(&mut cfgs);
 | |
| 
 | |
|     #[cfg(any(feature = "rt"))]
 | |
|     println!(
 | |
|         "cargo:rustc-link-search={}",
 | |
|         PathBuf::from(env::var_os("OUT_DIR").unwrap()).display(),
 | |
|     );
 | |
| 
 | |
|     cfgs.declare_all(&["gpio_pb", "gpio_pc", "int_group1"]);
 | |
| 
 | |
|     let chip_name = match env::vars()
 | |
|         .map(|(a, _)| a)
 | |
|         .filter(|x| x.starts_with("CARGO_FEATURE_MSPM0") || x.starts_with("CARGO_FEATURE_MSPS"))
 | |
|         .get_one()
 | |
|     {
 | |
|         Ok(x) => x,
 | |
|         Err(GetOneError::None) => panic!("No mspm0xx/mspsxx Cargo feature enabled"),
 | |
|         Err(GetOneError::Multiple) => panic!("Multiple mspm0xx/mspsxx Cargo features enabled"),
 | |
|     }
 | |
|     .strip_prefix("CARGO_FEATURE_")
 | |
|     .unwrap()
 | |
|     .to_ascii_lowercase()
 | |
|     .replace('_', "-");
 | |
| 
 | |
|     eprintln!("chip: {chip_name}");
 | |
| 
 | |
|     cfgs.enable_all(&get_chip_cfgs(&chip_name));
 | |
|     for chip in ALL_CHIPS {
 | |
|         cfgs.declare_all(&get_chip_cfgs(&chip));
 | |
|     }
 | |
| 
 | |
|     let mut singletons = get_singletons(&mut cfgs);
 | |
| 
 | |
|     time_driver(&mut singletons, &mut cfgs);
 | |
| 
 | |
|     let mut g = TokenStream::new();
 | |
| 
 | |
|     g.extend(generate_singletons(&singletons));
 | |
|     g.extend(generate_pincm_mapping());
 | |
|     g.extend(generate_pin());
 | |
|     g.extend(generate_timers());
 | |
|     g.extend(generate_interrupts());
 | |
|     g.extend(generate_peripheral_instances());
 | |
|     g.extend(generate_pin_trait_impls());
 | |
|     g.extend(generate_groups());
 | |
| 
 | |
|     let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
 | |
|     let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string();
 | |
|     fs::write(&out_file, g.to_string()).unwrap();
 | |
|     rustfmt(&out_file);
 | |
| }
 | |
| 
 | |
| fn get_chip_cfgs(chip_name: &str) -> Vec<String> {
 | |
|     let mut cfgs = Vec::new();
 | |
| 
 | |
|     // GPIO on C110x is special as it does not belong to an interrupt group.
 | |
|     if chip_name.starts_with("mspm0c110") || chip_name.starts_with("msps003f") {
 | |
|         cfgs.push("mspm0c110x".to_string());
 | |
|     }
 | |
| 
 | |
|     // Family ranges (temporary until int groups are generated)
 | |
|     //
 | |
|     // TODO: Remove this once int group stuff is generated.
 | |
|     if chip_name.starts_with("mspm0g110") {
 | |
|         cfgs.push("mspm0g110x".to_string());
 | |
|     }
 | |
| 
 | |
|     if chip_name.starts_with("mspm0g150") {
 | |
|         cfgs.push("mspm0g150x".to_string());
 | |
|     }
 | |
| 
 | |
|     if chip_name.starts_with("mspm0g151") {
 | |
|         cfgs.push("mspm0g151x".to_string());
 | |
|     }
 | |
| 
 | |
|     if chip_name.starts_with("mspm0g310") {
 | |
|         cfgs.push("mspm0g310x".to_string());
 | |
|     }
 | |
| 
 | |
|     if chip_name.starts_with("mspm0g350") {
 | |
|         cfgs.push("mspm0g350x".to_string());
 | |
|     }
 | |
| 
 | |
|     if chip_name.starts_with("mspm0g351") {
 | |
|         cfgs.push("mspm0g351x".to_string());
 | |
|     }
 | |
| 
 | |
|     if chip_name.starts_with("mspm0l110") {
 | |
|         cfgs.push("mspm0l110x".to_string());
 | |
|     }
 | |
| 
 | |
|     if chip_name.starts_with("mspm0l122") {
 | |
|         cfgs.push("mspm0l122x".to_string());
 | |
|     }
 | |
| 
 | |
|     if chip_name.starts_with("mspm0l130") {
 | |
|         cfgs.push("mspm0l130x".to_string());
 | |
|     }
 | |
| 
 | |
|     if chip_name.starts_with("mspm0l134") {
 | |
|         cfgs.push("mspm0l134x".to_string());
 | |
|     }
 | |
| 
 | |
|     if chip_name.starts_with("mspm0l222") {
 | |
|         cfgs.push("mspm0l222x".to_string());
 | |
|     }
 | |
| 
 | |
|     cfgs
 | |
| }
 | |
| 
 | |
| /// Interrupt groups use a weakly linked symbols and #[linkage = "extern_weak"] is nightly we need to
 | |
| /// do some linker magic to create weak linkage.
 | |
| fn interrupt_group_linker_magic() {
 | |
|     let mut file = String::new();
 | |
| 
 | |
|     for group in METADATA.interrupt_groups {
 | |
|         for interrupt in group.interrupts.iter() {
 | |
|             let name = interrupt.name;
 | |
| 
 | |
|             writeln!(&mut file, "PROVIDE({name} = DefaultHandler);").unwrap();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
 | |
|     let out_file = out_dir.join("interrupt_group.x");
 | |
|     fs::write(&out_file, file).unwrap();
 | |
| }
 | |
| 
 | |
| fn generate_groups() -> TokenStream {
 | |
|     let group_vectors = METADATA.interrupt_groups.iter().map(|group| {
 | |
|         let vectors = group.interrupts.iter().map(|interrupt| {
 | |
|             let fn_name = Ident::new(interrupt.name, Span::call_site());
 | |
| 
 | |
|             quote! {
 | |
|                 pub(crate) fn #fn_name();
 | |
|             }
 | |
|         });
 | |
| 
 | |
|         quote! { #(#vectors)* }
 | |
|     });
 | |
| 
 | |
|     let groups = METADATA.interrupt_groups.iter().map(|group| {
 | |
|         let interrupt_group_name = Ident::new(group.name, Span::call_site());
 | |
|         let group_enum = Ident::new(&format!("Group{}", &group.name[5..]), Span::call_site());
 | |
|         let group_number = Literal::u32_unsuffixed(group.number);
 | |
| 
 | |
|         let matches = group.interrupts.iter().map(|interrupt| {
 | |
|             let variant = Ident::new(&interrupt.name, Span::call_site());
 | |
| 
 | |
|             quote! {
 | |
|                 #group_enum::#variant => unsafe { group_vectors::#variant() },
 | |
|             }
 | |
|         });
 | |
| 
 | |
|         quote! {
 | |
|             #[cfg(feature = "rt")]
 | |
|             #[crate::pac::interrupt]
 | |
|             fn #interrupt_group_name() {
 | |
|                 use crate::pac::#group_enum;
 | |
| 
 | |
|                 let group = crate::pac::CPUSS.int_group(#group_number);
 | |
|                 // MUST subtract by 1 since 0 is NO_INTR
 | |
|                 let iidx = group.iidx().read().stat().to_bits() - 1;
 | |
| 
 | |
|                 let Ok(group) = #group_enum::try_from(iidx as u8) else {
 | |
|                     return;
 | |
|                 };
 | |
| 
 | |
|                 match group {
 | |
|                     #(#matches)*
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     quote! {
 | |
|         #(#groups)*
 | |
| 
 | |
|         #[cfg(feature = "rt")]
 | |
|         mod group_vectors {
 | |
|             extern "Rust" {
 | |
|                 #(#group_vectors)*
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[derive(Debug, Clone)]
 | |
| struct Singleton {
 | |
|     name: String,
 | |
| 
 | |
|     cfg: Option<TokenStream>,
 | |
| }
 | |
| 
 | |
| impl PartialEq for Singleton {
 | |
|     fn eq(&self, other: &Self) -> bool {
 | |
|         self.name == other.name
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl Eq for Singleton {}
 | |
| 
 | |
| impl PartialOrd for Singleton {
 | |
|     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
 | |
|         Some(self.cmp(other))
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl Ord for Singleton {
 | |
|     fn cmp(&self, other: &Self) -> Ordering {
 | |
|         self.name.cmp(&other.name)
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn get_singletons(cfgs: &mut common::CfgSet) -> Vec<Singleton> {
 | |
|     let mut singletons = Vec::<Singleton>::new();
 | |
| 
 | |
|     for peripheral in METADATA.peripherals {
 | |
|         // Some peripherals do not generate a singleton, but generate a singleton for each pin.
 | |
|         let skip_peripheral_singleton = match peripheral.kind {
 | |
|             "gpio" => {
 | |
|                 // Also enable ports that are present.
 | |
|                 match peripheral.name {
 | |
|                     "GPIOB" => cfgs.enable("gpio_pb"),
 | |
|                     "GPIOC" => cfgs.enable("gpio_pc"),
 | |
|                     _ => (),
 | |
|                 }
 | |
| 
 | |
|                 true
 | |
|             }
 | |
| 
 | |
|             // Each channel gets a singleton, handled separately.
 | |
|             "dma" => true,
 | |
| 
 | |
|             // These peripherals do not exist as singletons, and have no signals but are managed
 | |
|             // by the HAL.
 | |
|             "iomux" | "cpuss" => true,
 | |
| 
 | |
|             _ => false,
 | |
|         };
 | |
| 
 | |
|         if !skip_peripheral_singleton {
 | |
|             singletons.push(Singleton {
 | |
|                 name: peripheral.name.to_string(),
 | |
|                 cfg: None,
 | |
|             });
 | |
|         }
 | |
| 
 | |
|         let mut signals = BTreeSet::new();
 | |
| 
 | |
|         // Pick out each unique signal. There may be multiple instances of each signal due to
 | |
|         // iomux mappings.
 | |
|         for pin in peripheral.pins {
 | |
|             let signal = if peripheral.name.starts_with("GPIO")
 | |
|                 || peripheral.name.starts_with("VREF")
 | |
|                 || peripheral.name.starts_with("RTC")
 | |
|             {
 | |
|                 pin.signal.to_string()
 | |
|             } else {
 | |
|                 format!("{}_{}", peripheral.name, pin.signal)
 | |
|             };
 | |
| 
 | |
|             // We need to rename some signals to become valid Rust identifiers.
 | |
|             let signal = make_valid_identifier(&signal);
 | |
|             signals.insert(signal);
 | |
|         }
 | |
| 
 | |
|         singletons.extend(signals);
 | |
|     }
 | |
| 
 | |
|     // DMA channels get their own singletons
 | |
|     for dma_channel in METADATA.dma_channels.iter() {
 | |
|         singletons.push(Singleton {
 | |
|             name: format!("DMA_CH{}", dma_channel.number),
 | |
|             cfg: None,
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     singletons.sort_by(|a, b| a.name.cmp(&b.name));
 | |
|     singletons
 | |
| }
 | |
| 
 | |
| fn make_valid_identifier(s: &str) -> Singleton {
 | |
|     let name = s.replace('+', "_P").replace("-", "_N");
 | |
| 
 | |
|     Singleton { name, cfg: None }
 | |
| }
 | |
| 
 | |
| fn generate_pincm_mapping() -> TokenStream {
 | |
|     let pincms = METADATA.pins.iter().map(|mapping| {
 | |
|         let port_letter = mapping.pin.strip_prefix("P").unwrap();
 | |
|         let port_base = (port_letter.chars().next().unwrap() as u8 - b'A') * 32;
 | |
|         // This assumes all ports are single letter length.
 | |
|         // This is fine unless TI releases a part with 833+ GPIO pins.
 | |
|         let pin_number = mapping.pin[2..].parse::<u8>().unwrap();
 | |
| 
 | |
|         let num = port_base + pin_number;
 | |
| 
 | |
|         // But subtract 1 since pincm indices start from 0, not 1.
 | |
|         let pincm = Literal::u8_unsuffixed(mapping.pincm - 1);
 | |
|         quote! {
 | |
|             #num => #pincm
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     quote! {
 | |
|         #[doc = "Get the mapping from GPIO pin port to IOMUX PINCM index. This is required since the mapping from IO to PINCM index is not consistent across parts."]
 | |
|         pub(crate) fn gpio_pincm(pin_port: u8) -> u8 {
 | |
|             match pin_port {
 | |
|                 #(#pincms),*,
 | |
|                 _ => unreachable!(),
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn generate_pin() -> TokenStream {
 | |
|     let pin_impls = METADATA.pins.iter().map(|pin| {
 | |
|         let name = Ident::new(&pin.pin, Span::call_site());
 | |
|         let port_letter = pin.pin.strip_prefix("P").unwrap();
 | |
|         let port_letter = port_letter.chars().next().unwrap();
 | |
|         let pin_number = Literal::u8_unsuffixed(pin.pin[2..].parse::<u8>().unwrap());
 | |
| 
 | |
|         let port = Ident::new(&format!("Port{}", port_letter), Span::call_site());
 | |
| 
 | |
|         // TODO: Feature gate pins that can be used as NRST
 | |
| 
 | |
|         quote! {
 | |
|             impl_pin!(#name, crate::gpio::Port::#port, #pin_number);
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     quote! {
 | |
|         #(#pin_impls)*
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn time_driver(singletons: &mut Vec<Singleton>, cfgs: &mut CfgSet) {
 | |
|     // Timer features
 | |
|     for (timer, _) in TIMERS.iter() {
 | |
|         let name = timer.to_lowercase();
 | |
|         cfgs.declare(&format!("time_driver_{}", name));
 | |
|     }
 | |
| 
 | |
|     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 time-driver-xxx Cargo features enabled"),
 | |
|     };
 | |
| 
 | |
|     // Verify the selected timer is available
 | |
|     let selected_timer = match time_driver.as_ref().map(|x| x.as_ref()) {
 | |
|         None => "",
 | |
|         Some("timg0") => "TIMG0",
 | |
|         Some("timg1") => "TIMG1",
 | |
|         Some("timg2") => "TIMG2",
 | |
|         Some("timg3") => "TIMG3",
 | |
|         Some("timg4") => "TIMG4",
 | |
|         Some("timg5") => "TIMG5",
 | |
|         Some("timg6") => "TIMG6",
 | |
|         Some("timg7") => "TIMG7",
 | |
|         Some("timg8") => "TIMG8",
 | |
|         Some("timg9") => "TIMG9",
 | |
|         Some("timg10") => "TIMG10",
 | |
|         Some("timg11") => "TIMG11",
 | |
|         Some("timg14") => "TIMG14",
 | |
|         Some("tima0") => "TIMA0",
 | |
|         Some("tima1") => "TIMA1",
 | |
|         Some("any") => {
 | |
|             // Order of timer candidates:
 | |
|             // 1. 16-bit, 2 channel
 | |
|             // 2. 16-bit, 2 channel with shadow registers
 | |
|             // 3. 16-bit, 4 channel
 | |
|             // 4. 16-bit with QEI
 | |
|             // 5. Advanced timers
 | |
|             //
 | |
|             // TODO: Select RTC first if available
 | |
|             // TODO: 32-bit timers are not considered yet
 | |
|             [
 | |
|                 // 16-bit, 2 channel
 | |
|                 "TIMG0", "TIMG1", "TIMG2", "TIMG3", // 16-bit, 2 channel with shadow registers
 | |
|                 "TIMG4", "TIMG5", "TIMG6", "TIMG7",  // 16-bit, 4 channel
 | |
|                 "TIMG14", // 16-bit with QEI
 | |
|                 "TIMG8", "TIMG9", "TIMG10", "TIMG11", // Advanced timers
 | |
|                 "TIMA0", "TIMA1",
 | |
|             ]
 | |
|             .iter()
 | |
|             .find(|tim| singletons.iter().any(|s| s.name == **tim))
 | |
|             .expect("Could not find any timer")
 | |
|         }
 | |
|         _ => panic!("unknown time_driver {:?}", time_driver),
 | |
|     };
 | |
| 
 | |
|     if !selected_timer.is_empty() {
 | |
|         cfgs.enable(format!("time_driver_{}", selected_timer.to_lowercase()));
 | |
|     }
 | |
| 
 | |
|     // Apply cfgs to each timer and it's pins
 | |
|     for singleton in singletons.iter_mut() {
 | |
|         if singleton.name.starts_with("TIM") {
 | |
|             // Remove suffixes for pin singletons.
 | |
|             let name = if singleton.name.contains("_CCP") {
 | |
|                 singleton.name.split_once("_CCP").unwrap().0
 | |
|             } else if singleton.name.contains("_FAULT") {
 | |
|                 singleton.name.split_once("_FAULT").unwrap().0
 | |
|             } else if singleton.name.contains("_IDX") {
 | |
|                 singleton.name.split_once("_IDX").unwrap().0
 | |
|             } else {
 | |
|                 &singleton.name
 | |
|             };
 | |
| 
 | |
|             let feature = format!("time-driver-{}", name.to_lowercase());
 | |
| 
 | |
|             if singleton.name.contains(selected_timer) {
 | |
|                 singleton.cfg = Some(quote! { #[cfg(not(all(feature = "time-driver-any", feature = #feature)))] });
 | |
|             } else {
 | |
|                 singleton.cfg = Some(quote! { #[cfg(not(feature = #feature))] });
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn generate_singletons(singletons: &[Singleton]) -> TokenStream {
 | |
|     let singletons = singletons
 | |
|         .iter()
 | |
|         .map(|s| {
 | |
|             let cfg = s.cfg.clone().unwrap_or_default();
 | |
| 
 | |
|             let ident = format_ident!("{}", s.name);
 | |
| 
 | |
|             quote! {
 | |
|                 #cfg
 | |
|                 #ident
 | |
|             }
 | |
|         })
 | |
|         .collect::<Vec<_>>();
 | |
| 
 | |
|     quote! {
 | |
|         embassy_hal_internal::peripherals_definition!(#(#singletons),*);
 | |
|         embassy_hal_internal::peripherals_struct!(#(#singletons),*);
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn generate_timers() -> TokenStream {
 | |
|     // Generate timers
 | |
|     let timer_impls = METADATA
 | |
|         .peripherals
 | |
|         .iter()
 | |
|         .filter(|p| p.name.starts_with("TIM"))
 | |
|         .map(|peripheral| {
 | |
|             let name = Ident::new(&peripheral.name, Span::call_site());
 | |
|             let timers = &*TIMERS;
 | |
| 
 | |
|             let timer = timers.get(peripheral.name).expect("Timer does not exist");
 | |
|             assert!(timer.bits == 16 || timer.bits == 32);
 | |
|             let bits = if timer.bits == 16 {
 | |
|                 quote! { Bits16 }
 | |
|             } else {
 | |
|                 quote! { Bits32 }
 | |
|             };
 | |
| 
 | |
|             quote! {
 | |
|                 impl_timer!(#name, #bits);
 | |
|             }
 | |
|         });
 | |
| 
 | |
|     quote! {
 | |
|         #(#timer_impls)*
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn generate_interrupts() -> TokenStream {
 | |
|     // Generate interrupt module
 | |
|     let interrupts: Vec<Ident> = METADATA
 | |
|         .interrupts
 | |
|         .iter()
 | |
|         .map(|interrupt| Ident::new(interrupt.name, Span::call_site()))
 | |
|         .collect();
 | |
| 
 | |
|     let group_interrupt_enables = METADATA
 | |
|         .interrupts
 | |
|         .iter()
 | |
|         .filter(|interrupt| interrupt.name.contains("GROUP"))
 | |
|         .map(|interrupt| {
 | |
|             let name = Ident::new(interrupt.name, Span::call_site());
 | |
| 
 | |
|             quote! {
 | |
|                 crate::interrupt::typelevel::#name::enable();
 | |
|             }
 | |
|         });
 | |
| 
 | |
|     // Generate interrupt enables for groups
 | |
|     quote! {
 | |
|         embassy_hal_internal::interrupt_mod! {
 | |
|             #(#interrupts),*
 | |
|         }
 | |
| 
 | |
|         pub fn enable_group_interrupts(_cs: critical_section::CriticalSection) {
 | |
|             use crate::interrupt::typelevel::Interrupt;
 | |
| 
 | |
|             unsafe {
 | |
|                 #(#group_interrupt_enables)*
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn generate_peripheral_instances() -> TokenStream {
 | |
|     let mut impls = Vec::<TokenStream>::new();
 | |
| 
 | |
|     for peripheral in METADATA.peripherals {
 | |
|         let peri = format_ident!("{}", peripheral.name);
 | |
| 
 | |
|         // Will be filled in when uart implementation is finished
 | |
|         let _ = peri;
 | |
|         let tokens = match peripheral.kind {
 | |
|             "uart" => Some(quote! { impl_uart_instance!(#peri); }),
 | |
|             _ => None,
 | |
|         };
 | |
| 
 | |
|         if let Some(tokens) = tokens {
 | |
|             impls.push(tokens);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     quote! {
 | |
|         #(#impls)*
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn generate_pin_trait_impls() -> TokenStream {
 | |
|     let mut impls = Vec::<TokenStream>::new();
 | |
| 
 | |
|     for peripheral in METADATA.peripherals {
 | |
|         for pin in peripheral.pins {
 | |
|             let key = (peripheral.kind, pin.signal);
 | |
| 
 | |
|             let pin_name = format_ident!("{}", pin.pin);
 | |
|             let peri = format_ident!("{}", peripheral.name);
 | |
|             let pf = pin.pf;
 | |
| 
 | |
|             // Will be filled in when uart implementation is finished
 | |
|             let _ = pin_name;
 | |
|             let _ = peri;
 | |
|             let _ = pf;
 | |
| 
 | |
|             let tokens = match key {
 | |
|                 ("uart", "TX") => Some(quote! { impl_uart_tx_pin!(#peri, #pin_name, #pf); }),
 | |
|                 ("uart", "RX") => Some(quote! { impl_uart_rx_pin!(#peri, #pin_name, #pf); }),
 | |
|                 ("uart", "CTS") => Some(quote! { impl_uart_cts_pin!(#peri, #pin_name, #pf); }),
 | |
|                 ("uart", "RTS") => Some(quote! { impl_uart_rts_pin!(#peri, #pin_name, #pf); }),
 | |
|                 _ => None,
 | |
|             };
 | |
| 
 | |
|             if let Some(tokens) = tokens {
 | |
|                 impls.push(tokens);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     quote! {
 | |
|         #(#impls)*
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// 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();
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[allow(dead_code)]
 | |
| struct TimerDesc {
 | |
|     bits: u8,
 | |
|     /// Is there an 8-bit prescaler
 | |
|     prescaler: bool,
 | |
|     /// Is there a repeat counter
 | |
|     repeat_counter: bool,
 | |
|     ccp_channels_internal: u8,
 | |
|     ccp_channels_external: u8,
 | |
|     external_pwm_channels: u8,
 | |
|     phase_load: bool,
 | |
|     shadow_load: bool,
 | |
|     shadow_ccs: bool,
 | |
|     deadband: bool,
 | |
|     fault_handler: bool,
 | |
|     qei_hall: bool,
 | |
| }
 | |
| 
 | |
| /// Description of all timer instances.
 | |
| const TIMERS: LazyLock<HashMap<String, TimerDesc>> = LazyLock::new(|| {
 | |
|     let mut map = HashMap::new();
 | |
|     map.insert(
 | |
|         "TIMG0".into(),
 | |
|         TimerDesc {
 | |
|             bits: 16,
 | |
|             prescaler: true,
 | |
|             repeat_counter: false,
 | |
|             ccp_channels_internal: 2,
 | |
|             ccp_channels_external: 2,
 | |
|             external_pwm_channels: 2,
 | |
|             phase_load: false,
 | |
|             shadow_load: false,
 | |
|             shadow_ccs: false,
 | |
|             deadband: false,
 | |
|             fault_handler: false,
 | |
|             qei_hall: false,
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     map.insert(
 | |
|         "TIMG1".into(),
 | |
|         TimerDesc {
 | |
|             bits: 16,
 | |
|             prescaler: true,
 | |
|             repeat_counter: false,
 | |
|             ccp_channels_internal: 2,
 | |
|             ccp_channels_external: 2,
 | |
|             external_pwm_channels: 2,
 | |
|             phase_load: false,
 | |
|             shadow_load: false,
 | |
|             shadow_ccs: false,
 | |
|             deadband: false,
 | |
|             fault_handler: false,
 | |
|             qei_hall: false,
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     map.insert(
 | |
|         "TIMG2".into(),
 | |
|         TimerDesc {
 | |
|             bits: 16,
 | |
|             prescaler: true,
 | |
|             repeat_counter: false,
 | |
|             ccp_channels_internal: 2,
 | |
|             ccp_channels_external: 2,
 | |
|             external_pwm_channels: 2,
 | |
|             phase_load: false,
 | |
|             shadow_load: false,
 | |
|             shadow_ccs: false,
 | |
|             deadband: false,
 | |
|             fault_handler: false,
 | |
|             qei_hall: false,
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     map.insert(
 | |
|         "TIMG3".into(),
 | |
|         TimerDesc {
 | |
|             bits: 16,
 | |
|             prescaler: true,
 | |
|             repeat_counter: false,
 | |
|             ccp_channels_internal: 2,
 | |
|             ccp_channels_external: 2,
 | |
|             external_pwm_channels: 2,
 | |
|             phase_load: false,
 | |
|             shadow_load: false,
 | |
|             shadow_ccs: false,
 | |
|             deadband: false,
 | |
|             fault_handler: false,
 | |
|             qei_hall: false,
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     map.insert(
 | |
|         "TIMG4".into(),
 | |
|         TimerDesc {
 | |
|             bits: 16,
 | |
|             prescaler: true,
 | |
|             repeat_counter: false,
 | |
|             ccp_channels_internal: 2,
 | |
|             ccp_channels_external: 2,
 | |
|             external_pwm_channels: 2,
 | |
|             phase_load: false,
 | |
|             shadow_load: true,
 | |
|             shadow_ccs: true,
 | |
|             deadband: false,
 | |
|             fault_handler: false,
 | |
|             qei_hall: false,
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     map.insert(
 | |
|         "TIMG5".into(),
 | |
|         TimerDesc {
 | |
|             bits: 16,
 | |
|             prescaler: true,
 | |
|             repeat_counter: false,
 | |
|             ccp_channels_internal: 2,
 | |
|             ccp_channels_external: 2,
 | |
|             external_pwm_channels: 2,
 | |
|             phase_load: false,
 | |
|             shadow_load: true,
 | |
|             shadow_ccs: true,
 | |
|             deadband: false,
 | |
|             fault_handler: false,
 | |
|             qei_hall: false,
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     map.insert(
 | |
|         "TIMG6".into(),
 | |
|         TimerDesc {
 | |
|             bits: 16,
 | |
|             prescaler: true,
 | |
|             repeat_counter: false,
 | |
|             ccp_channels_internal: 2,
 | |
|             ccp_channels_external: 2,
 | |
|             external_pwm_channels: 2,
 | |
|             phase_load: false,
 | |
|             shadow_load: true,
 | |
|             shadow_ccs: true,
 | |
|             deadband: false,
 | |
|             fault_handler: false,
 | |
|             qei_hall: false,
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     map.insert(
 | |
|         "TIMG7".into(),
 | |
|         TimerDesc {
 | |
|             bits: 16,
 | |
|             prescaler: true,
 | |
|             repeat_counter: false,
 | |
|             ccp_channels_internal: 2,
 | |
|             ccp_channels_external: 2,
 | |
|             external_pwm_channels: 2,
 | |
|             phase_load: false,
 | |
|             shadow_load: true,
 | |
|             shadow_ccs: true,
 | |
|             deadband: false,
 | |
|             fault_handler: false,
 | |
|             qei_hall: false,
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     map.insert(
 | |
|         "TIMG8".into(),
 | |
|         TimerDesc {
 | |
|             bits: 16,
 | |
|             prescaler: true,
 | |
|             repeat_counter: false,
 | |
|             ccp_channels_internal: 2,
 | |
|             ccp_channels_external: 2,
 | |
|             external_pwm_channels: 2,
 | |
|             phase_load: false,
 | |
|             shadow_load: false,
 | |
|             shadow_ccs: false,
 | |
|             deadband: false,
 | |
|             fault_handler: false,
 | |
|             qei_hall: true,
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     map.insert(
 | |
|         "TIMG9".into(),
 | |
|         TimerDesc {
 | |
|             bits: 16,
 | |
|             prescaler: true,
 | |
|             repeat_counter: false,
 | |
|             ccp_channels_internal: 2,
 | |
|             ccp_channels_external: 2,
 | |
|             external_pwm_channels: 2,
 | |
|             phase_load: false,
 | |
|             shadow_load: false,
 | |
|             shadow_ccs: false,
 | |
|             deadband: false,
 | |
|             fault_handler: false,
 | |
|             qei_hall: true,
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     map.insert(
 | |
|         "TIMG10".into(),
 | |
|         TimerDesc {
 | |
|             bits: 16,
 | |
|             prescaler: true,
 | |
|             repeat_counter: false,
 | |
|             ccp_channels_internal: 2,
 | |
|             ccp_channels_external: 2,
 | |
|             external_pwm_channels: 2,
 | |
|             phase_load: false,
 | |
|             shadow_load: false,
 | |
|             shadow_ccs: false,
 | |
|             deadband: false,
 | |
|             fault_handler: false,
 | |
|             qei_hall: true,
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     map.insert(
 | |
|         "TIMG11".into(),
 | |
|         TimerDesc {
 | |
|             bits: 16,
 | |
|             prescaler: true,
 | |
|             repeat_counter: false,
 | |
|             ccp_channels_internal: 2,
 | |
|             ccp_channels_external: 2,
 | |
|             external_pwm_channels: 2,
 | |
|             phase_load: false,
 | |
|             shadow_load: false,
 | |
|             shadow_ccs: false,
 | |
|             deadband: false,
 | |
|             fault_handler: false,
 | |
|             qei_hall: true,
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     map.insert(
 | |
|         "TIMG12".into(),
 | |
|         TimerDesc {
 | |
|             bits: 32,
 | |
|             prescaler: false,
 | |
|             repeat_counter: false,
 | |
|             ccp_channels_internal: 2,
 | |
|             ccp_channels_external: 2,
 | |
|             external_pwm_channels: 2,
 | |
|             phase_load: false,
 | |
|             shadow_load: false,
 | |
|             shadow_ccs: true,
 | |
|             deadband: false,
 | |
|             fault_handler: false,
 | |
|             qei_hall: false,
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     map.insert(
 | |
|         "TIMG13".into(),
 | |
|         TimerDesc {
 | |
|             bits: 32,
 | |
|             prescaler: false,
 | |
|             repeat_counter: false,
 | |
|             ccp_channels_internal: 2,
 | |
|             ccp_channels_external: 2,
 | |
|             external_pwm_channels: 2,
 | |
|             phase_load: false,
 | |
|             shadow_load: false,
 | |
|             shadow_ccs: true,
 | |
|             deadband: false,
 | |
|             fault_handler: false,
 | |
|             qei_hall: false,
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     map.insert(
 | |
|         "TIMG14".into(),
 | |
|         TimerDesc {
 | |
|             bits: 16,
 | |
|             prescaler: true,
 | |
|             repeat_counter: false,
 | |
|             ccp_channels_internal: 4,
 | |
|             ccp_channels_external: 4,
 | |
|             external_pwm_channels: 4,
 | |
|             phase_load: false,
 | |
|             shadow_load: false,
 | |
|             shadow_ccs: false,
 | |
|             deadband: false,
 | |
|             fault_handler: false,
 | |
|             qei_hall: false,
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     map.insert(
 | |
|         "TIMA0".into(),
 | |
|         TimerDesc {
 | |
|             bits: 16,
 | |
|             prescaler: true,
 | |
|             repeat_counter: true,
 | |
|             ccp_channels_internal: 4,
 | |
|             ccp_channels_external: 2,
 | |
|             external_pwm_channels: 8,
 | |
|             phase_load: true,
 | |
|             shadow_load: true,
 | |
|             shadow_ccs: true,
 | |
|             deadband: true,
 | |
|             fault_handler: true,
 | |
|             qei_hall: false,
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     map.insert(
 | |
|         "TIMA1".into(),
 | |
|         TimerDesc {
 | |
|             bits: 16,
 | |
|             prescaler: true,
 | |
|             repeat_counter: true,
 | |
|             ccp_channels_internal: 2,
 | |
|             ccp_channels_external: 2,
 | |
|             external_pwm_channels: 4,
 | |
|             phase_load: true,
 | |
|             shadow_load: true,
 | |
|             shadow_ccs: true,
 | |
|             deadband: true,
 | |
|             fault_handler: true,
 | |
|             qei_hall: false,
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     map
 | |
| });
 | |
| 
 | |
| 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),
 | |
|             },
 | |
|         }
 | |
|     }
 | |
| }
 |