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