diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f3dbfb..8c262fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added register map validator and flattened map generator. +- Allow register with mask and fields iff the masks are matched. ### Changed diff --git a/src/converter.rs b/src/converter.rs index 81056e2..27d78b0 100644 --- a/src/converter.rs +++ b/src/converter.rs @@ -396,12 +396,6 @@ impl Register { .collect::>()?; // Validation - if mask.is_some() && !children.is_empty() { - return Err(DomConversionError::other_error( - "both mask and field are used in the same register", - node, - )); - } if default.is_some() && !children.is_empty() { return Err(DomConversionError::other_error( "both default and field are used in the same register", diff --git a/src/validator.rs b/src/validator.rs index d6f4c6f..439757c 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -28,7 +28,7 @@ impl Module { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, PartialEq)] pub enum ValidationError { #[error("address duplicated at 0x{address:08x}: \"{existing_name}\", \"{new_name}\"")] RegisterAddressDuplicated { @@ -44,6 +44,13 @@ pub enum ValidationError { reg_name: String, duplicated_mask: u32, }, + #[error("register \"{name}\" at 0x{address:08x} has mask 0x{reg_mask:08x} that doesn't match sum of field masks: 0x{fields_mask:08x}")] + RegisterAndFieldMaskMismatch { + address: u32, + name: String, + reg_mask: u32, + fields_mask: u32, + }, #[error("unsupported structure: {msg}")] UnsupportedStructure { msg: &'static str }, } @@ -131,7 +138,13 @@ impl Validate for Register { .elements .iter() .fold((0, 0), |(sum, _duplicate_bits), field| { - (sum + field.mask, sum & field.mask) + let field_mask = match &field.multiple { + Some(multiple) => (0..multiple.multiple) + .map(|multiple_id| field.mask << (multiple.offset * multiple_id)) + .sum(), + None => field.mask, + }; + (sum + field_mask, sum & field_mask) }), }; if duplicate_bits != 0 { @@ -142,6 +155,18 @@ impl Validate for Register { }); return (mapping, errors); } + if let Some(mask) = self.mask { + let has_fields = !self.elements.is_empty(); + if has_fields && mask != reg_mask { + errors.push(ValidationError::RegisterAndFieldMaskMismatch { + address: addr, + name: new_name.clone(), + reg_mask: mask, + fields_mask: reg_mask, + }); + return (mapping, errors); + } + }; let (len, offset) = match &self.multiple { Some(multiple) => (multiple.multiple, multiple.offset), @@ -213,8 +238,11 @@ impl Validate for Register { mod test { use itertools::Itertools; - use crate::types::{ - Field, Module, ModuleBlockElements, MultipleParams, Register, RwSpecifier, XmlGitInfo, + use crate::{ + types::{ + Field, Module, ModuleBlockElements, MultipleParams, Register, RwSpecifier, XmlGitInfo, + }, + validator::ValidationError, }; #[test] @@ -422,4 +450,136 @@ mod test { assert!(errors.is_empty()); assert_eq!(maps.iter().filter(|e| e.is_some()).collect_vec().len(), 0x1); } + + #[test] + fn register_with_mask_and_field() { + let reg1 = Register { + name: "test1".to_string(), + addr: 0, + r#type: crate::types::DataType::D32, + mask: Some(0x000f_00ff), + modf: RwSpecifier::R, + multiple: None, + default: None, + desc: None, + elements: vec![ + Field { + name: "field_1".to_string(), + mask: 0x0000_0001, + interface: None, + multiple: Some(MultipleParams { + multiple: 8, + offset: 1, + }), + default: None, + sclr: None, + desc: Some("field 1 description".to_string()), + elements: vec![], + }, + Field { + name: "field_2".to_string(), + mask: 0x0003_0000, + interface: None, + multiple: Some(MultipleParams { + multiple: 2, + offset: 2, + }), + default: None, + sclr: None, + desc: Some("field 1 description".to_string()), + elements: vec![], + }, + ], + }; + let module = Module { + name: "module".to_string(), + addr: 0, + size: 0x1, + amod: None, + r#type: None, + desc: None, + elements_bitstring: vec![], + elements_other: vec![ModuleBlockElements::Register(reg1)], + xmlhash: [0; 32], + git_info: XmlGitInfo { + describe: "".to_string(), + commit_timestamp: "".to_string(), + sha: "".to_string(), + }, + }; + + let (maps, errors) = module.validate(); + eprintln!("{:#?}", maps); + eprintln!("{:#?}", errors); + eprintln!( + "{:#?}", + maps.iter().filter(|e| e.is_some()).collect_vec().len() + ); + assert_eq!(maps.len(), 0x1); + assert!(errors.is_empty()); + assert_eq!(maps.iter().filter(|e| e.is_some()).collect_vec().len(), 0x1); + } + + #[test] + fn register_with_invalid_mask_and_field() { + let reg1 = Register { + name: "test1".to_string(), + addr: 0, + r#type: crate::types::DataType::D32, + mask: Some(0x0000_000f), + modf: RwSpecifier::R, + multiple: None, + default: None, + desc: None, + elements: vec![Field { + name: "field_1".to_string(), + mask: 0x0000_0001, + interface: None, + multiple: Some(MultipleParams { + multiple: 8, + offset: 1, + }), + default: None, + sclr: None, + desc: Some("field 1 description".to_string()), + elements: vec![], + }], + }; + let module = Module { + name: "module".to_string(), + addr: 0, + size: 0x1, + amod: None, + r#type: None, + desc: None, + elements_bitstring: vec![], + elements_other: vec![ModuleBlockElements::Register(reg1)], + xmlhash: [0; 32], + git_info: XmlGitInfo { + describe: "".to_string(), + commit_timestamp: "".to_string(), + sha: "".to_string(), + }, + }; + + let (maps, errors) = module.validate(); + eprintln!("{:#?}", maps); + eprintln!("{:#?}", errors); + eprintln!( + "{:#?}", + maps.iter().filter(|e| e.is_some()).collect_vec().len() + ); + assert_eq!(maps.len(), 0x1); + assert_eq!(errors.len(), 1); + assert_eq!(maps.iter().filter(|e| e.is_some()).collect_vec().len(), 0x0); + assert_eq!( + errors.first().unwrap(), + &ValidationError::RegisterAndFieldMaskMismatch { + address: 0x0, + name: "test1".to_string(), + reg_mask: 0x0000_000f, + fields_mask: 0x0000_00ff + } + ); + } }