mirror of
https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator.git
synced 2025-04-20 03:36:25 +09:00
269 lines
8.8 KiB
Rust
269 lines
8.8 KiB
Rust
//! Generate register interface rust code from types in [`crate::types`].
|
|
//!
|
|
//! root: [`CodeGen`]
|
|
//!
|
|
//! # TODO
|
|
//! add docs (especially fields and registers)
|
|
|
|
use crate::{
|
|
type_traits::GetName,
|
|
types::{Block, Module, ModuleBlockElements, Register},
|
|
};
|
|
use heck::{ToSnakeCase, ToUpperCamelCase};
|
|
use proc_macro2::TokenStream;
|
|
use quote::quote;
|
|
use thiserror::Error;
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum CodeGenError {
|
|
#[error("tokenization error: {0}")]
|
|
TokenizeError(#[from] syn::Error),
|
|
}
|
|
|
|
mod util {
|
|
use crate::types::DataType;
|
|
|
|
use super::CodeGenError;
|
|
|
|
pub(super) fn parse_to_ident(s: &str) -> Result<proc_macro2::Ident, CodeGenError> {
|
|
Ok(syn::parse_str(s)?)
|
|
}
|
|
|
|
pub(super) fn parse_to_literal(s: &str) -> Result<proc_macro2::Literal, CodeGenError> {
|
|
Ok(syn::parse_str(s)?)
|
|
}
|
|
|
|
// currently only U32 is used, so `dead_code` for Debug, PartialEq
|
|
#[allow(dead_code)]
|
|
#[derive(Debug, PartialEq)]
|
|
pub(crate) enum RustUxTypes {
|
|
Bool,
|
|
U8,
|
|
U16,
|
|
U32,
|
|
}
|
|
|
|
impl RustUxTypes {
|
|
/// Derive appropriate rust types for `mask`ed value.
|
|
pub(super) fn from_mask(mask: u32) -> RustUxTypes {
|
|
match 32 - mask.leading_zeros() - mask.trailing_zeros() {
|
|
0 => panic!("mask cannot be 0"),
|
|
1 => RustUxTypes::Bool,
|
|
x if 1 < x && x <= 8 => RustUxTypes::U8,
|
|
x if 8 < x && x <= 16 => RustUxTypes::U16,
|
|
x if 16 < x && x <= 32 => RustUxTypes::U32,
|
|
_ => panic!("supposed not to be reachable"),
|
|
}
|
|
}
|
|
|
|
pub(super) fn to_rust_type_token(&self) -> proc_macro2::Ident {
|
|
match self {
|
|
RustUxTypes::Bool => parse_to_ident("bool").unwrap(),
|
|
RustUxTypes::U8 => parse_to_ident("u8").unwrap(),
|
|
RustUxTypes::U16 => parse_to_ident("u16").unwrap(),
|
|
RustUxTypes::U32 => parse_to_ident("u32").unwrap(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&DataType> for RustUxTypes {
|
|
fn from(value: &DataType) -> Self {
|
|
match value {
|
|
DataType::D32 => Self::U32,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl DataType {
|
|
fn to_rust_type_token(&self) -> proc_macro2::Ident {
|
|
match self {
|
|
DataType::D32 => proc_macro2::Ident::new("u32", proc_macro2::Span::call_site()),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::RustUxTypes;
|
|
|
|
#[test]
|
|
fn rustuxtypes_from_mask() {
|
|
assert_eq!(RustUxTypes::from_mask(0x1), RustUxTypes::Bool);
|
|
assert_eq!(RustUxTypes::from_mask(0x20), RustUxTypes::Bool);
|
|
assert_eq!(RustUxTypes::from_mask(0x4000), RustUxTypes::Bool);
|
|
assert_eq!(RustUxTypes::from_mask(0x80_0000), RustUxTypes::Bool);
|
|
assert_eq!(RustUxTypes::from_mask(0x100_0000), RustUxTypes::Bool);
|
|
|
|
assert_eq!(RustUxTypes::from_mask(0x0300_0000), RustUxTypes::U8);
|
|
assert_eq!(RustUxTypes::from_mask(0x0000_01e0), RustUxTypes::U8);
|
|
|
|
assert_eq!(RustUxTypes::from_mask(0x0000_01f0), RustUxTypes::U8);
|
|
assert_eq!(RustUxTypes::from_mask(0x000f_f000), RustUxTypes::U8);
|
|
|
|
assert_eq!(RustUxTypes::from_mask(0x0fff_0000), RustUxTypes::U16);
|
|
assert_eq!(RustUxTypes::from_mask(0x0f0f_0000), RustUxTypes::U16);
|
|
assert_eq!(RustUxTypes::from_mask(0x010f_8000), RustUxTypes::U16);
|
|
|
|
assert_eq!(RustUxTypes::from_mask(0xffff_f000), RustUxTypes::U32);
|
|
assert_eq!(RustUxTypes::from_mask(0x1fff_ff00), RustUxTypes::U32);
|
|
}
|
|
|
|
#[test]
|
|
fn rustuxtypes_to_token() {
|
|
assert_eq!(
|
|
RustUxTypes::U8.to_rust_type_token(),
|
|
proc_macro2::Ident::new("u8", proc_macro2::Span::call_site())
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait CodeGen {
|
|
fn generate_register_interface(self) -> Result<proc_macro2::TokenStream, CodeGenError>;
|
|
}
|
|
|
|
impl CodeGen for Module {
|
|
fn generate_register_interface(self) -> Result<proc_macro2::TokenStream, CodeGenError> {
|
|
let mut out = TokenStream::new();
|
|
if !self.elements_bitstring.is_empty() {
|
|
todo!("bitstring generation is not yet implemented")
|
|
}
|
|
|
|
let child_mods = self
|
|
.elements_other
|
|
.into_iter()
|
|
.map(|e| e.generate_register_interface())
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
for child in child_mods {
|
|
out.extend(child);
|
|
}
|
|
|
|
Ok(out)
|
|
}
|
|
}
|
|
|
|
impl CodeGen for ModuleBlockElements {
|
|
fn generate_register_interface(self) -> Result<proc_macro2::TokenStream, CodeGenError> {
|
|
match self {
|
|
ModuleBlockElements::Block(block) => block.generate_register_interface(),
|
|
ModuleBlockElements::Register(register) => register.generate_register_interface(),
|
|
ModuleBlockElements::Memory(memory) => todo!(),
|
|
ModuleBlockElements::Fifo(fifo) => todo!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CodeGen for Block {
|
|
fn generate_register_interface(self) -> Result<proc_macro2::TokenStream, CodeGenError> {
|
|
let snake_case_name = util::parse_to_ident(&self.name.to_snake_case())?;
|
|
let upper_camel_name = util::parse_to_ident(&self.name.to_upper_camel_case())?;
|
|
let addr = util::parse_to_literal(&format!("0x{:x}", self.addr))?;
|
|
let desc = self.desc.unwrap_or("".to_string());
|
|
|
|
let accessor_methods = {
|
|
let mut out = TokenStream::new();
|
|
for child_name in self.elements.iter().map(|e| e.get_name()) {
|
|
let snake_case_name = util::parse_to_ident(&child_name.to_snake_case())?;
|
|
let upper_camel_name = util::parse_to_ident(&child_name.to_upper_camel_case())?;
|
|
|
|
out.extend(quote! {
|
|
pub fn #snake_case_name(&self) -> #snake_case_name::#upper_camel_name {
|
|
#snake_case_name::#upper_camel_name::new(self.mem_ptr)
|
|
}
|
|
});
|
|
}
|
|
|
|
out
|
|
};
|
|
|
|
let code_children = self
|
|
.elements
|
|
.into_iter()
|
|
.map(|e| e.generate_register_interface())
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
|
|
Ok(quote! {
|
|
pub mod #snake_case_name {
|
|
#[doc = #desc]
|
|
|
|
use std::marker::PhantomData;
|
|
|
|
use super::RegisterInterface;
|
|
|
|
#(#code_children)*
|
|
|
|
const OFFSET: usize = #addr;
|
|
|
|
pub struct #upper_camel_name<'a> {
|
|
mem_ptr: *mut u32,
|
|
_marker: PhantomData<&'a mut RegisterInterface>,
|
|
}
|
|
|
|
impl #upper_camel_name<'_> {
|
|
pub(crate) fn new(parent_ptr: *mut u32) -> Self {
|
|
#upper_camel_name {
|
|
mem_ptr: unsafe { parent_ptr.add(OFFSET) },
|
|
_marker: PhantomData,
|
|
}
|
|
}
|
|
|
|
#accessor_methods
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
mod codegen_register;
|
|
mod codegen_registerspec_impl;
|
|
|
|
impl CodeGen for Register {
|
|
fn generate_register_interface(self) -> Result<proc_macro2::TokenStream, CodeGenError> {
|
|
let snake_case_name = util::parse_to_ident(&self.name.to_snake_case())?;
|
|
let upper_camel_name = util::parse_to_ident(&self.name.to_upper_camel_case())?;
|
|
let reg_name = util::parse_to_ident(&format!("Reg{upper_camel_name}"))?;
|
|
let addr = util::parse_to_literal(&format!("0x{:x}", self.addr))?;
|
|
|
|
let (code_t_def, type_t, type_ux): (
|
|
proc_macro2::TokenStream,
|
|
proc_macro2::Ident,
|
|
proc_macro2::Ident,
|
|
) = codegen_register::reg_type_def(&self, &upper_camel_name)?;
|
|
|
|
let code_reg_def: proc_macro2::TokenStream =
|
|
codegen_registerspec_impl::gen_registerspec_impl(
|
|
reg_name.clone(),
|
|
self.modf,
|
|
type_t,
|
|
type_ux,
|
|
);
|
|
|
|
Ok(quote! {
|
|
pub mod #snake_case_name {
|
|
use std::marker::PhantomData;
|
|
|
|
use crate::register_spec::{DataConversionError, Modifiable, Readable, RegisterSpec, Writable};
|
|
|
|
const OFFSET: usize = #addr;
|
|
|
|
pub struct #reg_name<'a> {
|
|
mem_ptr: *mut u32,
|
|
_marker: PhantomData<&'a mut super::Debug<'a>>,
|
|
}
|
|
|
|
impl #reg_name<'_> {
|
|
pub(crate) fn new(parent_ptr: *mut u32) -> Self {
|
|
#reg_name {
|
|
mem_ptr: unsafe { parent_ptr.add(OFFSET) },
|
|
_marker: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
#code_reg_def
|
|
|
|
#code_t_def
|
|
}
|
|
})
|
|
}
|
|
}
|