#![recursion_limit = "128"]
extern crate either;
extern crate inflections;
extern crate svd_parser as svd;
#[macro_use]
extern crate quote;
extern crate syn;
use std::borrow::Cow;
use std::io::Write;
use std::io;
use std::rc::Rc;
use either::Either;
use inflections::Inflect;
use quote::Tokens;
use svd::{Access, Defaults, Peripheral, Register, RegisterInfo};
use syn::*;
trait SanitizeName {
fn sanitize(&self) -> String;
}
impl SanitizeName for String {
fn sanitize(&self) -> String {
match self.as_str() {
"fn" => "fn_".to_owned(),
"in" => "in_".to_owned(),
"match" => "match_".to_owned(),
"mod" => "mod_".to_owned(),
_ => self.to_owned(),
}
}
}
#[doc(hidden)]
pub fn gen_peripheral(p: &Peripheral, d: &Defaults) -> Vec<Tokens> {
assert!(p.derived_from.is_none(),
"DerivedFrom not supported here (should be resolved earlier)");
let mut items = vec![];
let mut fields = vec![];
let mut offset = 0;
let mut i = 0;
let registers = p.registers
.as_ref()
.expect(&format!("{:#?} has no `registers` field", p));
for register in expand(registers).iter() {
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.offset)
.ok();
continue;
};
if pad != 0 {
let name = Ident::new(format!("_reserved{}", i));
let pad = pad as usize;
fields.push(quote! {
#name : [u8; #pad]
});
i += 1;
}
let comment = &format!("0x{:02x} - {}",
register.offset,
respace(®ister.info
.description))[..];
let reg_ty = match register.ty {
Either::Left(ref ty) => Ident::from(&**ty),
Either::Right(ref ty) => Ident::from(&***ty),
};
let reg_name = Ident::new(&*register.name);
fields.push(quote! {
#[doc = #comment]
pub #reg_name : #reg_ty
});
offset = register.offset +
register.info
.size
.or(d.size)
.expect(&format!("{:#?} has no `size` field", register.info)) /
8;
}
let p_name = Ident::new(p.name.to_pascal_case());
if let Some(description) = p.description.as_ref() {
let comment = &respace(description)[..];
items.push(quote! {
#[doc = #comment]
});
}
let struct_ = quote! {
#[repr(C)]
pub struct #p_name {
#(#fields),*
}
};
items.push(struct_);
for register in registers {
let access = access(®ister);
items.extend(gen_register(register, d));
if let Some(ref fields) = register.fields {
if access != Access::WriteOnly {
items.extend(gen_register_r(register, d, fields));
}
if access != Access::ReadOnly {
items.extend(gen_register_w(register, d, fields));
}
}
}
items
}
struct ExpandedRegister<'a> {
info: &'a RegisterInfo,
name: String,
offset: u32,
ty: Either<String, Rc<String>>,
}
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_snake_case().sanitize(),
offset: info.address_offset,
ty: Either::Left(info.name.to_pascal_case()),
})
}
Register::Array(ref info, ref array_info) => {
let has_brackets = info.name.contains("[%s]");
let ty = if has_brackets {
info.name.replace("[%s]", "")
} else {
info.name.replace("%s", "")
};
let ty = Rc::new(ty.to_pascal_case());
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_snake_case().sanitize(),
offset: offset,
ty: Either::Right(ty.clone()),
});
}
}
}
}
out.sort_by_key(|x| x.offset);
out
}
fn type_of(r: &Register) -> String {
let ty = match *r {
Register::Single(ref info) => Cow::from(&*info.name),
Register::Array(ref info, _) => {
if info.name.contains("[%s]") {
info.name.replace("[%s]", "").into()
} else {
info.name.replace("%s", "").into()
}
}
};
(&*ty).to_pascal_case()
}
fn access(r: &Register) -> Access {
r.access.unwrap_or_else(|| {
if let Some(ref fields) = r.fields {
if fields.iter().all(|f| f.access == Some(Access::ReadOnly)) {
Access::ReadOnly
} else if fields.iter().all(|f| f.access == Some(Access::WriteOnly)) {
Access::WriteOnly
} else {
Access::ReadWrite
}
} else {
Access::ReadWrite
}
})
}
#[doc(hidden)]
pub fn gen_register(r: &Register, d: &Defaults) -> Vec<Tokens> {
let mut items = vec![];
let ty = type_of(r);
let name = Ident::new(&*ty);
let bits_ty = r.size
.or(d.size)
.expect(&format!("{:#?} has no `size` field", r))
.to_ty();
let access = access(r);
match access {
Access::ReadOnly => {
items.push(quote! {
#[repr(C)]
pub struct #name {
register: ::volatile_register::RO<#bits_ty>
}
});
}
Access::ReadWrite => {
items.push(quote! {
#[repr(C)]
pub struct #name {
register: ::volatile_register::RW<#bits_ty>
}
});
}
Access::WriteOnly => {
items.push(quote! {
#[repr(C)]
pub struct #name {
register: ::volatile_register::WO<#bits_ty>
}
});
}
_ => unreachable!(),
}
if r.fields.is_some() {
let name_r = Ident::new(format!("{}R", ty));
let name_w = Ident::new(format!("{}W", ty));
match access {
Access::ReadOnly => {
items.push(quote! {
impl #name {
pub fn read(&self) -> #name_r {
#name_r { bits: self.register.read() }
}
}
});
}
Access::ReadWrite => {
items.push(quote! {
impl #name {
pub fn modify<F>(&mut self, f: F)
where for<'w> F: FnOnce(&#name_r, &'w mut #name_w) -> &'w mut #name_w,
{
let bits = self.register.read();
let r = #name_r { bits: bits };
let mut w = #name_w { bits: bits };
f(&r, &mut w);
self.register.write(w.bits);
}
pub fn read(&self) -> #name_r {
#name_r { bits: self.register.read() }
}
pub fn write<F>(&mut self, f: F)
where F: FnOnce(&mut #name_w) -> &mut #name_w,
{
let mut w = #name_w::reset_value();
f(&mut w);
self.register.write(w.bits);
}
}
});
}
Access::WriteOnly => {
items.push(quote! {
impl #name {
pub fn write<F>(&self, f: F)
where F: FnOnce(&mut #name_w) -> &mut #name_w,
{
let mut w = #name_w::reset_value();
f(&mut w);
self.register.write(w.bits);
}
}
});
}
_ => unreachable!(),
}
} else {
match access {
Access::ReadOnly => {
items.push(quote! {
impl #name {
pub fn read(&self) -> #bits_ty {
self.register.read()
}
}
});
}
Access::ReadWrite => {
items.push(quote! {
impl #name {
pub fn read(&self) -> #bits_ty {
self.register.read()
}
pub fn write(&mut self, value: #bits_ty) {
self.register.write(value);
}
}
});
}
Access::WriteOnly => {
items.push(quote! {
impl #name {
pub fn write(&mut self, value: #bits_ty) {
self.register.write(value);
}
}
});
}
_ => unreachable!(),
}
}
items
}
#[doc(hidden)]
pub fn gen_register_r(r: &Register,
d: &Defaults,
fields: &[svd::Field])
-> Vec<Tokens> {
let mut items = vec![];
let name = Ident::new(format!("{}R", type_of(r)));
let bits_ty = r.size
.or(d.size)
.expect(&format!("{:#?} has no `size` field", r))
.to_ty();
items.push(quote! {
#[derive(Clone, Copy)]
#[repr(C)]
pub struct #name {
bits: #bits_ty,
}});
let mut impl_items = vec![];
for field in fields {
if let Some(Access::WriteOnly) = field.access {
continue;
}
let name = Ident::new(field.name.to_snake_case().sanitize());
let offset = field.bit_range.offset as u8;
let width = field.bit_range.width;
if let Some(description) = field.description.as_ref() {
let bits = if width == 1 {
format!("Bit {}", field.bit_range.offset)
} else {
format!("Bits {}:{}",
field.bit_range.offset,
field.bit_range.offset + width - 1)
};
let comment = &format!("{} - {}", bits, respace(description))[..];
impl_items.push(quote! {
#[doc = #comment]
});
}
let item = if width == 1 {
quote! {
pub fn #name(&self) -> bool {
const OFFSET: u8 = #offset;
self.bits & (1 << OFFSET) != 0
}
}
} else {
let width_ty = width.to_ty();
let mask: u64 = (1 << width) - 1;
let mask = Lit::Int(mask, IntTy::Unsuffixed);
quote! {
pub fn #name(&self) -> #width_ty {
const MASK: #bits_ty = #mask;
const OFFSET: u8 = #offset;
((self.bits >> OFFSET) & MASK) as #width_ty
}
}
};
impl_items.push(item);
}
items.push(quote! {
impl #name {
#(#impl_items)*
}
});
items
}
#[doc(hidden)]
pub fn gen_register_w(r: &Register,
d: &Defaults,
fields: &[svd::Field])
-> Vec<Tokens> {
let mut items = vec![];
let name = Ident::new(format!("{}W", type_of(r)));
let bits_ty = r.size
.or(d.size)
.expect(&format!("{:#?} has no `size` field", r))
.to_ty();
items.push(quote! {
#[derive(Clone, Copy)]
#[repr(C)]
pub struct #name {
bits: #bits_ty,
}
});
let mut impl_items = vec![];
if let Some(reset_value) =
r.reset_value
.or(d.reset_value)
.map(|x| Lit::Int(x as u64, IntTy::Unsuffixed)) {
impl_items.push(quote! {
pub fn reset_value() -> Self {
#name { bits: #reset_value }
}
});
}
for field in fields {
if let Some(Access::ReadOnly) = field.access {
continue;
}
let name = Ident::new(field.name.to_snake_case().sanitize());
let offset = field.bit_range.offset as u8;
let width = field.bit_range.width;
if let Some(description) = field.description.as_ref() {
let bits = if width == 1 {
format!("Bit {}", field.bit_range.offset)
} else {
format!("Bits {}:{}",
field.bit_range.offset,
field.bit_range.offset + width - 1)
};
let comment = &format!("{} - {}", bits, respace(description))[..];
impl_items.push(quote! {
#[doc = #comment]
});
}
let item = if width == 1 {
quote! {
pub fn #name(&mut self, value: bool) -> &mut Self {
const OFFSET: u8 = #offset;
if value {
self.bits |= 1 << OFFSET;
} else {
self.bits &= !(1 << OFFSET);
}
self
}
}
} else {
let width_ty = width.to_ty();
let mask = (1 << width) - 1;
let mask = Lit::Int(mask, IntTy::Unsuffixed);
quote! {
pub fn #name(&mut self, value: #width_ty) -> &mut Self {
const OFFSET: u8 = #offset;
const MASK: #width_ty = #mask;
self.bits &= !((MASK as #bits_ty) << OFFSET);
self.bits |= ((value & MASK) as #bits_ty) << OFFSET;
self
}
}
};
impl_items.push(item);
}
items.push(quote! {
impl #name {
#(#impl_items)*
}
});
items
}
trait U32Ext {
fn to_ty(&self) -> Ident;
}
impl U32Ext for u32 {
fn to_ty(&self) -> Ident {
match *self {
1...8 => Ident::new("u8"),
9...16 => Ident::new("u16"),
17...32 => Ident::new("u32"),
_ => panic!("{}.to_ty()", *self),
}
}
}
fn respace(s: &str) -> String {
s.split_whitespace().collect::<Vec<_>>().join(" ")
}