use crate::info::*;
use quote::{format_ident, quote};
use rust_embed::RustEmbed;
use serde::Deserialize;
use std::io::Write;
#[cfg(feature = "rustfmt")]
fn rustfmt(input: &str) -> String {
let mut buf = Vec::new();
{
let mut config = rustfmt_nightly::Config::default();
config.set().emit_mode(rustfmt_nightly::EmitMode::Stdout);
config.set().edition(rustfmt_nightly::Edition::Edition2018);
config.set().verbose(rustfmt_nightly::Verbosity::Quiet);
let mut session = rustfmt_nightly::Session::new(config, Some(&mut buf));
session
.format(rustfmt_nightly::Input::Text(input.to_string()))
.unwrap();
}
String::from_utf8(buf).unwrap()
}
#[cfg(not(feature = "rustfmt"))]
fn rustfmt(input: &str) -> String {
input.to_string()
}
fn generate_filler_field(lsb: u32, size: u32) -> proc_macro2::TokenStream {
let fieldname = format_ident!("reserved_filler_lsb{}", lsb);
let fieldtype = format_ident!("B{}", size);
quote! {
#fieldname: #fieldtype
}
}
fn generate_register_struct(register: &Register, word_size: usize) -> proc_macro2::TokenStream {
let regname = format_ident!("{}", register.name);
let mut enums = vec![];
let mut fields = vec![];
let mut current_bit = 0;
let mut sorted_fields = register.fields.clone();
sorted_fields.sort_by_key(|field| field.lsb);
for field in sorted_fields.iter() {
log::trace!(
"Processing field {}, [{}:{}]",
field.name,
field.msb,
field.lsb
);
if field.lsb != current_bit {
log::trace!(
"Filled to bit {}, field starts at bit {}",
current_bit,
field.lsb
);
let size = field.lsb - current_bit;
fields.push(generate_filler_field(current_bit, size));
}
let fieldname = format_ident!("{}", field.name.to_ascii_lowercase());
let nbits = proc_macro2::Literal::u32_unsuffixed(field.msb - field.lsb + 1);
if field.enumerations.len() > 0 {
let enumerations: Vec<_> = field
.enumerations
.iter()
.map(|enumeration| {
let enumname = format_ident!("{}", enumeration.name);
let value = enumeration.value as isize;
quote! {
#enumname = #value
}
})
.collect();
let enumname = format_ident!("{}", field.name);
enums.push(quote! {
#[derive(BitfieldSpecifier, Debug, PartialEq)]
#[bits = #nbits]
pub enum #enumname {
#(#enumerations,)*
}
});
fields.push(quote! {
#[bits = #nbits]
pub #fieldname: #enumname
});
} else {
let fieldtype = format_ident!("B{}", field.msb - field.lsb + 1);
fields.push(quote! { pub #fieldname: #fieldtype });
}
current_bit = field.msb + 1;
}
if current_bit != word_size as u32 {
let size = word_size as u32 - current_bit;
log::debug!("Inserting {}-bit field into {}", size, register.name);
fields.push(generate_filler_field(current_bit, size as u32));
}
quote! {
pub mod #regname {
use modular_bitfield::prelude::*;
#(#enums)*
#[bitfield]
#[derive(Debug, PartialEq)]
pub struct #regname {
#(#fields,)*
}
}
}
}
#[derive(RustEmbed)]
#[folder = "$CARGO_MANIFEST_DIR/src/templates/rust/"]
struct Templates;
fn generate_cargo() -> String {
let mut tera = tera::Tera::default();
tera.add_raw_template(
"Cargo.toml",
std::str::from_utf8(&Templates::get("Cargo.toml").unwrap()).unwrap(),
)
.unwrap();
tera.render("Cargo.toml", &tera::Context::new()).unwrap()
}
fn generate_periph(periph: &Peripheral) -> String {
let periph_name = format_ident!("{}", periph.name);
let accessortype = format_ident!("u{}", periph.word_size);
let periph_struct = quote! {
pub struct #periph_name<ACCESSOR: RegmapAccessor<#accessortype>> {
accessor: ACCESSOR,
}
};
let registers: Vec<_> = periph
.regs
.iter()
.map(|reg| generate_register_struct(®, periph.word_size))
.collect();
let word = format_ident!("u{}", periph.word_size);
let accessors: Vec<_> = periph
.regs
.iter()
.map(|reg| {
let getter_name = format_ident!("get_{}", reg.name.to_ascii_lowercase());
let setter_name = format_ident!("set_{}", reg.name.to_ascii_lowercase());
let regname = format_ident!("{}", reg.name);
let offset = match periph.word_size {
32 => proc_macro2::Literal::u32_suffixed(reg.offset as u32),
64 => proc_macro2::Literal::u64_suffixed(reg.offset),
_ => {
panic!("Unsupported word size!");
}
};
if reg.is_raw {
quote! {
pub fn #getter_name(&mut self) -> #word {
let value = self.accessor.peek(#offset as #word);
value
}
pub fn #setter_name(&mut self, value: #word) {
self.accessor.poke(#offset as #word, value);
}
}
} else {
quote! {
pub fn #getter_name(&mut self) -> ::std::result::Result<#regname::#regname, ACCESSOR::Error> {
let value = self.accessor.peek(#offset)?;
let value = value.to_le_bytes();
Ok(#regname::#regname::from_bytes(value))
}
pub fn #setter_name(&mut self, value: #regname::#regname) -> ::std::result::Result<(), ACCESSOR::Error> {
let raw = value.into_bytes();
let raw = #word::from_le_bytes(raw);
self.accessor.poke(#offset, raw)
}
}
}
})
.collect();
let code = quote! {
pub mod #periph_name {
use mockall::*;
use mockall::predicate::*;
use modular_bitfield::prelude::*;
use std::convert::TryInto;
use regmap_support::*;
#periph_struct
#(#registers)*
impl<ACCESSOR: RegmapAccessor<#accessortype>> #periph_name<ACCESSOR> {
pub fn new(accessor: ACCESSOR) -> Self {
Self {
accessor,
}
}
#(#accessors)*
}
}
};
rustfmt(&format!("{}", code))
}
#[derive(Debug, Deserialize)]
pub struct RustConfig {
pub path: String,
pub out_filename: Option<String>,
}
pub fn generate_rust(config: &RustConfig, periphs: &Vec<Peripheral>) {
let mut file = std::fs::File::create(&format!("{}", config.path)).unwrap();
for periph in periphs.iter() {
let lib_code = generate_periph(periph);
file.write_all(lib_code.as_bytes()).unwrap();
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::info::*;
#[test]
fn test_can_generate() {
let periph = Peripheral {
name: "TestPeripheral".to_string(),
word_size: 32,
regs: vec![Register {
name: "TestRegister".to_string(),
offset: 4,
is_raw: false,
fields: vec![Field {
name: "TestField".to_string(),
lsb: 0,
msb: 5,
enumerations: vec![
Enumeration {
name: "Value1".to_string(),
value: 1,
},
Enumeration {
name: "Value21".to_string(),
value: 21,
},
],
}],
}],
};
let s = generate_periph(&periph);
assert!(s.len() > 0);
}
}