From 3b2b9b91e49f9a0c5f40baebf3f1e200ad41870c Mon Sep 17 00:00:00 2001 From: Alex Zepeda Date: Sun, 4 Jan 2026 18:42:28 -0800 Subject: [PATCH 1/5] First pass at non-numeric array indices for registers - This gets hit when trying to parse the Renesas RA4M1 SVD - This doesn't respect the position of the index in the name - This doesn't handle the non-register cases - This doesn't handle numeric indices where the start index is > 0 --- src/generate/block.rs | 34 +++++++++++++++++++++++++--------- src/generate/fieldset.rs | 4 ++-- src/generate/mod.rs | 7 ++++--- src/ir.rs | 1 + src/svd2ir.rs | 38 ++++++++++++++++++++++++++++++++++++++ src/transform/common.rs | 10 +++++++++- 6 files changed, 79 insertions(+), 15 deletions(-) diff --git a/src/generate/block.rs b/src/generate/block.rs index 3a7b5665..3e0f2efb 100644 --- a/src/generate/block.rs +++ b/src/generate/block.rs @@ -43,15 +43,31 @@ pub fn render(opts: &super::Options, ir: &IR, b: &Block, path: &str) -> Result); if let Some(array) = &i.array { - let (len, offs_expr) = super::process_array(array); - items.extend(quote!( - #doc - #[inline(always)] - pub const fn #name(self, n: usize) -> #ty { - assert!(n < #len); - unsafe { #common_path::Reg::from_ptr(self.ptr.wrapping_add(#offset + #offs_expr) as _) } + let (len, offs_expr, indexes) = super::process_array(array); + + if let Some(indexes) = indexes { + for index in indexes.iter() { + // TODO don't erase %s so we can position the index in the name as our overlords intended? + let name = Ident::new(&format!("{}_{}", i.name, index), span); + items.extend(quote!( + #doc + #[inline(always)] + pub const fn #name(self, n: usize) -> #ty { + assert!(n < #len); + unsafe { #common_path::Reg::from_ptr(self.ptr.wrapping_add(#offset + #offs_expr) as _) } + } + )); } - )); + } else { + items.extend(quote!( + #doc + #[inline(always)] + pub const fn #name(self, n: usize) -> #ty { + assert!(n < #len); + unsafe { #common_path::Reg::from_ptr(self.ptr.wrapping_add(#offset + #offs_expr) as _) } + } + )); + } } else { items.extend(quote!( #doc @@ -67,7 +83,7 @@ pub fn render(opts: &super::Options, ir: &IR, b: &Block, path: &str) -> Result Resu BitOffset::Regular(off_in_reg) => { let off_in_reg = off_in_reg as usize; if let Some(array) = &f.array { - let (len, offs_expr) = super::process_array(array); + let (len, offs_expr, _indexes) = super::process_array(array); items.extend(quote!( #doc #[must_use] @@ -144,7 +144,7 @@ pub fn render(opts: &super::Options, ir: &IR, fs: &FieldSet, path: &str) -> Resu } if let Some(array) = &f.array { - let (len, offs_expr) = super::process_array(array); + let (len, offs_expr, _indexes) = super::process_array(array); items.extend(quote!( #doc #[must_use] diff --git a/src/generate/mod.rs b/src/generate/mod.rs index f6528037..c6123a0e 100644 --- a/src/generate/mod.rs +++ b/src/generate/mod.rs @@ -214,13 +214,14 @@ fn split_path(s: &str) -> (Vec<&str>, &str) { (v, n) } -fn process_array(array: &Array) -> (usize, TokenStream) { +fn process_array(array: &Array) -> (usize, TokenStream, Option>) { match array { Array::Regular(array) => { let len = array.len as usize; let stride = array.stride as usize; let offs_expr = quote!(n*#stride); - (len, offs_expr) + let indexes = array.indexes.clone(); + (len, offs_expr, indexes) } Array::Cursed(array) => { let len = array.offsets.len(); @@ -230,7 +231,7 @@ fn process_array(array: &Array) -> (usize, TokenStream) { .map(|&x| x as usize) .collect::>(); let offs_expr = quote!(([#(#offsets),*][n] as usize)); - (len, offs_expr) + (len, offs_expr, None) } } } diff --git a/src/ir.rs b/src/ir.rs index f6963289..fdcc4c88 100644 --- a/src/ir.rs +++ b/src/ir.rs @@ -113,6 +113,7 @@ impl Array { pub struct RegularArray { pub len: u32, pub stride: u32, + pub indexes: Option>, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] diff --git a/src/svd2ir.rs b/src/svd2ir.rs index 8d65855a..3be98ecf 100644 --- a/src/svd2ir.rs +++ b/src/svd2ir.rs @@ -42,12 +42,26 @@ pub fn convert_peripheral(ir: &mut IR, p: &svd::Peripheral) -> anyhow::Result<() let mut enums: Vec = Vec::new(); for block in &blocks { + let mut register_name_counts: BTreeMap = BTreeMap::new(); + for r in &block.registers { if let svd::RegisterCluster::Register(r) = r { if r.derived_from.is_some() { continue; } + let register_name_count = register_name_counts.entry(r.name.clone()).or_insert(0); + let mut register_name = r.name.clone(); + *register_name_count += 1; + if *register_name_count > 1 { + if r.is_array() { + register_name = format!("{}{}", register_name, register_name_count); + } else { + // This will blow up in unique_names + error!("Dupe Register: {register_name}"); + } + } + if let Some(fields) = &r.fields { let mut fieldset_name = block.name.clone(); fieldset_name.push(util::replace_suffix(&r.name, "")); @@ -193,9 +207,31 @@ pub fn convert_peripheral(ir: &mut IR, p: &svd::Peripheral) -> anyhow::Result<() }; let array = if let svd::Register::Array(_, dim) = r { + let indexes = match &dim.dim_index { + Some(indexes) => { + // If the indexes are a range of integers treat the register as an array + // TODO: Check the stop, start and finesse the accessors thusly + let indexes = indexes + .iter() + .filter_map(|i| match i.parse::() { + Ok(_) => None, + Err(_) => Some(i.to_lowercase()), + }) + .collect::>(); + + if !indexes.is_empty() { + Some(indexes.clone()) + } else { + None + } + } + None => None, + }; + Some(Array::Regular(RegularArray { len: dim.dim, stride: dim.dim_increment, + indexes, })) } else { None @@ -236,6 +272,7 @@ pub fn convert_peripheral(ir: &mut IR, p: &svd::Peripheral) -> anyhow::Result<() Some(Array::Regular(RegularArray { len: dim.dim, stride: dim.dim_increment, + indexes: dim.dim_index.clone(), })) } else { None @@ -278,6 +315,7 @@ pub fn convert_peripheral(ir: &mut IR, p: &svd::Peripheral) -> anyhow::Result<() Some(Array::Regular(RegularArray { len: dim.dim, stride: dim.dim_increment, + indexes: dim.dim_index.clone(), })) } else { None diff --git a/src/transform/common.rs b/src/transform/common.rs index b68c0c73..3c9446dd 100644 --- a/src/transform/common.rs +++ b/src/transform/common.rs @@ -311,6 +311,7 @@ pub(crate) fn calc_array(mut offsets: Vec, mode: ArrayMode) -> anyhow::Resu Array::Regular(RegularArray { len: offsets.len() as _, stride, + indexes: None, }), )); } @@ -328,7 +329,14 @@ pub(crate) fn calc_array(mut offsets: Vec, mode: ArrayMode) -> anyhow::Resu } ArrayMode::Holey => { let len = (offsets.last().unwrap() - offsets.first().unwrap()) / stride + 1; - Ok((start_offset, Array::Regular(RegularArray { len, stride }))) + Ok(( + start_offset, + Array::Regular(RegularArray { + len, + stride, + indexes: None, + }), + )) } } } From 81c100356a273e9d6fae6c7c2348409e4b15977d Mon Sep 17 00:00:00 2001 From: Alex Zepeda Date: Sun, 4 Jan 2026 20:23:36 -0800 Subject: [PATCH 2/5] Actually generate the intended code. --- src/generate/block.rs | 8 ++++---- src/generate/mod.rs | 10 ++++++++-- src/svd2ir.rs | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/generate/block.rs b/src/generate/block.rs index 3e0f2efb..020a9f4a 100644 --- a/src/generate/block.rs +++ b/src/generate/block.rs @@ -46,15 +46,15 @@ pub fn render(opts: &super::Options, ir: &IR, b: &Block, path: &str) -> Result #ty { - assert!(n < #len); - unsafe { #common_path::Reg::from_ptr(self.ptr.wrapping_add(#offset + #offs_expr) as _) } + pub const fn #name(self) -> #ty { + unsafe { #common_path::Reg::from_ptr(self.ptr.wrapping_add(#offset + #offset) as _) } } )); } diff --git a/src/generate/mod.rs b/src/generate/mod.rs index c6123a0e..41621d3f 100644 --- a/src/generate/mod.rs +++ b/src/generate/mod.rs @@ -214,13 +214,19 @@ fn split_path(s: &str) -> (Vec<&str>, &str) { (v, n) } -fn process_array(array: &Array) -> (usize, TokenStream, Option>) { +fn process_array(array: &Array) -> (usize, TokenStream, Option>) { match array { Array::Regular(array) => { let len = array.len as usize; let stride = array.stride as usize; let offs_expr = quote!(n*#stride); - let indexes = array.indexes.clone(); + let indexes = array.indexes.clone().map(|indexes| { + indexes + .into_iter() + .enumerate() + .map(|(i, index_name)| (quote!(#i * #stride), index_name)) + .collect() + }); (len, offs_expr, indexes) } Array::Cursed(array) => { diff --git a/src/svd2ir.rs b/src/svd2ir.rs index 3be98ecf..71c65f6e 100644 --- a/src/svd2ir.rs +++ b/src/svd2ir.rs @@ -64,7 +64,7 @@ pub fn convert_peripheral(ir: &mut IR, p: &svd::Peripheral) -> anyhow::Result<() if let Some(fields) = &r.fields { let mut fieldset_name = block.name.clone(); - fieldset_name.push(util::replace_suffix(&r.name, "")); + fieldset_name.push(util::replace_suffix(®ister_name, "")); fieldsets.push(ProtoFieldset { name: fieldset_name.clone(), description: r.description.clone(), From 172d6e0de9243315c9c14e8e938e6fdce660258d Mon Sep 17 00:00:00 2001 From: Alex Zepeda Date: Sun, 4 Jan 2026 21:41:25 -0800 Subject: [PATCH 3/5] Use a unique identifier for the array offset. --- src/generate/block.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generate/block.rs b/src/generate/block.rs index 020a9f4a..d50a8296 100644 --- a/src/generate/block.rs +++ b/src/generate/block.rs @@ -46,7 +46,7 @@ pub fn render(opts: &super::Options, ir: &IR, b: &Block, path: &str) -> Result Result #ty { - unsafe { #common_path::Reg::from_ptr(self.ptr.wrapping_add(#offset + #offset) as _) } + unsafe { #common_path::Reg::from_ptr(self.ptr.wrapping_add(#offset + #array_offset) as _) } } )); } From 68679dd9c714f454367682edd1da0670c534c3e3 Mon Sep 17 00:00:00 2001 From: Alex Zepeda Date: Sun, 4 Jan 2026 21:42:04 -0800 Subject: [PATCH 4/5] Use named index instead of %s for the doc string --- src/generate/block.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/generate/block.rs b/src/generate/block.rs index d50a8296..b25e49d5 100644 --- a/src/generate/block.rs +++ b/src/generate/block.rs @@ -49,6 +49,8 @@ pub fn render(opts: &super::Options, ir: &IR, b: &Block, path: &str) -> Result Date: Sun, 4 Jan 2026 23:24:55 -0800 Subject: [PATCH 5/5] Add numeric suffix to duplicate fieldset names. This is useful for the case where multiple fieldsets are arrays with explicit indices. This is allowed at the IR level with the assumption that the indices will be named and thus get unique accessors. If they're numbered we'll end up here. E.g. Renesas RA4M1 USBFS::PIPE%sCTR. --- src/transform/sanitize.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/transform/sanitize.rs b/src/transform/sanitize.rs index c1d45245..064f5763 100644 --- a/src/transform/sanitize.rs +++ b/src/transform/sanitize.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::util::StringExt; +use crate::{ir::BlockItemInner, util::StringExt}; use super::{map_names, NameKind, IR}; @@ -28,6 +28,7 @@ impl Sanitize { rename_duplicate_variants(enumm); } + rename_duplicate_fieldsets(ir); Ok(()) } } @@ -92,3 +93,28 @@ fn rename_duplicate_variants(enumm: &mut crate::ir::Enum) { } } } + +fn rename_duplicate_fieldsets(ir: &mut crate::ir::IR) { + use std::collections::BTreeMap; + + for (_name, blk) in ir.blocks.iter_mut() { + let mut register_name_counts: BTreeMap = BTreeMap::new(); + for block_item in blk.items.iter_mut() { + if let BlockItemInner::Register(r) = &mut block_item.inner { + let name_count = register_name_counts + .entry(block_item.name.clone()) + .or_insert(0); + + *name_count += 1; + + if *name_count > 1 { + block_item.name = format!("{}{}", block_item.name, name_count); + r.fieldset = r + .fieldset + .as_mut() + .map(|path| format!("{}{}", path, name_count)); + } + } + } + } +}