use std::collections::{HashMap, HashSet}; use anyhow::{anyhow, bail, Ok}; use chiptool::ir::IR; use stm32_data_serde::chip::core::peripheral::rcc::{Field, StopMode}; use stm32_data_serde::chip::core::peripheral::{self, rcc}; use crate::regex; use crate::registers::Registers; #[derive(Debug)] pub struct ParsedRccs { /// RCC version -> parsed info rccs: HashMap, } #[derive(Debug)] struct ParsedRcc { /// name -> en/rst bit info en_rst: HashMap, /// name -> mux info mux: HashMap, } #[derive(Debug)] struct MuxInfo { mux: Field, variants: Vec, } #[derive(Debug)] struct EnRst { enable: rcc::Field, reset: Option, bus_clock: String, stop_mode: StopMode, } impl ParsedRccs { pub fn parse(registers: &Registers) -> anyhow::Result { let mut rccs = HashMap::new(); for (rcc_name, ir) in ®isters.registers { if let Some(rcc_name) = rcc_name.strip_prefix("rcc_") { rccs.insert(rcc_name.to_string(), Self::parse_rcc(rcc_name, ir)?); } } Ok(Self { rccs }) } fn parse_rcc(rcc_version: &str, ir: &IR) -> anyhow::Result { let allowed_variants = HashSet::from([ "DISABLE", "SYS", "PCLK1", "PCLK1_TIM", "PCLK2", "PCLK2_TIM", "PCLK3", "PCLK4", "PCLK5", "PCLK6", "PCLK7", "HCLK1", "HCLK2", "HCLK3", "HCLK4", "HCLK5", "HCLK6", "HCLK7", "PLLI2S1_P", "PLLI2S1_Q", "PLLI2S1_R", "PLLI2S2_P", "PLLI2S2_Q", "PLLI2S2_R", "PLLSAI1_P", "PLLSAI1_Q", "PLLSAI1_R", "PLLSAI2_P", "PLLSAI2_Q", "PLLSAI2_R", "PLL1_P", "PLL1_P_MUL_2", "PLL1_Q", "PLL1_R", "PLL1_VCO", // used for L0 USB "PLL2_P", "PLL2_Q", "PLL2_R", "PLL3_P", "PLL3_Q", "PLL3_R", "HSI", "SHSI", "HSI48", "HSIKER", "LSI", "CSI", "MSI", "MSIS", "MSIK", "HSE", "LSE", "AUDIOCLK", "PER", "CLK48", // TODO: variants to cleanup "AFIF", "HSI_HSE", "DSI_PHY", "HSI_Div488", "SAI1_EXTCLK", "SAI2_EXTCLK", "B_0x0", "B_0x1", "I2S_CKIN", "DAC_HOLD", "DAC_HOLD_2", "RTCCLK", "RTC_WKUP", "DSIPHY", "ICLK", "DCLK", "I2S1", "I2S2", "SAI1", "SAI2", "HSI256_MSIS1024_MSIS4", "HSI256_MSIS1024_MSIK4", "HSI256_MSIK1024_MSIS4", "HSI256_MSIK1024_MSIK4", ]); let mux_regexes = &[ regex!(r"^DCKCFGR\d?/(.+)SEL$"), regex!(r"^CCIPR\d?/(.+)SEL$"), regex!(r"^D\dCCIP\d?R/(.+)SEL$"), regex!(r"^CFGR\d/(.+)SW$"), ]; let mut mux = HashMap::new(); for (reg, body) in &ir.fieldsets { for field in &body.fields { let key = format!("{}/{}", reg, field.name); if let Some(capture) = mux_regexes.iter().find_map(|r| r.captures(&key)) { let peri = capture.get(1).unwrap().as_str(); // TODO: these bits are duplicated on F4, we need to split the F4 RCCs more. if rcc_version.starts_with("f4") && reg == "DCKCFGR2" && (peri == "CLK48" || peri == "SDIO") { continue; } // ignore switches with missing enum. let Some(enum_name) = field.enumm.as_deref() else { continue; }; let enumm = ir.enums.get(enum_name).unwrap(); for v in &enumm.variants { let mut vname = v.name.as_str(); if let Some(captures) = regex!(r"^([A-Z0-9_]+)_DIV_\d+?$").captures(v.name.as_str()) { vname = captures.get(1).unwrap().as_str(); } if !allowed_variants.contains(vname) { return Err(anyhow!( "rcc: prohibited variant name {} in enum {} for rcc_{}", v.name.as_str(), enum_name, rcc_version )); } } let val = MuxInfo { mux: Field { register: reg.clone(), field: field.name.clone(), }, variants: enumm.variants.iter().map(|v| v.name.clone()).collect(), }; if mux.insert(peri.to_string(), val).is_some() { bail!("rcc: duplicate mux for {} for rcc_{}", peri, rcc_version); } } } } // Parse xxEN/xxRST bits. let mut en_rst = HashMap::new(); for (reg, body) in &ir.fieldsets { if let Some(m) = regex!(r"^((A[PH]B\d?)|GPIO)[LH]?ENR\d?$").captures(reg) { let clock = m.get(1).unwrap().as_str(); let clock = match clock { "AHB" => "AHB1", "APB" => "APB1", clock => clock, }; for field in &body.fields { if let Some(peri) = field.name.strip_suffix("EN") { let peri = if peri == "RTCAPB" { "RTC" } else { peri }; let mut reset = None; if let Some(rstr) = ir.fieldsets.get(®.replace("ENR", "RSTR")) { if let Some(_field) = rstr.fields.iter().find(|field| field.name == format!("{peri}RST")) { reset = Some(rcc::Field { register: reg.replace("ENR", "RSTR"), field: format!("{peri}RST"), }); } } let stop_mode = if peri == "RTC" { StopMode::Standby } else if peri.starts_with("LP") { StopMode::Stop2 } else { StopMode::Stop1 }; let clock = clock.replace("AHB", "HCLK").replace("APB", "PCLK"); let val = EnRst { enable: rcc::Field { register: reg.clone(), field: field.name.clone(), }, reset, bus_clock: clock, stop_mode, }; if en_rst.insert(peri.to_string(), val).is_some() { bail!("rcc: duplicate en/rst for {} for rcc_{}", peri, rcc_version); } } } } } Ok(ParsedRcc { en_rst, mux }) } pub fn match_peri_clock( &self, rcc_version: &str, peri_name: &str, ) -> Option { const FALLBACKS: &[(&str, &[&str])] = &[ ("DCMI", &["DCMI_PSSI"]), ("PSSI", &["DCMI_PSSI"]), ("FDCAN1", &["FDCAN12"]), ("FDCAN2", &["FDCAN12"]), ("ADC", &["ADC1", "ADCDAC"]), ("ADC1", &["ADC12", "ADCDAC"]), ("ADC2", &["ADC12", "ADCDAC"]), ("ADC3", &["ADC34", "ADC345", "ADCDAC"]), ("ADC4", &["ADC34", "ADC345", "ADCDAC"]), ("ADC5", &["ADC345", "ADCDAC"]), ("DAC", &["DAC1", "ADCDAC"]), ("DAC1", &["DAC12", "ADCDAC"]), ("DAC2", &["DAC12", "ADCDAC"]), ("ETH", &["ETHMAC", "ETH1MAC"]), ("SPI1", &["SPI12", "SPI123"]), ("SPI2", &["SPI12", "SPI123"]), ("SPI3", &["SPI123"]), ("SPI4", &["SPI145"]), ("SPI5", &["SPI145"]), ("SAI1", &["SAI12"]), ("SAI2", &["SAI12", "SAI23"]), ("SAI3", &["SAI23"]), ("USART2", &["USART234578"]), ("USART3", &["USART234578"]), ("UART4", &["USART234578"]), ("UART5", &["USART234578"]), ("UART7", &["USART234578"]), ("UART8", &["USART234578"]), ("USART1", &["USART16910"]), ("USART6", &["USART16910"]), ("USART10", &["USART16910"]), ("UART9", &["USART16910"]), ("I2C1", &["I2C1235"]), ("I2C2", &["I2C1235"]), ("I2C3", &["I2C1235"]), ("I2C5", &["I2C1235"]), ("USB", &["USB", "CLK48", "ICLK"]), ("USB_OTG_FS", &["USB", "CLK48", "ICLK"]), ("USB_OTG_HS", &["USB", "CLK48", "ICLK"]), ]; let rcc = self.rccs.get(rcc_version)?; let en_rst = get_with_fallback(peri_name, &rcc.en_rst, FALLBACKS)?; let mux = get_with_fallback(peri_name, &rcc.mux, FALLBACKS); let phclk = regex!("^[PH]CLK"); let mut maybe_kernel_clock = en_rst.bus_clock.clone(); // Timers are a bit special, they may have a x2 freq if regex!(r"^(HR)?TIM\d+$").is_match(peri_name) { maybe_kernel_clock.push_str("_TIM"); } let kernel_clock = match mux { Some(mux) => { // check for mismatch between mux and bus clock. // // U5 has one ADCDACSEL for multiple ADCs which may be on // different HCLKs, so we skip the check in that case if !(rcc_version == "u5" && peri_name.starts_with("ADC")) && phclk.is_match(&en_rst.bus_clock) { for v in &mux.variants { if phclk.is_match(v) && v != &maybe_kernel_clock { panic!( "rcc_{}: peripheral {} is on bus {} but mux {}.{} refers to {}", rcc_version, peri_name, maybe_kernel_clock, mux.mux.register, mux.mux.field, v ) } } } rcc::KernelClock::Mux(mux.mux.clone()) } None => { if peri_name.starts_with("USB") { if rcc_version.starts_with("f1") || rcc_version.starts_with("f3") { maybe_kernel_clock = "USB".to_string(); } else if rcc_version.starts_with("f2") { maybe_kernel_clock = "PLL1_Q".to_string(); } else if rcc_version.starts_with("l1") { maybe_kernel_clock = "PLL1_VCO_DIV_2".to_string(); } else { panic!("rcc_{}: peripheral {} missing mux", rcc_version, peri_name) } } rcc::KernelClock::Clock(maybe_kernel_clock) } }; Some(peripheral::Rcc { bus_clock: en_rst.bus_clock.clone(), kernel_clock, enable: en_rst.enable.clone(), reset: en_rst.reset.clone(), stop_mode: en_rst.stop_mode.clone(), }) } } fn get_with_fallback<'a, T>(key: &str, map: &'a HashMap, fallbacks: &[(&str, &[&str])]) -> Option<&'a T> { if let Some(res) = map.get(key) { return Some(res); } if let Some((_, rename)) = fallbacks.iter().find(|(n, _)| *n == key) { for &n in *rename { if let Some(res) = map.get(n) { return Some(res); } } } if let Some(capture) = regex!("^([A-Z]+)\\d+$").captures(key) { if let Some(res) = map.get(capture.get(1).unwrap().as_str()) { return Some(res); } } None }