use std::collections::BTreeMap;
use svd_parser::svd::{
Access, DeriveFrom, Device, DimElement, Endian, FieldInfo, MaybeArray, ModifiedWriteValues,
Peripheral, PeripheralInfo, ReadAction, RegisterCluster, RegisterInfo, RegisterProperties,
};
use crate::svd::Error;
pub(crate) struct Model {
pub(crate) device_name: String,
pub(crate) cpu: Cpu,
pub(crate) instances: Vec<Instance>,
pub(crate) layouts: BTreeMap<String, Vec<Register>>,
}
pub(crate) struct Cpu {
pub(crate) name: String,
pub(crate) fpu: bool,
pub(crate) interrupt_lines: u16,
pub(crate) interrupt_lines_stated: bool,
pub(crate) endian: crate::soc::Endian,
}
pub(crate) struct Instance {
pub(crate) name: String,
pub(crate) base: u64,
pub(crate) size: u64,
pub(crate) interrupts: Vec<u16>,
pub(crate) layout: String,
}
pub(crate) struct Register {
pub(crate) name: String,
pub(crate) offset: u32,
pub(crate) bits: u32,
pub(crate) reset: u64,
pub(crate) access: Access,
pub(crate) read: Option<ReadAction>,
pub(crate) write: Option<ModifiedWriteValues>,
pub(crate) fields: Vec<Field>,
pub(crate) alternate: bool,
}
pub(crate) struct Field {
pub(crate) name: String,
pub(crate) bit: u8,
pub(crate) width: u8,
pub(crate) access: Access,
pub(crate) read: Option<ReadAction>,
pub(crate) write: Option<ModifiedWriteValues>,
}
const BITS_PER_BYTE: u32 = 8;
const DEFAULT_REGISTER_BITS: u32 = 32;
const REGISTER_WIDTHS: [u32; 4] = [8, 16, 32, 64];
impl Register {
pub(crate) const fn end(&self) -> u32 {
self.offset + self.bits / BITS_PER_BYTE
}
}
const MOST_IN_AN_ARRAY: u64 = 1 << 16;
const MOST_ELEMENTS: u64 = 1 << 20;
fn arrays_fit(device: &Device) -> Result<(), Error> {
fn repeats(name: &str, dim: &DimElement, outer: u64) -> Result<u64, Error> {
let span = u64::from(dim.dim) * u64::from(dim.dim_increment);
if span > u64::from(u32::MAX) {
return Err(Error::TooLarge {
element: name.to_owned(),
what: "the dim times the dimIncrement",
value: span,
});
}
let elements = outer.saturating_mul(u64::from(dim.dim));
if elements > MOST_IN_AN_ARRAY {
return Err(Error::TooLarge {
element: name.to_owned(),
what: "the elements its arrays expand to",
value: elements,
});
}
Ok(u64::from(dim.dim))
}
fn size(children: &[RegisterCluster], outer: u64) -> Result<u64, Error> {
let mut total = 0_u64;
for child in children {
let (name, elements) = match child {
RegisterCluster::Register(register) => {
let times = match register {
MaybeArray::Array(info, dim) => repeats(&info.name, dim, outer)?,
MaybeArray::Single(_) => 1,
};
let mut fields = 0_u64;
for field in register.fields.iter().flatten() {
fields = fields.saturating_add(match field {
MaybeArray::Array(info, dim) => {
repeats(&info.name, dim, outer.saturating_mul(times))?
}
MaybeArray::Single(_) => 1,
});
}
(®ister.name, times.saturating_mul(1 + fields))
}
RegisterCluster::Cluster(cluster) => {
let times = match cluster {
MaybeArray::Array(info, dim) => repeats(&info.name, dim, outer)?,
MaybeArray::Single(_) => 1,
};
let inside = size(&cluster.children, outer.saturating_mul(times))?;
(&cluster.name, times.saturating_mul(1 + inside))
}
};
total = total.saturating_add(elements);
if total > MOST_ELEMENTS {
return Err(too_many(name, total));
}
}
Ok(total)
}
fn too_many(name: &str, elements: u64) -> Error {
Error::TooLarge {
element: name.to_owned(),
what: "the elements the file expands to",
value: elements,
}
}
let mut largest_dim = 1;
for peripheral in &device.peripherals {
if let MaybeArray::Array(info, dim) = peripheral {
largest_dim = largest_dim.max(repeats(&info.name, dim, 1)?);
}
}
let mut own = Vec::new();
for peripheral in &device.peripherals {
let times = match peripheral {
MaybeArray::Array(info, dim) => repeats(&info.name, dim, 1)?,
MaybeArray::Single(_) if peripheral.derived_from.is_some() => largest_dim,
MaybeArray::Single(_) => 1,
};
let registers = peripheral.registers.as_deref().unwrap_or(&[]);
own.push((size(registers, times)?, times));
}
let largest = own.iter().map(|&(size, _)| size).max().unwrap_or(0);
let mut total = 0_u64;
for (peripheral, &(size, times)) in device.peripherals.iter().zip(&own) {
let size = if peripheral.derived_from.is_some() && peripheral.registers.is_none() {
largest
} else {
size
};
total = total.saturating_add(times.saturating_mul(1 + size));
if total > MOST_ELEMENTS {
return Err(too_many(&peripheral.name, total));
}
}
Ok(())
}
pub(crate) fn from_device(device: &Device) -> Result<Model, Error> {
arrays_fit(device)?;
let layout_of = layout_roots(device)?;
let expanded = svd_parser::expand::expand(device).map_err(Error::Svd)?;
let cpu = expanded.cpu.as_ref().ok_or(Error::NoCpu)?;
let mut instances = Vec::new();
let mut layouts = BTreeMap::new();
for peripheral in &expanded.peripherals {
let defaults = peripheral
.default_register_properties
.derive_from(&expanded.default_register_properties);
let layout = layout_of
.get(&peripheral.name)
.cloned()
.unwrap_or_else(|| peripheral.name.clone());
if layout == peripheral.name {
layouts.insert(layout.clone(), registers_of(peripheral, &defaults)?);
}
instances.push(instance_from(peripheral, layout)?);
}
instances.sort_by(|left, right| (left.base, &left.name).cmp(&(right.base, &right.name)));
let highest_line = instances
.iter()
.flat_map(|instance| &instance.interrupts)
.max()
.copied();
let interrupt_lines = cpu
.device_num_interrupts
.unwrap_or_else(|| highest_line.map_or(0, |line| u32::from(line) + 1));
Ok(Model {
device_name: expanded.name.clone(),
cpu: Cpu {
name: cpu.name.clone(),
fpu: cpu.fpu_present,
interrupt_lines: narrow(interrupt_lines.into(), "cpu", "deviceNumInterrupts")?,
interrupt_lines_stated: cpu.device_num_interrupts.is_some(),
endian: if cpu.endian == Endian::Big {
crate::soc::Endian::Big
} else {
crate::soc::Endian::Little
},
},
instances,
layouts,
})
}
fn layout_roots(device: &Device) -> Result<BTreeMap<String, String>, Error> {
let index = svd_parser::expand::Index::create(device);
for peripheral in &device.peripherals {
let mut seen: Vec<*const Peripheral> = vec![peripheral];
let mut base = peripheral.derived_from.as_deref();
while let Some(name) = base {
let Some(&next) = index
.peripherals
.get(&svd_parser::expand::BlockPath::new(name))
else {
break;
};
if seen.contains(&std::ptr::from_ref(next)) {
return Err(Error::DerivationCycle {
instance: peripheral.name.clone(),
});
}
seen.push(next);
base = next.derived_from.as_deref();
}
}
let derived: BTreeMap<&str, &str> = device
.peripherals
.iter()
.filter(|peripheral| peripheral.registers.is_none())
.filter_map(|peripheral| {
peripheral
.derived_from
.as_deref()
.map(|parent| (peripheral.name.as_str(), parent))
})
.collect();
derived
.keys()
.map(|&name| {
let mut root = name;
for _ in 0..=derived.len() {
match derived.get(root) {
Some(&parent) => root = parent,
None => return Ok((name.to_owned(), root.to_owned())),
}
}
Err(Error::DerivationCycle {
instance: name.to_owned(),
})
})
.collect()
}
fn instance_from(peripheral: &PeripheralInfo, layout: String) -> Result<Instance, Error> {
let blocks = peripheral.address_block.as_deref().unwrap_or(&[]);
let size = blocks
.iter()
.map(|block| u64::from(block.offset) + u64::from(block.size))
.max()
.ok_or_else(|| Error::NoAddressBlock {
instance: peripheral.name.clone(),
})?;
let mut interrupts = peripheral
.interrupt
.iter()
.map(|interrupt| narrow(interrupt.value.into(), &peripheral.name, "interrupt"))
.collect::<Result<Vec<u16>, Error>>()?;
interrupts.sort_unstable();
interrupts.dedup();
Ok(Instance {
name: peripheral.name.clone(),
base: peripheral.base_address,
size,
interrupts,
layout,
})
}
fn registers_of(
peripheral: &PeripheralInfo,
defaults: &RegisterProperties,
) -> Result<Vec<Register>, Error> {
let mut registers = Vec::new();
for item in peripheral.registers.as_deref().unwrap_or(&[]) {
let RegisterCluster::Register(info) = item else {
return Err(Error::Svd(anyhow::anyhow!(
"{}: a cluster survived expansion",
peripheral.name
)));
};
registers.push(register_from(&peripheral.name, info, defaults)?);
}
registers.sort_by(|left, right| (left.offset, &left.name).cmp(&(right.offset, &right.name)));
Ok(registers)
}
fn register_from(
peripheral_name: &str,
info: &RegisterInfo,
defaults: &RegisterProperties,
) -> Result<Register, Error> {
let path = format!("{peripheral_name}.{}", info.name);
let properties = info.properties.derive_from(defaults);
let bits = properties.size.unwrap_or(DEFAULT_REGISTER_BITS);
if !REGISTER_WIDTHS.contains(&bits) {
return Err(Error::Width {
register: path,
bits,
});
}
let access = properties.access.unwrap_or(Access::ReadWrite);
let mut fields = info
.fields
.as_deref()
.unwrap_or(&[])
.iter()
.map(|field| field_from(&path, field, bits, access, info))
.collect::<Result<Vec<Field>, Error>>()?;
fields.sort_by(|left, right| (left.bit, &left.name).cmp(&(right.bit, &right.name)));
Ok(Register {
name: info.name.clone(),
offset: info.address_offset,
bits,
reset: properties.reset_value.unwrap_or(0),
access,
read: info.read_action,
write: info.modified_write_values,
fields,
alternate: info.alternate_register.is_some() || info.alternate_group.is_some(),
})
}
fn field_from(
register_path: &str,
field: &FieldInfo,
register_bits: u32,
register_access: Access,
register: &RegisterInfo,
) -> Result<Field, Error> {
let end = field.bit_range.offset + field.bit_range.width;
if end > register_bits {
return Err(Error::FieldOutOfRange {
register: register_path.to_owned(),
field: field.name.clone(),
end,
bits: register_bits,
});
}
Ok(Field {
name: field.name.clone(),
bit: narrow(field.bit_range.offset.into(), register_path, "bitOffset")?,
width: narrow(field.bit_range.width.into(), register_path, "bitWidth")?,
access: field.access.unwrap_or(register_access),
read: field.read_action.or(register.read_action),
write: field
.modified_write_values
.or(register.modified_write_values),
})
}
fn narrow<T: TryFrom<u64>>(value: u64, element: &str, what: &'static str) -> Result<T, Error> {
T::try_from(value).map_err(|_overflow| Error::TooLarge {
element: element.to_owned(),
what,
value,
})
}