mirror of
https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator.git
synced 2025-04-11 16:07:55 +09:00
Compare commits
5 commits
205b08400b
...
e6e7ada193
Author | SHA1 | Date | |
---|---|---|---|
e6e7ada193 | |||
b9f6dc6c23 | |||
1104f826fc | |||
5c7aa91d56 | |||
fa777f43fb |
7 changed files with 184 additions and 18 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -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
2
Cargo.lock
generated
|
@ -374,7 +374,7 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
|||
|
||||
[[package]]
|
||||
name = "endcap-sl-software-ri-generator"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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()
|
||||
))
|
||||
|
|
|
@ -5,7 +5,7 @@ use thiserror::Error;
|
|||
|
||||
use crate::{
|
||||
converter, generator,
|
||||
io::{get_xml_gitinfo, XmlGitInfoError},
|
||||
io::{XmlGitInfoError, get_xml_gitinfo},
|
||||
types, validator,
|
||||
};
|
||||
|
||||
|
|
174
src/validator.rs
174
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 {
|
||||
|
@ -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
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue