Remove the #[interrupt] macro (#1409)

This commit is contained in:
Björn Quentin 2024-04-09 12:11:57 +02:00 committed by GitHub
parent 41aa556764
commit 1fdfc9270e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 39 additions and 223 deletions

View File

@ -34,16 +34,6 @@
//! //!
//! ## Examples //! ## Examples
//! //!
//! #### `interrupt` macro
//!
//! Requires the `interrupt` feature to be enabled.
//!
//! ```no_run
//! #[interrupt]
//! fn INTR_NAME() {
//! // Interrupt handling code here
//! }
//! ```
//! //!
//! #### `main` macro //! #### `main` macro
//! //!
@ -184,187 +174,12 @@ pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
output.into() 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. /// Mark a function as an interrupt handler.
/// ///
/// Optionally a priority can be specified, e.g. `#[handler("Priority3")]`, /// Optionally a priority can be specified, e.g. `#[handler(priority =
/// "min" and "max" are special values. /// 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")] #[cfg(feature = "interrupt")]
#[proc_macro_error::proc_macro_error] #[proc_macro_error::proc_macro_error]
#[proc_macro_attribute] #[proc_macro_attribute]

View File

@ -1,54 +1,55 @@
//! # Interrupt support //! # 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 //! This is the preferred way to register handlers.
//! vectoring to the peripheral interrupt, for example `UART0`.
//! //!
//! It is also possible, but not recommended, to bind an interrupt directly to a //! ## Example using the peripheral driver to register an interrupt handler
//! 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
//! //!
//! ```no_run //! ```no_run
//! #[entry] //! #[entry]
//! fn main() -> ! { //! fn main() -> ! {
//! ... //! ...
//! critical_section::with(|cs| SWINT.borrow_ref_mut(cs).replace(sw_int)); //! let mut sw_int = system.software_interrupt_control;
//! //! critical_section::with(|cs| {
//! // enable the interrupt //! sw_int
//! interrupt::enable( //! .software_interrupt0
//! peripherals::Interrupt::FROM_CPU_INTR0, //! .set_interrupt_handler(swint0_handler);
//! interrupt::Priority::Priority1, //! SWINT0
//! ) //! .borrow_ref_mut(cs)
//! .unwrap(); //! .replace(sw_int.software_interrupt0);
//! });
//! //!
//! // trigger the interrupt //! // trigger the interrupt
//! SWINT //! critical_section::with(|cs| {
//! .borrow_ref_mut(cs) //! SWINT0.borrow_ref_mut(cs).as_mut().unwrap().raise();
//! .as_mut() //! });
//! .unwrap() //! ...
//! .raise(SoftwareInterrupt::SoftwareInterrupt0);
//!
//! loop {}
//! } //! }
//! //!
//! #[interrupt] //! // use the `handler` macro to define a handler, optionally you can set a priority
//! fn FROM_CPU_INTR0() { //! #[handler(priority = esp_hal::interrupt::Priority::Priority2)]
//! fn swint0_handler() {
//! esp_println::println!("SW interrupt0"); //! esp_println::println!("SW interrupt0");
//! critical_section::with(|cs| { //! critical_section::with(|cs| {
//! SWINT //! SWINT0.borrow_ref_mut(cs).as_mut().unwrap().reset();
//! .borrow_ref_mut(cs)
//! .as_mut()
//! .unwrap()
//! .reset(SoftwareInterrupt::SoftwareInterrupt0);
//! }); //! });
//! }
//! ``` //! ```
//!
//! 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)] #[cfg(riscv)]
pub use self::riscv::*; pub use self::riscv::*;