329 lines
12 KiB
Rust

use std::collections::HashMap;
use crate::regex;
pub struct Headers {
map: HeaderMap,
parsed: HeadersParsed,
regexes: Vec<(regex::Regex, String)>,
}
impl Headers {
pub fn parse() -> anyhow::Result<Self> {
let map = HeaderMap::parse()?;
let parsed = HeadersParsed::parse()?;
let regexes = parsed
.0
.keys()
.map(|h| {
let pattern = h.replace('x', ".");
let regex = regex::Regex::new(&format!("^{pattern}$")).unwrap();
(regex, h.clone())
})
.collect();
Ok(Self { map, parsed, regexes })
}
pub fn get_for_chip(&self, model: &str) -> Option<&ParsedHeader> {
let model = model.to_ascii_lowercase();
match self.map.0.get(&model) {
// if it's in the map, just go
Some(name) => Some(self.parsed.0.get(name).unwrap()),
// if not, find it by regex, taking `x` meaning `anything`
None => {
let mut results = self
.regexes
.iter()
.filter_map(|(r, name)| if r.is_match(&model) { Some(name) } else { None });
let res = results.next();
assert_eq!(results.next(), None, "found more than one match");
res.map(|name| self.parsed.0.get(name).unwrap())
}
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct HeaderMap(pub HashMap<String, String>);
impl HeaderMap {
pub fn parse() -> anyhow::Result<Self> {
let mut res = HashMap::new();
for (mut header, chips) in
serde_yaml::from_str::<HashMap<String, String>>(&std::fs::read_to_string("data/header_map.yaml")?)?
{
header.make_ascii_lowercase();
for chip in chips.split(',') {
let chip = chip.trim().to_ascii_lowercase();
if let Some(old) = res.insert(chip.clone(), header.clone()) {
panic!("Duplicate {chip} found! Overwriting {old} with {header}");
}
}
}
Ok(Self(res))
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct HeadersParsed(pub HashMap<String, ParsedHeader>);
impl HeadersParsed {
pub fn parse() -> anyhow::Result<Self> {
let files = glob::glob("sources/headers/*.h").unwrap().map(Result::unwrap);
let for_each_file = |f: std::path::PathBuf| {
let ff = f.file_name().unwrap().to_string_lossy();
let ff = ff.strip_suffix(".h").unwrap();
let parsed_header = ParsedHeader::parse(&f).unwrap();
(ff.to_string(), parsed_header)
};
#[cfg(feature = "rayon")]
{
use rayon::prelude::*;
Ok(Self(files.par_bridge().map(for_each_file).collect()))
}
#[cfg(not(feature = "rayon"))]
{
Ok(Self(files.map(for_each_file).collect()))
}
}
}
fn parens_ok(val: &str) -> bool {
let mut n: i32 = 0;
for c in val.chars() {
match c {
'(' => n += 1,
')' => {
n -= 1;
if n < 0 {
return false;
}
}
_ => {}
}
}
n == 0
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Defines(pub HashMap<String, i64>);
impl Defines {
// warning: horrible abomination ahead
fn parse_value(&self, val: &str) -> i64 {
let val = val.trim();
if val.is_empty() {
0
} else if let Some(m) = regex!(r"^(0([1-9][0-9]*)(U))").captures(val) {
m.get(2).unwrap().as_str().parse().unwrap()
} else if let Some(m) = regex!(r"^((0x[0-9a-fA-F]+|\d+))(|u|ul|U|UL)$").captures(val) {
let x = m.get(1).unwrap().as_str();
match x.strip_prefix("0x") {
Some(x) => i64::from_str_radix(x, 16),
None => x.parse(),
}
.unwrap()
} else if let Some(m) = regex!(r"^([0-9A-Za-z_]+)$").captures(val) {
self.0.get(m.get(1).unwrap().as_str()).copied().unwrap_or(0)
} else if let Some(x) = regex!(r"^\((.*)\)$")
.captures(val)
.map(|m| m.get(1).unwrap().as_str())
.filter(|x| parens_ok(x))
{
self.parse_value(x)
} else if let Some(m) = regex!(r"^\*?\([0-9A-Za-z_]+ *\*?\)(.*)$").captures(val) {
self.parse_value(m.get(1).unwrap().as_str())
} else if let Some(m) = regex!(r"^(.*)/(.*)$").captures(val) {
self.parse_value(m.get(1).unwrap().as_str()) / self.parse_value(m.get(2).unwrap().as_str())
} else if let Some(m) = regex!(r"^(.*)<<(.*)$").captures(val) {
self.parse_value(m.get(1).unwrap().as_str()) << self.parse_value(m.get(2).unwrap().as_str()) & 0xFFFFFFFF
} else if let Some(m) = regex!(r"^(.*)>>(.*)$").captures(val) {
self.parse_value(m.get(1).unwrap().as_str()) >> self.parse_value(m.get(2).unwrap().as_str())
} else if let Some(m) = regex!(r"^(.*)\|(.*)$").captures(val) {
self.parse_value(m.get(1).unwrap().as_str()) | self.parse_value(m.get(2).unwrap().as_str())
} else if let Some(m) = regex!(r"^(.*)&(.*)$").captures(val) {
self.parse_value(m.get(1).unwrap().as_str()) & self.parse_value(m.get(2).unwrap().as_str())
} else if let Some(m) = regex!(r"^~(.*)$").captures(val) {
!self.parse_value(m.get(1).unwrap().as_str()) & 0xFFFFFFFF
} else if let Some(m) = regex!(r"^(.*)\+(.*)$").captures(val) {
self.parse_value(m.get(1).unwrap().as_str()) + self.parse_value(m.get(2).unwrap().as_str())
} else if let Some(m) = regex!(r"^(.*)-(.*)$").captures(val) {
self.parse_value(m.get(1).unwrap().as_str()) - self.parse_value(m.get(2).unwrap().as_str())
} else {
panic!("can't parse: {val:?}")
}
}
pub fn get_peri_addr(&self, pname: &str) -> Option<u32> {
const ALT_PERI_DEFINES: &[(&str, &[&str])] = &[
("DBGMCU", &["DBGMCU_BASE", "DBG_BASE"]),
("QUADSPI", &["QUADSPI_BASE", "QSPI_R", "QSPI_R_BASE", "QSPI_REG_BASE"]),
("QUADSPI1", &["QUADSPI1_BASE", "QSPI_R", "QSPI_R_BASE", "QSPI_REG_BASE"]),
(
"OCTOSPI",
&["OSPI_R", "OCTOSPI_R_BASE", "OCTOSPI_R_BASE_NS", "OCTOSPI_REG_BASE"],
),
(
"OCTOSPI1",
&["OSPI_R", "OCTOSPI1_R_BASE", "OCTOSPI1_R_BASE_NS", "OCTOSPI1_REG_BASE"],
),
(
"OCTOSPI2",
&["OCTOSPI2_R_BASE", "OCTOSPI2_R_BASE_NS", "OCTOSPI2_REG_BASE"],
),
("FLASH", &["FLASH_R_BASE", "FLASH_REG_BASE"]),
(
"ADC_COMMON",
&["ADC_COMMON", "ADC1_COMMON", "ADC12_COMMON", "ADC123_COMMON"],
),
("ADC3_COMMON", &["ADC3_COMMON", "ADC4_COMMON", "ADC34_COMMON"]),
("CAN", &["CAN_BASE", "CAN1_BASE"]),
("FMC", &["FMC_BASE", "FMC_R_BASE"]),
("FSMC", &["FSMC_R_BASE"]),
("USB", &["USB_BASE", "USB_DRD_BASE", "USB_BASE_NS", "USB_DRD_BASE_NS"]),
(
"USBRAM",
&["USB_PMAADDR", "USB_DRD_PMAADDR", "USB_PMAADDR_NS", "USB_DRD_PMAADDR_NS"],
),
("FDCANRAM", &["SRAMCAN_BASE", "SRAMCAN_BASE_NS"]),
("VREFINTCAL", &["VREFINT_CAL_ADDR_CMSIS"]),
];
let alt_peri_defines: HashMap<_, _> = ALT_PERI_DEFINES.iter().copied().collect();
let possible_defines: Vec<String> = alt_peri_defines
.get(pname)
.map(|x| x.iter().map(ToString::to_string).collect())
.unwrap_or_else(|| vec![format!("{pname}_BASE"), pname.to_string()]);
possible_defines
.into_iter()
.find_map(|d| self.0.get(&d).filter(|&&addr| addr != 0))
.map(|x| u32::try_from(*x).unwrap())
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct ParsedHeader {
pub cores: Vec<String>,
pub interrupts: HashMap<String, HashMap<String, u8>>,
pub defines: HashMap<String, Defines>,
}
impl ParsedHeader {
/// Get C header defines for this core.
pub fn get_defines(&self, core_name: &str) -> &Defines {
let core_name = if !self.interrupts.contains_key(core_name) || !self.defines.contains_key(core_name) {
"all"
} else {
core_name
};
self.defines.get(core_name).unwrap()
}
/// Get interrupts for this core.
pub fn get_interrupts(&self, core_name: &str) -> &HashMap<String, u8> {
let core_name = if !self.interrupts.contains_key(core_name) || !self.defines.contains_key(core_name) {
"all"
} else {
core_name
};
self.interrupts.get(core_name).unwrap()
}
fn parse(f: impl AsRef<std::path::Path>) -> anyhow::Result<Self> {
let mut irqs = HashMap::<String, HashMap<String, u8>>::new();
let mut defines = HashMap::<String, Defines>::new();
let mut cores = Vec::<String>::new();
let mut cur_core = "all".to_string();
let mut accum = String::new();
let f = std::fs::read(f)?;
for l in f.split(|b| b == &b'\n') {
let l = String::from_utf8_lossy(l);
let l = l.trim();
let l = accum.clone() + l;
if l.ends_with('\\') {
accum = l.strip_suffix('\\').unwrap().to_string();
continue;
}
accum = String::new();
// Scoped by a single core
if let Some(m) = regex!(r".*if defined.*CORE_CM(\d+)(PLUS)?.*").captures(&l) {
cur_core = format!("cm{}", m.get(1).unwrap().as_str());
if m.get(2).is_some() {
cur_core += "p";
}
if !cores.contains(&cur_core) {
cores.push(cur_core.clone())
}
} else if regex!(r".*else.*").is_match(&l) {
cur_core = "all".to_string();
if let Some(m) = regex!(".*else.*CORE_CM(\\d+)(PLUS)?.*").captures(&l) {
cur_core = format!("cm{}", m.get(1).unwrap().as_str());
if m.get(2).is_some() {
cur_core += "p";
}
} else if cores.len() > 1 {
// Pick the second core assuming we've already parsed one
cur_core = cores[1].clone();
}
if !cores.contains(&cur_core) {
cores.push(cur_core.clone());
}
} else if regex!(r".*endif.*").is_match(&l) {
cur_core = "all".to_string();
}
let irq_entry = irqs.entry(cur_core.clone()).or_default();
let defines_entry = defines.entry(cur_core.clone()).or_default();
if let Some(m) = regex!(r"^([a-zA-Z0-9_]+)_IRQn *= *(\d+),? +/\*!<(.*)\*/").captures(&l) {
irq_entry.insert(
m.get(1).unwrap().as_str().to_string(),
m.get(2).unwrap().as_str().parse().unwrap(),
);
}
if let Some(m) = regex!(r"^#define +([0-9A-Za-z_]+)\(").captures(&l) {
defines_entry.0.insert(m.get(1).unwrap().as_str().to_string(), -1);
}
if let Some(m) = regex!(r"^#define +([0-9A-Za-z_]+) +(.*)").captures(&l) {
let name = m.get(1).unwrap().as_str().trim();
if name == "FLASH_SIZE" {
continue;
}
let val = m.get(2).unwrap().as_str();
let val = val.split("/*").next().unwrap().trim();
let val = defines_entry.parse_value(val);
defines_entry.0.insert(name.to_string(), val);
}
}
if cores.is_empty() {
cores = vec!["all".to_string()];
}
for core in &mut cores {
if core != "all" {
let all_irqs = irqs.get("all").unwrap().clone();
irqs.get_mut(core).unwrap().extend(all_irqs);
let all_defines = defines.get("all").unwrap().clone();
defines.get_mut(core).unwrap().0.extend(all_defines.0);
}
}
Ok(Self {
cores,
interrupts: irqs,
defines,
})
}
}