use crate::svd::{array::names, Device, Peripheral};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, ToTokens};
use log::debug;
use std::borrow::Cow;
use std::fs::File;
use std::io::Write;
use crate::util::{self, Config, ToSanitizedCase};
use crate::Target;
use anyhow::{Context, Result};
use crate::generate::{interrupt, peripheral};
pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result<TokenStream> {
let index = svd_parser::expand::Index::create(d);
let mut out = TokenStream::new();
let commit_info = {
let tmp = include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"));
if tmp.is_empty() {
" (untracked)"
} else {
tmp
}
};
let doc = format!(
"Peripheral access API for {0} microcontrollers \
(generated using svd2rust v{1}{commit_info})\n\n\
You can find an overview of the generated API [here].\n\n\
API features to be included in the [next] svd2rust \
release can be generated by cloning the svd2rust [repository], \
checking out the above commit, and running `cargo doc --open`.\n\n\
[here]: https://docs.rs/svd2rust/{1}/svd2rust/#peripheral-api\n\
[next]: https://github.com/rust-embedded/svd2rust/blob/master/CHANGELOG.md#unreleased\n\
[repository]: https://github.com/rust-embedded/svd2rust",
d.name.to_uppercase(),
env!("CARGO_PKG_VERSION"),
);
if config.target == Target::Msp430 {
out.extend(quote! {
#![feature(abi_msp430_interrupt)]
});
}
out.extend(quote! { #![doc = #doc] });
if !config.make_mod {
out.extend(quote! {
#![deny(dead_code)]
#![deny(improper_ctypes)]
#![deny(missing_docs)]
#![deny(no_mangle_generic_items)]
#![deny(non_shorthand_field_patterns)]
#![deny(overflowing_literals)]
#![deny(path_statements)]
#![deny(patterns_in_fns_without_body)]
#![deny(private_in_public)]
#![deny(unconditional_recursion)]
#![deny(unused_allocation)]
#![deny(unused_comparisons)]
#![deny(unused_parens)]
#![deny(while_true)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![no_std]
});
}
out.extend(quote! {
use core::ops::Deref;
use core::marker::PhantomData;
});
let mut fpu_present = true;
if let Some(cpu) = d.cpu.as_ref() {
let bits = util::unsuffixed(u64::from(cpu.nvic_priority_bits));
out.extend(quote! {
pub const NVIC_PRIO_BITS: u8 = #bits;
});
fpu_present = cpu.fpu_present;
}
let core_peripherals: &[_] = if fpu_present {
&[
"CBP", "CPUID", "DCB", "DWT", "FPB", "FPU", "ITM", "MPU", "NVIC", "SCB", "SYST", "TPIU",
]
} else {
&[
"CBP", "CPUID", "DCB", "DWT", "FPB", "ITM", "MPU", "NVIC", "SCB", "SYST", "TPIU",
]
};
let mut fields = TokenStream::new();
let mut exprs = TokenStream::new();
if config.target == Target::CortexM {
out.extend(quote! {
pub use cortex_m::peripheral::Peripherals as CorePeripherals;
#[cfg(feature = "rt")]
pub use cortex_m_rt::interrupt;
#[cfg(feature = "rt")]
pub use self::Interrupt as interrupt;
});
if fpu_present {
out.extend(quote! {
pub use cortex_m::peripheral::{
CBP, CPUID, DCB, DWT, FPB, FPU, ITM, MPU, NVIC, SCB, SYST, TPIU,
};
});
} else {
out.extend(quote! {
pub use cortex_m::peripheral::{
CBP, CPUID, DCB, DWT, FPB, ITM, MPU, NVIC, SCB, SYST, TPIU,
};
});
}
}
if config.target == Target::Msp430 {
out.extend(quote! {
#[cfg(feature = "rt")]
pub use msp430_rt::interrupt;
#[cfg(feature = "rt")]
pub use self::Interrupt as interrupt;
});
}
if config.target == Target::Mips {
out.extend(quote! {
#[cfg(feature = "rt")]
pub use mips_rt::interrupt;
});
}
let generic_file = include_str!("generic.rs");
let generic_atomic_file = include_str!("generic_atomic.rs");
let array_proxy = include_str!("array_proxy.rs");
if config.generic_mod {
let mut file = File::create(config.output_dir.join("generic.rs"))?;
writeln!(file, "{}", generic_file)?;
if config.atomics {
writeln!(file, "\n{}", generic_atomic_file)?;
}
if config.const_generic {
writeln!(file, "{}", array_proxy)?;
}
if !config.make_mod {
out.extend(quote! {
#[allow(unused_imports)]
use generic::*;
#[doc="Common register and bit access and modify traits"]
pub mod generic;
});
}
} else {
let mut tokens = syn::parse_file(generic_file)?.into_token_stream();
if config.atomics {
syn::parse_file(generic_atomic_file)?.to_tokens(&mut tokens);
}
if config.const_generic {
syn::parse_file(array_proxy)?.to_tokens(&mut tokens);
}
out.extend(quote! {
#[allow(unused_imports)]
use generic::*;
pub mod generic {
#tokens
}
});
}
debug!("Rendering interrupts");
out.extend(interrupt::render(
config.target,
&d.peripherals,
device_x,
config,
)?);
for p in &d.peripherals {
if config.target == Target::CortexM
&& core_peripherals.contains(&p.name.to_uppercase().as_ref())
{
continue;
}
debug!("Rendering peripheral {}", p.name);
match peripheral::render(p, &index, config) {
Ok(periph) => out.extend(periph),
Err(e) => {
let descrip = p.description.as_deref().unwrap_or("No description");
let group_name = p.group_name.as_deref().unwrap_or("No group name");
let mut context_string = format!(
"Rendering error at peripheral\nName: {}\nDescription: {descrip}\nGroup: {group_name}",
p.name
);
if let Some(dname) = p.derived_from.as_ref() {
context_string = format!("{context_string}\nDerived from: {dname}");
}
let mut e = Err(e);
e = e.with_context(|| context_string);
return e;
}
};
if p.registers
.as_ref()
.map(|v| &v[..])
.unwrap_or(&[])
.is_empty()
&& p.derived_from.is_none()
{
continue;
}
let mut feature_attribute = TokenStream::new();
if config.feature_group && p.group_name.is_some() {
let feature_name = p.group_name.as_ref().unwrap().to_sanitized_snake_case();
feature_attribute.extend(quote! { #[cfg(feature = #feature_name)] })
};
match p {
Peripheral::Single(_p) => {
let p_name = util::name_of(p, config.ignore_groups);
let p_snake = p_name.to_sanitized_snake_case();
let p = p_name.to_sanitized_constant_case();
let id = Ident::new(&p, Span::call_site());
if config.feature_peripheral {
feature_attribute.extend(quote! { #[cfg(feature = #p_snake)] })
};
fields.extend(quote! {
#[doc = #p]
#feature_attribute
pub #id: #id,
});
exprs.extend(quote!(#feature_attribute #id: #id { _marker: PhantomData },));
}
Peripheral::Array(_p, dim_element) => {
let p_names: Vec<Cow<str>> = names(p, dim_element).map(|n| n.into()).collect();
let p = p_names.iter().map(|p| p.to_sanitized_constant_case());
let ids_f = p.clone().map(|p| Ident::new(&p, Span::call_site()));
let ids_e = ids_f.clone();
let feature_attribute = p_names
.iter()
.map(|p_name| {
let p_snake = p_name.to_sanitized_snake_case();
let mut feature_attribute = feature_attribute.clone();
if config.feature_peripheral {
feature_attribute.extend(quote! { #[cfg(feature = #p_snake)] })
};
feature_attribute
})
.collect::<Vec<_>>();
fields.extend(quote! {
#(
#[doc = #p]
#feature_attribute
pub #ids_f: #ids_f,
)*
});
exprs.extend(
quote!(#(#feature_attribute #ids_e: #ids_e { _marker: PhantomData },)*),
);
}
}
}
out.extend(quote! {
#[no_mangle]
static mut DEVICE_PERIPHERALS: bool = false;
#[allow(non_snake_case)]
pub struct Peripherals {
#fields
}
impl Peripherals {
#[cfg(feature = "critical-section")]
#[inline]
pub fn take() -> Option<Self> {
critical_section::with(|_| {
if unsafe { DEVICE_PERIPHERALS } {
return None
}
Some(unsafe { Peripherals::steal() })
})
}
#[inline]
pub unsafe fn steal() -> Self {
DEVICE_PERIPHERALS = true;
Peripherals {
#exprs
}
}
}
});
Ok(out)
}