Skip to content
Open
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
246 changes: 116 additions & 130 deletions src/svd2ir.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use anyhow::{bail, Context};
use anyhow::Context;
use clap::ValueEnum;
use log::*;
use std::collections::{BTreeMap, BTreeSet};
use svd_parser::svd::{self, PeripheralInfo};
use std::collections::BTreeMap;
use svd_parser::svd::{self, EnumeratedValue, FieldInfo, PeripheralInfo};

use crate::{ir::*, transform};

Expand Down Expand Up @@ -53,139 +53,55 @@ pub fn convert_peripheral(ir: &mut IR, p: &svd::Peripheral) -> anyhow::Result<()

for block in &blocks {
for r in &block.registers {
if let svd::RegisterCluster::Register(r) = r {
if r.derived_from.is_some() {
let svd::RegisterCluster::Register(r) = r else {
continue;
};

if r.derived_from.is_some() {
continue;
}

let Some(fields) = &r.fields else {
continue;
};

let mut fieldset_name = block.name.clone();
let mut field_name_counts: BTreeMap<String, usize> = BTreeMap::new();
fieldset_name.push(replace_suffix(&r.name, ""));

let mut out_fields = Vec::with_capacity(fields.len());

for f in fields {
if f.derived_from.is_some() {
continue;
}

if let Some(fields) = &r.fields {
let mut fieldset_name = block.name.clone();
let mut field_name_counts: BTreeMap<String, usize> = BTreeMap::new();
fieldset_name.push(replace_suffix(&r.name, ""));

let mut out_fields = Vec::with_capacity(fields.len());

for f in fields {
if f.derived_from.is_some() {
continue;
}

let mut enum_read = None;
let mut enum_write = None;
let mut enum_readwrite = None;

let mut field_name = replace_suffix(&f.name, "");

let field_name_count =
field_name_counts.entry(field_name.clone()).or_insert(0);
*field_name_count += 1;
if *field_name_count > 1 {
field_name = format!("{}{}", field_name, field_name_count);
}

for e in &f.enumerated_values {
let e = if let Some(derived_from) = &e.derived_from {
let Some(e) = enum_from_name.get(derived_from.as_str()) else {
warn!(
"unknown enum to derive from ({} -> {})",
field_name, derived_from
);
continue;
};
e
} else {
e
};

let usage = e.usage.unwrap_or(svd::Usage::ReadWrite);
let target = match usage {
svd::Usage::Read => &mut enum_read,
svd::Usage::Write => &mut enum_write,
svd::Usage::ReadWrite => &mut enum_readwrite,
};

if target.is_some() {
warn!("ignoring enum with dup usage {:?}", usage);
continue;
}

*target = Some(e)
}

enum EnumSet<'a> {
Single(&'a svd::EnumeratedValues),
ReadWrite(&'a svd::EnumeratedValues, &'a svd::EnumeratedValues),
}

let set = match (enum_read, enum_write, enum_readwrite) {
(None, None, None) => None,
(Some(e), None, None) => Some(EnumSet::Single(e)),
(None, Some(e), None) => Some(EnumSet::Single(e)),
(None, None, Some(e)) => Some(EnumSet::Single(e)),
(Some(r), Some(w), None) => Some(EnumSet::ReadWrite(r, w)),
(Some(r), None, Some(w)) => Some(EnumSet::ReadWrite(r, w)),
(None, Some(w), Some(r)) => Some(EnumSet::ReadWrite(r, w)),
(Some(_), Some(_), Some(_)) => {
bail!("cannot have enumeratedvalues for read, write and readwrite!")
}
};

let enumm = if let Some(set) = set {
let variants = match set {
EnumSet::Single(e) => e.values.clone(),
EnumSet::ReadWrite(r, w) => {
let r_values = r.values.iter().map(|v| v.value.unwrap());
let w_values = w.values.iter().map(|v| v.value.unwrap());
let values: BTreeSet<_> = r_values.chain(w_values).collect();
let mut values: Vec<_> = values.iter().collect();
values.sort();

let r_values: BTreeMap<_, _> =
r.values.iter().map(|v| (v.value.unwrap(), v)).collect();
let w_values: BTreeMap<_, _> =
w.values.iter().map(|v| (v.value.unwrap(), v)).collect();

values
.into_iter()
.map(|&v| match (r_values.get(&v), w_values.get(&v)) {
(None, None) => unreachable!(),
(Some(&r), None) => r.clone(),
(None, Some(&w)) => w.clone(),
(Some(&r), Some(&w)) => {
let mut m = r.clone();
if r.name != w.name {
m.name = format!("R_{}_W_{}", r.name, w.name);
}
m
}
})
.collect()
}
};

let mut name = fieldset_name.clone();
name.push(field_name);
enums.push(ProtoEnum {
name: name.clone(),
bit_size: f.bit_range.width,
variants,
});
Some(name)
} else {
None
};

out_fields.push((f.clone(), enumm));
}
let mut field_name = replace_suffix(&f.name, "");

fieldsets.push(ProtoFieldset {
name: fieldset_name.clone(),
description: r.description.clone(),
bit_size: r.properties.size.unwrap_or(32),
fields: out_fields,
});
let field_name_count = field_name_counts.entry(field_name.clone()).or_insert(0);
*field_name_count += 1;
if *field_name_count > 1 {
field_name = format!("{}{}", field_name, field_name_count);
}

let enum_name = fieldset_name.iter().chain(std::iter::once(&field_name));
let enumm = if let Some(enumm) = extract_enum(enum_name, &enum_from_name, &f) {
let name = enumm.name.clone();
enums.push(enumm);
Some(name)
} else {
None
};

out_fields.push((f.clone(), enumm));
}

fieldsets.push(ProtoFieldset {
name: fieldset_name.clone(),
description: r.description.clone(),
bit_size: r.properties.size.unwrap_or(32),
fields: out_fields,
});
}
}

Expand Down Expand Up @@ -445,6 +361,76 @@ pub fn convert_svd(svd: &svd::Device, namespace: NamespaceMode) -> anyhow::Resul
Ok(ir)
}

fn extract_enum<'a>(
enum_name: impl Iterator<Item = &'a String>,
enum_from_name: &BTreeMap<&str, &svd::EnumeratedValues>,
field: &FieldInfo,
) -> Option<ProtoEnum> {
let mut r = Vec::new();
let mut w = Vec::new();

for e in &field.enumerated_values {
let e = if let Some(derived_from) = &e.derived_from {
let Some(e) = enum_from_name.get(derived_from.as_str()) else {
warn!(
"unknown enum to derive from ({} -> {derived_from})",
field.name
);
continue;
};
e
} else {
e
};

let usage = e.usage.unwrap_or(svd::Usage::ReadWrite);
match usage {
svd::Usage::Read => r.extend_from_slice(&e.values),
svd::Usage::Write => w.extend_from_slice(&e.values),
svd::Usage::ReadWrite => {
r.extend_from_slice(&e.values);
w.extend_from_slice(&e.values);
}
};
}

let variants = if !r.is_empty() || !w.is_empty() {
fn value_to_kv(v: &EnumeratedValue) -> Option<(u64, &EnumeratedValue)> {
v.value.map(|value| (value, v))
}

let r: BTreeMap<_, _> = r.iter().filter_map(value_to_kv).collect();
let w: BTreeMap<_, _> = w.iter().filter_map(value_to_kv).collect();
let mut enum_values: Vec<_> = r.keys().chain(w.keys()).collect();
enum_values.sort();
enum_values.dedup();

enum_values
.into_iter()
.map(|v| match (r.get(&v), w.get(&v)) {
(None, None) => unreachable!(),
(Some(&r), None) => r.clone(),
(None, Some(&w)) => w.clone(),
(Some(&r), Some(&w)) => {
let mut m = r.clone();
if r.name != w.name {
m.name = format!("R_{}_W_{}", r.name, w.name);
}
m
}
})
.collect()
} else {
return None;
};

Some(ProtoEnum {
name: enum_name.cloned().collect(),
bit_size: field.bit_range.width,
variants,
})
}

/// Create a map of all enums by name.
/// Ignores potential duplicates of names.
fn enum_map(blocks: &[ProtoBlock]) -> BTreeMap<&'_ str, &'_ svd::EnumeratedValues> {
Expand Down