mirror of
https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator.git
synced 2025-04-20 03:36:25 +09:00
also added a lot of docs, since register codegen is quite complex (This became too large commit...)
412 lines
12 KiB
Rust
412 lines
12 KiB
Rust
//! Generator for [`Register`].
|
|
//! The entry point is [`reg_type_def`].
|
|
|
|
use heck::{ToShoutySnekCase, ToSnakeCase};
|
|
use itertools::Itertools;
|
|
use proc_macro2::{Ident, TokenStream};
|
|
use quote::quote;
|
|
|
|
use crate::types::{DataType, Field, MultipleParams, Register};
|
|
|
|
use super::{
|
|
util::{self, RustUxTypes},
|
|
CodeGenError,
|
|
};
|
|
|
|
/// Generate underlying `T` and its implementation for `RegisterSpec`.
|
|
///
|
|
/// # Returns
|
|
/// tuple of
|
|
/// - code of `T` definition and its impls
|
|
/// - [`Ident`] of `T`
|
|
/// - [`Ident`] of `Ux`
|
|
///
|
|
/// # Cases
|
|
/// - `T` == `Ux` => [`reg_type_def_simple`]
|
|
/// - `T` is masked `Ux` => [`reg_type_def_masked`]
|
|
/// - `T` has 1+ fields => [`reg_type_def_with_field`]
|
|
pub(super) fn reg_type_def(
|
|
reg: &Register,
|
|
upper_camel_name: &Ident,
|
|
) -> Result<(TokenStream, Ident, Ident), CodeGenError> {
|
|
Ok(match reg.elements.is_empty() {
|
|
true => match reg.mask {
|
|
Some(mask) => reg_type_def_masked(®.r#type, mask, upper_camel_name),
|
|
None => reg_type_def_simple(®.r#type),
|
|
},
|
|
false => reg_type_def_with_field(®.r#type, ®.elements, upper_camel_name)?,
|
|
})
|
|
}
|
|
|
|
/// Where `T` == `Ux`.
|
|
///
|
|
/// No `T` def nor impl are required.
|
|
///
|
|
/// # Returns
|
|
/// same as [`reg_type_def`]
|
|
fn reg_type_def_simple(basetype: &DataType) -> (TokenStream, Ident, Ident) {
|
|
let type_t_ux = {
|
|
let x: RustUxTypes = basetype.into();
|
|
x.to_rust_type_token()
|
|
};
|
|
|
|
let out = quote! {};
|
|
(out, type_t_ux.clone(), type_t_ux)
|
|
}
|
|
|
|
/// Where `T` is masked `Ux`.
|
|
///
|
|
/// `T` is a one-length "tuple struct" `T(pub Ux)`.
|
|
/// `impl TryFrom<Ux> for T` and `impl From<T> for Ux` are included,
|
|
/// but without `impl T`.
|
|
///
|
|
/// # Returns
|
|
/// same as [`reg_type_def`]
|
|
fn reg_type_def_masked(
|
|
basetype: &DataType,
|
|
mask: u32,
|
|
upper_camel_name: &Ident,
|
|
) -> (TokenStream, Ident, Ident) {
|
|
let type_ux = {
|
|
let x: RustUxTypes = basetype.into();
|
|
x.to_rust_type_token()
|
|
};
|
|
|
|
let out = quote! {
|
|
#[derive(Debug, Clone, Copy, Default)]
|
|
pub struct #upper_camel_name(pub #type_ux);
|
|
impl TryFrom<#type_ux> for #upper_camel_name {
|
|
type Error = DataConversionError<#type_ux, Self>;
|
|
|
|
fn try_from(value: #type_ux) -> Result<Self, Self::Error> {
|
|
let mask = #mask;
|
|
Ok(SlId(value & mask))
|
|
}
|
|
}
|
|
impl From<#upper_camel_name> for #type_ux {
|
|
fn from(value: #upper_camel_name) -> Self {
|
|
value.0
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
(out, upper_camel_name.clone(), type_ux)
|
|
}
|
|
|
|
/// Where `T` has fields.
|
|
///
|
|
/// `T` is a "struct struct" which has a single field `inner: Ux`.
|
|
/// Also, a bunch of "mask" constants and getter/setter are defined for each fields.
|
|
///
|
|
/// # Returns
|
|
/// same as [`reg_type_def`]
|
|
///
|
|
/// # Field generation
|
|
/// This function calls [`generate_field`] for each [`Field`] entries to generate field definitions.
|
|
/// See the doc of `generate_field` for more detail
|
|
fn reg_type_def_with_field(
|
|
basetype: &DataType,
|
|
fields: &[Field],
|
|
upper_camel_name: &Ident,
|
|
) -> Result<(TokenStream, Ident, Ident), CodeGenError> {
|
|
let type_ux = {
|
|
let x: RustUxTypes = basetype.into();
|
|
x.to_rust_type_token()
|
|
};
|
|
|
|
let (code_masks, code_getters, code_setters): (
|
|
Vec<TokenStream>,
|
|
Vec<TokenStream>,
|
|
Vec<TokenStream>,
|
|
) = fields
|
|
.iter()
|
|
.map(|field| generate_field(field, basetype))
|
|
.process_results(|iter| iter.multiunzip())?;
|
|
|
|
let out = quote! {
|
|
#(#code_masks)*
|
|
|
|
#[derive(Debug, Clone, Copy, Default)]
|
|
pub struct #upper_camel_name {
|
|
inner: #type_ux,
|
|
}
|
|
|
|
impl GtyDelayBank123 {
|
|
#(#code_getters)*
|
|
|
|
#(#code_setters)*
|
|
}
|
|
impl TryFrom<#type_ux> for #upper_camel_name {
|
|
type Error = DataConversionError<#type_ux, Self>;
|
|
|
|
fn try_from(value: #type_ux) -> Result<Self, Self::Error> {
|
|
Ok(Self { inner: value })
|
|
}
|
|
}
|
|
impl From<GtyDelayBank123> for u32 {
|
|
fn from(value: #upper_camel_name) -> Self {
|
|
value.inner
|
|
}
|
|
}
|
|
};
|
|
|
|
Ok((out, upper_camel_name.clone(), type_ux))
|
|
}
|
|
|
|
/// Generate code for each field, which consists of these three.
|
|
/// 1. mask definition
|
|
/// 2. getter method
|
|
/// 3. setter method
|
|
///
|
|
/// # Cases
|
|
/// This function is separated into two cases.
|
|
/// See each function docs for more detail.
|
|
///
|
|
/// 1. field is not "multiple" => [`generate_single_field`]
|
|
/// 2. field is "multiple" => [`generate_multiple_field`]
|
|
fn generate_field(
|
|
field: &Field,
|
|
basetype: &DataType,
|
|
) -> Result<(TokenStream, TokenStream, TokenStream), CodeGenError> {
|
|
let mask_name = util::parse_to_ident(&format!("{}_MASK", field.name.TO_SHOUTY_SNEK_CASE()))?;
|
|
let base_type = util::RustUxTypes::from(basetype).to_rust_type_token();
|
|
|
|
let snake_case_name = util::parse_to_ident(&field.name.to_snake_case())?;
|
|
let field_type = util::RustUxTypes::from_mask(field.mask);
|
|
|
|
let (code_mask, code_getter, code_setter) = match &field.multiple {
|
|
Some(multiple_params) => generate_multiple_field(
|
|
mask_name,
|
|
base_type,
|
|
field.mask,
|
|
field_type,
|
|
snake_case_name,
|
|
multiple_params,
|
|
),
|
|
None => generate_single_field(
|
|
mask_name,
|
|
base_type,
|
|
field.mask,
|
|
field_type,
|
|
snake_case_name,
|
|
),
|
|
};
|
|
|
|
Ok((code_mask, code_getter, code_setter))
|
|
}
|
|
|
|
/// Generate "single" field definition (mask, getter, setter).
|
|
///
|
|
/// # Cases
|
|
/// This function is separated into two cases.
|
|
///
|
|
/// - field is `bool` => [`generate_single_bool_field`]
|
|
/// - field is `u8`/`u16`/`u32` => [`generate_single_ux_field`]
|
|
///
|
|
/// For the details of types of register/field, see [`DataType`].
|
|
/// Note that in both cases, mask definitions are the same.
|
|
fn generate_single_field(
|
|
mask_name: Ident,
|
|
base_type: Ident,
|
|
mask_val: u32,
|
|
field_type: RustUxTypes,
|
|
snake_case_name: Ident,
|
|
) -> (TokenStream, TokenStream, TokenStream) {
|
|
let mask_val = util::parse_to_literal(&format!("0x{:x}", mask_val)).unwrap();
|
|
let code_mask = quote! {
|
|
const #mask_name: #base_type = #mask_val;
|
|
};
|
|
let (code_getter, code_setter) = match field_type {
|
|
RustUxTypes::Bool => generate_single_bool_field(mask_name, snake_case_name),
|
|
RustUxTypes::U8 | RustUxTypes::U16 | RustUxTypes::U32 => {
|
|
generate_single_ux_field(mask_name, base_type, snake_case_name, field_type)
|
|
}
|
|
};
|
|
|
|
(code_mask, code_getter, code_setter)
|
|
}
|
|
|
|
/// Generate bool "single" field definition (getter, setter).
|
|
fn generate_single_bool_field(
|
|
mask_name: Ident,
|
|
snake_case_name: Ident,
|
|
) -> (TokenStream, TokenStream) {
|
|
let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap();
|
|
let code_getter = quote! {
|
|
pub fn #snake_case_name(&self) -> bool {
|
|
(self.inner & #mask_name) == #mask_name
|
|
}
|
|
};
|
|
let code_setter = quote! {
|
|
pub fn #setter_name(self, val: bool) -> Self {
|
|
let mut inner = self.inner;
|
|
if val {
|
|
inner |= #mask_name
|
|
} else {
|
|
inner &= !#mask_name
|
|
};
|
|
Self { inner }
|
|
}
|
|
};
|
|
(code_getter, code_setter)
|
|
}
|
|
|
|
/// Generate u8/u16/u32 "single" field definition (getter, setter).
|
|
fn generate_single_ux_field(
|
|
mask_name: Ident,
|
|
base_type: Ident,
|
|
snake_case_name: Ident,
|
|
field_type: RustUxTypes,
|
|
) -> (TokenStream, TokenStream) {
|
|
let field_type = field_type.to_rust_type_token();
|
|
let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap();
|
|
|
|
let code_getter = quote! {
|
|
pub fn #snake_case_name(&self) -> #field_type {
|
|
const RIGHT_SHIFT: #base_type = #mask_name.trailing_zeros();
|
|
((self.inner & #mask_name) >> RIGHT_SHIFT)
|
|
.try_into()
|
|
.unwrap()
|
|
}
|
|
};
|
|
let code_setter = quote! {
|
|
pub fn #setter_name(&self, val: #field_type) -> Self {
|
|
let update: #base_type = val & #mask_name;
|
|
let mut inner = self.inner;
|
|
inner &= !mask;
|
|
inner |= update;
|
|
Self { inner }
|
|
}
|
|
};
|
|
(code_getter, code_setter)
|
|
}
|
|
|
|
/// Generate "multiple" field definition (mask, getter, setter).
|
|
///
|
|
/// # Cases
|
|
/// As "single" field cases, this function is separated into two cases;
|
|
///
|
|
/// - fields are `bool` => [`generate_multiple_bool_field`]
|
|
/// - fields are `u8`/`u16`/`u32` => todo
|
|
fn generate_multiple_field(
|
|
mask_name: Ident,
|
|
base_type: Ident,
|
|
single_mask_val: u32,
|
|
single_field_type: RustUxTypes,
|
|
snake_case_name: Ident,
|
|
multiple_params: &MultipleParams,
|
|
) -> (TokenStream, TokenStream, TokenStream) {
|
|
let num_multiple = multiple_params.multiple;
|
|
let id_num_multiple = util::parse_to_ident(&num_multiple.to_string()).unwrap();
|
|
let id_field_type = single_field_type.to_rust_type_token();
|
|
let masks: Vec<_> = (0..multiple_params.multiple)
|
|
.map(|x| x * multiple_params.offset)
|
|
.map(|offset| single_mask_val << offset)
|
|
.map(|mask| util::parse_to_ident(&format!("0x{mask:x}")).unwrap())
|
|
.collect();
|
|
debug_assert_eq!(masks.len(), num_multiple.try_into().unwrap());
|
|
let code_mask = quote! {
|
|
const #mask_name: [#id_field_type; #id_num_multiple] = [#(#masks),*];
|
|
};
|
|
|
|
let (code_getter, code_setter) = match single_field_type {
|
|
RustUxTypes::Bool => {
|
|
generate_multiple_bool_field(mask_name, base_type, snake_case_name, masks.clone())
|
|
}
|
|
RustUxTypes::U8 | RustUxTypes::U16 | RustUxTypes::U32 => generate_multiple_ux_field(
|
|
mask_name,
|
|
base_type,
|
|
snake_case_name,
|
|
single_field_type,
|
|
masks,
|
|
),
|
|
};
|
|
|
|
(code_mask, code_getter, code_setter)
|
|
}
|
|
|
|
/// Generate bool "multiple" field definition (getter, setter).
|
|
fn generate_multiple_bool_field(
|
|
mask_name: Ident,
|
|
base_type: Ident,
|
|
snake_case_name: Ident,
|
|
masks: Vec<Ident>,
|
|
) -> (TokenStream, TokenStream) {
|
|
let num_multiple = masks.len();
|
|
let elem_getter = masks.iter().enumerate().map(|(i, _mask)| {
|
|
quote! {
|
|
(self.inner & #mask_name[#i]) == #mask_name[#i]
|
|
}
|
|
});
|
|
let code_getter = quote! {
|
|
pub fn #snake_case_name(&self) -> [bool; #num_multiple] {
|
|
[
|
|
#(#elem_getter),*
|
|
]
|
|
}
|
|
};
|
|
|
|
let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap();
|
|
let code_setter = quote! {
|
|
pub fn #setter_name(self, val: [bool; #num_multiple]) -> Self {
|
|
let mask: #base_type = #mask_name.iter().sum();
|
|
let update: #base_type = BANK121_GTY_CHANNEL_MASK
|
|
.iter()
|
|
.zip(val)
|
|
.filter_map(|(mask, val)| val.then_some(mask))
|
|
.sum();
|
|
let mut inner = self.inner;
|
|
inner &= !mask;
|
|
inner |= update;
|
|
Self { inner }
|
|
}
|
|
};
|
|
|
|
(code_getter, code_setter)
|
|
}
|
|
|
|
/// Generate u8/u16/u32 "multiple" field definition (getter, setter).
|
|
fn generate_multiple_ux_field(
|
|
mask_name: Ident,
|
|
base_type: Ident,
|
|
snake_case_name: Ident,
|
|
single_field_type: RustUxTypes,
|
|
masks: Vec<Ident>,
|
|
) -> (TokenStream, TokenStream) {
|
|
let field_type = single_field_type.to_rust_type_token();
|
|
let num_multiple = masks.len();
|
|
let elem_getter = masks.iter().enumerate().map(|(i, _mask)| {
|
|
quote! {
|
|
((self.inner & #mask_name[#i])
|
|
>> (#mask_name[#i].trailing_zeros()))
|
|
.try_into()
|
|
.unwrap()
|
|
}
|
|
});
|
|
let code_getter = quote! {
|
|
pub fn #snake_case_name(&self) -> [#field_type; #num_multiple] {
|
|
[
|
|
#(#elem_getter),*
|
|
]
|
|
}
|
|
};
|
|
|
|
let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap();
|
|
let code_setter = quote! {
|
|
pub fn #setter_name(&self, val: [#field_type; #num_multiple]) -> Self {
|
|
let mask: #base_type = #mask_name.iter().sum();
|
|
let update: #base_type = #mask_name
|
|
.iter()
|
|
.zip(val)
|
|
.map(|(mask, val)| (#base_type::from(val)) << (mask.trailing_zeros()))
|
|
.sum();
|
|
let mut inner = self.inner;
|
|
inner &= !mask;
|
|
inner |= update;
|
|
Self { inner }
|
|
}
|
|
};
|
|
|
|
(code_getter, code_setter)
|
|
}
|