mirror of
https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator.git
synced 2025-04-19 19:26:02 +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]
|
## [unreleased]
|
||||||
|
|
||||||
|
## [0.3.0] - 2025-03-27
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added register map validator and flattened map generator.
|
- Added register map validator and flattened map generator.
|
||||||
|
- Allow register with mask and fields iff the masks are matched.
|
||||||
|
|
||||||
### Changed
|
### 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.
|
- 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
|
## [0.2.0] - 2025-02-12
|
||||||
|
|
||||||
### Added
|
### 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.
|
- 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.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
|
[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]]
|
[[package]]
|
||||||
name = "endcap-sl-software-ri-generator"
|
name = "endcap-sl-software-ri-generator"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
[package]
|
[package]
|
||||||
name = "endcap-sl-software-ri-generator"
|
name = "endcap-sl-software-ri-generator"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
authors = ["Wataru Otsubo <wataru.otsubo@cern.ch>"]
|
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"
|
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"
|
repository = "https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
|
@ -396,12 +396,6 @@ impl Register {
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
// Validation
|
// 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() {
|
if default.is_some() && !children.is_empty() {
|
||||||
return Err(DomConversionError::other_error(
|
return Err(DomConversionError::other_error(
|
||||||
"both default and field are used in the same register",
|
"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 crate::types::{DataType, Field, MultipleParams, Register, Value};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
util::{self, RustUxTypes},
|
|
||||||
CodeGenError,
|
CodeGenError,
|
||||||
|
util::{self, RustUxTypes},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Generate underlying `T` and its implementation for `RegisterSpec`.
|
/// 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 {
|
fn custom_value_const_name(field_name: &Ident, value_name: &str) -> Ident {
|
||||||
util::parse_to_ident(&format!(
|
util::parse_to_ident(&format!(
|
||||||
"{}_{}",
|
"{}_VAL_{}",
|
||||||
field_name.to_string().to_shouty_snake_case(),
|
field_name.to_string().to_shouty_snake_case(),
|
||||||
value_name.to_shouty_snake_case()
|
value_name.to_shouty_snake_case()
|
||||||
))
|
))
|
||||||
|
|
|
@ -5,7 +5,7 @@ use thiserror::Error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
converter, generator,
|
converter, generator,
|
||||||
io::{get_xml_gitinfo, XmlGitInfoError},
|
io::{XmlGitInfoError, get_xml_gitinfo},
|
||||||
types, validator,
|
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 {
|
pub enum ValidationError {
|
||||||
#[error("address duplicated at 0x{address:08x}: \"{existing_name}\", \"{new_name}\"")]
|
#[error("address duplicated at 0x{address:08x}: \"{existing_name}\", \"{new_name}\"")]
|
||||||
RegisterAddressDuplicated {
|
RegisterAddressDuplicated {
|
||||||
|
@ -38,12 +38,23 @@ pub enum ValidationError {
|
||||||
},
|
},
|
||||||
#[error("address 0x{address:08x} is out of range: \"{name}\"")]
|
#[error("address 0x{address:08x} is out of range: \"{name}\"")]
|
||||||
AddressOutofRange { address: u32, name: String },
|
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 {
|
FieldMaskDuplicated {
|
||||||
address: u32,
|
address: u32,
|
||||||
reg_name: String,
|
reg_name: String,
|
||||||
duplicated_mask: u32,
|
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}")]
|
#[error("unsupported structure: {msg}")]
|
||||||
UnsupportedStructure { msg: &'static str },
|
UnsupportedStructure { msg: &'static str },
|
||||||
}
|
}
|
||||||
|
@ -131,7 +142,13 @@ impl Validate for Register {
|
||||||
.elements
|
.elements
|
||||||
.iter()
|
.iter()
|
||||||
.fold((0, 0), |(sum, _duplicate_bits), field| {
|
.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 {
|
if duplicate_bits != 0 {
|
||||||
|
@ -142,6 +159,18 @@ impl Validate for Register {
|
||||||
});
|
});
|
||||||
return (mapping, errors);
|
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 {
|
let (len, offset) = match &self.multiple {
|
||||||
Some(multiple) => (multiple.multiple, multiple.offset),
|
Some(multiple) => (multiple.multiple, multiple.offset),
|
||||||
|
@ -213,8 +242,11 @@ impl Validate for Register {
|
||||||
mod test {
|
mod test {
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::types::{
|
use crate::{
|
||||||
Field, Module, ModuleBlockElements, MultipleParams, Register, RwSpecifier, XmlGitInfo,
|
types::{
|
||||||
|
Field, Module, ModuleBlockElements, MultipleParams, Register, RwSpecifier, XmlGitInfo,
|
||||||
|
},
|
||||||
|
validator::ValidationError,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -422,4 +454,136 @@ mod test {
|
||||||
assert!(errors.is_empty());
|
assert!(errors.is_empty());
|
||||||
assert_eq!(maps.iter().filter(|e| e.is_some()).collect_vec().len(), 0x1);
|
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