diff --git a/Cargo.toml b/Cargo.toml index 3f9b01a..44c12ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,10 +78,8 @@ rt = ["avr-device-macros"] critical-section-impl = ["critical-section/restore-state-u8"] -# Unfortunately, we can only build documentation for a subset of the supported -# MCUs on docs.rs. If you think a very popular chip is missing from the list, -# feel free to add it here. -docsrs = ["rt", "ufmt", "atmega328p", "atmega32u4", "atmega2560", "attiny85", "atmega4809"] +# Unfortunately, we can only build documentation for a single MCU. +docsrs = ["rt", "ufmt", "atmega328p"] [dependencies] bare-metal = "1.0.0" diff --git a/Makefile b/Makefile index ebb4956..b267a37 100644 --- a/Makefile +++ b/Makefile @@ -7,10 +7,9 @@ RUSTUP_TOOLCHAIN ?= nightly PATCHES := $(foreach chip, $(CHIPS), $(wildcard patch/$(chip).yaml)) DEPS := $(foreach patch, $(PATCHES), $(patsubst patch/%.yaml, .deps/%.d, $(patch))) -.PHONY: chips deps $(CHIPS) vector all clean +.PHONY: chips deps $(CHIPS) all clean chips: $(CHIPS) deps: $(DEPS) -vector: macros/src/vector.rs $(foreach chip, $(CHIPS), $(eval $(chip): src/devices/$(chip)/mod.rs)) @@ -51,12 +50,6 @@ src/devices/%/mod.rs: src/devices/%/mod.full.rs @# Fixup the take() implementation @sed -i'' -e '/#\[cfg(feature = "critical-section")]/d' $@ @sed -i'' -e 's/critical_section::with/crate::interrupt::free/' $@ - @echo -e "\tGEN-VECTOR\t>macros/src/vector.rs" - @./gen-intr-lut.sh svd/*.patched >macros/src/vector.rs - -macros/src/vector.rs: svd/*.patched - @echo -e "\tGEN-VECTOR\t>macros/src/vector.rs" - @./gen-intr-lut.sh $^ >$@ clean: @echo -e "\tCLEAN\t\t./svd/" @@ -67,8 +60,6 @@ clean: @rm -f src/generic.rs @echo -e "\tCLEAN\t\t./.deps/" @rm -rf .deps/ - @echo -e "\tCLEAN\t\t./macros/src/vector.rs" - @rm -rf macros/src/vector.rs # Patch dependencies patch/%.yaml: .deps/%.d diff --git a/examples/atmega328p/src/main.rs b/examples/atmega328p/src/main.rs index b4227aa..fc2b5dd 100644 --- a/examples/atmega328p/src/main.rs +++ b/examples/atmega328p/src/main.rs @@ -35,7 +35,7 @@ fn panic(_info: &core::panic::PanicInfo) -> ! { } } -#[avr_device::interrupt(atmega328p)] +#[avr_device::interrupt] fn TIMER0_OVF() { // This interrupt should raise every (1024*255)/16MHz s ≈ 0.01s // We then count 61 times to approximate 1s. diff --git a/gen-intr-lut.sh b/gen-intr-lut.sh deleted file mode 100755 index a343fc1..0000000 --- a/gen-intr-lut.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -# Generate a lookup table function for interrupts of all supported chips -set -e - -echo "// Autogenerated. Do not edit." -echo "pub fn lookup_vector(chip: &str, intr: &str) -> Option {" -echo " match chip {" - -for intr_path in "$@"; do - chip="$(basename "${intr_path%.svd.patched}")" - echo " \"$chip\" => match intr {" - # toupper() to be compliant with svd2rust interrupts name - svdtools interrupts --no-gaps $intr_path | awk '{print " \""toupper(substr($2, 1, length($2)-1))"\"" " => Some(" $1"),"}' - echo " _ => None," - echo " }," -done - -echo " _ => None," -echo " }" -echo "}" diff --git a/macros/src/.gitignore b/macros/src/.gitignore deleted file mode 100644 index 9a74c56..0000000 --- a/macros/src/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/vector.rs diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 1139cec..34bc69c 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,10 +1,14 @@ -// Adapted from https://github.com/rust-embedded/cortex-m-rt/blob/master/macros/src/lib.rs +//! Adapted from +//! +//! Do not use this crate directly. extern crate proc_macro; -mod vector; - -use syn::spanned::Spanned; +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::quote; +use std::iter; +use syn::{parse, spanned::Spanned, Attribute, FnArg, Ident, ItemFn, ReturnType, Type, Visibility}; #[proc_macro_attribute] pub fn entry( @@ -15,19 +19,19 @@ pub fn entry( // check the function signature let valid_signature = f.sig.constness.is_none() - && f.vis == syn::Visibility::Inherited + && f.vis == Visibility::Inherited && f.sig.abi.is_none() && f.sig.inputs.is_empty() && f.sig.generics.params.is_empty() && f.sig.generics.where_clause.is_none() && f.sig.variadic.is_none() && match f.sig.output { - syn::ReturnType::Default => false, - syn::ReturnType::Type(_, ref ty) => matches!(**ty, syn::Type::Never(_)), + ReturnType::Default => false, + ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)), }; if !valid_signature { - return syn::parse::Error::new( + return parse::Error::new( f.span(), "`#[entry]` function must have signature `[unsafe] fn() -> !`", ) @@ -36,12 +40,9 @@ pub fn entry( } if !args.is_empty() { - return syn::parse::Error::new( - proc_macro2::Span::call_site(), - "This attribute accepts no arguments", - ) - .to_compile_error() - .into(); + return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") + .to_compile_error() + .into(); } let (statics, stmts) = match extract_static_muts(f.block.stmts) { @@ -50,10 +51,7 @@ pub fn entry( }; // Rename the function so it is not callable - f.sig.ident = syn::Ident::new( - &format!("__avr_device_rt_{}", f.sig.ident), - proc_macro2::Span::call_site(), - ); + f.sig.ident = Ident::new(&format!("__avr_device_{}", f.sig.ident), Span::call_site()); f.sig.inputs.extend(statics.iter().map(|statik| { let ident = &statik.ident; let ty = &statik.ty; @@ -68,10 +66,7 @@ pub fn entry( })); f.block.stmts = stmts; - let tramp_ident = syn::Ident::new( - &format!("{}_trampoline", f.sig.ident), - proc_macro2::Span::call_site(), - ); + let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); let ident = &f.sig.ident; let resource_args = statics @@ -81,7 +76,7 @@ pub fn entry( let ident = &statik.ident; let ty = &statik.ty; let expr = &statik.expr; - quote::quote! { + quote! { #(#cfgs)* { #(#attrs)* @@ -92,7 +87,13 @@ pub fn entry( }) .collect::>(); - quote::quote! ( + if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Entry) { + return error; + } + + let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); + + quote! ( #[cfg(not(any(doc, target_arch = "avr")))] compile_error!( "Ensure that you are using an AVR target! You may need to change \ @@ -100,6 +101,8 @@ pub fn entry( https://github.com/Rahix/avr-device/pull/41 for more details." ); + #(#cfgs)* + #(#attrs)* #[doc(hidden)] #[export_name = "main"] pub unsafe extern "C" fn #tramp_ident() { @@ -108,63 +111,43 @@ pub fn entry( ) } - #[doc(hidden)] #f ) .into() } #[proc_macro_attribute] -pub fn interrupt( - args: proc_macro::TokenStream, - input: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - let mut f: syn::ItemFn = - syn::parse(input).expect("`#[interrupt]` must be applied to a function"); - let args: Vec<_> = args.into_iter().collect(); +pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { + let mut f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function"); + + if !args.is_empty() { + return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") + .to_compile_error() + .into(); + } let fspan = f.span(); let ident = f.sig.ident.clone(); let ident_s = ident.to_string(); - let chip = if let Some(tree) = args.get(0) { - if let proc_macro::TokenTree::Ident(ident) = tree { - ident.to_string() - } else { - return syn::parse::Error::new( - proc_macro2::Span::call_site(), - "#[interrupt(chip)]: chip must be an ident", - ) - .to_compile_error() - .into(); - } - } else { - return syn::parse::Error::new( - proc_macro2::Span::call_site(), - "#[interrupt(chip)] needs a chip argument", - ) - .to_compile_error() - .into(); - }; - let valid_signature = f.sig.constness.is_none() - && f.vis == syn::Visibility::Inherited + && f.vis == Visibility::Inherited && f.sig.abi.is_none() && f.sig.inputs.is_empty() && f.sig.generics.params.is_empty() && f.sig.generics.where_clause.is_none() && f.sig.variadic.is_none() && match f.sig.output { - syn::ReturnType::Default => true, - syn::ReturnType::Type(_, ref ty) => match **ty { - syn::Type::Tuple(ref tuple) => tuple.elems.is_empty(), - syn::Type::Never(..) => true, + ReturnType::Default => true, + ReturnType::Type(_, ref ty) => match **ty { + Type::Tuple(ref tuple) => tuple.elems.is_empty(), + Type::Never(..) => true, _ => false, }, }; if !valid_signature { - return syn::parse::Error::new( + return parse::Error::new( fspan, "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`", ) @@ -177,25 +160,25 @@ pub fn interrupt( Ok(x) => x, }; - f.sig.ident = syn::Ident::new( - &format!("__avr_device_rt_{}", f.sig.ident), - proc_macro2::Span::call_site(), - ); + f.sig.ident = Ident::new(&format!("__avr_device_{}", f.sig.ident), Span::call_site()); f.sig.inputs.extend(statics.iter().map(|statik| { let ident = &statik.ident; let ty = &statik.ty; let attrs = &statik.attrs; - syn::parse::( - quote::quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into(), - ) - .unwrap() + syn::parse::(quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into()) + .unwrap() })); - f.block.stmts = stmts; + f.block.stmts = iter::once( + syn::parse2(quote! {{ + // Check that this interrupt actually exists + ::avr_device::Interrupt::#ident; + }}) + .unwrap(), + ) + .chain(stmts) + .collect(); - let tramp_ident = syn::Ident::new( - &format!("{}_trampoline", f.sig.ident), - proc_macro2::Span::call_site(), - ); + let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); let ident = &f.sig.ident; let resource_args = statics @@ -205,7 +188,7 @@ pub fn interrupt( let ident = &statik.ident; let ty = &statik.ty; let expr = &statik.expr; - quote::quote! { + quote! { #(#cfgs)* { #(#attrs)* @@ -216,30 +199,23 @@ pub fn interrupt( }) .collect::>(); - let vect = if let Some(v) = vector::lookup_vector(&chip, &ident_s) { - v - } else { - return syn::parse::Error::new( - proc_macro2::Span::call_site(), - &format!("Chip `{}` or interrupt `{}` unknown", chip, ident_s), - ) - .to_compile_error() - .into(); - }; - let vector = format!("__vector_{}", vect); - let vector_ident = syn::Ident::new(&vector, proc_macro2::Span::call_site()); - let vector_ident_s = vector_ident.to_string(); + if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Interrupt) { + return error; + } + + let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); - quote::quote! ( + quote!( + #(#cfgs)* + #(#attrs)* #[doc(hidden)] - #[export_name = #vector_ident_s] + #[export_name = #ident_s] pub unsafe extern "avr-interrupt" fn #tramp_ident() { #ident( #(#resource_args),* ) } - #[doc(hidden)] #f ) .into() @@ -298,6 +274,46 @@ fn extract_cfgs(attrs: Vec) -> (Vec, Vec Result<(), TokenStream> { + let whitelist = &[ + "doc", + "link_section", + "cfg", + "allow", + "warn", + "deny", + "forbid", + "cold", + "naked", + ]; + + 'o: for attr in attrs { + for val in whitelist { + if eq(attr, val) { + continue 'o; + } + } + + let err_str = match caller { + WhiteListCaller::Entry => "this attribute is not allowed on an avr-device entry point", + WhiteListCaller::Interrupt => { + "this attribute is not allowed on an interrupt handler controlled by avr-device" + } + }; + + return Err(parse::Error::new(attr.span(), err_str) + .to_compile_error() + .into()); + } + + Ok(()) +} + /// Returns `true` if `attr.path` matches `name` fn eq(attr: &syn::Attribute, name: &str) -> bool { attr.style == syn::AttrStyle::Outer && attr.path.is_ident(name) diff --git a/src/lib.rs b/src/lib.rs index 7db14ec..c3e3229 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,7 +55,7 @@ //! #![cfg_attr( feature = "docsrs", - doc = "**Warning**: The doc-build here on docs.rs is only for a subset of supported chips. Please build documentation locally if your MCU's registers are not documented here.\n\n" + doc = "**Warning**: The doc-build here on docs.rs is only for a single one of the supported chips. Please build documentation locally if your MCU's registers are not documented here.\n\n" )] //! Which chips the crate is built for depends on the feature flag used. //! The following chips are available (using feature flags of the same name): @@ -192,20 +192,24 @@ pub mod generic; /// Attribute to declare an interrupt service routine /// /// ``` -/// #[avr_device::interrupt(atmega32u4)] +/// #[avr_device::interrupt] /// fn INT6() { /// // ... /// } /// ``` /// /// # Constraints -/// - The name of the function must be the name of an interrupt. Each chip's +/// - The name of the function must be the name of an interrupt. Each chip's /// module has a `Interrupt` enum defining the available names. -/// - The attribute needs the chip-name to correctly map the interrupt to its -/// vector. This is an unfortunate requirement of the current crate -/// architecture and might change in the future. +/// - Any function-local persistent data (such as any inner `static`s it may +/// have) must come in the beginning, as the macro performs special processing +/// on those to make them safer. /// - The function must have a signature of `[unsafe] fn() [-> !]`. /// - This macro requires the avr-device `rt` crate feature. +/// - The function will not be callable in any way other than as an interrupt +/// routine. It would be unsound to call it anyway, and we need to perform +/// some renaming to conform to the linker's expectations, which prevents that +/// fully. #[cfg(feature = "rt")] pub use avr_device_macros::interrupt; @@ -225,6 +229,10 @@ pub use avr_device_macros::interrupt; /// # Constraints /// - The entry function must have a signature of `[unsafe] fn() -> !`. /// - This macro requires the avr-device `rt` crate feature. +/// - The function will not be callable in any way other than as the entrypoint. +/// It would be unsound to call it again anyway, and we need to perform some +/// renaming to conform to the linker's expectations, which prevents that +/// fully. #[cfg(feature = "rt")] pub use avr_device_macros::entry; @@ -289,104 +297,104 @@ compile_error!( mod devices; #[cfg(feature = "at90usb1286")] -pub use crate::devices::at90usb1286; +pub use crate::devices::at90usb1286::{self, Interrupt}; #[cfg(feature = "atmega1280")] -pub use crate::devices::atmega1280; +pub use crate::devices::atmega1280::{self, Interrupt}; #[cfg(feature = "atmega1284p")] -pub use crate::devices::atmega1284p; +pub use crate::devices::atmega1284p::{self, Interrupt}; #[cfg(feature = "atmega128a")] -pub use crate::devices::atmega128a; +pub use crate::devices::atmega128a::{self, Interrupt}; #[cfg(feature = "atmega128rfa1")] -pub use crate::devices::atmega128rfa1; +pub use crate::devices::atmega128rfa1::{self, Interrupt}; #[cfg(feature = "atmega16")] -pub use crate::devices::atmega16; +pub use crate::devices::atmega16::{self, Interrupt}; #[cfg(feature = "atmega164pa")] -pub use crate::devices::atmega164pa; +pub use crate::devices::atmega164pa::{self, Interrupt}; #[cfg(feature = "atmega168")] -pub use crate::devices::atmega168; +pub use crate::devices::atmega168::{self, Interrupt}; #[cfg(feature = "atmega16u2")] -pub use crate::devices::atmega16u2; +pub use crate::devices::atmega16u2::{self, Interrupt}; #[cfg(feature = "atmega2560")] -pub use crate::devices::atmega2560; +pub use crate::devices::atmega2560::{self, Interrupt}; #[cfg(feature = "atmega324pa")] -pub use crate::devices::atmega324pa; +pub use crate::devices::atmega324pa::{self, Interrupt}; #[cfg(feature = "atmega328p")] -pub use crate::devices::atmega328p; +pub use crate::devices::atmega328p::{self, Interrupt}; #[cfg(feature = "atmega328pb")] -pub use crate::devices::atmega328pb; +pub use crate::devices::atmega328pb::{self, Interrupt}; #[cfg(feature = "atmega32a")] -pub use crate::devices::atmega32a; +pub use crate::devices::atmega32a::{self, Interrupt}; #[cfg(feature = "atmega32u2")] -pub use crate::devices::atmega32u2; +pub use crate::devices::atmega32a::{self, Interrupt}; #[cfg(feature = "atmega32u4")] -pub use crate::devices::atmega32u4; +pub use crate::devices::atmega32u4::{self, Interrupt}; #[cfg(feature = "atmega3208")] -pub use crate::devices::atmega3208; +pub use crate::devices::atmega3208::{self, Interrupt}; #[cfg(feature = "atmega3209")] -pub use crate::devices::atmega3209; +pub use crate::devices::atmega3209::{self, Interrupt}; #[cfg(feature = "atmega4808")] -pub use crate::devices::atmega4808; +pub use crate::devices::atmega4808::{self, Interrupt}; #[cfg(feature = "atmega4809")] -pub use crate::devices::atmega4809; +pub use crate::devices::atmega4809::{self, Interrupt}; #[cfg(feature = "atmega48p")] -pub use crate::devices::atmega48p; +pub use crate::devices::atmega48p::{self, Interrupt}; #[cfg(feature = "atmega64")] -pub use crate::devices::atmega64; +pub use crate::devices::atmega64::{self, Interrupt}; #[cfg(feature = "atmega644")] -pub use crate::devices::atmega644; +pub use crate::devices::atmega644::{self, Interrupt}; #[cfg(feature = "atmega8")] -pub use crate::devices::atmega8; +pub use crate::devices::atmega8::{self, Interrupt}; #[cfg(feature = "atmega88p")] -pub use crate::devices::atmega88p; +pub use crate::devices::atmega88p::{self, Interrupt}; #[cfg(feature = "atmega8u2")] -pub use crate::devices::atmega8u2; +pub use crate::devices::atmega8u2::{self, Interrupt}; #[cfg(feature = "attiny13a")] -pub use crate::devices::attiny13a; +pub use crate::devices::attiny13a::{self, Interrupt}; #[cfg(feature = "attiny1614")] -pub use crate::devices::attiny1614; +pub use crate::devices::attiny1614::{self, Interrupt}; #[cfg(feature = "attiny167")] -pub use crate::devices::attiny167; +pub use crate::devices::attiny167::{self, Interrupt}; #[cfg(feature = "attiny202")] -pub use crate::devices::attiny202; +pub use crate::devices::attiny202::{self, Interrupt}; #[cfg(feature = "attiny212")] -pub use crate::devices::attiny212; +pub use crate::devices::attiny212::{self, Interrupt}; #[cfg(feature = "attiny214")] -pub use crate::devices::attiny214; +pub use crate::devices::attiny214::{self, Interrupt}; #[cfg(feature = "attiny2313")] -pub use crate::devices::attiny2313; +pub use crate::devices::attiny2313::{self, Interrupt}; #[cfg(feature = "attiny2313a")] -pub use crate::devices::attiny2313a; +pub use crate::devices::attiny2313a::{self, Interrupt}; #[cfg(feature = "attiny26")] -pub use crate::devices::attiny26; +pub use crate::devices::attiny26::{self, Interrupt}; #[cfg(feature = "attiny402")] -pub use crate::devices::attiny402; +pub use crate::devices::attiny402::{self, Interrupt}; #[cfg(feature = "attiny404")] -pub use crate::devices::attiny404; +pub use crate::devices::attiny404::{self, Interrupt}; #[cfg(feature = "attiny412")] -pub use crate::devices::attiny412; +pub use crate::devices::attiny412::{self, Interrupt}; #[cfg(feature = "attiny414")] -pub use crate::devices::attiny414; +pub use crate::devices::attiny414::{self, Interrupt}; #[cfg(feature = "attiny416")] -pub use crate::devices::attiny416; +pub use crate::devices::attiny416::{self, Interrupt}; #[cfg(feature = "attiny44a")] -pub use crate::devices::attiny44a; +pub use crate::devices::attiny44a::{self, Interrupt}; #[cfg(feature = "attiny816")] -pub use crate::devices::attiny816; +pub use crate::devices::attiny816::{self, Interrupt}; #[cfg(feature = "attiny828")] -pub use crate::devices::attiny828; +pub use crate::devices::attiny828::{self, Interrupt}; #[cfg(feature = "attiny84")] -pub use crate::devices::attiny84; +pub use crate::devices::attiny84::{self, Interrupt}; #[cfg(feature = "attiny841")] -pub use crate::devices::attiny841; +pub use crate::devices::attiny841::{self, Interrupt}; #[cfg(feature = "attiny84a")] -pub use crate::devices::attiny84a; +pub use crate::devices::attiny84a::{self, Interrupt}; #[cfg(feature = "attiny85")] -pub use crate::devices::attiny85; +pub use crate::devices::attiny85::{self, Interrupt}; #[cfg(feature = "attiny861")] -pub use crate::devices::attiny861; +pub use crate::devices::attiny861::{self, Interrupt}; #[cfg(feature = "attiny88")] -pub use crate::devices::attiny88; +pub use crate::devices::attiny88::{self, Interrupt}; #[cfg(feature = "avr64du28")] -pub use crate::devices::avr64du28; +pub use crate::devices::avr64du28::{self, Interrupt}; #[cfg(feature = "avr64du32")] -pub use crate::devices::avr64du32; +pub use crate::devices::avr64du32::{self, Interrupt};