//! 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 { Ok(syn::parse_str(s)?) } pub(super) fn parse_to_literal(s: &str) -> Result { 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; } impl CodeGen for Module { fn generate_register_interface(self) -> Result { 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::, _>>()?; for child in child_mods { out.extend(child); } Ok(out) } } impl CodeGen for ModuleBlockElements { fn generate_register_interface(self) -> Result { 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 { 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::, _>>()?; 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 { 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 } }) } }