diff --git a/src/generate.rs b/src/generate.rs index a5e4fa18..3ffc2455 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -2,14 +2,13 @@ use std::collections::HashMap; use std::io::{self, Write}; use cast::u64; -use either::Either; -use quote::Tokens; +use quote::{Tokens, ToTokens}; use svd::{Access, BitRange, Defaults, Device, EnumeratedValues, Field, Peripheral, Register, Usage, WriteConstraint}; -use syn::{Ident, Lit}; +use syn::{self, Ident, Lit}; use errors::*; -use util::{self, ToSanitizedSnakeCase, ToSanitizedUpperCase, U32Ext}; +use util::{self, ToSanitizedSnakeCase, ToSanitizedUpperCase, U32Ext, BITS_PER_BYTE}; use Target; /// Whole device generation @@ -461,13 +460,87 @@ pub fn peripheral( Ok(()) } +struct RegisterBlockField { + field: syn::Field, + description: String, + offset: u32, + size: u32, +} + fn register_block(registers: &[Register], defs: &Defaults) -> Result { - let mut fields = vec![]; + let mut fields = Tokens::new(); // enumeration of reserved fields let mut i = 0; // offset from the base address, in bytes let mut offset = 0; - for register in util::expand(registers) { + let mut registers_expanded = vec![]; + + // If svd register arrays can't be converted to rust arrays (non sequential adresses, non numeral indexes, or not containing all elements from 0 to size) they will be expanded + for register in registers { + let register_size = register.size.or(defs.size) + .ok_or_else( + || { + format!("Register {} has no `size` field", register.name) + },)?; + + match *register { + Register::Single(ref info) => registers_expanded.push( + RegisterBlockField{ + field: util::convert_svd_register(register), + description: info.description.clone(), + offset: info.address_offset, + size: register_size, + } + ), + Register::Array(ref info, ref array_info) => { + let sequential_adresses = register_size == array_info.dim_increment*BITS_PER_BYTE; + + let numeral_indexes = array_info.dim_index.clone() + .ok_or_else( || format!("Register {} has no `dim_index` field", register.name))? + .iter() + .all(|element| element.parse::().is_ok()); + + let sequential_indexes = if numeral_indexes && sequential_adresses { + array_info.dim_index.clone() + .unwrap() + .iter() + .map(|element| element.parse::().unwrap()) + .collect::>() + .eq(&(0..array_info.dim).collect::>()) + } else { + false + }; + + let array_convertible = sequential_indexes && numeral_indexes && sequential_adresses; + + if array_convertible { + registers_expanded.push( + RegisterBlockField{ + field: util::convert_svd_register(®ister), + description: info.description.clone(), + offset: info.address_offset, + size: register_size * array_info.dim, + }); + } else { + let mut field_num = 0; + for field in util::expand_svd_register(register).iter() { + registers_expanded.push( + RegisterBlockField{ + field: field.clone(), + description: info.description.clone(), + offset: info.address_offset + field_num * array_info.dim_increment, + size: register_size, + }); + field_num += 1; + } + } + }, + } + } + + registers_expanded.sort_by_key(|x| x.offset); + + for register in registers_expanded { let pad = if let Some(pad) = register.offset.checked_sub(offset) { pad } else { @@ -475,55 +548,47 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { io::stderr(), "WARNING {} overlaps with another register at offset {}. \ Ignoring.", - register.name, + register.field.ident.unwrap(), register.offset - ).ok(); + ) + .ok(); continue; }; if pad != 0 { let name = Ident::new(format!("_reserved{}", i)); let pad = pad as usize; - fields.push(quote! { - #name : [u8; #pad], - }); + fields.append( + quote! { + #name : [u8; #pad], + }); i += 1; } let comment = &format!( "0x{:02x} - {}", register.offset, - util::respace(®ister.info.description) + util::respace(®ister.description), ) [..]; - - let rty = match register.ty { - Either::Left(ref ty) => Ident::from(&**ty), - Either::Right(ref ty) => Ident::from(&***ty), - }; - let reg_name = Ident::new(&*register.name.to_sanitized_snake_case()); - fields.push(quote! { - #[doc = #comment] - pub #reg_name : #rty, - }); - - offset = register.offset + - register - .info - .size - .or(defs.size) - .ok_or_else( - || { - format!("Register {} has no `size` field", register.name) - }, - )? / 8; + + fields.append( + quote! { + #[doc = #comment] + } + ); + + register.field.to_tokens(&mut fields); + Ident::new(",").to_tokens(&mut fields); + + offset = register.offset + register.size/BITS_PER_BYTE; } Ok(quote! { /// Register block #[repr(C)] pub struct RegisterBlock { - #(#fields)* + #fields } }) } diff --git a/src/util.rs b/src/util.rs index ca419943..c90583a0 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,14 +1,14 @@ use std::borrow::Cow; -use std::rc::Rc; -use either::Either; use inflections::Inflect; -use svd::{Access, EnumeratedValues, Field, Peripheral, Register, RegisterInfo, +use svd::{self, Access, EnumeratedValues, Field, Peripheral, Register, Usage}; -use syn::{Ident, IntTy, Lit}; +use syn::{self, Ident, IntTy, Lit}; use errors::*; +pub const BITS_PER_BYTE: u32 = 8; + /// List of chars that some vendors use in their peripheral/field names but /// that are not valid in Rust ident const BLACKLIST_CHARS: &'static [char] = &['(', ')', '[', ']']; @@ -136,86 +136,114 @@ pub fn respace(s: &str) -> String { s.split_whitespace().collect::>().join(" ") } -pub struct ExpandedRegister<'a> { - pub info: &'a RegisterInfo, - pub name: String, - pub offset: u32, - pub ty: Either>, -} +/// Takes a svd::Register which may be a register array, and turn in into +/// a list of syn::Field where the register arrays have been expanded. +pub fn expand_svd_register(register: &Register) -> Vec { + let name_to_ty = |name: &String| -> syn::Ty { + syn::Ty::Path(None, syn::Path{ + global: false, + segments: vec![syn::PathSegment{ + ident: Ident::new(name.to_sanitized_upper_case()), + parameters: syn::PathParameters::none(), + }], + }) + }; -/// Takes a list of "registers", some of which may actually be register arrays, -/// and turns it into a new *sorted* (by address offset) list of registers where -/// the register arrays have been expanded. -pub fn expand(registers: &[Register]) -> Vec { let mut out = vec![]; - for r in registers { - match *r { - Register::Single(ref info) => { - out.push( - ExpandedRegister { - info: info, - name: info.name.to_sanitized_snake_case().into_owned(), - offset: info.address_offset, - ty: Either::Left( - info.name - .to_sanitized_upper_case() - .into_owned(), - ), + match *register { + Register::Single(ref _info) => out.push( convert_svd_register(register) ), + Register::Array(ref info, ref array_info) => { + let has_brackets = info.name.contains("[%s]"); + + let indices = array_info + .dim_index + .as_ref() + .map(|v| Cow::from(&**v)) + .unwrap_or_else( + || { + Cow::from( + (0..array_info.dim) + .map(|i| i.to_string()) + .collect::>(), + ) }, - ) - } - Register::Array(ref info, ref array_info) => { - let has_brackets = info.name.contains("[%s]"); - - let ty = if has_brackets { + ); + + for (idx, _i) in indices.iter().zip(0..) { + let name = if has_brackets { + info.name.replace("[%s]", format!("{}", idx).as_str()) + } else { + info.name.replace("%s", format!("{}", idx).as_str()) + }; + + let ty_name = if has_brackets { info.name.replace("[%s]", "") } else { info.name.replace("%s", "") }; + + let ident = Ident::new(name.to_sanitized_snake_case()); + let ty = name_to_ty(&ty_name); - let ty = Rc::new(ty.to_sanitized_upper_case().into_owned()); - - let indices = array_info - .dim_index - .as_ref() - .map(|v| Cow::from(&**v)) - .unwrap_or_else( - || { - Cow::from( - (0..array_info.dim) - .map(|i| i.to_string()) - .collect::>(), - ) - }, - ); - - for (idx, i) in indices.iter().zip(0..) { - let name = if has_brackets { - info.name.replace("[%s]", idx) - } else { - info.name.replace("%s", idx) - }; - - let offset = info.address_offset + - i * array_info.dim_increment; - - out.push( - ExpandedRegister { - info: info, - name: name.to_sanitized_snake_case().into_owned(), - offset: offset, - ty: Either::Right(ty.clone()), - }, - ); - } + out.push( + syn::Field{ + ident: Some(ident), + vis: syn::Visibility::Public, + attrs: vec![], + ty: ty, + } + ); } - } + }, } + out +} - out.sort_by_key(|x| x.offset); +pub fn convert_svd_register(register: &svd::Register) -> syn::Field { + let name_to_ty = |name: &String| -> syn::Ty { + syn::Ty::Path(None, syn::Path{ + global: false, + segments: vec![syn::PathSegment{ + ident: Ident::new(name.to_sanitized_upper_case()), + parameters: syn::PathParameters::none(), + }], + }) + }; + + match *register { + Register::Single(ref info) => { + syn::Field{ + ident: Some(Ident::new(info.name.to_sanitized_snake_case())), + vis: syn::Visibility::Public, + attrs: vec![], + ty: name_to_ty(&info.name), + } + }, + Register::Array(ref info, ref array_info) => { + let has_brackets = info.name.contains("[%s]"); - out + let name = if has_brackets { + info.name.replace("[%s]", "") + } else { + info.name.replace("%s", "") + }; + + let ident = Ident::new(name.to_sanitized_snake_case()); + + let ty = syn::Ty::Array( + Box::new(name_to_ty(&name)), + syn::ConstExpr::Lit(syn::Lit::Int(array_info.dim as u64, syn::IntTy::Unsuffixed)), + ); + + syn::Field{ + ident: Some(ident), + vis: syn::Visibility::Public, + attrs: vec![], + ty: ty, + } + }, + } } pub fn name_of(register: &Register) -> Cow {