M .gitignore => .gitignore +2 -1
@@ 1,4 1,5 @@
-/target
+/target/
+/macros/target/
**/*.rs.bk
Cargo.lock
M Cargo.toml => Cargo.toml +5 -0
@@ 11,7 11,12 @@ atmega8 = []
atmega328p = []
atmega32u4 = []
attiny85 = []
+rt = ["avr-device-macros"]
[dependencies]
bare-metal = "0.2.4"
vcell = "0.1.0"
+
+[dependencies.avr-device-macros]
+path = "macros/"
+optional = true
M Makefile => Makefile +10 -1
@@ 5,9 5,10 @@ CHIPS := atmega1280 atmega8 atmega328p atmega32u4 attiny85
PATCHES := $(foreach chip, $(CHIPS), $(wildcard patch/$(chip).yaml))
DEPS := $(foreach patch, $(PATCHES), $(patsubst patch/%.yaml, .deps/%.d, $(patch)))
-.PHONY: chips deps $(CHIPS)
+.PHONY: chips deps $(CHIPS) vector
chips: $(CHIPS)
deps: $(DEPS)
+vector: macros/src/vector.rs
$(foreach chip, $(CHIPS), $(eval $(chip): src/devices/$(chip)/mod.rs))
@@ 45,6 46,12 @@ src/devices/%/mod.rs: src/devices/%/mod.full.rs
@# Remove DEVICE_PERIPHERALS declaration and replace it with a reference
@# to the global version
@sed -i'' -e '/^\#\[allow(renamed_and_removed_lints)\]/,+3cuse crate::devices::DEVICE_PERIPHERALS;' $@
+ @echo -e "\tGEN-VECTOR\t>macros/src/vector.rs"
+ @./gen-intr-lut.sh src/devices/*/interrupt.rs >macros/src/vector.rs
+
+macros/src/vector.rs: src/devices/*/interrupt.rs
+ @echo -e "\tGEN-VECTOR\t>macros/src/vector.rs"
+ @./gen-intr-lut.sh $^ >$@
clean:
@echo -e "\tCLEAN\t\t./svd/"
@@ 53,6 60,8 @@ clean:
@rm -rf src/devices/at*
@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
.deps/%.d: patch/%.yaml
A gen-intr-lut.sh => gen-intr-lut.sh +22 -0
@@ 0,0 1,22 @@
+#!/bin/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<usize> {"
+echo " match chip {"
+
+for intr_path in "$@"; do
+ chip="$(basename "$(dirname "$intr_path")")"
+ echo " \"$chip\" => match intr {"
+
+ sed '/Interrupt::.\+ =>/!d
+s/ \+Interrupt::\(.\+\) => \(.\+\),/ "\1" => Some(\2),/' "$intr_path"
+
+ echo " _ => None,"
+ echo " },"
+done
+
+echo " _ => None,"
+echo " }"
+echo "}"
A macros/Cargo.toml => macros/Cargo.toml +16 -0
@@ 0,0 1,16 @@
+[package]
+name = "avr-device-macros"
+version = "0.1.0"
+authors = ["Rahix <rahix@rahix.de>"]
+edition = "2018"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+quote = "0.6.12"
+proc-macro2 = "0.4.30"
+
+[dependencies.syn]
+version = "0.15.39"
+features = ["extra-traits", "full"]
A macros/src/.gitignore => macros/src/.gitignore +1 -0
@@ 0,0 1,1 @@
+/vector.rs
A macros/src/lib.rs => macros/src/lib.rs +95 -0
@@ 0,0 1,95 @@
+extern crate proc_macro;
+
+mod vector;
+
+use syn::spanned::Spanned;
+
+#[proc_macro_attribute]
+pub fn interrupt(
+ args: proc_macro::TokenStream,
+ input: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+ // Adapted from https://github.com/rust-embedded/cortex-m-rt/blob/master/macros/src/lib.rs
+ let f: syn::ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function");
+ let args: Vec<_> = args.into_iter().collect();
+
+ let fspan = f.span();
+ let ident = f.ident;
+ let ident_s = ident.to_string();
+ let attrs = f.attrs;
+ let block = f.block;
+ let stmts = block.stmts;
+ let unsafety = f.unsafety;
+
+ 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.constness.is_none()
+ && f.vis == syn::Visibility::Inherited
+ && f.abi.is_none()
+ && f.decl.inputs.is_empty()
+ && f.decl.generics.params.is_empty()
+ && f.decl.generics.where_clause.is_none()
+ && f.decl.variadic.is_none()
+ && match f.decl.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,
+ _ => false,
+ },
+ };
+
+ if !valid_signature {
+ return syn::parse::Error::new(
+ fspan,
+ "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`",
+ )
+ .to_compile_error()
+ .into();
+ }
+
+ 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());
+
+ quote::quote! (
+ #[no_mangle]
+ pub unsafe extern "avr-interrupt" fn #vector_ident() {
+ #ident();
+ }
+
+ #(#attrs)*
+ #[allow(non_snake_case)]
+ #unsafety fn #ident() {
+ #(#stmts)*
+ }
+ )
+ .into()
+}
M src/lib.rs => src/lib.rs +3 -0
@@ 18,6 18,9 @@
pub mod interrupt;
+#[cfg(feature = "rt")]
+pub use avr_device_macros::interrupt;
+
#[allow(non_camel_case_types, unused_attributes, unreachable_patterns)]
mod devices;