embassy/embassy-macros/src/lib.rs
Ulf Lilleengen 9586365b07 Pass config directly to chip specific configure function
This removes the need to duplicate the configuration for each individual
chip, but will instead pass on the configuration specified in the config
attribute.

Update nrf, stm32, rp macros with passing the config to a per-chip
configure function which assumes the appropriate configuration to be
passed to it.

To demonstrate this feature, the stm32l0xx clock setup and RTC is added which exposes
clock configuration different from stm32f4xx (and has a different set of timers and HAL APIs).
2021-04-22 09:10:46 +02:00

396 lines
11 KiB
Rust

#![feature(proc_macro_diagnostic)]
extern crate proc_macro;
use darling::FromMeta;
use proc_macro::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::spanned::Spanned;
mod path;
use path::ModulePrefix;
#[derive(Debug, FromMeta)]
struct TaskArgs {
#[darling(default)]
pool_size: Option<usize>,
#[darling(default)]
send: bool,
#[darling(default)]
embassy_prefix: ModulePrefix,
}
#[proc_macro_attribute]
pub fn task(args: TokenStream, item: TokenStream) -> TokenStream {
let macro_args = syn::parse_macro_input!(args as syn::AttributeArgs);
let mut task_fn = syn::parse_macro_input!(item as syn::ItemFn);
let macro_args = match TaskArgs::from_list(&macro_args) {
Ok(v) => v,
Err(e) => {
return TokenStream::from(e.write_errors());
}
};
let embassy_prefix = macro_args.embassy_prefix.append("embassy");
let embassy_path = embassy_prefix.path();
let pool_size: usize = macro_args.pool_size.unwrap_or(1);
let mut fail = false;
if task_fn.sig.asyncness.is_none() {
task_fn
.sig
.span()
.unwrap()
.error("task functions must be async")
.emit();
fail = true;
}
if !task_fn.sig.generics.params.is_empty() {
task_fn
.sig
.span()
.unwrap()
.error("task functions must not be generic")
.emit();
fail = true;
}
if pool_size < 1 {
Span::call_site()
.error("pool_size must be 1 or greater")
.emit();
fail = true
}
let mut arg_names: syn::punctuated::Punctuated<syn::Ident, syn::Token![,]> =
syn::punctuated::Punctuated::new();
let mut args = task_fn.sig.inputs.clone();
for arg in args.iter_mut() {
match arg {
syn::FnArg::Receiver(_) => {
arg.span()
.unwrap()
.error("task functions must not have receiver arguments")
.emit();
fail = true;
}
syn::FnArg::Typed(t) => match t.pat.as_mut() {
syn::Pat::Ident(i) => {
arg_names.push(i.ident.clone());
i.mutability = None;
}
_ => {
arg.span()
.unwrap()
.error("pattern matching in task arguments is not yet supporteds")
.emit();
fail = true;
}
},
}
}
if fail {
return TokenStream::new();
}
let name = task_fn.sig.ident.clone();
let visibility = &task_fn.vis;
task_fn.sig.ident = format_ident!("task");
let impl_ty = if macro_args.send {
quote!(impl ::core::future::Future + Send + 'static)
} else {
quote!(impl ::core::future::Future + 'static)
};
let result = quote! {
#visibility fn #name(#args) -> #embassy_path::executor::SpawnToken<#impl_ty> {
use #embassy_path::executor::raw::Task;
#task_fn
type F = #impl_ty;
const NEW_TASK: Task<F> = Task::new();
static POOL: [Task<F>; #pool_size] = [NEW_TASK; #pool_size];
unsafe { Task::spawn_pool(&POOL, move || task(#arg_names)) }
}
};
result.into()
}
#[proc_macro]
pub fn interrupt_declare(item: TokenStream) -> TokenStream {
let name = syn::parse_macro_input!(item as syn::Ident);
let name = format_ident!("{}", name);
let name_interrupt = format_ident!("{}", name);
let name_handler = format!("__EMBASSY_{}_HANDLER", name);
let result = quote! {
#[allow(non_camel_case_types)]
pub struct #name_interrupt(());
unsafe impl Interrupt for #name_interrupt {
type Priority = crate::interrupt::Priority;
fn number(&self) -> u16 {
use cortex_m::interrupt::InterruptNumber;
let irq = crate::pac::Interrupt::#name;
irq.number() as u16
}
unsafe fn steal() -> Self {
Self(())
}
unsafe fn __handler(&self) -> &'static ::embassy::interrupt::Handler {
#[export_name = #name_handler]
static HANDLER: ::embassy::interrupt::Handler = ::embassy::interrupt::Handler::new();
&HANDLER
}
}
impl ::embassy::util::Unborrow for #name_interrupt {
type Target = #name_interrupt;
unsafe fn unborrow(self) -> #name_interrupt {
self
}
}
impl ::embassy::util::Unborrow for &mut #name_interrupt {
type Target = #name_interrupt;
unsafe fn unborrow(self) -> #name_interrupt {
::core::ptr::read(self)
}
}
};
result.into()
}
#[proc_macro]
pub fn interrupt_take(item: TokenStream) -> TokenStream {
let name = syn::parse_macro_input!(item as syn::Ident);
let name = format!("{}", name);
let name_interrupt = format_ident!("{}", name);
let name_handler = format!("__EMBASSY_{}_HANDLER", name);
let result = quote! {
{
#[allow(non_snake_case)]
#[export_name = #name]
pub unsafe extern "C" fn trampoline() {
extern "C" {
#[link_name = #name_handler]
static HANDLER: ::embassy::interrupt::Handler;
}
let func = HANDLER.func.load(::embassy::export::atomic::Ordering::Relaxed);
let ctx = HANDLER.ctx.load(::embassy::export::atomic::Ordering::Relaxed);
let func: fn(*mut ()) = ::core::mem::transmute(func);
func(ctx)
}
static TAKEN: ::embassy::export::atomic::AtomicBool = ::embassy::export::atomic::AtomicBool::new(false);
if TAKEN.compare_exchange(false, true, ::embassy::export::atomic::Ordering::AcqRel, ::embassy::export::atomic::Ordering::Acquire).is_err() {
panic!("IRQ Already taken");
}
let irq: interrupt::#name_interrupt = unsafe { ::core::mem::transmute(()) };
irq
}
};
result.into()
}
#[cfg(feature = "nrf")]
#[path = "chip/nrf.rs"]
mod chip;
#[cfg(feature = "stm32")]
#[path = "chip/stm32.rs"]
mod chip;
#[cfg(feature = "rp")]
#[path = "chip/rp.rs"]
mod chip;
#[derive(Debug, FromMeta)]
struct MainArgs {
#[darling(default)]
embassy_prefix: ModulePrefix,
#[darling(default)]
config: Option<syn::LitStr>,
}
#[cfg(any(feature = "nrf", feature = "stm32", feature = "rp"))]
#[proc_macro_attribute]
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
let macro_args = syn::parse_macro_input!(args as syn::AttributeArgs);
let task_fn = syn::parse_macro_input!(item as syn::ItemFn);
let macro_args = match MainArgs::from_list(&macro_args) {
Ok(v) => v,
Err(e) => {
return TokenStream::from(e.write_errors());
}
};
let mut fail = false;
if task_fn.sig.asyncness.is_none() {
task_fn
.sig
.span()
.unwrap()
.error("task functions must be async")
.emit();
fail = true;
}
if !task_fn.sig.generics.params.is_empty() {
task_fn
.sig
.span()
.unwrap()
.error("main function must not be generic")
.emit();
fail = true;
}
let args = task_fn.sig.inputs.clone();
if args.len() != 1 {
task_fn
.sig
.span()
.unwrap()
.error("main function must have one argument")
.emit();
fail = true;
}
if fail {
return TokenStream::new();
}
let embassy_prefix = macro_args.embassy_prefix;
let embassy_prefix_lit = embassy_prefix.literal();
let embassy_path = embassy_prefix.append("embassy").path();
let task_fn_body = task_fn.block;
let config = macro_args
.config
.map(|s| s.parse::<syn::Expr>().unwrap())
.unwrap_or_else(|| {
syn::Expr::Verbatim(quote! {
Default::default()
})
});
let chip_setup = chip::generate(&embassy_prefix, config);
let result = quote! {
#[#embassy_path::task(embassy_prefix = #embassy_prefix_lit)]
async fn __embassy_main(#args) {
#task_fn_body
}
#[cortex_m_rt::entry]
fn main() -> ! {
unsafe fn make_static<T>(t: &mut T) -> &'static mut T {
::core::mem::transmute(t)
}
let mut executor = #embassy_path::executor::Executor::new();
let executor = unsafe { make_static(&mut executor) };
#chip_setup
executor.run(|spawner| {
spawner.spawn(__embassy_main(spawner)).unwrap();
})
}
};
result.into()
}
#[cfg(feature = "std")]
#[proc_macro_attribute]
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
let macro_args = syn::parse_macro_input!(args as syn::AttributeArgs);
let task_fn = syn::parse_macro_input!(item as syn::ItemFn);
let macro_args = match MainArgs::from_list(&macro_args) {
Ok(v) => v,
Err(e) => {
return TokenStream::from(e.write_errors());
}
};
let embassy_path = macro_args.embassy_prefix.append("embassy");
let embassy_std_path = macro_args.embassy_prefix.append("embassy_std");
let mut fail = false;
if task_fn.sig.asyncness.is_none() {
task_fn
.sig
.span()
.unwrap()
.error("task functions must be async")
.emit();
fail = true;
}
if !task_fn.sig.generics.params.is_empty() {
task_fn
.sig
.span()
.unwrap()
.error("main function must not be generic")
.emit();
fail = true;
}
let args = task_fn.sig.inputs.clone();
if args.len() != 1 {
task_fn
.sig
.span()
.unwrap()
.error("main function must have one argument")
.emit();
fail = true;
}
if fail {
return TokenStream::new();
}
let task_fn_body = task_fn.block.clone();
let embassy_path = embassy_path.path();
let embassy_std_path = embassy_std_path.path();
let embassy_prefix_lit = macro_args.embassy_prefix.literal();
let result = quote! {
#[#embassy_path::task(embassy_prefix = #embassy_prefix_lit)]
async fn __embassy_main(#args) {
#task_fn_body
}
fn main() -> ! {
unsafe fn make_static<T>(t: &mut T) -> &'static mut T {
::core::mem::transmute(t)
}
let mut executor = #embassy_std_path::Executor::new();
let executor = unsafe { make_static(&mut executor) };
executor.run(|spawner| {
spawner.spawn(__embassy_main(spawner)).unwrap();
})
}
};
result.into()
}