mod svd2temp;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use std::vec;
use super::ir::*;
use super::util::*;
use crate::SvdValidationLevel;
use crate::svd_util::*;
use anyhow::Ok;
use anyhow::Result;
use linked_hash_map::LinkedHashMap;
use log::{debug, error, warn};
use svd_parser::svd;
use svd_parser::svd::Name;
use svd2temp::*;
trait RegisterHelper {
fn get_name_id_internal(&self) -> String;
}
impl RegisterHelper for svd::RegisterInfo {
fn get_name_id_internal(&self) -> String {
match self.alternate_group {
None => self.name.to_internal_ident(),
Some(ref alt_group_name) => {
(self.name.to_owned() + "_" + alt_group_name).to_internal_ident()
}
}
}
}
enum PeripheralClusterE<'a> {
Peripheral(&'a mut PeripheralMod),
Cluster(&'a mut Cluster),
}
impl PeripheralClusterE<'_> {
pub fn get_mut_registers(&mut self) -> &mut LinkedHashMap<String, Rc<RefCell<Register>>> {
match self {
PeripheralClusterE::Peripheral(p) => &mut p.registers,
PeripheralClusterE::Cluster(c) => &mut c.registers,
}
}
pub fn get_mut_clusters(&mut self) -> &mut LinkedHashMap<String, Rc<RefCell<Cluster>>> {
match self {
PeripheralClusterE::Peripheral(p) => &mut p.clusters,
PeripheralClusterE::Cluster(c) => &mut c.clusters,
}
}
}
fn get_dim_dim_increment<T: Name>(array: &svd::array::MaybeArray<T>) -> (u32, u32, Vec<String>) {
match array {
svd::array::MaybeArray::Single(_) => (1, 0, vec![]),
svd::array::MaybeArray::Array(item, dim_element) => {
let dim_index: Vec<String> = if let Some(dim_index_elements) = &dim_element.dim_index {
if dim_index_elements.len() != dim_element.dim as usize {
warn!(
"dim_index length is not equal to dim. dim_index: {:?} dim: {} ignoring dimIndex tag",
dim_index_elements, dim_element.dim
);
vec![]
} else if !item.name().contains("%s") {
warn!(
"dimIndex tag is used but name doesn't contain %s. dim_index: {:?} dim: {} ignoring dimIndex tag",
dim_index_elements, dim_element.dim
);
vec![]
} else {
dim_index_elements
.iter()
.map(|x| {
let replaced = item.name().replace("%s", x);
replaced.to_internal_ident()
})
.collect()
}
} else {
vec![]
};
(dim_element.dim, dim_element.dim_increment, dim_index)
}
}
}
#[derive(Debug)]
enum DeviceItem {
Register(Rc<RefCell<Register>>),
Cluster(Rc<RefCell<Cluster>>),
#[allow(dead_code)]
Peripheral(Rc<RefCell<PeripheralMod>>),
}
#[derive(Default)]
struct Visitor {
device: Device,
svd_ref_to_ir_item: HashMap<String, DeviceItem>,
current_item_svd_path: Vec<String>,
current_mod_ir_path: Vec<String>,
}
impl Visitor {
fn visit_device(&mut self, device: &svd::Device) -> Result<()> {
self.device.name.clone_from(&device.name);
self.device.description.clone_from(&device.description);
for svd_peripheral in device.peripherals.iter() {
let derived_peripheral: Option<PeripheralMod> =
if let Some(derived_ref) = &svd_peripheral.derived_from {
if let Some(ref_item) = self.svd_ref_to_ir_item.get(derived_ref) {
match ref_item {
DeviceItem::Peripheral(ref_peripheral) => {
Some(ref_peripheral.borrow().clone())
}
_ => {
return Err(ParseError::InvalidPeripheral {
peripheral_name: svd_peripheral.name.clone(),
msg: format!(
"reference {derived_ref} doesn't point to a peripheral"
),
}
.into());
}
}
} else {
return Err(ParseError::InvalidPeripheral {
peripheral_name: svd_peripheral.name.clone(),
msg: format!("Missing reference {derived_ref}"),
}.into());
}
} else {
None
};
self.push_current_item_svd_path(svd_peripheral)?;
let mut peripheral = derived_peripheral
.as_ref()
.map_or_else(PeripheralMod::default, |x| x.clone());
self.visit_peripheral(svd_peripheral, &mut peripheral)?;
let name = peripheral.name.clone();
peripheral.derived_from = if let Some(derived_peri) = derived_peripheral {
if peripheral.has_same_type(&derived_peri) {
Some(derived_peri.name)
} else {
None
}
} else {
None
};
let peripheral_mod = Rc::new(RefCell::new(peripheral));
self.device
.peripheral_mod
.insert(name, peripheral_mod.clone());
self.pop_current_item_svd_path(DeviceItem::Peripheral(peripheral_mod));
}
Ok(())
}
fn visit_peripheral(
&mut self,
svd_peripheral: &svd::Peripheral,
peripheral: &mut PeripheralMod,
) -> Result<()> {
debug!("Parsing peripheral: {}", &svd_peripheral.name);
peripheral.name = svd_peripheral.name.to_internal_ident();
peripheral.description = svd_peripheral.description.clone().unwrap_or_default();
if let Some(header_struct) = &svd_peripheral.header_struct_name {
peripheral.struct_id = header_struct.to_sanitized_struct_ident();
peripheral.module_id = header_struct.to_sanitized_mod_ident();
} else {
if peripheral.struct_id.is_empty() {
peripheral.struct_id = svd_peripheral.name.to_sanitized_struct_ident();
}
if peripheral.module_id.is_empty() {
peripheral.module_id = svd_peripheral.name.to_sanitized_mod_ident();
}
}
let (dim, dim_increment, _) = get_dim_dim_increment(svd_peripheral);
peripheral.base_addr = (0..dim)
.map(|index| svd_peripheral.base_address + (index * dim_increment) as u64)
.collect();
peripheral.interrupts = svd_peripheral
.interrupt
.iter()
.map(|x| Interrupt {
name: x.name.clone(),
value: x.value,
description: x
.description
.as_ref()
.map_or_else(String::new, |x| x.clone()),
})
.collect();
for cluster_register in svd_peripheral.registers.as_ref().unwrap_or(&Vec::new()) {
self.visit_cluster_register(
cluster_register,
PeripheralClusterE::Peripheral(peripheral),
)?;
}
Ok(())
}
fn visit_register(&mut self, reg: &svd::Register, register: &mut Register) -> Result<()> {
register.name = reg.get_name_id_internal();
register.description = reg.description.clone().unwrap_or_default();
register.offset = reg.address_offset;
(register.dim, register.dim_increment, register.dim_index) = get_dim_dim_increment(reg);
if register.struct_id.is_empty() {
register.struct_module_path = Vec::with_capacity(10);
register.struct_module_path.extend_from_slice(
&self.current_mod_ir_path[0..self.current_mod_ir_path.len() - 1],
);
register.struct_id = register.name.to_sanitized_struct_ident();
}
let mut fields = Vec::new();
for field in reg.fields() {
if field.derived_from.is_some() {
return Err(ParseError::Unsupported(
"derived_from is not supported in field".to_string(),
)
.into());
}
let description = field.description.clone().unwrap_or_default();
let offset = field.bit_range.offset;
let mask = (0..field.bit_range.width - 1).fold(0x1u32, |acc, _| (acc << 1) | 0x1);
let name = field.name.to_internal_ident();
let svd_field_access = match field.access {
None => {
error!(
"Inheritance of access is not supported. Bitfield: {} access shall be specified. Bitfield skipped",
name
);
continue;
}
Some(acc) => acc,
};
let access = match svd_field_access {
svd::Access::ReadOnly => RegisterBitfieldAccess::R,
svd::Access::WriteOnly => RegisterBitfieldAccess::W,
svd::Access::ReadWrite => RegisterBitfieldAccess::RW,
svd::Access::WriteOnce => RegisterBitfieldAccess::W,
svd::Access::ReadWriteOnce => RegisterBitfieldAccess::RW,
};
let (dim, dim_increment, dim_index) = get_dim_dim_increment(field);
let enum_types = get_values_types(field)?;
let enum_type_write = enum_types
.iter()
.find(|x| {
x.usage == EnumeratedValueUsage::Write
|| x.usage == EnumeratedValueUsage::ReadWrite
})
.map(|x| x.name.clone());
let enum_type_read = enum_types
.iter()
.find(|x| {
x.usage == EnumeratedValueUsage::Read
|| x.usage == EnumeratedValueUsage::ReadWrite
})
.map(|x| x.name.clone());
fields.push(FieldGetterSetter {
name,
description,
offset,
mask,
enum_types,
enum_type_write,
enum_type_read,
access,
size: BitSize::val_2_bit_size(mask.into()),
dim,
dim_increment,
dim_index,
});
}
match reg.properties.size {
Some(value) => {
register.size = match value {
64 => BitSize::BIT64,
32 => BitSize::BIT32,
16 => BitSize::BIT16,
8 => BitSize::BIT8,
register_size => {
return Err(ParseError::Unsupported(format!(
"Unsupported register size {register_size}"
))
.into());
}
}
}
None => {
if reg.derived_from.is_none() {
return Err(ParseError::InvalidRegister {
register_name: reg.name.clone(),
msg: "register is not derived and it has no specified size".to_string(),
}
.into());
}
}
}
match reg.properties.reset_value {
Some(value) => register.reset_value = value,
None => {
if reg.derived_from.is_none() {
return Err(ParseError::InvalidRegister {
register_name: register.name.clone(),
msg: "register is not derived and it has no specified reset value"
.to_string(),
}
.into());
}
}
}
register.has_enumerated_fields = fields.iter().any(|f| !f.enum_types.is_empty());
match reg.properties.access {
Some(reg_access) => {
register.access = match reg_access {
svd::Access::ReadOnly => RegisterAccess::R,
svd::Access::WriteOnly => RegisterAccess::W,
svd::Access::ReadWrite => RegisterAccess::RW,
svd::Access::WriteOnce => RegisterAccess::W,
svd::Access::ReadWriteOnce => RegisterAccess::RW,
}
}
None => {
if reg.derived_from.is_none() {
warn!(
"Access mode is not defined for register ({}) inferring from bitfield",
®ister.name
);
let is_register_writable = fields.iter().any(|f| {
f.access == RegisterBitfieldAccess::W
|| f.access == RegisterBitfieldAccess::RW
});
let is_register_readable = fields.iter().any(|f| {
f.access == RegisterBitfieldAccess::R
|| f.access == RegisterBitfieldAccess::RW
});
register.access = match (is_register_readable, is_register_writable) {
(true, true) => RegisterAccess::RW,
(true, false) => RegisterAccess::R,
(false, true) => RegisterAccess::W,
(false, false) => {
error!(
"No bitfield in register '{}' specifies an access mode. Not able to infer register access mode",
®ister.name
);
RegisterAccess::R
}
}
}
}
};
register.fields = fields
.into_iter()
.map(|f| (f.name.clone(), Rc::new(RefCell::new(f))))
.collect();
Ok(())
}
fn visit_cluster(&mut self, cluster_svd: &svd::Cluster, cluster: &mut Cluster) -> Result<()> {
cluster.name = cluster_svd.name.to_internal_ident();
cluster.description = cluster_svd.description.clone().unwrap_or_default();
cluster.offset = cluster_svd.address_offset;
(cluster.dim, cluster.dim_increment, cluster.dim_index) =
get_dim_dim_increment(cluster_svd);
if let Some(header_struct_name) = &cluster_svd.header_struct_name {
cluster.struct_module_path = Vec::with_capacity(10);
cluster.struct_module_path.extend_from_slice(
&self.current_mod_ir_path[0..self.current_mod_ir_path.len() - 1],
);
cluster.struct_id = header_struct_name.to_sanitized_struct_ident();
} else if cluster.struct_id.is_empty() {
cluster.struct_module_path = Vec::with_capacity(10);
cluster.struct_module_path.extend_from_slice(
&self.current_mod_ir_path[0..self.current_mod_ir_path.len() - 1],
);
cluster.struct_id = cluster.name.to_sanitized_struct_ident();
}
cluster.module_id = self.current_mod_ir_path.last().unwrap().clone();
for cluster_register in &cluster_svd.children {
self.visit_cluster_register(cluster_register, PeripheralClusterE::Cluster(cluster))?;
}
Ok(())
}
fn visit_cluster_register(
&mut self,
register_cluster: &svd::RegisterCluster,
mut parent_peripheral_cluster: PeripheralClusterE,
) -> Result<()> {
match register_cluster {
svd::RegisterCluster::Register(reg_svd) => {
let derived_register: Option<Register> = if let Some(derived_ref) =
register_cluster.derived_from()
{
let absolute_reference_path = self.get_absolute_svd_path(derived_ref);
if let Some(ref_item) = self.svd_ref_to_ir_item.get(&absolute_reference_path) {
if let DeviceItem::Register(ref_register) = ref_item {
Some(ref_register.borrow().clone())
} else {
return Err(ParseError::InvalidRegister {
register_name: reg_svd.name.clone(),
msg: format!(
"reference {derived_ref} doesn't point to register svd item"
),
}
.into());
}
} else {
return Err(ParseError::InvalidRegister {
register_name: reg_svd.name.clone(),
msg: format!("Missing reference {derived_ref}"),
}
.into());
}
} else {
None
};
self.push_current_item_svd_path(reg_svd)?;
let mut register = derived_register
.as_ref()
.map_or_else(Register::default, |x| x.clone());
self.visit_register(reg_svd, &mut register)?;
let name = register.name.clone();
register.is_derived_from = derived_register
.is_some_and(|derived_register| register.has_same_type(&derived_register));
let register = Rc::new(RefCell::new(register));
parent_peripheral_cluster
.get_mut_registers()
.insert(name, register.clone());
self.pop_current_item_svd_path(DeviceItem::Register(register));
}
svd::RegisterCluster::Cluster(cluster_svd) => {
let derived_cluster: Option<Cluster> = if let Some(derived_ref) =
register_cluster.derived_from()
{
let absolute_reference_path = self.get_absolute_svd_path(derived_ref);
if let Some(ref_item) = self.svd_ref_to_ir_item.get(&absolute_reference_path) {
if let DeviceItem::Cluster(ref_cluster) = ref_item {
Some(ref_cluster.borrow().clone())
} else {
return Err(ParseError::InvalidCluster {
cluster_name: cluster_svd.name.clone(),
msg: format!(
"reference {derived_ref} doesn't point to cluster svd item"
),
}
.into());
}
} else {
return Err(ParseError::InvalidCluster {
cluster_name: cluster_svd.name.clone(),
msg: format!("Missing reference {derived_ref}"),
}
.into());
}
} else {
None
};
self.push_current_item_svd_path(cluster_svd)?;
let mut cluster = derived_cluster
.as_ref()
.map_or_else(Cluster::default, |x| x.clone());
self.visit_cluster(cluster_svd, &mut cluster)?;
cluster.is_derived_from = derived_cluster
.is_some_and(|derived_cluster| cluster.has_same_type(&derived_cluster));
let name = cluster.name.clone();
let cluster = Rc::new(RefCell::new(cluster));
parent_peripheral_cluster
.get_mut_clusters()
.insert(name, cluster.clone());
self.pop_current_item_svd_path(DeviceItem::Cluster(cluster));
}
}
Ok(())
}
fn pop_current_item_svd_path(&mut self, ir_item: DeviceItem) {
self.svd_ref_to_ir_item
.insert(self.current_item_svd_path.join("."), ir_item);
assert!(self.current_item_svd_path.pop().is_some());
assert!(self.current_mod_ir_path.pop().is_some());
}
fn push_current_item_svd_path(
&mut self,
svd_item: &(impl ExpandedName + HeaderStructName),
) -> Result<()> {
self.current_item_svd_path
.push(svd_item.get_expanded_name()?);
match svd_item.header_struct_name() {
None => self
.current_mod_ir_path
.push(svd_item.name().to_sanitized_mod_ident()),
Some(header_struct_name) => self
.current_mod_ir_path
.push(header_struct_name.to_sanitized_mod_ident()),
}
Ok(())
}
fn get_absolute_svd_path(&self, local_svd_name: &str) -> String {
if local_svd_name.contains('.') {
local_svd_name.to_string()
} else {
let mut result: Vec<&str> = Vec::with_capacity(self.current_item_svd_path.len() + 1);
self.current_item_svd_path
.iter()
.for_each(|s| result.push(s));
result.push(local_svd_name);
result.join(".")
}
}
}
fn get_values_types(field: &svd::Field) -> Result<Vec<EnumeratedValueType>> {
if field.enumerated_values.is_empty() {
return Ok(vec![]);
};
let mut result = Vec::new();
for enum_values in &field.enumerated_values {
if enum_values.derived_from.is_some() {
return Err(ParseError::Unsupported(format!(
"Derived from is not supported in enumerated values. Bitfield: {} shall not have derived_from tag",
field.name
))
.into());
}
let mut max_value = 0u64; let mut values = Vec::new();
for val_entry in &enum_values.values {
if val_entry.name.is_empty() {
return Err(ParseError::InvalidField {
field_name: field.name.clone(),
msg: "Value of enumeration shall have a name".to_string(),
}
.into());
}
let description = val_entry.description.clone().unwrap_or_default();
let val_name: String = if let Some(ref enumerated_values_name) = enum_values.name {
format!("{}_{}", enumerated_values_name, val_entry.name)
} else {
val_entry.name.clone()
};
let value = if let Some(value) = val_entry.value {
value
} else {
return Err(ParseError::Unsupported("Default value is unsupported, all value in enumeration shall have a value defined".to_string()).into());
};
values.push(EnumeratedSingleValue {
name: val_name,
value,
description,
});
max_value = max_value.max(value);
}
let usage = match enum_values.usage {
None => EnumeratedValueUsage::ReadWrite,
Some(svd::Usage::Read) => EnumeratedValueUsage::Read,
Some(svd::Usage::Write) => EnumeratedValueUsage::Write,
Some(svd::Usage::ReadWrite) => EnumeratedValueUsage::ReadWrite,
};
let name = match usage {
EnumeratedValueUsage::Read => format!("{}_Read", field.name.to_internal_ident()),
EnumeratedValueUsage::Write => format!("{}_Write", field.name.to_internal_ident()),
EnumeratedValueUsage::ReadWrite => field.name.to_internal_ident(),
};
result.push(EnumeratedValueType {
name,
usage,
size: BitSize::val_2_bit_size(max_value),
values,
});
}
if result.len() > 2 {
return Err(ParseError::InvalidField {
field_name: field.name.clone(),
msg: "Only up to two enumeratedValue are supported".to_string(),
}
.into());
}
if result
.iter()
.any(|f| f.usage == EnumeratedValueUsage::ReadWrite)
&& result.len() == 2
{
return Err(ParseError::InvalidField {
field_name: field.name.clone(),
msg: "If two enumeratedValue are defined, one shall be read and the other write."
.to_string(),
}
.into());
}
if result.len() == 2 && result[0].usage == result[1].usage {
return Err(ParseError::InvalidField {
field_name: field.name.clone(),
msg: "If two enumeratedValue are defined, one shall be read and the other write."
.to_string(),
}
.into());
}
Ok(result)
}
pub(super) fn parse_xml(
xml: &mut str,
svd_validation_level: SvdValidationLevel,
) -> Result<svd::Device> {
let mut parser_config = svd_parser::Config::default();
parser_config.expand_properties = true;
parser_config.ignore_enums = false;
parser_config.validate_level = match svd_validation_level {
SvdValidationLevel::Disabled => svd::ValidateLevel::Disabled,
SvdValidationLevel::Weak => svd::ValidateLevel::Weak,
SvdValidationLevel::Strict => svd::ValidateLevel::Strict,
};
let result = svd_parser::parse_with_config(xml, &parser_config);
if let Err(err) = &result {
if let Some(error_at) = err.downcast_ref::<svd_parser::SVDErrorAt>() {
error!("Error while parsing {}", error_at);
}
}
result
}
fn get_interrupt_table(
peripheral_types: &LinkedHashMap<String, Rc<RefCell<PeripheralMod>>>,
) -> Vec<Option<Interrupt>> {
match peripheral_types
.values()
.flat_map(|x| {
x.borrow()
.interrupts
.iter()
.map(|x| x.value)
.collect::<Vec<_>>()
})
.max()
{
None => Vec::new(),
Some(max_int_index) => {
let mut result = vec![None; max_int_index as usize + 1];
for interrupt in peripheral_types
.values()
.flat_map(|x| x.borrow().interrupts.clone())
{
let interrupt_id = interrupt.value as usize;
if result[interrupt_id].is_some() {
error!(
"Duplicated interrupt definition at index {}",
interrupt.value
);
}
result[interrupt_id] = Some(interrupt);
}
result
}
}
}
pub(super) fn svd_device2ir(
svd_device: &svd::Device,
custom_license_text: &Option<String>,
) -> Result<IR> {
let entity_db = get_entity_db(svd_device);
let license_text = custom_license_text.as_ref().map_or_else(
|| {
svd_device.license_text.as_ref().map_or_else(
|| {
error!("No license defined in SVD. Use --license-file option in command line");
String::new()
},
|license_txt| license_txt.replace("\\n", "\n"),
)
},
|file_license| file_license.clone(),
);
let mut visitor = Visitor::default();
visitor.visit_device(svd_device)?;
let device = visitor.device;
let interrupt_table = get_interrupt_table(&device.peripheral_mod);
Ok(IR {
device,
register_addresses: entity_db.register_addresses,
license_text,
version: svd_device.version.clone(),
interrupt_table,
nvic_prio_bits: svd_device.cpu.as_ref().map(|x| x.nvic_priority_bits),
vendor_systick_config: svd_device.cpu.as_ref().map(|x| x.has_vendor_systick),
fpu_present: svd_device.cpu.as_ref().map(|x| x.fpu_present),
mpu_present: svd_device.cpu.as_ref().map(|x| x.mpu_present),
})
}