endcap-sl-software-ri-gener.../src/generator.rs

266 lines
8.7 KiB
Rust

//! Generate register interface rust code from types in [`crate::types`].
//!
//! root: [`CodeGen`]
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 = child_name.to_snake_case();
let upper_camel_name = 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
}
})
}
}