//! Validate the address assignment and generate a flattened register map. use thiserror::Error; use crate::types::{Block, Module, ModuleBlockElements, Register}; type FlattenedRegisterMap<'a> = Vec, &'a Register, Option)>>; impl Module { /// Validate the address assignment, generating a flatten register map. pub fn validate(&self) -> (FlattenedRegisterMap, Vec) { let mut mapping = Vec::new(); mapping.resize(self.size.try_into().unwrap(), None); let errors = Vec::new(); self.fill(mapping, errors, (0x0, Vec::new())) } } #[derive(Debug, Error)] pub enum ValidationError { #[error("address duplicated at 0x{address:08x}: \"{existing_name}\", \"{new_name}\"")] AddressDuplicated { address: u32, existing_name: String, new_name: String, }, #[error("address 0x{address:08x} is out of range: \"{name}\"")] AddressOutofRange { address: u32, name: String }, #[error("unsupported structure: {msg}")] UnsupportedStructure { msg: &'static str }, } trait Validate { fn fill<'a>( &'a self, mapping: FlattenedRegisterMap<'a>, errors: Vec, base: (u32, Vec<&'a str>), ) -> (FlattenedRegisterMap<'a>, Vec); } impl Validate for Module { fn fill<'a>( &'a self, mut mapping: FlattenedRegisterMap<'a>, mut errors: Vec, base: (u32, Vec<&'a str>), ) -> (FlattenedRegisterMap<'a>, Vec) { for child in &self.elements_other { (mapping, errors) = child.fill(mapping, errors, base.clone()); } (mapping, errors) } } impl Validate for ModuleBlockElements { fn fill<'a>( &'a self, mapping: FlattenedRegisterMap<'a>, errors: Vec, base: (u32, Vec<&'a str>), ) -> (FlattenedRegisterMap<'a>, Vec) { match self { ModuleBlockElements::Block(block) => block.fill(mapping, errors, base), ModuleBlockElements::Register(register) => register.fill(mapping, errors, base), ModuleBlockElements::Memory(_memory) => todo!(), ModuleBlockElements::Fifo(_fifo) => todo!(), } } } impl Validate for Block { fn fill<'a>( &'a self, mut mapping: FlattenedRegisterMap<'a>, mut errors: Vec, base: (u32, Vec<&'a str>), ) -> (FlattenedRegisterMap<'a>, Vec) { if self.multiple.is_some() { errors.push(ValidationError::UnsupportedStructure { msg: "multiple in block", }); } let addr = base.0 + self.addr; let mut path = base.1; path.push(&self.name); for child in &self.elements { (mapping, errors) = child.fill(mapping, errors, (addr, path.clone())); } (mapping, errors) } } impl Validate for Register { fn fill<'a>( &'a self, mut mapping: FlattenedRegisterMap<'a>, mut errors: Vec, base: (u32, Vec<&'a str>), ) -> (FlattenedRegisterMap<'a>, Vec) { let addr = base.0 + self.addr; let path = base.1; let (len, offset) = match &self.multiple { Some(multiple) => (multiple.multiple, multiple.offset), None => (1, 1), }; for id in 0..len { let addr = addr + id * offset; let regmap: &mut Option<_> = match mapping.get_mut::(addr.try_into().unwrap()) { Some(regmap) => regmap, None => { errors.push(ValidationError::AddressOutofRange { address: addr, name: self.name.clone(), }); continue; } }; if let Some(old) = regmap { let existing_name = { let mut path = old.0.clone(); path.push(&old.1.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, }); } else { let multiple_id = if len == 1 { None } else { Some(id) }; *regmap = Some((path.clone(), self, multiple_id)) } } (mapping, errors) } } #[cfg(test)] mod test { use itertools::Itertools; use crate::types::{ Module, ModuleBlockElements, MultipleParams, Register, RwSpecifier, XmlGitInfo, }; #[test] fn duplicate() { 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![], }; 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![], }; 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); } #[test] fn duplicate_with_multiple() { let reg1 = Register { name: "test1".to_string(), addr: 0, r#type: crate::types::DataType::D32, mask: None, modf: RwSpecifier::R, multiple: Some(MultipleParams { multiple: 16, offset: 1, }), default: None, desc: None, elements: vec![], }; let reg2 = Register { name: "test1".to_string(), addr: 15, r#type: crate::types::DataType::D32, mask: None, modf: RwSpecifier::R, multiple: None, default: None, desc: None, elements: vec![], }; let module = Module { name: "module".to_string(), addr: 0, size: 0x1000, 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(); assert_eq!(maps.len(), 0x1000); assert_eq!(errors.len(), 1); } }