use std::collections::BTreeMap;
use crate::svd::Error;
use crate::svd::emit::{module_name, static_name, variant_name};
use crate::svd::model::{Model, Register};
const RESERVED_STATICS: [&str; 2] = ["CPU", "PERIPHERALS"];
pub(crate) fn check(model: &Model) -> Result<(), Error> {
for pair in model.instances.windows(2) {
let (lower, upper) = (&pair[0], &pair[1]);
if upper.base < lower.base.saturating_add(lower.size) {
return Err(Error::OverlappingInstances {
lower: lower.name.clone(),
upper: upper.name.clone(),
});
}
}
for (layout, registers) in &model.layouts {
if let Some((lower, upper)) = first_undeclared_overlap(registers) {
return Err(Error::OverlappingRegisters {
lower: format!("{layout}.{}", lower.name),
upper: format!("{layout}.{}", upper.name),
});
}
}
distinct(
model
.layouts
.keys()
.map(|layout| (module_name(layout), layout.as_str())),
)?;
distinct(
RESERVED_STATICS
.into_iter()
.map(|reserved| (reserved.to_owned(), reserved))
.chain(
model
.instances
.iter()
.map(|instance| (static_name(&instance.name), instance.name.as_str())),
),
)?;
for registers in model.layouts.values() {
distinct(
registers
.iter()
.map(|register| (variant_name(®ister.name), register.name.as_str())),
)?;
distinct(
registers
.iter()
.map(|register| (module_name(®ister.name), register.name.as_str())),
)?;
for register in registers {
distinct(
register
.fields
.iter()
.map(|field| (static_name(&field.name), field.name.as_str())),
)?;
}
}
Ok(())
}
fn first_undeclared_overlap(registers: &[Register]) -> Option<(&Register, &Register)> {
let mut furthest: Option<&Register> = None;
for register in registers.iter().filter(|register| !register.alternate) {
if let Some(earlier) = furthest.filter(|earlier| register.offset < earlier.end()) {
return Some((earlier, register));
}
if furthest.is_none_or(|earlier| register.end() > earlier.end()) {
furthest = Some(register);
}
}
None
}
fn distinct<'a>(names: impl Iterator<Item = (String, &'a str)>) -> Result<(), Error> {
let mut seen: BTreeMap<String, &str> = BTreeMap::new();
for (identifier, name) in names {
if let Some(first) = seen.insert(identifier, name) {
return Err(Error::NameCollision {
first: first.to_owned(),
second: name.to_owned(),
});
}
}
Ok(())
}