Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8ed24a1
Extracted sort from expand into its own function
kjetilkjeka Jul 5, 2017
c47764d
Made expand work directly on Register structure instead of using Expa…
kjetilkjeka Jul 5, 2017
dd8c1b1
Refactored out the piece of code that formats the rust field
kjetilkjeka Jul 5, 2017
806f722
Made it possible to only expand certain registers by making expand ca…
kjetilkjeka Jul 5, 2017
63d1af8
Implemented conversion from Array type register to rust array
kjetilkjeka Jul 5, 2017
6110797
Wrote the condition that checks if the array is convertable. If it on…
kjetilkjeka Jul 5, 2017
e8b5035
Fixed bug where offset was calculated incorrectly with register arrays
kjetilkjeka Jul 5, 2017
244b06c
Require that the svd array represented a packed memory area to be con…
kjetilkjeka Jul 5, 2017
f0e85fd
Used regex to replace %s on expanded fields, the previous used hacked…
kjetilkjeka Jul 5, 2017
a24a245
Added function for converting svd registers into rust fields
kjetilkjeka Jul 6, 2017
8a52649
Instead of working on Register structs inside register_block we now w…
kjetilkjeka Jul 6, 2017
fc238af
Removed everything relating to regex as it's not needed (and never sh…
kjetilkjeka Jul 6, 2017
09f191e
Refactored out register size to avoid duplication of error detection …
kjetilkjeka Jul 6, 2017
db90f78
Added comment explaining when registers will be expanded
kjetilkjeka Jul 6, 2017
aff4410
Added BITS_PER_BYTE constant
kjetilkjeka Jul 6, 2017
d3c0632
Fixed coding error. A condition for converting from svd array to rust…
kjetilkjeka Jul 6, 2017
3d3cd94
Cleaned up conditions that find out if register should be expanded or…
kjetilkjeka Jul 6, 2017
5d1a0f7
Swaped out magic number 8 by BITS_PER_BYTE
kjetilkjeka Jul 6, 2017
26849d6
Made include self style consistent with the rest of the project
kjetilkjeka Jul 7, 2017
1ee0872
Changed description type to String from Ident
kjetilkjeka Sep 15, 2017
52cf267
Added respace to avoid breaking docs
kjetilkjeka Sep 15, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 99 additions & 34 deletions src/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -461,69 +460,135 @@ pub fn peripheral(
Ok(())
}

struct RegisterBlockField {
field: syn::Field,
description: String,
offset: u32,
size: u32,
}

fn register_block(registers: &[Register], defs: &Defaults) -> Result<Tokens> {
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::<usize>().is_ok());

let sequential_indexes = if numeral_indexes && sequential_adresses {
array_info.dim_index.clone()
.unwrap()
.iter()
.map(|element| element.parse::<u32>().unwrap())
.collect::<Vec<u32>>()
.eq(&(0..array_info.dim).collect::<Vec<u32>>())
} else {
false
};

let array_convertible = sequential_indexes && numeral_indexes && sequential_adresses;

if array_convertible {
registers_expanded.push(
RegisterBlockField{
field: util::convert_svd_register(&register),
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 {
writeln!(
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(&register.info.description)
util::respace(&register.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
}
})
}
Expand Down
170 changes: 99 additions & 71 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -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] = &['(', ')', '[', ']'];
Expand Down Expand Up @@ -136,86 +136,114 @@ pub fn respace(s: &str) -> String {
s.split_whitespace().collect::<Vec<_>>().join(" ")
}

pub struct ExpandedRegister<'a> {
pub info: &'a RegisterInfo,
pub name: String,
pub offset: u32,
pub ty: Either<String, Rc<String>>,
}
/// 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<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(),
}],
})
};

/// 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<ExpandedRegister> {
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::<Vec<_>>(),
)
},
)
}
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::<Vec<_>>(),
)
},
);

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<str> {
Expand Down