From 1fdfc9270e3b5505d5ed2dd3eddd077c9d4f638f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Quentin?= Date: Tue, 9 Apr 2024 12:11:57 +0200 Subject: [PATCH] Remove the `#[interrupt]` macro (#1409) --- esp-hal-procmacros/src/lib.rs | 191 +--------------------------------- esp-hal/src/interrupt/mod.rs | 71 ++++++------- 2 files changed, 39 insertions(+), 223 deletions(-) diff --git a/esp-hal-procmacros/src/lib.rs b/esp-hal-procmacros/src/lib.rs index ab14c8da4..17fd11937 100644 --- a/esp-hal-procmacros/src/lib.rs +++ b/esp-hal-procmacros/src/lib.rs @@ -34,16 +34,6 @@ //! //! ## Examples //! -//! #### `interrupt` macro -//! -//! Requires the `interrupt` feature to be enabled. -//! -//! ```no_run -//! #[interrupt] -//! fn INTR_NAME() { -//! // Interrupt handling code here -//! } -//! ``` //! //! #### `main` macro //! @@ -184,187 +174,12 @@ pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream { output.into() } -/// Marks a function as an interrupt handler -/// -/// Used to handle on of the [interrupts](enum.Interrupt.html). -/// -/// When specified between braces (`#[interrupt(example)]`) that interrupt will -/// be used and the function can have an arbitrary name. Otherwise the name of -/// the function must be the name of the interrupt. -/// -/// Example usage: -/// -/// ```rust -/// #[interrupt] -/// fn GPIO() { -/// // code -/// } -/// ``` -/// -/// The interrupt context can also be supplied by adding a argument to the -/// interrupt function for example, on Xtensa based chips: -/// -/// ```rust -/// fn GPIO(context: &mut xtensa_lx_rt::exception::Context) { -/// // code -/// } -/// ``` -#[cfg(feature = "interrupt")] -#[proc_macro_attribute] -pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { - use std::iter; - - use darling::{ast::NestedMeta, Error}; - use proc_macro::Span; - use proc_macro2::Ident; - use proc_macro_crate::{crate_name, FoundCrate}; - use proc_macro_error::abort; - use quote::quote; - use syn::{ - parse::Error as ParseError, - spanned::Spanned, - ItemFn, - Meta, - ReturnType, - Type, - Visibility, - }; - - use self::interrupt::{check_attr_whitelist, extract_cfgs, WhiteListCaller}; - - let mut f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function"); - - let attr_args = match NestedMeta::parse_meta_list(args.into()) { - Ok(v) => v, - Err(e) => { - return TokenStream::from(Error::from(e).write_errors()); - } - }; - - if attr_args.len() > 1 { - abort!( - Span::call_site(), - "This attribute accepts zero or 1 arguments" - ) - } - - let ident = f.sig.ident.clone(); - let mut ident_s = &ident.clone(); - - if attr_args.len() == 1 { - if let NestedMeta::Meta(Meta::Path(x)) = &attr_args[0] { - ident_s = x.get_ident().unwrap(); - } else { - abort!( - Span::call_site(), - format!( - "This attribute accepts a string attribute {:?}", - attr_args[0] - ) - ) - } - } - - // XXX should we blacklist other attributes? - - if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Interrupt) { - return error; - } - - let valid_signature = f.sig.constness.is_none() - && f.vis == Visibility::Inherited - && f.sig.abi.is_none() - && f.sig.generics.params.is_empty() - && f.sig.generics.where_clause.is_none() - && f.sig.variadic.is_none() - && match f.sig.output { - ReturnType::Default => true, - ReturnType::Type(_, ref ty) => match **ty { - Type::Tuple(ref tuple) => tuple.elems.is_empty(), - Type::Never(..) => true, - _ => false, - }, - } - && f.sig.inputs.len() <= 1; - - if !valid_signature { - return ParseError::new( - f.span(), - "`#[interrupt]` handlers must have signature `[unsafe] fn([&mut Context]) [-> !]`", - ) - .to_compile_error() - .into(); - } - - f.sig.ident = Ident::new( - &format!("__esp_hal_internal_{}", f.sig.ident), - proc_macro2::Span::call_site(), - ); - - let hal_crate = if cfg!(any(feature = "is-lp-core", feature = "is-ulp-core")) { - crate_name("esp-lp-hal") - } else { - crate_name("esp-hal") - }; - - let interrupt_in_hal_crate = if let Ok(FoundCrate::Name(ref name)) = hal_crate { - let ident = Ident::new(&name, Span::call_site().into()); - quote!( #ident::peripherals::Interrupt::#ident_s ) - } else { - quote!( crate::peripherals::Interrupt::#ident_s ) - }; - - f.block.stmts.extend(iter::once( - syn::parse2(quote! {{ - // Check that this interrupt actually exists - #interrupt_in_hal_crate; - }}) - .unwrap(), - )); - - let tramp_ident = Ident::new( - &format!("{}_trampoline", f.sig.ident), - proc_macro2::Span::call_site(), - ); - let ident = &f.sig.ident; - - let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); - - let export_name = ident_s.to_string(); - - let trap_frame_in_hal_crate = if let Ok(FoundCrate::Name(ref name)) = hal_crate { - let ident = Ident::new(&name, Span::call_site().into()); - quote!( #ident::trapframe::TrapFrame ) - } else { - quote!(crate::trapframe::TrapFrame) - }; - - let context_call = - (f.sig.inputs.len() == 1).then(|| Ident::new("context", proc_macro2::Span::call_site())); - - quote!( - #(#cfgs)* - #(#attrs)* - #[doc(hidden)] - #[export_name = #export_name] - pub unsafe extern "C" fn #tramp_ident(context: &mut #trap_frame_in_hal_crate) { - #ident( - #context_call - ) - } - - #[inline(always)] - #f - ) - .into() -} - /// Mark a function as an interrupt handler. /// -/// Optionally a priority can be specified, e.g. `#[handler("Priority3")]`, -/// "min" and "max" are special values. +/// Optionally a priority can be specified, e.g. `#[handler(priority = +/// esp_hal::interrupt::Priority::Priority2)]`. /// -/// If no priority is given, "Priority::min()" is assumed +/// If no priority is given, `Priority::min()` is assumed #[cfg(feature = "interrupt")] #[proc_macro_error::proc_macro_error] #[proc_macro_attribute] diff --git a/esp-hal/src/interrupt/mod.rs b/esp-hal/src/interrupt/mod.rs index fe83c7bb7..8f28f6b25 100644 --- a/esp-hal/src/interrupt/mod.rs +++ b/esp-hal/src/interrupt/mod.rs @@ -1,54 +1,55 @@ //! # Interrupt support //! -//! Interrupt support functionality depends heavily on the features enabled. +//! Usually peripheral drivers offer a mechanism to register your interrupt +//! handler. e.g. the systimer offers `set_interrupt_handler` to register a +//! handler for a specific alarm. Other drivers might take an interrupt handler +//! as an optional parameter to their constructor. //! -//! The [`enable`] method will map interrupt to a CPU interrupt, and handle the -//! vectoring to the peripheral interrupt, for example `UART0`. +//! This is the preferred way to register handlers. //! -//! It is also possible, but not recommended, to bind an interrupt directly to a -//! CPU interrupt. This can offer lower latency, at the cost of more complexity -//! in the interrupt handler. -//! -//! We reserve a number of CPU interrupts, which cannot be used; see -//! [`RESERVED_INTERRUPTS`]. -//! -//! ## Example +//! ## Example using the peripheral driver to register an interrupt handler //! //! ```no_run //! #[entry] //! fn main() -> ! { -//! ... -//! critical_section::with(|cs| SWINT.borrow_ref_mut(cs).replace(sw_int)); -//! -//! // enable the interrupt -//! interrupt::enable( -//! peripherals::Interrupt::FROM_CPU_INTR0, -//! interrupt::Priority::Priority1, -//! ) -//! .unwrap(); +//! ... +//! let mut sw_int = system.software_interrupt_control; +//! critical_section::with(|cs| { +//! sw_int +//! .software_interrupt0 +//! .set_interrupt_handler(swint0_handler); +//! SWINT0 +//! .borrow_ref_mut(cs) +//! .replace(sw_int.software_interrupt0); +//! }); //! //! // trigger the interrupt -//! SWINT -//! .borrow_ref_mut(cs) -//! .as_mut() -//! .unwrap() -//! .raise(SoftwareInterrupt::SoftwareInterrupt0); -//! -//! loop {} +//! critical_section::with(|cs| { +//! SWINT0.borrow_ref_mut(cs).as_mut().unwrap().raise(); +//! }); +//! ... //! } //! -//! #[interrupt] -//! fn FROM_CPU_INTR0() { +//! // use the `handler` macro to define a handler, optionally you can set a priority +//! #[handler(priority = esp_hal::interrupt::Priority::Priority2)] +//! fn swint0_handler() { //! esp_println::println!("SW interrupt0"); //! critical_section::with(|cs| { -//! SWINT -//! .borrow_ref_mut(cs) -//! .as_mut() -//! .unwrap() -//! .reset(SoftwareInterrupt::SoftwareInterrupt0); +//! SWINT0.borrow_ref_mut(cs).as_mut().unwrap().reset(); //! }); -//! } //! ``` +//! +//! There are additional ways to register interrupt handlers which are generally +//! only meant to be used in very special situations (mostly internal to the HAL +//! or the supporting libraries). Those are outside the scope of this +//! documentation. +//! +//! It is even possible, but not recommended, to bind an interrupt directly to a +//! CPU interrupt. This can offer lower latency, at the cost of more complexity +//! in the interrupt handler. See the `direct_vectoring.rs` example +//! +//! We reserve a number of CPU interrupts, which cannot be used; see +//! [`RESERVED_INTERRUPTS`]. #[cfg(riscv)] pub use self::riscv::*;