1326 lines
54 KiB
Rust
1326 lines
54 KiB
Rust
use std::collections::hash_map::Entry;
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
use stm32_data_serde::chip::core::peripheral::Pin;
|
|
|
|
use super::*;
|
|
|
|
mod xml {
|
|
use serde::Deserialize;
|
|
|
|
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
|
pub struct Mcu {
|
|
#[serde(rename = "Family")]
|
|
pub family: String,
|
|
#[serde(rename = "Line")]
|
|
pub line: String,
|
|
#[serde(rename = "Die")]
|
|
pub die: String,
|
|
#[serde(rename = "RefName")]
|
|
pub ref_name: String,
|
|
#[serde(rename = "Package")]
|
|
pub package: String,
|
|
#[serde(rename = "Core")]
|
|
pub cores: Vec<String>,
|
|
#[serde(rename = "Ram")]
|
|
pub rams: Vec<u32>,
|
|
#[serde(rename = "Flash")]
|
|
pub flashs: Vec<u32>,
|
|
#[serde(rename = "IP")]
|
|
pub ips: Vec<Ip>,
|
|
#[serde(rename = "Pin")]
|
|
pub pins: Vec<Pin>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
|
pub struct Pin {
|
|
#[serde(rename = "Name")]
|
|
pub name: String,
|
|
#[serde(rename = "Signal", default)]
|
|
pub signals: Vec<PinSignal>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
|
pub struct PinSignal {
|
|
#[serde(rename = "Name")]
|
|
pub name: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
|
pub struct Ip {
|
|
#[serde(rename = "InstanceName")]
|
|
pub instance_name: String,
|
|
#[serde(rename = "Name")]
|
|
pub name: String,
|
|
#[serde(rename = "Version")]
|
|
pub version: String,
|
|
}
|
|
}
|
|
|
|
pub struct Chip {
|
|
flash: u32,
|
|
ram: u32,
|
|
group_idx: usize,
|
|
packages: Vec<stm32_data_serde::chip::Package>,
|
|
}
|
|
|
|
pub struct ChipGroup {
|
|
chip_names: Vec<String>,
|
|
xml: xml::Mcu,
|
|
ips: HashMap<String, xml::Ip>,
|
|
pins: HashMap<stm32_data_serde::chip::core::peripheral::pin::Pin, xml::Pin>,
|
|
family: Option<String>,
|
|
line: Option<String>,
|
|
die: Option<String>,
|
|
}
|
|
|
|
fn chip_name_from_package_name(x: &str) -> String {
|
|
let regexes = [
|
|
(regex!("^(STM32L1....).x([AX])$"), "$1-$2"),
|
|
(regex!("^(STM32G0....).xN$"), "$1"),
|
|
(regex!("^(STM32F412..).xP$"), "$1"),
|
|
(regex!("^(STM32L4....).x[PS]$"), "$1"),
|
|
(regex!("^(STM32WB....).x[AE]$"), "$1"),
|
|
(regex!("^(STM32G0....).xN$"), "$1"),
|
|
(regex!("^(STM32L5....).x[PQ]$"), "$1"),
|
|
(regex!("^(STM32L0....).xS$"), "$1"),
|
|
(regex!("^(STM32H7....).xQ$"), "$1"),
|
|
(regex!("^(STM32U5....).xQ$"), "$1"),
|
|
(regex!("^(STM32H5....).xQ$"), "$1"),
|
|
(regex!("^(STM32WBA....).x$"), "$1"),
|
|
(regex!("^(STM32......).x$"), "$1"),
|
|
];
|
|
|
|
regexes
|
|
.iter()
|
|
.find_map(|(a, b)| {
|
|
a.captures(x).map(|cap| {
|
|
let mut res = String::new();
|
|
cap.expand(b, &mut res);
|
|
res
|
|
})
|
|
})
|
|
.unwrap_or_else(|| panic!("bad name: {x}"))
|
|
}
|
|
|
|
struct PeriMatcher {
|
|
regexes: Vec<(regex::Regex, (&'static str, &'static str, &'static str))>,
|
|
cached: HashMap<String, Option<(&'static str, &'static str, &'static str)>>,
|
|
}
|
|
|
|
impl PeriMatcher {
|
|
fn new() -> Self {
|
|
const PERIMAP: &[(&str, (&str, &str, &str))] = &[
|
|
(".*:USART:sci2_v1_1", ("usart", "v1", "USART")),
|
|
(".*:USART:sci2_v1_2_F1", ("usart", "v1", "USART")),
|
|
(".*:USART:sci2_v1_2", ("usart", "v2", "USART")),
|
|
(".*:USART:sci2_v2_0", ("usart", "v3", "USART")),
|
|
(".*:USART:sci2_v2_1", ("usart", "v3", "USART")),
|
|
(".*:USART:sci2_v2_2", ("usart", "v3", "USART")),
|
|
(".*:USART:sci3_v1_0", ("usart", "v3", "USART")),
|
|
(".*:USART:sci3_v1_1", ("usart", "v3", "USART")),
|
|
(".*:USART:sci3_v1_2", ("usart", "v4", "USART")),
|
|
(".*:USART:sci3_v2_0", ("usart", "v4", "USART")),
|
|
(".*:USART:sci3_v2_1", ("usart", "v4", "USART")),
|
|
(".*:UART:sci2_v1_2_F4", ("usart", "v2", "USART")),
|
|
(".*:UART:sci2_v2_1", ("usart", "v3", "USART")),
|
|
(".*:UART:sci2_v3_0", ("usart", "v4", "USART")),
|
|
(".*:UART:sci2_v3_1", ("usart", "v4", "USART")),
|
|
(".*:LPUART:sci3_v1_1", ("usart", "v3", "LPUART")),
|
|
(".*:LPUART:sci3_v1_2", ("usart", "v4", "LPUART")),
|
|
(".*:LPUART:sci3_v1_3", ("usart", "v4", "LPUART")),
|
|
(".*:LPUART:sci3_v1_4", ("usart", "v4", "LPUART")),
|
|
("STM32[HU]5.*:RNG:.*", ("rng", "v3", "RNG")),
|
|
("STM32L5.*:RNG:.*", ("rng", "v2", "RNG")),
|
|
("STM32L4[PQ]5.*:RNG:.*", ("rng", "v2", "RNG")),
|
|
("STM32WL.*:RNG:.*", ("rng", "v2", "RNG")),
|
|
("STM32F2.*:RNG:.*", ("rng", "v1", "RNG")),
|
|
("STM32F4.*:RNG:.*", ("rng", "v1", "RNG")),
|
|
("STM32F7.*:RNG:.*", ("rng", "v1", "RNG")),
|
|
("STM32L0.*:RNG:.*", ("rng", "v1", "RNG")),
|
|
("STM32L4.*:RNG:.*", ("rng", "v1", "RNG")),
|
|
("STM32H7.*:RNG:.*", ("rng", "v1", "RNG")),
|
|
("STM32G0.*:RNG:.*", ("rng", "v1", "RNG")),
|
|
("STM32G4.*:RNG:.*", ("rng", "v1", "RNG")),
|
|
(".*:SPI:spi2_v1_4", ("spi", "f1", "SPI")),
|
|
(".*:SPI:spi2s1_v2_1", ("spi", "v1", "SPI")),
|
|
(".*:SPI:spi2s1_v2_2", ("spi", "v1", "SPI")),
|
|
(".*:SPI:spi2s1_v2_4", ("spi", "v1", "SPI")),
|
|
(".*:SPI:spi2s1_v3_0", ("spi", "v2", "SPI")),
|
|
(".*:SPI:spi2s1_v3_2", ("spi", "v2", "SPI")),
|
|
(".*:SPI:spi2s1_v3_3", ("spi", "v2", "SPI")),
|
|
(".*:SPI:spi2s1_v3_5", ("spi", "v2", "SPI")),
|
|
(".*:SUBGHZSPI:.*", ("spi", "v2", "SPI")),
|
|
(".*:SPI:spi2s1_v3_1", ("spi", "v2", "SPI")),
|
|
(".*:SPI:spi2s2_v1_1", ("spi", "v3", "SPI")),
|
|
(".*:SPI:spi2s2_v1_0", ("spi", "v3", "SPI")),
|
|
(".*:SPI:spi2s3_v2_1", ("spi", "v4", "SPI")),
|
|
(".*:SPI:spi2s3_v1_1", ("spi", "v5", "SPI")),
|
|
(".*:FMAC:matrix1_v1_0", ("fmac", "v1", "FMAC")),
|
|
(".*:I2C:i2c1_v1_5", ("i2c", "v1", "I2C")),
|
|
(".*:I2C:i2c2_v1_1", ("i2c", "v2", "I2C")),
|
|
(".*:I2C:F0-i2c2_v1_1", ("i2c", "v2", "I2C")),
|
|
(".*:I2C:i2c2_v1_1F7", ("i2c", "v2", "I2C")),
|
|
(".*:I2C:i2c2_v1_1U5", ("i2c", "v2", "I2C")),
|
|
(".*:DAC:dacif_v1_1", ("dac", "v1", "DAC")),
|
|
(".*:DAC:dacif_v1_1F1", ("dac", "v1", "DAC")),
|
|
(".*:DAC:F0dacif_v1_1", ("dac", "v1", "DAC")),
|
|
(".*:DAC:dacif_v2_0", ("dac", "v2", "DAC")),
|
|
(".*:DAC:dacif_v3_0", ("dac", "v2", "DAC")),
|
|
(".*:DAC:F3_dacif_v1_1", ("dac", "v1", "DAC")),
|
|
(".*:ADC:aditf_v2_5F1", ("adc", "f1", "ADC")),
|
|
(".*:ADC:aditf4_v1_1", ("adc", "v1", "ADC")),
|
|
(".*:ADC:aditf2_v1_1", ("adc", "v2", "ADC")),
|
|
(".*:ADC:aditf5_v2_0", ("adc", "v3", "ADC")),
|
|
(".*:ADC:aditf5_v2_2", ("adc", "v3", "ADC")),
|
|
(".*:ADC:aditf5_v3_0", ("adc", "v4", "ADC")),
|
|
("STM32G0.*:ADC:.*", ("adc", "g0", "ADC")),
|
|
("STM32G0.*:ADC_COMMON:.*", ("adccommon", "v3", "ADC_COMMON")),
|
|
(".*:ADC_COMMON:aditf2_v1_1", ("adccommon", "v2", "ADC_COMMON")),
|
|
(".*:ADC_COMMON:aditf5_v2_0", ("adccommon", "v3", "ADC_COMMON")),
|
|
(".*:ADC_COMMON:aditf5_v2_2", ("adccommon", "v3", "ADC_COMMON")),
|
|
(".*:ADC_COMMON:aditf4_v3_0_WL", ("adccommon", "v3", "ADC_COMMON")),
|
|
("STM32H7.*:ADC_COMMON:.*", ("adccommon", "v4", "ADC_COMMON")),
|
|
("STM32H7.*:ADC3_COMMON:.*", ("adccommon", "v4", "ADC_COMMON")),
|
|
(".*:DCMI:.*", ("dcmi", "v1", "DCMI")),
|
|
("STM32C0.*:SYSCFG:.*", ("syscfg", "c0", "SYSCFG")),
|
|
("STM32F0.*:SYSCFG:.*", ("syscfg", "f0", "SYSCFG")),
|
|
("STM32F2.*:SYSCFG:.*", ("syscfg", "f2", "SYSCFG")),
|
|
("STM32F3.*:SYSCFG:.*", ("syscfg", "f3", "SYSCFG")),
|
|
("STM32F4.*:SYSCFG:.*", ("syscfg", "f4", "SYSCFG")),
|
|
("STM32F7.*:SYSCFG:.*", ("syscfg", "f7", "SYSCFG")),
|
|
("STM32L0.*:SYSCFG:.*", ("syscfg", "l0", "SYSCFG")),
|
|
("STM32L1.*:SYSCFG:.*", ("syscfg", "l1", "SYSCFG")),
|
|
("STM32L4.*:SYSCFG:.*", ("syscfg", "l4", "SYSCFG")),
|
|
("STM32L5.*:SYSCFG:.*", ("syscfg", "l5", "SYSCFG")),
|
|
("STM32G0.*:SYSCFG:.*", ("syscfg", "g0", "SYSCFG")),
|
|
("STM32G4.*:SYSCFG:.*", ("syscfg", "g4", "SYSCFG")),
|
|
("STM32H7.*:SYSCFG:.*", ("syscfg", "h7", "SYSCFG")),
|
|
("STM32U5.*:SYSCFG:.*", ("syscfg", "u5", "SYSCFG")),
|
|
("STM32WB.*:SYSCFG:.*", ("syscfg", "wb", "SYSCFG")),
|
|
("STM32WL5.*:SYSCFG:.*", ("syscfg", "wl5", "SYSCFG")),
|
|
("STM32WLE.*:SYSCFG:.*", ("syscfg", "wle", "SYSCFG")),
|
|
("STM32H50.*:SBS:.*", ("sbs", "h50", "SBS")),
|
|
("STM32H5.*:SBS:.*", ("sbs", "h5", "SBS")),
|
|
(".*:IWDG:iwdg1_v1_1", ("iwdg", "v1", "IWDG")),
|
|
(".*:IWDG:iwdg1_v2_0", ("iwdg", "v2", "IWDG")),
|
|
(".*:WWDG:wwdg1_v1_0", ("wwdg", "v1", "WWDG")),
|
|
(".*:JPEG:jpeg1_v1_0", ("jpeg", "v1", "JPEG")),
|
|
(".*:LPTIM:F7_lptimer1_v1_1", ("lptim", "v1", "LPTIM")),
|
|
(".*:HRTIM:hrtim_v1_0", ("hrtim", "v1", "HRTIM")),
|
|
(".*:HRTIM:hrtim_H7", ("hrtim", "v1", "HRTIM")),
|
|
(".*:HRTIM:hrtim_G4", ("hrtim", "v2", "HRTIM")),
|
|
(".*:LTDC:lcdtft1_v1_1", ("ltdc", "v1", "LTDC")),
|
|
(".*:MDIOS:mdios1_v1_0", ("mdios", "v1", "MDIOS")),
|
|
(".*:QUADSPI:quadspi1_v1_0", ("quadspi", "v1", "QUADSPI")),
|
|
("STM32F1.*:BKP.*", ("bkp", "v1", "BKP")),
|
|
(".*:RTC:rtc1_v1_1", ("rtc", "v1", "RTC")),
|
|
("STM32F0.*:RTC:rtc2_.*", ("rtc", "v2f0", "RTC")),
|
|
("STM32F2.*:RTC:rtc2_.*", ("rtc", "v2f2", "RTC")),
|
|
("STM32F3.*:RTC:rtc2_.*", ("rtc", "v2f3", "RTC")),
|
|
("STM32F4.*:RTC:rtc2_.*", ("rtc", "v2f4", "RTC")),
|
|
("STM32F7.*:RTC:rtc2_.*", ("rtc", "v2f7", "RTC")),
|
|
("STM32H7.*:RTC:rtc2_.*", ("rtc", "v2h7", "RTC")),
|
|
("STM32L0.*:RTC:rtc2_.*", ("rtc", "v2l0", "RTC")),
|
|
("STM32L1.*:RTC:rtc2_.*", ("rtc", "v2l1", "RTC")),
|
|
("STM32L4.*:RTC:rtc2_.*", ("rtc", "v2l4", "RTC")),
|
|
("STM32WB.*:RTC:rtc2_.*", ("rtc", "v2wb", "RTC")),
|
|
("STM32U5.*:RTC:rtc2_.*", ("rtc", "v3u5", "RTC")), // Cube says v2, but it's v3 with security stuff
|
|
(".*:RTC:rtc3_v1_0", ("rtc", "v3", "RTC")),
|
|
(".*:RTC:rtc3_v1_1", ("rtc", "v3", "RTC")),
|
|
(".*:RTC:rtc3_v2_0", ("rtc", "v3", "RTC")),
|
|
(".*:RTC:rtc3_v3_0", ("rtc", "v3", "RTC")),
|
|
(".*:SAI:sai1_v1_0", ("sai", "v1", "SAI")),
|
|
(".*:SAI:sai1_v1_1", ("sai", "v2", "SAI")),
|
|
(".*:SAI:sai1_v2_0", ("sai", "v1", "SAI")),
|
|
(".*:SAI:sai1_H7", ("sai", "v3", "SAI")),
|
|
(".*:SAI:sai1_v2_1", ("sai", "v4", "SAI")),
|
|
(".*:SDIO:sdmmc_v1_2", ("sdmmc", "v1", "SDMMC")),
|
|
(".*:SDMMC:sdmmc_v1_3", ("sdmmc", "v1", "SDMMC")),
|
|
(".*:SPDIFRX:spdifrx1_v1_0", ("spdifrx", "v1", "SPDIFRX")),
|
|
// # USB
|
|
("STM32(F1|L1).*:USB:.*", ("usb", "v1", "USB")),
|
|
("STM32(F1|L1).*:USBRAM:.*", ("usbram", "16x1_512", "USBRAM")),
|
|
("STM32F30[23].[BC].*:USB:.*", ("usb", "v1", "USB")),
|
|
("STM32F30[23].[BC].*:USBRAM:.*", ("usbram", "16x1_512", "USBRAM")),
|
|
("STM32F30[23].[68DE].*:USB:.*", ("usb", "v2", "USB")),
|
|
("STM32F30[23].[68DE].*:USBRAM:.*", ("usbram", "16x2_1024", "USBRAM")),
|
|
("STM32F373.*:USB:.*", ("usb", "v1", "USB")),
|
|
("STM32F373.*:USBRAM:.*", ("usbram", "16x2_512", "USBRAM")),
|
|
("STM32(F0|L[045]|G4|WB).*:USB:.*", ("usb", "v3", "USB")),
|
|
("STM32(F0|L[045]|G4|WB).*:USBRAM:.*", ("usbram", "16x2_1024", "USBRAM")),
|
|
("STM32(G0|H5|U5).*:USB:.*", ("usb", "v4", "USB")),
|
|
("STM32(G0|H5|U5).*:USBRAM:.*", ("usbram", "32_2048", "USBRAM")),
|
|
// # USB OTG
|
|
(".*:USB_OTG_FS:otgfs1_.*", ("otg", "v1", "OTG")),
|
|
(".*:USB_OTG_HS:otghs1_.*", ("otg", "v1", "OTG")),
|
|
("STM32C0.*:RCC:.*", ("rcc", "c0", "RCC")),
|
|
("STM32F0.*:RCC:.*", ("rcc", "f0", "RCC")),
|
|
("STM32F100.*:RCC:.*", ("rcc", "f100", "RCC")),
|
|
("STM32F10[123].*:RCC:.*", ("rcc", "f1", "RCC")),
|
|
("STM32F10[57].*:RCC:.*", ("rcc", "f1cl", "RCC")),
|
|
("STM32F2.*:RCC:.*", ("rcc", "f2", "RCC")),
|
|
("STM32F3.*:RCC:.*", ("rcc", "f3", "RCC")),
|
|
("STM32F410.*:RCC:.*", ("rcc", "f410", "RCC")),
|
|
("STM32F4.*:RCC:.*", ("rcc", "f4", "RCC")),
|
|
("STM32F7.*:RCC:.*", ("rcc", "f7", "RCC")),
|
|
("STM32G0.*:RCC:.*", ("rcc", "g0", "RCC")),
|
|
("STM32G4.*:RCC:.*", ("rcc", "g4", "RCC")),
|
|
("STM32H7[AB].*:RCC:.*", ("rcc", "h7ab", "RCC")),
|
|
("STM32H7.*:RCC:.*", ("rcc", "h7", "RCC")),
|
|
("STM32L0.*:RCC:.*", ("rcc", "l0", "RCC")),
|
|
("STM32L1.*:RCC:.*", ("rcc", "l1", "RCC")),
|
|
("STM32L4.*:RCC:.*", ("rcc", "l4", "RCC")),
|
|
("STM32L5.*:RCC:.*", ("rcc", "l5", "RCC")),
|
|
("STM32U5.*:RCC:.*", ("rcc", "u5", "RCC")),
|
|
("STM32H50.*:RCC:.*", ("rcc", "h50", "RCC")),
|
|
("STM32H5.*:RCC:.*", ("rcc", "h5", "RCC")),
|
|
("STM32WB.*:RCC:.*", ("rcc", "wb", "RCC")),
|
|
("STM32WL5.*:RCC:.*", ("rcc", "wl5", "RCC")),
|
|
("STM32WLE.*:RCC:.*", ("rcc", "wle", "RCC")),
|
|
("STM32F1.*:SPI[1234]:.*", ("spi", "f1", "SPI")),
|
|
("STM32F3.*:SPI[1234]:.*", ("spi", "v2", "SPI")),
|
|
("STM32F1.*:AFIO:.*", ("afio", "f1", "AFIO")),
|
|
("STM32L5.*:EXTI:.*", ("exti", "l5", "EXTI")),
|
|
("STM32C0.*:EXTI:.*", ("exti", "c0", "EXTI")),
|
|
("STM32G0.*:EXTI:.*", ("exti", "g0", "EXTI")),
|
|
("STM32H7.*:EXTI:.*", ("exti", "h7", "EXTI")),
|
|
("STM32U5.*:EXTI:.*", ("exti", "u5", "EXTI")),
|
|
("STM32WB.*:EXTI:.*", ("exti", "w", "EXTI")),
|
|
("STM32WL5.*:EXTI:.*", ("exti", "w", "EXTI")),
|
|
("STM32WLE.*:EXTI:.*", ("exti", "wle", "EXTI")),
|
|
("STM32H50.*:EXTI:.*", ("exti", "h50", "EXTI")),
|
|
("STM32H5.*:EXTI:.*", ("exti", "h5", "EXTI")),
|
|
(".*:EXTI:.*", ("exti", "v1", "EXTI")),
|
|
("STM32L0.*:CRS:.*", ("crs", "v1", "CRS")),
|
|
("STM32G0B1.*:CRS:.*", ("crs", "v1", "CRS")),
|
|
("STM32G0C1.*:CRS:.*", ("crs", "v1", "CRS")),
|
|
("STM32G4.*:CRS:.*", ("crs", "v1", "CRS")),
|
|
(".*SDMMC:sdmmc2_v1_0", ("sdmmc", "v2", "SDMMC")),
|
|
("STM32C0.*:PWR:.*", ("pwr", "c0", "PWR")),
|
|
("STM32G0.*:PWR:.*", ("pwr", "g0", "PWR")),
|
|
("STM32G4.*:PWR:.*", ("pwr", "g4", "PWR")),
|
|
("STM32H7(42|43|53|50).*:PWR:.*", ("pwr", "h7", "PWR")),
|
|
("STM32H7.*:PWR:.*", ("pwr", "h7smps", "PWR")),
|
|
("STM32F2.*:PWR:.*", ("pwr", "f2", "PWR")),
|
|
("STM32F3.*:PWR:.*", ("pwr", "f3", "PWR")),
|
|
("STM32F4.*:PWR:.*", ("pwr", "f4", "PWR")),
|
|
("STM32F7.*:PWR:.*", ("pwr", "f7", "PWR")),
|
|
("STM32L1.*:PWR:.*", ("pwr", "l1", "PWR")),
|
|
("STM32L4.*:PWR:.*", ("pwr", "l4", "PWR")),
|
|
("STM32L5.*:PWR:.*", ("pwr", "l5", "PWR")),
|
|
("STM32U5.*:PWR:.*", ("pwr", "u5", "PWR")),
|
|
("STM32WL.*:PWR:.*", ("pwr", "wl5", "PWR")),
|
|
("STM32WB.*:PWR:.*", ("pwr", "wb55", "PWR")),
|
|
("STM32H50.*:PWR:.*", ("pwr", "h50", "PWR")),
|
|
("STM32H5.*:PWR:.*", ("pwr", "h5", "PWR")),
|
|
("STM32H7.*:FLASH:.*", ("flash", "h7", "FLASH")),
|
|
("STM32F0.*:FLASH:.*", ("flash", "f0", "FLASH")),
|
|
("STM32F1.*:FLASH:.*", ("flash", "f1", "FLASH")),
|
|
("STM32F2.*:FLASH:.*", ("flash", "f2", "FLASH")),
|
|
("STM32F3.*:FLASH:.*", ("flash", "f3", "FLASH")),
|
|
("STM32F4.*:FLASH:.*", ("flash", "f4", "FLASH")),
|
|
("STM32F7.*:FLASH:.*", ("flash", "f7", "FLASH")),
|
|
("STM32L0[0-9]2.*:FLASH:.*", ("flash", "l0", "FLASH")),
|
|
("STM32L1.*:FLASH:.*", ("flash", "l1", "FLASH")),
|
|
("STM32L4.*:FLASH:.*", ("flash", "l4", "FLASH")),
|
|
("STM32L5.*:FLASH:.*", ("flash", "l5", "FLASH")),
|
|
("STM32U5.*:FLASH:.*", ("flash", "u5", "FLASH")),
|
|
("STM32WB.*:FLASH:.*", ("flash", "wb", "FLASH")),
|
|
("STM32WL.*:FLASH:.*", ("flash", "wl", "FLASH")),
|
|
("STM32C0.*:FLASH:.*", ("flash", "c0", "FLASH")),
|
|
("STM32G0.*:FLASH:.*", ("flash", "g0", "FLASH")),
|
|
("STM32G4.*:FLASH:.*", ("flash", "g4", "FLASH")),
|
|
("STM32H50.*:FLASH:.*", ("flash", "h50", "FLASH")),
|
|
("STM32H5.*:FLASH:.*", ("flash", "h5", "FLASH")),
|
|
("STM32F107.*:ETH:.*", ("eth", "v1a", "ETH")),
|
|
("STM32F[24].*:ETH:.*", ("eth", "v1b", "ETH")),
|
|
("STM32F7.*:ETH:.*", ("eth", "v1c", "ETH")),
|
|
(".*ETH:ethermac110_v3_0", ("eth", "v2", "ETH")),
|
|
("STM32F4[23][79].*:FMC:.*", ("fmc", "v1x3", "FMC")),
|
|
("STM32F446.*:FMC:.*", ("fmc", "v2x1", "FMC")),
|
|
("STM32F469.*:FMC:.*", ("fmc", "v2x1", "FMC")),
|
|
("STM32F7.*:FMC:.*", ("fmc", "v2x1", "FMC")),
|
|
("STM32H7.*:FMC:.*", ("fmc", "v3x1", "FMC")),
|
|
("STM32F100.*:FSMC:.*", ("fsmc", "v1x0", "FSMC")),
|
|
("STM32F10[12357].*:FSMC:.*", ("fsmc", "v1x3", "FSMC")),
|
|
("STM32F2.*:FSMC:.*", ("fsmc", "v1x3", "FSMC")),
|
|
("STM32F3.*:FSMC:.*", ("fsmc", "v2x3", "FSMC")),
|
|
("STM32F412.*:FSMC:.*", ("fsmc", "v1x0", "FSMC")),
|
|
("STM32F4[12]3.*:FSMC:.*", ("fsmc", "v1x0", "FSMC")),
|
|
("STM32F4[01]5.*:FSMC:.*", ("fsmc", "v1x3", "FSMC")),
|
|
("STM32F4[01]7.*:FSMC:.*", ("fsmc", "v1x3", "FSMC")),
|
|
("STM32L1.*:FSMC:.*", ("fsmc", "v1x0", "FSMC")),
|
|
("STM32L4.*:FSMC:.*", ("fsmc", "v3x1", "FSMC")),
|
|
("STM32G4.*:FSMC:.*", ("fsmc", "v4x1", "FSMC")),
|
|
("STM32L5.*:FSMC:.*", ("fsmc", "v4x1", "FSMC")),
|
|
("STM32U5.*:FSMC:.*", ("fsmc", "v5x1", "FSMC")),
|
|
(r".*LPTIM\d.*:G0xx_lptimer1_v1_4", ("lptim", "g0", "LPTIM")),
|
|
("STM32F1.*:TIM(1|8):.*", ("timer", "v1", "TIM_ADV")),
|
|
("STM32F1.*:TIM(2|5):.*", ("timer", "v1", "TIM_GP16")),
|
|
("STM32F1.*:TIM(6|7):.*", ("timer", "v1", "TIM_BASIC")),
|
|
("STM32L0.*:TIM2:.*", ("timer", "v1", "TIM_GP16")),
|
|
("STM32U5.*:TIM(2|3|4|5):.*", ("timer", "v1", "TIM_GP32")),
|
|
("STM32.*:TIM(1|8|20):.*", ("timer", "v1", "TIM_ADV")),
|
|
("STM32.*:TIM(2|5|23|24):.*", ("timer", "v1", "TIM_GP32")),
|
|
("STM32.*:TIM(6|7|18):.*", ("timer", "v1", "TIM_BASIC")),
|
|
(r".*TIM\d.*:gptimer.*", ("timer", "v1", "TIM_GP16")),
|
|
("STM32F0.*:DBGMCU:.*", ("dbgmcu", "f0", "DBGMCU")),
|
|
("STM32F1.*:DBGMCU:.*", ("dbgmcu", "f1", "DBGMCU")),
|
|
("STM32F2.*:DBGMCU:.*", ("dbgmcu", "f2", "DBGMCU")),
|
|
("STM32F3.*:DBGMCU:.*", ("dbgmcu", "f3", "DBGMCU")),
|
|
("STM32F4.*:DBGMCU:.*", ("dbgmcu", "f4", "DBGMCU")),
|
|
("STM32F7.*:DBGMCU:.*", ("dbgmcu", "f7", "DBGMCU")),
|
|
("STM32C0.*:DBGMCU:.*", ("dbgmcu", "c0", "DBGMCU")),
|
|
("STM32G0.*:DBGMCU:.*", ("dbgmcu", "g0", "DBGMCU")),
|
|
("STM32G4.*:DBGMCU:.*", ("dbgmcu", "g4", "DBGMCU")),
|
|
("STM32H7.*:DBGMCU:.*", ("dbgmcu", "h7", "DBGMCU")),
|
|
("STM32L0.*:DBGMCU:.*", ("dbgmcu", "l0", "DBGMCU")),
|
|
("STM32L1.*:DBGMCU:.*", ("dbgmcu", "l1", "DBGMCU")),
|
|
("STM32L4.*:DBGMCU:.*", ("dbgmcu", "l4", "DBGMCU")),
|
|
("STM32U5.*:DBGMCU:.*", ("dbgmcu", "u5", "DBGMCU")),
|
|
("STM32WB.*:DBGMCU:.*", ("dbgmcu", "wb", "DBGMCU")),
|
|
("STM32WL.*:DBGMCU:.*", ("dbgmcu", "wl", "DBGMCU")),
|
|
("STM32F1.*:GPIO.*", ("gpio", "v1", "GPIO")),
|
|
(".*:GPIO.*", ("gpio", "v2", "GPIO")),
|
|
(".*:IPCC:v1_0", ("ipcc", "v1", "IPCC")),
|
|
(".*:DMAMUX.*", ("dmamux", "v1", "DMAMUX")),
|
|
(r".*:GPDMA\d?:.*", ("gpdma", "v1", "GPDMA")),
|
|
(r".*:BDMA\d?:.*", ("bdma", "v1", "DMA")),
|
|
("STM32H7.*:DMA2D:DMA2D:dma2d1_v1_0", ("dma2d", "v2", "DMA2D")),
|
|
(".*:DMA2D:dma2d1_v1_0", ("dma2d", "v1", "DMA2D")),
|
|
("STM32L4[PQRS].*:DMA.*", ("bdma", "v1", "DMA")), // L4+
|
|
("STM32L[04].*:DMA.*", ("bdma", "v2", "DMA")), // L0, L4 non-plus (since plus is handled above)
|
|
("STM32F030.C.*:DMA.*", ("bdma", "v2", "DMA")), // Weird F0
|
|
("STM32F09.*:DMA.*", ("bdma", "v2", "DMA")), // Weird F0
|
|
("STM32F[247].*:DMA.*", ("dma", "v2", "DMA")),
|
|
("STM32H7.*:DMA.*", ("dma", "v1", "DMA")),
|
|
(".*:DMA.*", ("bdma", "v1", "DMA")),
|
|
(".*:CAN:bxcan1_v1_1.*", ("can", "bxcan", "CAN")),
|
|
(".*:FDCAN:fdcan1_v1_0.*", ("can", "fdcan", "FDCAN")),
|
|
// # stm32F4 CRC peripheral
|
|
// # ("STM32F4*:CRC:CRC:crc_f4")
|
|
// # v1: F1, F2, F4, L1
|
|
// # v2, adds INIT reg: F0
|
|
// # v3, adds POL reg: F3, F7, G0, G4, H7, L0, L4, L5, WB, WL
|
|
(".*:CRC:integtest1_v1_0", ("crc", "v1", "CRC")),
|
|
("STM32L[04].*:CRC:integtest1_v2_0", ("crc", "v3", "CRC")),
|
|
(".*:CRC:integtest1_v2_0", ("crc", "v2", "CRC")),
|
|
(".*:CRC:integtest1_v2_2", ("crc", "v3", "CRC")),
|
|
(".*:LCD:lcdc1_v1.0.*", ("lcd", "v1", "LCD")),
|
|
(".*:LCD:lcdc1_v1.2.*", ("lcd", "v2", "LCD")),
|
|
(".*:LCD:lcdc1_v1.3.*", ("lcd", "v2", "LCD")),
|
|
(".*:UID:.*", ("uid", "v1", "UID")),
|
|
];
|
|
|
|
Self {
|
|
regexes: PERIMAP
|
|
.iter()
|
|
.map(|(a, b)| (regex::Regex::new(&format!("^{a}$")).unwrap(), *b))
|
|
.collect(),
|
|
cached: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
fn match_peri(&mut self, peri: &str) -> Option<(&'static str, &'static str, &'static str)> {
|
|
*self
|
|
.cached
|
|
.entry(peri.to_string())
|
|
.or_insert_with(|| self.regexes.iter().find(|(r, _block)| r.is_match(peri)).map(|x| x.1))
|
|
}
|
|
}
|
|
|
|
fn corename(d: &str) -> String {
|
|
let m = regex!(r".*Cortex-M(\d+)(\+?)\s*(.*)").captures(d).unwrap();
|
|
let cm = m.get(1).unwrap().as_str();
|
|
let p = if m.get(2).unwrap().as_str() == "+" { "p" } else { "" };
|
|
let s = if m.get(3).unwrap().as_str() == "secure" {
|
|
"s"
|
|
} else {
|
|
""
|
|
};
|
|
format!("cm{cm}{p}{s}")
|
|
}
|
|
|
|
fn merge_periph_pins_info(
|
|
is_f1: bool,
|
|
periph_name: &str,
|
|
core_pins: &mut Vec<stm32_data_serde::chip::core::peripheral::Pin>,
|
|
af_pins: &[stm32_data_serde::chip::core::peripheral::Pin],
|
|
) {
|
|
if is_f1 {
|
|
// TODO: actually handle the F1 AFIO information when it will be extracted
|
|
return;
|
|
}
|
|
|
|
// covert to hashmap
|
|
let af_pins: HashMap<(stm32_data_serde::chip::core::peripheral::pin::Pin, &str), Option<u8>> =
|
|
af_pins.iter().map(|v| ((v.pin, v.signal.as_str()), v.af)).collect();
|
|
|
|
for pin in core_pins {
|
|
let af = af_pins.get(&(pin.pin, &pin.signal)).copied().flatten();
|
|
|
|
// try to look for a signal with another name
|
|
let af = af.or_else(|| {
|
|
if pin.signal == "CTS" {
|
|
// for some godforsaken reason UART4's and UART5's CTS are called CTS_NSS in the GPIO xml
|
|
// so try to match with these
|
|
af_pins.get(&(pin.pin, "CTS_NSS")).copied().flatten()
|
|
} else if periph_name == "I2C1" {
|
|
// it appears that for __some__ STM32 MCUs there is no AFIO specified in GPIO file
|
|
// (notably - STM32F030C6 with it's I2C1 on PF6 and PF7)
|
|
// but the peripheral can actually be mapped to different pins
|
|
// this breaks embassy's model, so we pretend that it's AF 0
|
|
// Reference Manual states that there's no GPIOF_AFR register
|
|
// but according to Cube-generated core it's OK to write to AFIO reg, it seems to be ignored
|
|
// TODO: are there any more signals that have this "feature"
|
|
Some(0)
|
|
} else {
|
|
None
|
|
}
|
|
});
|
|
|
|
if let Some(af) = af {
|
|
pin.af = Some(af);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn parse_groups() -> Result<(HashMap<String, Chip>, Vec<ChipGroup>), anyhow::Error> {
|
|
// XMLs group together chips that are identical except flash/ram size.
|
|
// For example STM32L471Z(E-G)Jx.xml is STM32L471ZEJx, STM32L471ZGJx.
|
|
// However they do NOT group together identical chips with different package.
|
|
|
|
// We want exactly the opposite: group all packages of a chip together, but
|
|
// NOT group equal-except-memory-size chips together. Yay.
|
|
|
|
// We first read all XMLs, and fold together all packages. We don't expand
|
|
// flash/ram sizes yet, we want to do it as late as possible to avoid duplicate
|
|
// work so that generation is faster.
|
|
|
|
let mut chips = HashMap::<String, Chip>::new();
|
|
let mut chip_groups = Vec::new();
|
|
|
|
let mut files: Vec<_> = glob::glob("sources/cubedb/mcu/STM32*.xml")?
|
|
.map(Result::unwrap)
|
|
.collect();
|
|
files.sort();
|
|
|
|
for f in files {
|
|
parse_group(f, &mut chips, &mut chip_groups)?;
|
|
}
|
|
|
|
for (chip_name, chip) in &chips {
|
|
chip_groups[chip.group_idx].chip_names.push(chip_name.clone());
|
|
}
|
|
Ok((chips, chip_groups))
|
|
}
|
|
|
|
static NOPELIST: &[&str] = &[
|
|
// Not supported, not planned unless someone wants to do it.
|
|
"STM32MP",
|
|
// Not supported yet, planned.
|
|
"STM32WBA",
|
|
// Does not exist in ST website. No datasheet, no RM.
|
|
"STM32GBK",
|
|
"STM32L485",
|
|
// STM32WxM modules. These are based on a chip that's supported on its own,
|
|
// not sure why we want a separate target for it.
|
|
"STM32WL5M",
|
|
"STM32WB1M",
|
|
"STM32WB3M",
|
|
"STM32WB5M",
|
|
];
|
|
|
|
fn parse_group(
|
|
f: std::path::PathBuf,
|
|
chips: &mut HashMap<String, Chip>,
|
|
chip_groups: &mut Vec<ChipGroup>,
|
|
) -> anyhow::Result<()> {
|
|
let ff = f.file_name().unwrap().to_string_lossy();
|
|
|
|
for nope in NOPELIST {
|
|
if ff.contains(nope) {
|
|
return Ok(());
|
|
}
|
|
}
|
|
|
|
let parsed: xml::Mcu = quick_xml::de::from_str(&std::fs::read_to_string(f)?)?;
|
|
|
|
let package_names = {
|
|
let name = &parsed.ref_name;
|
|
if !name.contains('(') {
|
|
vec![name.to_string()]
|
|
} else {
|
|
let (prefix, suffix) = name.split_once('(').unwrap();
|
|
let (letters, suffix) = suffix.split_once(')').unwrap();
|
|
letters.split('-').map(|x| format!("{prefix}{x}{suffix}")).collect()
|
|
}
|
|
};
|
|
|
|
let package_rams = {
|
|
if parsed.rams.len() == 1 {
|
|
vec![parsed.rams[0]; package_names.len()]
|
|
} else {
|
|
parsed.rams.clone()
|
|
}
|
|
};
|
|
let package_flashes = {
|
|
if parsed.flashs.len() == 1 {
|
|
vec![parsed.flashs[0]; package_names.len()]
|
|
} else {
|
|
parsed.flashs.clone()
|
|
}
|
|
};
|
|
|
|
let group_idx = package_names.iter().find_map(|package_name| {
|
|
let chip_name = chip_name_from_package_name(package_name);
|
|
chips.get(&chip_name).map(|chip| chip.group_idx)
|
|
});
|
|
|
|
let group_idx = group_idx.unwrap_or_else(|| {
|
|
let group_idx = chip_groups.len();
|
|
chip_groups.push(ChipGroup {
|
|
chip_names: Vec::new(),
|
|
xml: parsed.clone(),
|
|
ips: HashMap::new(),
|
|
pins: HashMap::new(),
|
|
family: None,
|
|
line: None,
|
|
die: None,
|
|
});
|
|
group_idx
|
|
});
|
|
|
|
for (package_i, package_name) in package_names.iter().enumerate() {
|
|
let chip_name = chip_name_from_package_name(package_name);
|
|
if !chips.contains_key(&chip_name) {
|
|
chips.insert(
|
|
chip_name.clone(),
|
|
Chip {
|
|
flash: package_flashes[package_i],
|
|
ram: package_rams[package_i],
|
|
group_idx,
|
|
packages: Vec::new(),
|
|
},
|
|
);
|
|
}
|
|
chips
|
|
.get_mut(&chip_name)
|
|
.unwrap()
|
|
.packages
|
|
.push(stm32_data_serde::chip::Package {
|
|
name: package_name.clone(),
|
|
package: parsed.package.clone(),
|
|
});
|
|
}
|
|
|
|
// Some packages have some peripehrals removed because the package had to
|
|
// remove GPIOs useful for that peripheral. So we merge all peripherals from all packages.
|
|
let group = &mut chip_groups[group_idx];
|
|
for ip in parsed.ips {
|
|
group.ips.insert(ip.instance_name.clone(), ip);
|
|
}
|
|
for pin in parsed.pins {
|
|
if let Some(pin_name) = gpio_af::clean_pin(&pin.name) {
|
|
group
|
|
.pins
|
|
.entry(pin_name)
|
|
.and_modify(|p| {
|
|
// merge signals.
|
|
p.signals.extend_from_slice(&pin.signals);
|
|
p.signals.dedup();
|
|
})
|
|
.or_insert(pin);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
fn process_group(
|
|
mut group: ChipGroup,
|
|
peri_matcher: &mut PeriMatcher,
|
|
headers: &header::Headers,
|
|
af: &gpio_af::Af,
|
|
chip_interrupts: &interrupts::ChipInterrupts,
|
|
peripheral_to_clock: &rcc::PeripheralToClock,
|
|
dma_channels: &dma::DmaChannels,
|
|
chips: &HashMap<String, Chip>,
|
|
memories: &memory::Memories,
|
|
docs: &docs::Docs,
|
|
) -> Result<(), anyhow::Error> {
|
|
let chip_name = group.chip_names[0].clone();
|
|
group.family = Some(group.xml.family.clone());
|
|
group.line = Some(group.xml.line.clone());
|
|
group.die = Some(group.xml.die.clone());
|
|
let rcc_kind = group.ips.values().find(|x| x.name == "RCC").unwrap().version.clone();
|
|
let rcc_block = peri_matcher
|
|
.match_peri(&format!("{chip_name}:RCC:{rcc_kind}"))
|
|
.unwrap_or_else(|| panic!("could not get rcc for {}", &chip_name));
|
|
let h = headers
|
|
.get_for_chip(&chip_name)
|
|
.unwrap_or_else(|| panic!("could not get header for {}", &chip_name));
|
|
let chip_af = &group.ips.values().find(|x| x.name == "GPIO").unwrap().version;
|
|
let chip_af = chip_af.strip_suffix("_gpio_v1_0").unwrap();
|
|
let chip_af = af.0.get(chip_af);
|
|
let cores: Vec<_> = group
|
|
.xml
|
|
.cores
|
|
.iter()
|
|
.map(|core_xml| {
|
|
process_core(
|
|
core_xml,
|
|
h,
|
|
&chip_name,
|
|
&group,
|
|
chip_interrupts,
|
|
peri_matcher,
|
|
peripheral_to_clock,
|
|
rcc_block,
|
|
chip_af,
|
|
dma_channels,
|
|
)
|
|
})
|
|
.collect();
|
|
|
|
for chip_name in &group.chip_names {
|
|
process_chip(chips, chip_name, h, memories, docs, &group, &cores)?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
fn process_core(
|
|
core_xml: &str,
|
|
h: &header::ParsedHeader,
|
|
chip_name: &str,
|
|
group: &ChipGroup,
|
|
chip_interrupts: &interrupts::ChipInterrupts,
|
|
peri_matcher: &mut PeriMatcher,
|
|
peripheral_to_clock: &rcc::PeripheralToClock,
|
|
rcc_block: (&str, &str, &str),
|
|
chip_af: Option<&HashMap<String, Vec<stm32_data_serde::chip::core::peripheral::Pin>>>,
|
|
dma_channels: &dma::DmaChannels,
|
|
) -> stm32_data_serde::chip::Core {
|
|
let real_core_name = corename(core_xml);
|
|
|
|
let core_name = if !h.interrupts.contains_key(&real_core_name) || !h.defines.contains_key(&real_core_name) {
|
|
"all"
|
|
} else {
|
|
&real_core_name
|
|
};
|
|
// C header defines for this core.
|
|
let defines = h.defines.get(core_name).unwrap();
|
|
// Interrupts!
|
|
let want_nvic_name = {
|
|
// Most chips have a single NVIC, named "NVIC"
|
|
let mut want_nvic_name = "NVIC";
|
|
|
|
// Exception 1: Multicore: NVIC1 is the first core, NVIC2 is the second. We have to pick the right one.
|
|
if ["H745", "H747", "H755", "H757", "WL54", "WL55"].contains(&&chip_name[5..9]) {
|
|
if core_name == "cm7" {
|
|
want_nvic_name = "NVIC1";
|
|
} else {
|
|
want_nvic_name = "NVIC2"
|
|
}
|
|
}
|
|
if &chip_name[5..8] == "WL5" {
|
|
if core_name == "cm4" {
|
|
want_nvic_name = "NVIC1";
|
|
} else {
|
|
want_nvic_name = "NVIC2"
|
|
}
|
|
}
|
|
// Exception 2: TrustZone: NVIC1 is Secure mode, NVIC2 is NonSecure mode. For now, we pick the NonSecure one.
|
|
if ["L5", "U5"].contains(&&chip_name[5..7]) {
|
|
want_nvic_name = "NVIC2"
|
|
}
|
|
if ["H56", "H57"].contains(&&chip_name[5..8]) {
|
|
want_nvic_name = "NVIC2"
|
|
}
|
|
|
|
want_nvic_name
|
|
};
|
|
let chip_nvic = group
|
|
.ips
|
|
.values()
|
|
.find(|x| x.name == want_nvic_name)
|
|
.ok_or_else(|| format!("couldn't find nvic. chip_name={chip_name} want_nvic_name={want_nvic_name}"))
|
|
.unwrap();
|
|
|
|
// With the current data sources, this value is always either 2 or 4, and never resolves to None
|
|
let nvic_priority_bits = defines.0.get("__NVIC_PRIO_BITS").map(|bits| *bits as u8);
|
|
|
|
let mut header_irqs = h.interrupts.get(core_name).unwrap().clone();
|
|
let chip_irqs = chip_interrupts
|
|
.0
|
|
.get(&(chip_nvic.name.clone(), chip_nvic.version.clone()))
|
|
.unwrap();
|
|
// F100xE MISC_REMAP remaps some DMA IRQs, so ST decided to give two names
|
|
// to the same IRQ number.
|
|
if chip_name.starts_with("STM32F100") {
|
|
header_irqs.remove("DMA2_Channel4_5");
|
|
}
|
|
let mut interrupts: Vec<_> = header_irqs
|
|
.iter()
|
|
.map(|(k, v)| stm32_data_serde::chip::core::Interrupt {
|
|
name: k.clone(),
|
|
number: *v,
|
|
})
|
|
.collect();
|
|
interrupts.sort_unstable_by_key(|x| x.number);
|
|
let mut peri_kinds = HashMap::new();
|
|
peri_kinds.insert("UID".to_string(), "UID".to_string());
|
|
for ip in group.ips.values() {
|
|
let pname = ip.instance_name.clone();
|
|
let pkind = format!("{}:{}", ip.name, ip.version);
|
|
let pkind = pkind.strip_suffix("_Cube").unwrap_or(&pkind);
|
|
|
|
const FAKE_PERIPHERALS: &[&str] = &[
|
|
// These are real peripherals but with special handling
|
|
"NVIC",
|
|
"GPIO",
|
|
"DMA",
|
|
// IRTIM is just TIM16+TIM17
|
|
"IRTIM",
|
|
// We add this as ghost peri
|
|
"SYS",
|
|
// These are software libraries
|
|
"FREERTOS",
|
|
"PDM2PCM",
|
|
"FATFS",
|
|
"LIBJPEG",
|
|
"MBEDTLS",
|
|
"LWIP",
|
|
"USB_HOST",
|
|
"USB_DEVICE",
|
|
"GUI_INTERFACE",
|
|
"TRACER_EMB",
|
|
"TOUCHSENSING",
|
|
];
|
|
|
|
if FAKE_PERIPHERALS.contains(&pname.as_str()) {
|
|
continue;
|
|
}
|
|
|
|
let pname = match pname.as_str() {
|
|
"HDMI_CEC" => "CEC".to_string(),
|
|
"SUBGHZ" => "SUBGHZSPI".to_string(),
|
|
// remove when https://github.com/stm32-rs/stm32-rs/pull/789 merges
|
|
"USB_DRD_FS" => "USB".to_string(),
|
|
_ => pname,
|
|
};
|
|
|
|
if pname.starts_with("ADC") {
|
|
if let Entry::Vacant(entry) = peri_kinds.entry("ADC_COMMON".to_string()) {
|
|
entry.insert(format!("ADC_COMMON:{}", ip.version.strip_suffix("_Cube").unwrap()));
|
|
}
|
|
}
|
|
if pname.starts_with("ADC3") && chip_name.starts_with("STM32H7") {
|
|
if let Entry::Vacant(entry) = peri_kinds.entry("ADC3_COMMON".to_string()) {
|
|
entry.insert(format!("ADC3_COMMON:{}", ip.version.strip_suffix("_Cube").unwrap()));
|
|
}
|
|
}
|
|
peri_kinds.insert(pname, pkind.to_string());
|
|
}
|
|
const GHOST_PERIS: &[&str] = &[
|
|
"GPIOA", "GPIOB", "GPIOC", "GPIOD", "GPIOE", "GPIOF", "GPIOG", "GPIOH", "GPIOI", "GPIOJ", "GPIOK", "GPIOL",
|
|
"GPIOM", "GPION", "GPIOO", "GPIOP", "GPIOQ", "GPIOR", "GPIOS", "GPIOT", "DMA1", "DMA2", "BDMA", "DMAMUX",
|
|
"DMAMUX1", "DMAMUX2", "SBS", "SYSCFG", "EXTI", "FLASH", "DBGMCU", "CRS", "PWR", "AFIO", "BKP", "USBRAM",
|
|
];
|
|
for pname in GHOST_PERIS {
|
|
if let Entry::Vacant(entry) = peri_kinds.entry(pname.to_string()) {
|
|
if defines.get_peri_addr(pname).is_some() {
|
|
entry.insert("unknown".to_string());
|
|
}
|
|
}
|
|
}
|
|
if peri_kinds.contains_key("BDMA1") {
|
|
peri_kinds.remove("BDMA");
|
|
}
|
|
// get possible used GPIOs for each peripheral from the chip xml
|
|
// it's not the full info we would want (stuff like AFIO info which comes from GPIO xml),
|
|
// but we actually need to use it because of F1 line
|
|
// which doesn't include non-remappable peripherals in GPIO xml
|
|
// and some weird edge cases like STM32F030C6 (see merge_periph_pins_info)
|
|
let mut periph_pins = HashMap::<_, Vec<_>>::new();
|
|
for (pin_name, pin) in &group.pins {
|
|
for signal in &pin.signals {
|
|
let mut signal = signal.name.clone();
|
|
if signal.starts_with("DEBUG_SUBGHZSPI-") {
|
|
signal = format!("SUBGHZSPI_{}", &signal[16..(signal.len() - 3)]);
|
|
}
|
|
// TODO: What are those signals (well, GPIO is clear) Which peripheral do they belong to?
|
|
if !["GPIO", "CEC", "AUDIOCLK", "VDDTCXO"].contains(&signal.as_str()) && !signal.contains("EXTI") {
|
|
// both peripherals and signals can have underscores in their names so there is no easy way to split
|
|
// check if signal name starts with one of the peripheral names
|
|
for periph in peri_kinds.keys() {
|
|
if let Some(signal) = signal.strip_prefix(&format!("{periph}_")) {
|
|
periph_pins.entry(periph.to_string()).or_default().push(
|
|
stm32_data_serde::chip::core::peripheral::Pin {
|
|
pin: *pin_name,
|
|
signal: signal.to_string(),
|
|
af: None,
|
|
},
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for pins in periph_pins.values_mut() {
|
|
pins.sort();
|
|
pins.dedup();
|
|
}
|
|
let mut peripherals = Vec::new();
|
|
for (pname, pkind) in peri_kinds {
|
|
// We cannot add this to FAKE peripherals because we need the pins
|
|
if pname.starts_with("I2S") {
|
|
continue;
|
|
}
|
|
|
|
let addr = if chip_name.starts_with("STM32F0") && pname == "ADC" {
|
|
defines.get_peri_addr("ADC1")
|
|
} else if chip_name.starts_with("STM32H7") && pname == "HRTIM" {
|
|
defines.get_peri_addr("HRTIM1")
|
|
} else {
|
|
defines.get_peri_addr(&pname)
|
|
};
|
|
|
|
let addr = match addr {
|
|
Some(addr) => addr,
|
|
None => continue,
|
|
};
|
|
|
|
let mut p = stm32_data_serde::chip::core::Peripheral {
|
|
name: pname.clone(),
|
|
address: addr,
|
|
registers: None,
|
|
rcc: None,
|
|
interrupts: None,
|
|
dma_channels: Vec::new(),
|
|
pins: Vec::new(),
|
|
};
|
|
|
|
if let Some(block) = peri_matcher.match_peri(&format!("{chip_name}:{pname}:{pkind}")) {
|
|
p.registers = Some(stm32_data_serde::chip::core::peripheral::Registers {
|
|
kind: block.0.to_string(),
|
|
version: block.1.to_string(),
|
|
block: block.2.to_string(),
|
|
});
|
|
}
|
|
|
|
if let Some(rcc_info) = peripheral_to_clock.match_peri_clock(
|
|
&(
|
|
rcc_block.0.to_string(),
|
|
rcc_block.1.to_string(),
|
|
rcc_block.2.to_string(),
|
|
),
|
|
&pname,
|
|
) {
|
|
p.rcc = Some(rcc_info.clone());
|
|
}
|
|
if let Some(pins) = periph_pins.get_mut(&pname) {
|
|
// merge the core xml info with GPIO xml info to hopefully get the full picture
|
|
// if the peripheral does not exist in the GPIO xml (one of the notable one is ADC)
|
|
// it probably doesn't need any AFIO writes to work
|
|
if let Some(af_pins) = chip_af.and_then(|x| x.get(&pname)) {
|
|
merge_periph_pins_info(chip_name.contains("STM32F1"), &pname, pins, af_pins.as_slice());
|
|
}
|
|
p.pins = pins.clone();
|
|
}
|
|
|
|
let i2s_name = if pname.starts_with("SPI") {
|
|
"I2S".to_owned() + pname.trim_start_matches("SPI")
|
|
} else {
|
|
"".to_owned()
|
|
};
|
|
|
|
if let Some(i2s_pins) = periph_pins.get_mut(&i2s_name) {
|
|
// merge the core xml info with GPIO xml info to hopefully get the full picture
|
|
// if the peripheral does not exist in the GPIO xml (one of the notable one is ADC)
|
|
// it probably doesn't need any AFIO writes to work
|
|
if let Some(af_pins) = chip_af.and_then(|x| x.get(&i2s_name)) {
|
|
merge_periph_pins_info(chip_name.contains("STM32F1"), &i2s_name, i2s_pins, af_pins.as_slice());
|
|
}
|
|
|
|
p.pins.extend(i2s_pins.iter().map(|p| Pin {
|
|
pin: p.pin,
|
|
signal: "I2S_".to_owned() + &p.signal,
|
|
af: p.af,
|
|
}));
|
|
}
|
|
|
|
if let Some(peri_irqs) = chip_irqs.get(&pname) {
|
|
use stm32_data_serde::chip::core::peripheral::Interrupt;
|
|
|
|
//filter by available, because some are conditioned on <Die>
|
|
|
|
let mut irqs: Vec<_> =
|
|
if pname == "CAN" && (chip_name.starts_with("STM32F358") || chip_name.starts_with("STM32F398")) {
|
|
/*
|
|
This section is needed because the interrupt definition file does not apply uniformly to
|
|
all chips that it covers in this specific case. This cannot be done in interrupt.rs
|
|
because there, it's only possible to modify the interrupt definition file in a uniform manner.
|
|
*/
|
|
|
|
peri_irqs
|
|
.iter()
|
|
.map(|i| Interrupt {
|
|
interrupt: i
|
|
.interrupt
|
|
.trim_start_matches("USB_LP_")
|
|
.trim_start_matches("USB_HP_")
|
|
.to_owned(),
|
|
signal: i.signal.clone(),
|
|
})
|
|
.filter(|i| header_irqs.contains_key(&i.interrupt))
|
|
.collect()
|
|
} else {
|
|
peri_irqs
|
|
.iter()
|
|
.filter(|i| header_irqs.contains_key(&i.interrupt))
|
|
.cloned()
|
|
.collect()
|
|
};
|
|
irqs.sort_by_key(|x| (x.signal.clone(), x.interrupt.clone()));
|
|
irqs.dedup_by_key(|x| (x.signal.clone(), x.interrupt.clone()));
|
|
|
|
p.interrupts = Some(irqs);
|
|
}
|
|
peripherals.push(p);
|
|
}
|
|
if let Ok(extra_f) = std::fs::read(format!("data/extra/family/{}.yaml", group.family.as_ref().unwrap())) {
|
|
#[derive(serde::Deserialize)]
|
|
struct Extra {
|
|
peripherals: Vec<stm32_data_serde::chip::core::Peripheral>,
|
|
}
|
|
|
|
let extra: Extra = serde_yaml::from_slice(&extra_f).unwrap();
|
|
for p in extra.peripherals {
|
|
peripherals.push(p);
|
|
}
|
|
}
|
|
peripherals.sort_by_key(|x| x.name.clone());
|
|
let have_peris: HashSet<_> = peripherals.iter_mut().map(|p| p.name.clone()).collect();
|
|
// Collect DMA versions in the chip
|
|
let mut chip_dmas: Vec<_> = group
|
|
.ips
|
|
.values()
|
|
.filter_map(|ip| {
|
|
let version = &ip.version;
|
|
let sort = match ip.name.as_str() {
|
|
"DMA" => 1,
|
|
"BDMA" => 2,
|
|
"BDMA1" => 3,
|
|
"BDMA2" => 4,
|
|
"GPDMA" => 5,
|
|
_ => 0,
|
|
};
|
|
if sort > 0 && dma_channels.0.contains_key(version) {
|
|
Some((sort, version.clone()))
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect();
|
|
chip_dmas.sort();
|
|
chip_dmas.dedup();
|
|
let chip_dmas: Vec<_> = chip_dmas.into_iter().map(|(_sort, version)| version).collect();
|
|
// Process DMA channels
|
|
let chs = chip_dmas
|
|
.iter()
|
|
.flat_map(|dma| dma_channels.0.get(dma).unwrap().channels.clone());
|
|
// The dma_channels[xx] is generic for multiple chips. The current chip may have less DMAs,
|
|
// so we have to filter it.
|
|
let chs: Vec<_> = chs.filter(|ch| have_peris.contains(&ch.dma)).collect();
|
|
let core_dma_channels = chs.clone();
|
|
let have_chs: HashSet<_> = chs.into_iter().collect();
|
|
// Process peripheral - DMA channel associations
|
|
for p in &mut peripherals {
|
|
let mut chs = Vec::new();
|
|
for dma in &chip_dmas {
|
|
let mut peri_chs = dma_channels.0.get(dma).unwrap().peripherals.get(&p.name);
|
|
|
|
// DAC1 is sometimes interchanged with DAC
|
|
if peri_chs.is_none() && p.name == "DAC1" {
|
|
peri_chs = dma_channels.0.get(dma).unwrap().peripherals.get("DAC");
|
|
}
|
|
|
|
if let Some(peri_chs) = peri_chs {
|
|
chs.extend(
|
|
peri_chs
|
|
.iter()
|
|
.filter(|ch| {
|
|
if let Some(ch_channel) = &ch.channel {
|
|
have_chs.iter().any(|x| &x.name == ch_channel)
|
|
} else {
|
|
true
|
|
}
|
|
})
|
|
.cloned(),
|
|
);
|
|
}
|
|
}
|
|
if !chs.is_empty() {
|
|
chs.sort_by_key(|ch| (ch.channel.clone(), ch.dmamux.clone(), ch.request));
|
|
p.dma_channels = chs;
|
|
}
|
|
}
|
|
stm32_data_serde::chip::Core {
|
|
name: real_core_name.clone(),
|
|
peripherals,
|
|
nvic_priority_bits,
|
|
interrupts,
|
|
dma_channels: core_dma_channels,
|
|
}
|
|
}
|
|
|
|
fn process_chip(
|
|
chips: &HashMap<String, Chip>,
|
|
chip_name: &str,
|
|
h: &header::ParsedHeader,
|
|
memories: &memory::Memories,
|
|
docs: &docs::Docs,
|
|
group: &ChipGroup,
|
|
cores: &[stm32_data_serde::chip::Core],
|
|
) -> Result<(), anyhow::Error> {
|
|
let chip = chips.get(chip_name).unwrap();
|
|
let flash_size = chip.flash * 1024;
|
|
let ram_total = chip.ram * 1024;
|
|
let memory = memories.get(group.die.as_ref().unwrap());
|
|
let mut flash_remaining = flash_size;
|
|
let mut memory_regions = Vec::new();
|
|
let mut found = HashSet::<&str>::new();
|
|
for each in [
|
|
// We test FLASH_BANKx _before_ FLASH as we prefer their definition over the legacy one
|
|
"FLASH_BANK1",
|
|
"FLASH_BANK2",
|
|
"FLASH",
|
|
"FLASH_OTP",
|
|
"D1_AXIFLASH",
|
|
"D1_AXIICP",
|
|
] {
|
|
if let Some(address) = h.defines.get("all").unwrap().0.get(&format!("{each}_BASE")) {
|
|
let (key, banks) = match each {
|
|
"FLASH" => (
|
|
"BANK_1",
|
|
Some([memory::FlashBank::Bank1, memory::FlashBank::Bank2].as_ref()),
|
|
),
|
|
"FLASH_BANK1" => ("BANK_1", Some([memory::FlashBank::Bank1].as_ref())),
|
|
"FLASH_BANK2" => ("BANK_2", Some([memory::FlashBank::Bank2].as_ref())),
|
|
"FLASH_OTP" => ("OTP", Some([memory::FlashBank::Otp].as_ref())),
|
|
each => (each, None),
|
|
};
|
|
|
|
if found.contains(key) {
|
|
continue;
|
|
}
|
|
found.insert(key);
|
|
|
|
if let Some(banks) = banks {
|
|
for bank in banks {
|
|
let bank_name = match bank {
|
|
memory::FlashBank::Bank1 => "BANK_1",
|
|
memory::FlashBank::Bank2 => "BANK_2",
|
|
memory::FlashBank::Otp => "OTP",
|
|
};
|
|
let regions: Vec<_> = memory
|
|
.flash_regions
|
|
.iter()
|
|
.filter(|region| region.bank == *bank)
|
|
.enumerate()
|
|
.map_while(|(index, region)| {
|
|
let size = if *bank == memory::FlashBank::Bank1 || *bank == memory::FlashBank::Bank2 {
|
|
// Truncate region to the total amount of remaining chip flash
|
|
let size = std::cmp::min(region.bytes, flash_remaining);
|
|
flash_remaining -= size;
|
|
if size == 0 {
|
|
// No more regions are present on this chip
|
|
return None;
|
|
}
|
|
size
|
|
} else {
|
|
region.bytes
|
|
};
|
|
|
|
Some((index, region.address, size, region.settings.clone()))
|
|
})
|
|
.collect();
|
|
let has_multiple_regions = regions.len() > 1;
|
|
for (index, address, size, settings) in regions {
|
|
let name = if has_multiple_regions {
|
|
format!("{}_REGION_{}", bank_name, index + 1)
|
|
} else {
|
|
bank_name.to_string()
|
|
};
|
|
|
|
memory_regions.push(stm32_data_serde::chip::Memory {
|
|
name,
|
|
kind: stm32_data_serde::chip::memory::Kind::Flash,
|
|
address,
|
|
size,
|
|
settings: Some(settings.clone()),
|
|
});
|
|
}
|
|
}
|
|
} else {
|
|
memory_regions.push(stm32_data_serde::chip::Memory {
|
|
name: key.to_string(),
|
|
kind: stm32_data_serde::chip::memory::Kind::Flash,
|
|
address: u32::try_from(*address).unwrap(),
|
|
size: 0,
|
|
settings: None,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
let mut found = HashSet::new();
|
|
for each in [
|
|
"SRAM",
|
|
"SRAM1",
|
|
"SRAM2",
|
|
"D1_AXISRAM",
|
|
"D1_ITCMRAM",
|
|
"D1_DTCMRAM",
|
|
"D1_AHBSRAM",
|
|
"D2_AXISRAM",
|
|
"D3_BKPSRAM",
|
|
"D3_SRAM",
|
|
] {
|
|
if let Some(address) = h.defines.get("all").unwrap().0.get(&format!("{each}_BASE")) {
|
|
let key = match each {
|
|
"D1_AXISRAM" => "SRAM",
|
|
"SRAM1" => "SRAM",
|
|
each => each,
|
|
};
|
|
|
|
if found.contains(key) {
|
|
continue;
|
|
}
|
|
found.insert(key);
|
|
|
|
let size = if key == "SRAM" {
|
|
// if memory.ram.bytes != ram_total {
|
|
// println!(
|
|
// "SRAM mismatch for chip {} with die {}: Expected {} was {}",
|
|
// chip_name,
|
|
// group.die.as_ref().unwrap(),
|
|
// ram_total,
|
|
// memory.ram.bytes,
|
|
// );
|
|
// }
|
|
std::cmp::min(memory.ram.bytes, ram_total)
|
|
} else {
|
|
0
|
|
};
|
|
|
|
memory_regions.push(stm32_data_serde::chip::Memory {
|
|
name: key.to_string(),
|
|
kind: stm32_data_serde::chip::memory::Kind::Ram,
|
|
address: u32::try_from(*address).unwrap(),
|
|
size,
|
|
settings: None,
|
|
})
|
|
}
|
|
}
|
|
let docs = docs.documents_for(chip_name);
|
|
let chip = stm32_data_serde::Chip {
|
|
name: chip_name.to_string(),
|
|
family: group.family.clone().unwrap(),
|
|
line: group.line.clone().unwrap(),
|
|
die: group.die.clone().unwrap(),
|
|
device_id: memory.device_id,
|
|
packages: chip.packages.clone(),
|
|
memory: memory_regions,
|
|
docs,
|
|
cores: cores.to_vec(),
|
|
};
|
|
let dump = serde_json::to_string_pretty(&chip)?;
|
|
|
|
// TODO: delete this.
|
|
// This makes the formating match the output of the original python script, to prevent unnecessary churn
|
|
let dump = {
|
|
let mut cleaned = String::new();
|
|
for line in dump.lines() {
|
|
let spaces = line.bytes().take_while(|b| *b == b' ').count();
|
|
for _ in 0..spaces {
|
|
// add an extra space for every existing space
|
|
// this converts two-space indents to four-space indents
|
|
cleaned.push(' ');
|
|
}
|
|
// escape non-ascii symbols
|
|
let line = line.replace('\u{00ae}', r"\u00ae");
|
|
let line = line.replace('\u{2122}', r"\u2122");
|
|
cleaned.push_str(&line);
|
|
cleaned.push('\n');
|
|
}
|
|
// remove trailing newline
|
|
cleaned.pop();
|
|
cleaned
|
|
};
|
|
|
|
std::fs::write(format!("build/data/chips/{chip_name}.json"), dump)?;
|
|
Ok(())
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn dump_all_chips(
|
|
chip_groups: Vec<ChipGroup>,
|
|
headers: header::Headers,
|
|
af: gpio_af::Af,
|
|
chip_interrupts: interrupts::ChipInterrupts,
|
|
peripheral_to_clock: rcc::PeripheralToClock,
|
|
dma_channels: dma::DmaChannels,
|
|
chips: std::collections::HashMap<String, Chip>,
|
|
memories: memory::Memories,
|
|
docs: docs::Docs,
|
|
) -> Result<(), anyhow::Error> {
|
|
std::fs::create_dir_all("build/data/chips")?;
|
|
|
|
#[cfg(feature = "rayon")]
|
|
{
|
|
use rayon::prelude::*;
|
|
|
|
chip_groups
|
|
.into_par_iter()
|
|
.try_for_each_init(PeriMatcher::new, |peri_matcher, group| {
|
|
process_group(
|
|
group,
|
|
peri_matcher,
|
|
&headers,
|
|
&af,
|
|
&chip_interrupts,
|
|
&peripheral_to_clock,
|
|
&dma_channels,
|
|
&chips,
|
|
&memories,
|
|
&docs,
|
|
)
|
|
})
|
|
}
|
|
#[cfg(not(feature = "rayon"))]
|
|
{
|
|
let mut peri_matcher = PeriMatcher::new();
|
|
|
|
chip_groups.into_iter().try_for_each(|group| {
|
|
process_group(
|
|
group,
|
|
&mut peri_matcher,
|
|
&headers,
|
|
&af,
|
|
&chip_interrupts,
|
|
&peripheral_to_clock,
|
|
&dma_channels,
|
|
&chips,
|
|
&memories,
|
|
&docs,
|
|
)
|
|
})
|
|
}
|
|
}
|