use std::cmp::Ordering; use std::collections::HashMap; use std::fs; #[derive(Debug, PartialEq)] pub struct Memory { pub device_id: u16, pub ram: Ram, pub flash_size: u32, pub flash_regions: Vec, } #[derive(Clone, Copy, Debug, PartialEq)] pub struct Ram { pub address: u32, pub bytes: u32, } #[derive(Clone, Debug, PartialEq)] pub struct FlashRegion { pub bank: FlashBank, pub address: u32, pub bytes: u32, pub settings: stm32_data_serde::chip::memory::Settings, } #[derive(Clone, Copy, Debug, PartialEq)] pub enum FlashBank { Bank1, Bank2, Otp, } mod xml { use serde::Deserialize; pub fn from_hex<'de, T, D>(deserializer: D) -> Result where D: serde::Deserializer<'de>, T: num::Num, T::FromStrRadixErr: std::fmt::Display, { use serde::de::Error; let s: &str = Deserialize::deserialize(deserializer)?; let s = s.trim(); let (prefix, num) = s.split_at(2); if prefix != "0x" && prefix != "0X" { panic!("no hex prefix"); } T::from_str_radix(num, 16).map_err(D::Error::custom) } pub fn opt_from_hex<'de, T, D>(deserializer: D) -> Result, D::Error> where D: serde::Deserializer<'de>, T: num::Num, T::FromStrRadixErr: std::fmt::Display, { Ok(Some(from_hex(deserializer)?)) } #[derive(Debug, Deserialize, PartialEq)] pub struct Root { #[serde(rename = "Device")] pub device: root::Device, } mod root { use serde::Deserialize; use super::from_hex; #[derive(Debug, Deserialize, PartialEq)] pub struct Device { #[serde(rename = "DeviceID", deserialize_with = "from_hex")] pub device_id: u16, #[serde(rename = "Name")] pub name: String, #[serde(rename = "Peripherals")] pub peripherals: device::Peripherals, } mod device { use serde::Deserialize; #[derive(Debug, Deserialize, PartialEq)] pub struct Peripherals { #[serde(rename = "Peripheral")] pub peripharal: Vec, } mod peripherals { use serde::Deserialize; use super::super::super::opt_from_hex; #[derive(Debug, Deserialize, PartialEq)] pub struct Peripheral { #[serde(rename = "Name")] // pub name: peripheral::Name, pub name: String, #[serde(rename = "ErasedValue", deserialize_with = "opt_from_hex", default)] pub erased_value: Option, #[serde(rename = "Configuration", default)] pub configuration: Vec, } mod peripheral { use serde::Deserialize; use super::super::super::super::opt_from_hex; #[derive(Debug, Deserialize, PartialEq)] pub struct Configuration { #[serde(rename = "Parameters", default)] pub parameters: Option, #[serde(rename = "Organization", default)] pub organization: Option, #[serde(rename = "Allignement", deserialize_with = "opt_from_hex", default)] pub allignement: Option, #[serde(rename = "Bank")] pub bank: Vec, } mod configuration { use serde::Deserialize; use super::super::super::super::super::{from_hex, opt_from_hex}; #[derive(Debug, Deserialize, PartialEq)] pub struct Parameters { #[serde(deserialize_with = "from_hex")] pub address: u32, #[serde(deserialize_with = "from_hex")] pub size: u32, #[serde(deserialize_with = "opt_from_hex", default)] pub occurence: Option, } #[derive(Debug, Deserialize, PartialEq)] pub struct Bank { #[serde(default)] pub name: Option, #[serde(rename = "Field", default)] pub field: Vec, } mod bank { use serde::Deserialize; #[derive(Debug, Deserialize, PartialEq)] pub struct Field { #[serde(rename = "Parameters")] pub parameters: super::Parameters, } } } } } } } } pub struct Memories(HashMap); impl Memories { pub fn parse() -> anyhow::Result { let mut paths: Vec<_> = glob::glob("sources/cubeprogdb/db/*.xml") .unwrap() .map(Result::unwrap) .collect(); paths.sort(); let mut memories = HashMap::new(); for f in paths { // println!("Parsing {f:?}"); let file = fs::read_to_string(f)?; let parsed: xml::Root = quick_xml::de::from_str(&file)?; // dbg!(&parsed); let device_id = parsed.device.device_id; let mut ram = None; let mut flash_size = None; let mut flash_regions = vec![]; for mut peripheral in parsed.device.peripherals.peripharal { if peripheral.name == "Embedded SRAM" && ram.is_none() { let config = peripheral.configuration.first().unwrap(); let parameters = config.parameters.as_ref().unwrap(); ram = Some(Ram { address: parameters.address, bytes: parameters.size, }); } enum BlockKind { Main, Otp, } let kind = match peripheral.name.as_str() { "Embedded Flash" => Some(BlockKind::Main), "OTP" => Some(BlockKind::Otp), _ => None, }; if let Some(kind) = kind { peripheral.configuration.sort_by(|a, b| { // Prefer largest size let ordering = b .parameters .as_ref() .unwrap() .size .partial_cmp(&a.parameters.as_ref().unwrap().size) .unwrap(); // ... then prefer single ordering over dual if ordering == Ordering::Equal { // Possible values are Single and Dual b.organization.partial_cmp(&a.organization).unwrap() } else { ordering } }); let config = peripheral.configuration.first().unwrap(); if flash_size.is_none() { let parameters = config.parameters.as_ref().unwrap(); flash_size = Some(parameters.size); } for bank in config.bank.iter() { let flash_bank = match kind { BlockKind::Main => match bank.name.as_deref() { Some("Bank 1") => Some(FlashBank::Bank1), Some("Bank 2") => Some(FlashBank::Bank2), Some("EEPROM1") => None, Some("EEPROM2") => None, None => { assert_eq!(1, config.bank.len()); Some(FlashBank::Bank1) } Some(other) => unimplemented!("Unsupported flash bank {}", other), }, BlockKind::Otp => Some(FlashBank::Otp), }; if let Some(flash_bank) = flash_bank { let erase_value = peripheral.erased_value.unwrap(); let write_size = config.allignement.unwrap(); flash_regions.extend(bank.field.iter().map(|field| FlashRegion { bank: flash_bank, address: field.parameters.address, bytes: field.parameters.occurence.unwrap() * field.parameters.size, settings: stm32_data_serde::chip::memory::Settings { erase_value, write_size, erase_size: field.parameters.size, }, })); } } } } memories.insert( device_id, Memory { device_id, ram: ram.unwrap(), flash_size: flash_size.unwrap_or_default(), flash_regions, }, ); } Ok(Self(memories)) } pub fn get(&self, die: &str) -> &Memory { assert!(die.starts_with("DIE")); let device_id = u16::from_str_radix(&die[3..], 16).unwrap(); self.0.get(&device_id).unwrap() } }