Remove the #[interrupt] macro (#1409)
This commit is contained in:
parent
41aa556764
commit
1fdfc9270e
@ -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]
|
||||
|
||||
@ -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::*;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user