Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/atdf/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,26 @@ impl UnsupportedError {
UnsupportedError(what.into(), el.debug())
}
}

pub struct RecursiveRegisterGroupError {
group_name: String,
}

impl crate::DisplayError for RecursiveRegisterGroupError {
fn format(&self, w: &mut dyn std::io::Write) -> std::io::Result<()> {
write!(
w,
"{}: Recursive register group reference detected leading to {}",
"Error".red().bold(),
self.group_name.dimmed()
)
}
}

impl RecursiveRegisterGroupError {
pub fn new<S: Into<String>>(group_name: S) -> Self {
let group_name = group_name.into();

RecursiveRegisterGroupError { group_name }
}
}
1 change: 1 addition & 0 deletions src/atdf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod interrupt;
pub mod patch;
pub mod peripheral;
pub mod register;
pub mod register_group;
pub mod values;

pub fn parse<R: std::io::Read>(
Expand Down
17 changes: 11 additions & 6 deletions src/atdf/patch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub fn signals_to_port_fields(chip: &mut chip::Chip, tree: &xmltree::Element) ->
.map(|f| (f.name.clone(), f))
.collect();

for reg in port.registers.values_mut() {
for reg in port.register_group.registers.values_mut() {
if reg.name.ends_with(name) || NEW_PORT_REGS.iter().any(|r| r == &reg.name) {
reg.fields = fields.clone();
// Ensure that direct access to the register is unsafe
Expand All @@ -57,8 +57,8 @@ pub fn signals_to_port_fields(chip: &mut chip::Chip, tree: &xmltree::Element) ->

pub fn remove_unsafe_cpu_regs(chip: &mut chip::Chip, _el: &xmltree::Element) -> crate::Result<()> {
if let Some(cpu) = chip.peripherals.get_mut("CPU") {
cpu.registers.remove("SREG");
cpu.registers.remove("SP");
cpu.register_group.registers.remove("SREG");
cpu.register_group.registers.remove("SP");
}

Ok(())
Expand Down Expand Up @@ -88,16 +88,21 @@ pub fn remove_register_common_prefix(chip: &mut chip::Chip) -> crate::Result<()>
for peripheral in chip.peripherals.values_mut() {
// There's not enough quorum in less than two elements to find a
// prefix.
if peripheral.registers.len() < 2 {
if peripheral.register_group.registers.len() < 2 {
continue;
}

let register_names: Vec<_> = peripheral.registers.keys().map(String::as_str).collect();
let register_names: Vec<_> = peripheral
.register_group
.registers
.keys()
.map(String::as_str)
.collect();
let common_prefix = longest_common_prefix(&register_names).to_string();

let is_valid_prefix = common_prefix.ends_with("_") && common_prefix.chars().count() >= 2;
if is_valid_prefix {
for register in peripheral.registers.values_mut() {
for register in peripheral.register_group.registers.values_mut() {
if let Some(s) = register.name.strip_prefix(&common_prefix) {
log::debug!(
"[remove_register_common_prefix] Renaming {} to {}",
Expand Down
90 changes: 59 additions & 31 deletions src/atdf/peripheral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,41 @@ use crate::chip;
use crate::util;
use crate::ElementExt;

fn update_register_group(register_group: &mut chip::RegisterGroup, delta: usize) {
for register in register_group.registers.values_mut() {
register.offset -= delta;
}
for subgroup in register_group.subgroups.iter_mut() {
update_register_group(subgroup, delta);
}
}

/// Adjusts the peripheral's base address to the first register's absolute address.
///
/// Finds the minimum address among all registers and updates register offsets
/// accordingly. This normalizes the peripheral's address space representation.
fn base_line_address(peripheral: &mut chip::Peripheral) {
let register_addresses = peripheral
.register_group
.registers
.values()
.map(|r| peripheral.address + r.offset)
.collect::<Vec<_>>();

if register_addresses.is_empty() {
return;
}
let min_address = match register_addresses.iter().min() {
Some(addr) => *addr,
None => peripheral.address,
};
if min_address > peripheral.address {
let delta = min_address - peripheral.address;
peripheral.address = min_address;
update_register_group(&mut peripheral.register_group, delta);
}
}

pub fn parse_list(
el: &xmltree::Element,
modules: &xmltree::Element,
Expand All @@ -13,53 +48,46 @@ pub fn parse_list(
let module_name = module.attr("name")?;

for instance in module.iter_children_with_name("instance", Some("module")) {
let mut registers = vec![];

// Find corresponding module
let module = modules.first_child_by_attr(Some("module"), "name", module_name)?;

// The register definitions can reference value-groups, that are stored on the same
// level as the register-groups, so we parse them in here first.
let value_groups = atdf::values::parse_value_groups(module)?;

for register_group in instance
.children
.iter()
.filter_map(|node| node.as_element().filter(|e| e.name == "register-group"))
{
let name = register_group.attr("name-in-module")?;
let offset = util::parse_int(register_group.attr("offset")?)?;

let group = module.first_child_by_attr(Some("register-group"), "name", name)?;
// An instance should always have one register-group
let instance_register_group = match instance.first_child("register-group") {
Ok(rg) => rg,
Err(_) => continue,
};
let name_in_module = instance_register_group.attr("name-in-module")?;
let address = util::parse_int(instance_register_group.attr("offset")?)?;

for register in group.iter_children_with_name("register", Some("register-group")) {
registers.push(atdf::register::parse(register, offset, &value_groups)?);
}
}
let mut register_groups = atdf::register_group::parse_list(module, 0, &value_groups)?;
let mut main_register_group = register_groups.get(name_in_module).cloned().unwrap();
atdf::register_group::build_register_group_hierarchy(
&mut main_register_group,
&mut register_groups,
address,
0,
)?;

let registers = registers
.into_iter()
.map(|r| {
(
match r.mode {
Some(ref mode) => format!("{mode}_{}", r.name),
_ => r.name.clone(),
},
r,
)
})
.collect();

peripherals.push(chip::Peripheral {
let mut peripheral = chip::Peripheral {
name: instance.attr("name")?.clone(),
name_in_module: name_in_module.clone(),
description: instance
.attr("caption")
.or(module.attr("caption"))
.ok()
.cloned()
.and_then(|d| if !d.is_empty() { Some(d) } else { None }),
registers,
})
address,
register_group: main_register_group,
};

base_line_address(&mut peripheral);

peripherals.push(peripheral)
}
}

Expand Down
26 changes: 24 additions & 2 deletions src/atdf/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn field_map_from_bitfield_children(

pub fn parse(
el: &xmltree::Element,
offset: usize,
address: usize,
values: &atdf::values::ValueGroups,
) -> crate::Result<chip::Register> {
let name = el.attr("name")?.clone();
Expand Down Expand Up @@ -69,11 +69,14 @@ pub fn parse(
crate::Result::Ok(())
})?;

let offset = util::parse_int(el.attr("offset")?)?;

Ok(chip::Register {
name,
description,
mode,
address: util::parse_int(el.attr("offset")?)? + offset,
address: address + offset,
offset,
size: util::parse_int(el.attr("size")?)?,
access,
restriction: if fields.is_empty() {
Expand All @@ -84,3 +87,22 @@ pub fn parse(
fields,
})
}

pub fn parse_list(
register_group_el: &xmltree::Element,
offset: usize,
value_groups: &atdf::values::ValueGroups,
) -> crate::Result<BTreeMap<String, chip::Register>> {
register_group_el
.iter_children_with_name("register", Some("register-group"))
.map(|reg| {
atdf::register::parse(reg, offset, value_groups).map(|r| {
let key = match r.mode {
Some(ref mode) => format!("{mode}_{}", r.name),
None => r.name.clone(),
};
(key, r)
})
})
.collect()
}
126 changes: 126 additions & 0 deletions src/atdf/register_group.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use crate::atdf;
use crate::chip;
use crate::util;
use crate::ElementExt;
use std::collections::BTreeMap;

pub fn parse_list(
module_el: &xmltree::Element,
address: usize,
value_groups: &atdf::values::ValueGroups,
) -> crate::Result<BTreeMap<String, chip::RegisterGroup>> {
let mut register_group_headers = BTreeMap::new();
for register_group_el in module_el
.children
.iter()
.filter_map(|node| node.as_element().filter(|e| e.name == "register-group"))
{
let name = register_group_el.attr("name")?.clone();
let description = register_group_el
.attributes
.get("caption")
.filter(|d| !d.is_empty())
.cloned();
let class = register_group_el
.attributes
.get("class")
.filter(|d| !d.is_empty())
.cloned();

let references = parse_references(register_group_el)?;
let registers = atdf::register::parse_list(register_group_el, address, value_groups)?;

register_group_headers.insert(
name.clone(),
chip::RegisterGroup {
name,
description,
offset: 0,
is_union: class == Some("union".to_string()),
registers,
references,
// subgroups will be filled in when all register-groups are parsed
subgroups: vec![],
},
);
}
Ok(register_group_headers)
}

pub fn build_register_group_hierarchy(
register_group: &mut chip::RegisterGroup,
register_groups: &mut BTreeMap<String, chip::RegisterGroup>,
current_address: usize,
level: usize,
) -> crate::Result<()> {
// Infinite recursion guard
if level > 20 {
return Err(
atdf::error::RecursiveRegisterGroupError::new(register_group.name.clone()).into(),
);
}
for reference in register_group.references.iter() {
if let Some(name_in_module) = &reference.name_in_module {
if let Some(subgroup) = register_groups.get(name_in_module) {
let mut subgroup = subgroup.clone();
// The reference offset & name is used to override the subgroup
let offset = reference.offset.unwrap_or(0);
let new_address = current_address + offset;

if offset > 0 {
subgroup.offset = offset;
// Adjust each register's address by the calculated adjustment
subgroup.registers.iter_mut().for_each(|(_, register)| {
register.address = new_address + register.offset;
});
}
subgroup.name = reference.name.clone();

build_register_group_hierarchy(
&mut subgroup,
register_groups,
new_address,
level + 1,
)?;
register_group.subgroups.push(subgroup);
}
}
}

Ok(())
}

pub fn parse_references(
register_group_el: &xmltree::Element,
) -> crate::Result<Vec<chip::RegisterGroupReference>> {
let mut register_group_references = vec![];

for register_group_ref_el in register_group_el
.children
.iter()
.filter_map(|node| node.as_element().filter(|e| e.name == "register-group"))
{
let name = register_group_ref_el.attr("name")?.clone();

let name_in_module = register_group_ref_el
.attributes
.get("name-in-module")
.filter(|d| !d.is_empty())
.cloned();

let offset = register_group_ref_el
.attributes
.get("offset")
.filter(|d| !d.is_empty())
.map(|d| util::parse_int(d))
.transpose()?;

register_group_references.push(chip::RegisterGroupReference {
name,
name_in_module,
offset,
});
}

Ok(register_group_references)
}
Loading