From 205b08400bf219913149385883bd7f88dcdce79e Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Thu, 13 Mar 2025 13:12:49 +0000 Subject: [PATCH] Allow definition of overlapped register with non-overlapped fields --- src/integrated.rs | 14 ++-- src/validator.rs | 197 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 190 insertions(+), 21 deletions(-) diff --git a/src/integrated.rs b/src/integrated.rs index c4d0ae1..aa4ce80 100644 --- a/src/integrated.rs +++ b/src/integrated.rs @@ -117,15 +117,19 @@ pub fn generate_flatmap(xml: &path::Path, out: Option<&path::Path>) -> Result<() for (addr, reg) in maps.iter().enumerate() { match reg { Some(reg) => { - let path = reg.0.join("/"); - let multiple_id = reg.2.map_or("".to_string(), |x| x.to_string()); + let path = reg.path.join("/"); + let multiple_id = reg.multiple_id.map_or("".to_string(), |x| x.to_string()); wtr.write_record([ format!("0x{:04x}", addr), path, - reg.1.name.clone(), + reg.register.name.clone(), multiple_id, - reg.1.modf.to_string(), - reg.1.desc.as_ref().unwrap_or(&"".to_string()).to_string(), + reg.register.modf.to_string(), + reg.register + .desc + .as_ref() + .unwrap_or(&"".to_string()) + .to_string(), ])?; } None => wtr.write_record([ diff --git a/src/validator.rs b/src/validator.rs index a075b2b..d6f4c6f 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -4,7 +4,19 @@ use thiserror::Error; use crate::types::{Block, Module, ModuleBlockElements, Register}; -type FlattenedRegisterMap<'a> = Vec, &'a Register, Option)>>; +#[derive(Debug, Clone)] +pub struct FlattenedRegisterEntry<'a> { + /// contains all parents + pub path: Vec<&'a str>, + pub register: &'a Register, + /// Id of multiple, if the register is multiple + pub multiple_id: Option, + /// Used bits + pub field_masks: u32, +} + +/// Flattened register map. +type FlattenedRegisterMap<'a> = Vec>>; impl Module { /// Validate the address assignment, generating a flatten register map. @@ -19,13 +31,19 @@ impl Module { #[derive(Debug, Error)] pub enum ValidationError { #[error("address duplicated at 0x{address:08x}: \"{existing_name}\", \"{new_name}\"")] - AddressDuplicated { + RegisterAddressDuplicated { address: u32, existing_name: String, new_name: String, }, #[error("address 0x{address:08x} is out of range: \"{name}\"")] AddressOutofRange { address: u32, name: String }, + #[error("field masks are duplicated at 0x{address:08x}: \"{reg_name}\", bits: 0x{duplicated_mask:08x}")] + FieldMaskDuplicated { + address: u32, + reg_name: String, + duplicated_mask: u32, + }, #[error("unsupported structure: {msg}")] UnsupportedStructure { msg: &'static str }, } @@ -101,6 +119,30 @@ impl Validate for Register { ) -> (FlattenedRegisterMap<'a>, Vec) { let addr = base.0 + self.addr; let path = base.1; + + let new_name = { + let mut path = path.clone(); + path.push(&self.name); + path.join("/") + }; + let (reg_mask, duplicate_bits) = match self.elements.is_empty() { + true => (0xffff_ffff, 0), + false => self + .elements + .iter() + .fold((0, 0), |(sum, _duplicate_bits), field| { + (sum + field.mask, sum & field.mask) + }), + }; + if duplicate_bits != 0 { + errors.push(ValidationError::FieldMaskDuplicated { + address: addr, + reg_name: new_name, + duplicated_mask: duplicate_bits, + }); + return (mapping, errors); + } + let (len, offset) = match &self.multiple { Some(multiple) => (multiple.multiple, multiple.offset), None => (1, 1), @@ -119,23 +161,47 @@ impl Validate for Register { }; if let Some(old) = regmap { let existing_name = { - let mut path = old.0.clone(); - path.push(&old.1.name); + let mut path = old.path.clone(); + path.push(&old.register.name); path.join("/") }; - let new_name = { - let mut path = path.clone(); - path.push(&self.name); - path.join("/") - }; - errors.push(ValidationError::AddressDuplicated { - address: addr, - existing_name, - new_name, - }); + + if old.register.name != self.name { + errors.push(ValidationError::RegisterAddressDuplicated { + address: addr, + existing_name, + new_name: new_name.clone(), + }); + continue; + } + log::trace!( + "defining same register at 0x{addr:08x}: {:?}, {:?}", + old.register, + self + ); + let duplicate_bits = old.field_masks & reg_mask; + if duplicate_bits != 0 { + errors.push(ValidationError::FieldMaskDuplicated { + address: addr, + reg_name: new_name.clone(), + duplicated_mask: duplicate_bits, + }); + } + let multiple_id = if len == 1 { None } else { Some(id) }; + *regmap = Some(FlattenedRegisterEntry { + path: path.clone(), + register: self, + multiple_id, + field_masks: old.field_masks | reg_mask, + }) } else { let multiple_id = if len == 1 { None } else { Some(id) }; - *regmap = Some((path.clone(), self, multiple_id)) + *regmap = Some(FlattenedRegisterEntry { + path: path.clone(), + register: self, + multiple_id, + field_masks: reg_mask, + }) } } @@ -148,7 +214,7 @@ mod test { use itertools::Itertools; use crate::types::{ - Module, ModuleBlockElements, MultipleParams, Register, RwSpecifier, XmlGitInfo, + Field, Module, ModuleBlockElements, MultipleParams, Register, RwSpecifier, XmlGitInfo, }; #[test] @@ -255,6 +321,105 @@ mod test { let (maps, errors) = module.validate(); assert_eq!(maps.len(), 0x1000); + assert!(!errors.is_empty()); assert_eq!(errors.len(), 1); } + + // Fields are orthogonal + #[test] + fn duplicate_reg_but_fields_are_ok() { + let reg1 = Register { + name: "test1".to_string(), + addr: 0, + r#type: crate::types::DataType::D32, + mask: None, + modf: RwSpecifier::R, + multiple: None, + default: None, + desc: None, + elements: vec![ + Field { + name: "field_1".to_string(), + mask: 0x0000_00ff, + interface: None, + multiple: None, + default: None, + sclr: None, + desc: Some("field 1 description".to_string()), + elements: vec![], + }, + Field { + name: "field_2".to_string(), + mask: 0x0000_ff00, + interface: None, + multiple: None, + default: None, + sclr: None, + desc: Some("field 2 description".to_string()), + elements: vec![], + }, + ], + }; + let reg2 = Register { + name: "test1".to_string(), + addr: 0, + r#type: crate::types::DataType::D32, + mask: None, + modf: RwSpecifier::R, + multiple: None, + default: None, + desc: None, + elements: vec![ + Field { + name: "field_3".to_string(), + mask: 0x00ff_0000, + interface: None, + multiple: None, + default: None, + sclr: None, + desc: Some("field 3 description".to_string()), + elements: vec![], + }, + Field { + name: "field_4".to_string(), + mask: 0xff00_0000, + interface: None, + multiple: None, + default: None, + sclr: None, + desc: Some("field 4 description".to_string()), + elements: vec![], + }, + ], + }; + let module = Module { + name: "module".to_string(), + addr: 0, + size: 0x10, + amod: None, + r#type: None, + desc: None, + elements_bitstring: vec![], + elements_other: vec![ + ModuleBlockElements::Register(reg1), + ModuleBlockElements::Register(reg2), + ], + xmlhash: [0; 32], + git_info: XmlGitInfo { + describe: "".to_string(), + commit_timestamp: "".to_string(), + sha: "".to_string(), + }, + }; + + let (maps, errors) = module.validate(); + eprintln!("{:#?}", maps); + eprintln!( + "{:#?}", + maps.iter().filter(|e| e.is_some()).collect_vec().len() + ); + assert_eq!(maps.len(), 0x10); + assert!(errors.is_empty()); + assert_eq!(maps.iter().filter(|e| e.is_some()).collect_vec().len(), 0x1); + } }