use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{
parse_macro_input, AttributeArgs, ItemFn, Meta, NestedMeta, ReturnType, Signature, Type,
};
#[proc_macro_attribute]
pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
let main_func = parse_macro_input!(item as ItemFn);
check_main_function_signature(&main_func.sig);
let func_name = main_func.sig.ident.to_string();
let trampoline = format!(
"\
#[no_mangle]\n\
extern \"Rust\" fn __main_trampoline(arg: core::sync::atomic::AtomicPtr<u8>) {{\n\
let arg = arg.load(core::sync::atomic::Ordering::SeqCst) as *mut cortex_m::Peripherals;\n\
let arg = unsafe {{ alloc::boxed::Box::from_raw(arg) }};\n\
{}(*arg)\n\
}}",
func_name
);
let trampoline = syn::parse_str::<TokenStream2>(trampoline.as_str()).unwrap();
quote! {
#trampoline
#main_func
}
.into()
}
#[cfg(feature = "armv6m")]
#[proc_macro_attribute]
pub fn handler(attr: TokenStream, item: TokenStream) -> TokenStream {
let handler_func = parse_macro_input!(item as ItemFn);
let attr_args = parse_macro_input!(attr as AttributeArgs);
check_handler_function_signature(&handler_func.sig);
let irq = parse_attribute_arg_to_irq(&attr_args);
let lower_caes_irq = irq.to_lowercase();
let func_name = handler_func.sig.ident.to_string();
let entry_asm = format!(
"\
#[naked]\n\
#[export_name = \"{}\"]\n\
unsafe extern \"C\" fn __hopter_{}_entry() {{\n\
core::arch::asm!(\n\
\"ldr r0, ={{tls_mem_addr}}\",\n\
\"ldmia r0!, {{{{r1-r3}}}}\",\n\
\"push {{{{r1-r3, lr}}}}\",\n\
\"ldr r0, ={{tls_mem_addr}}\",\n\
\"ldr r1, ={{cont_stk_boundary}}\",\n\
\"movs r2, #0\",\n\
\"str r1, [r0]\",\n\
\"str r2, [r0, #4]\",\n\
\"str r2, [r0, #8]\",\n\
// Run the IRQ handler.\n\
\"bl {{handler_trampoline}}\",\n\
\"pop {{{{r1-r3}}}}\",\n\
\"ldr r0, ={{tls_mem_addr}}\",\n\
\"stmia r0!, {{{{r1-r3}}}}\",\n\
// Exception return.\n\
\"pop {{{{pc}}}}\",\n\
tls_mem_addr = const hopter::config::__TLS_MEM_ADDR,\n\
cont_stk_boundary = const hopter::config::__CONTIGUOUS_STACK_BOUNDARY,\n\
handler_trampoline = sym __hopter_{}_trampoline,\n\
options(noreturn)\n\
)\n\
}}\n\
",
irq, lower_caes_irq, lower_caes_irq,
);
let entry_asm = syn::parse_str::<TokenStream2>(entry_asm.as_str()).unwrap();
let trampoline = format!(
"\
unsafe extern \"C\" fn __hopter_{}_trampoline() {{\n\
let prev_is_handler_unwinding = hopter::unwind::unwind::save_and_clear_isr_unwinding();\n\
let _ = hopter::unwind::unw_catch::catch_unwind({});\n\
hopter::unwind::unwind::set_isr_unwinding(prev_is_handler_unwinding);\n\
}}\n\
",
lower_caes_irq, func_name,
);
let trampoline = syn::parse_str::<TokenStream2>(trampoline.as_str()).unwrap();
quote! {
#entry_asm
#trampoline
#handler_func
}
.into()
}
#[cfg(not(feature = "armv6m"))]
#[proc_macro_attribute]
pub fn handler(attr: TokenStream, item: TokenStream) -> TokenStream {
let handler_func = parse_macro_input!(item as ItemFn);
let attr_args = parse_macro_input!(attr as AttributeArgs);
check_handler_function_signature(&handler_func.sig);
let irq = parse_attribute_arg_to_irq(&attr_args);
let lower_caes_irq = irq.to_lowercase();
let func_name = handler_func.sig.ident.to_string();
let entry_asm = format!(
"\
#[naked]\n\
#[export_name = \"{}\"]\n\
unsafe extern \"C\" fn __hopter_{}_entry() {{\n\
core::arch::asm!(\n\
\"ldr r0, ={{tls_mem_addr}}\",\n\
\"ldmia r0, {{{{r1-r3}}}}\",\n\
\"push {{{{r1-r3, lr}}}}\",\n\
\"ldr r1, ={{cont_stk_boundary}}\",\n\
\"mov r2, #0\",\n\
\"strd r1, r2, [r0]\",\n\
\"str r2, [r0, #8]\",\n\
// Run the IRQ handler.\n\
\"bl {{handler_trampoline}}\",\n\
\"pop {{{{r1-r3}}}}\",\n\
\"ldr r0, ={{tls_mem_addr}}\",\n\
\"stmia r0, {{{{r1-r3}}}}\",\n\
// Exception return.\n\
\"pop {{{{pc}}}}\",\n\
tls_mem_addr = const hopter::config::__TLS_MEM_ADDR,\n\
cont_stk_boundary = const hopter::config::__CONTIGUOUS_STACK_BOUNDARY,\n\
handler_trampoline = sym __hopter_{}_trampoline,\n\
options(noreturn)\n\
)\n\
}}\n\
",
irq, lower_caes_irq, lower_caes_irq,
);
let entry_asm = syn::parse_str::<TokenStream2>(entry_asm.as_str()).unwrap();
let trampoline = format!(
"\
unsafe extern \"C\" fn __hopter_{}_trampoline() {{\n\
let prev_is_handler_unwinding = hopter::unwind::unwind::save_and_clear_isr_unwinding();\n\
let _ = hopter::unwind::unw_catch::catch_unwind({});\n\
hopter::unwind::unwind::set_isr_unwinding(prev_is_handler_unwinding);\n\
}}\n\
",
lower_caes_irq, func_name,
);
let trampoline = syn::parse_str::<TokenStream2>(trampoline.as_str()).unwrap();
quote! {
#entry_asm
#trampoline
#handler_func
}
.into()
}
macro_rules! hander_macro_arg_error {
() => {
"Handler's argument must be one of the supported IRQs. Forgot to set the MCU model feature?"
};
}
macro_rules! hander_macro_retval_error {
() => {
"Handler's return type must be ()."
};
}
fn check_main_function_signature(sig: &Signature) {
if sig.inputs.iter().count() != 1 {
panic!("Main function must receive one argument of type `cortex_m::Peripherals`.");
}
match &sig.output {
ReturnType::Default => {}
ReturnType::Type(_, b) => match &**b {
Type::Tuple(t) => {
if t.elems.len() != 0 {
panic!(hander_macro_retval_error!());
}
}
Type::Never(_) => {}
_ => panic!(hander_macro_retval_error!()),
},
}
if sig.asyncness.is_some() {
panic!("Main function cannot be `async`.");
}
if sig.unsafety.is_some() {
panic!("Main function must be safe.");
}
if sig.variadic.is_some() {
panic!("Handler function cannot be variadic.");
}
}
fn check_handler_function_signature(sig: &Signature) {
if sig.inputs.iter().count() != 0 {
panic!("Handler function should not have any parameter.");
}
match &sig.output {
ReturnType::Default => {}
ReturnType::Type(_, b) => match &**b {
Type::Tuple(t) => {
if t.elems.len() != 0 {
panic!(hander_macro_retval_error!());
}
}
_ => panic!(hander_macro_retval_error!()),
},
}
if sig.abi.is_some() {
panic!("Handler function must have Rust ABI.");
}
if sig.asyncness.is_some() {
panic!("Handler function cannot be `async`.");
}
if sig.variadic.is_some() {
panic!("Handler function cannot be variadic.");
}
}
fn parse_attribute_arg_to_irq(attr_args: &[NestedMeta]) -> String {
if attr_args.len() != 1 {
panic!(hander_macro_arg_error!());
}
let arg = match attr_args.first().unwrap() {
NestedMeta::Meta(Meta::Path(ss)) => quote! { #ss }.to_string(),
_ => panic!(hander_macro_arg_error!()),
};
if !SUPPORTED_IRQS.iter().any(|irq| irq == &arg) {
panic!(hander_macro_arg_error!());
}
arg
}
#[cfg(any(
feature = "stm32f401",
feature = "stm32f405",
feature = "stm32f407",
feature = "stm32f410",
feature = "stm32f411",
feature = "stm32f412",
feature = "stm32f413",
feature = "stm32f427",
feature = "stm32f429",
feature = "stm32f446",
feature = "stm32f469",
))]
mod stm32f4;
#[cfg(any(
feature = "stm32f401",
feature = "stm32f405",
feature = "stm32f407",
feature = "stm32f410",
feature = "stm32f411",
feature = "stm32f412",
feature = "stm32f413",
feature = "stm32f427",
feature = "stm32f429",
feature = "stm32f446",
feature = "stm32f469",
))]
use stm32f4::SUPPORTED_IRQS;
#[cfg(any(
feature = "stm32f030",
feature = "stm32f030x4",
feature = "stm32f030x6",
feature = "stm32f030x8",
feature = "stm32f030xc",
feature = "stm32f031",
feature = "stm32f038",
feature = "stm32f042",
feature = "stm32f048",
feature = "stm32f051",
feature = "stm32f058",
feature = "stm32f070",
feature = "stm32f070x6",
feature = "stm32f070xb",
feature = "stm32f071",
feature = "stm32f072",
feature = "stm32f078",
feature = "stm32f091",
feature = "stm32f098",
))]
mod stm32f0;
#[cfg(any(
feature = "stm32f030",
feature = "stm32f030x4",
feature = "stm32f030x6",
feature = "stm32f030x8",
feature = "stm32f030xc",
feature = "stm32f031",
feature = "stm32f038",
feature = "stm32f042",
feature = "stm32f048",
feature = "stm32f051",
feature = "stm32f058",
feature = "stm32f070",
feature = "stm32f070x6",
feature = "stm32f070xb",
feature = "stm32f071",
feature = "stm32f072",
feature = "stm32f078",
feature = "stm32f091",
feature = "stm32f098",
))]
use stm32f0::SUPPORTED_IRQS;
#[cfg(not(any(
feature = "stm32f401",
feature = "stm32f405",
feature = "stm32f407",
feature = "stm32f410",
feature = "stm32f411",
feature = "stm32f412",
feature = "stm32f413",
feature = "stm32f427",
feature = "stm32f429",
feature = "stm32f446",
feature = "stm32f469",
feature = "stm32f030",
feature = "stm32f030x4",
feature = "stm32f030x6",
feature = "stm32f030x8",
feature = "stm32f030xc",
feature = "stm32f031",
feature = "stm32f038",
feature = "stm32f042",
feature = "stm32f048",
feature = "stm32f051",
feature = "stm32f058",
feature = "stm32f070",
feature = "stm32f070x6",
feature = "stm32f070xb",
feature = "stm32f071",
feature = "stm32f072",
feature = "stm32f078",
feature = "stm32f091",
feature = "stm32f098",
)))]
const SUPPORTED_IRQS: [&str; 0] = [];