Compare commits

...

5 commits

7 changed files with 184 additions and 18 deletions

View file

@ -7,14 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [unreleased]
## [0.3.0] - 2025-03-27
### Added
- Added register map validator and flattened map generator.
- Allow register with mask and fields iff the masks are matched.
### Changed
- Changed block, register type definitions to support multiple backends. See [the merge request at mpsoc software](https://gitlab.cern.ch/wotsubo/mpsoc-software/-/merge_requests/9) for more information.
### Fixed
- Avoid mask and value constant name duplication by inserting `_VAL_` to value constants.
## [0.2.0] - 2025-02-12
### Added
@ -33,6 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Implemented basic code generation covering current CSR XML.
[unreleased]: https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator/-/compare/v0.2.0...main
[unreleased]: https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator/-/compare/v0.3.0...main
[0.3.0]: https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator/-/compare/v0.2.0...v0.3.0
[0.2.0]: https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator/-/compare/v0.1.0...v0.2.0
[0.1.0]: https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator/~/tags/v0.1.0

2
Cargo.lock generated
View file

@ -374,7 +374,7 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "endcap-sl-software-ri-generator"
version = "0.2.0"
version = "0.3.0"
dependencies = [
"anyhow",
"chrono",

View file

@ -1,8 +1,8 @@
[package]
name = "endcap-sl-software-ri-generator"
version = "0.2.0"
version = "0.3.0"
authors = ["Wataru Otsubo <wataru.otsubo@cern.ch>"]
edition = "2021"
edition = "2024"
description = "A generator of register interface for mpsoc software from register map in xml format"
repository = "https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator"
build = "build.rs"

View file

@ -396,12 +396,6 @@ impl Register {
.collect::<Result<_, _>>()?;
// 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",

View file

@ -9,8 +9,8 @@ use quote::quote;
use crate::types::{DataType, Field, MultipleParams, Register, Value};
use super::{
util::{self, RustUxTypes},
CodeGenError,
util::{self, RustUxTypes},
};
/// Generate underlying `T` and its implementation for `RegisterSpec`.
@ -322,7 +322,7 @@ fn generate_single_ux_field(
fn custom_value_const_name(field_name: &Ident, value_name: &str) -> Ident {
util::parse_to_ident(&format!(
"{}_{}",
"{}_VAL_{}",
field_name.to_string().to_shouty_snake_case(),
value_name.to_shouty_snake_case()
))

View file

@ -5,7 +5,7 @@ use thiserror::Error;
use crate::{
converter, generator,
io::{get_xml_gitinfo, XmlGitInfoError},
io::{XmlGitInfoError, get_xml_gitinfo},
types, validator,
};

View file

@ -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 {
@ -38,12 +38,23 @@ pub enum ValidationError {
},
#[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}")]
#[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(
"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 +142,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 +159,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 +242,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 +454,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
}
);
}
}