~ruther/avr-device

88fcf0c8d5024e0559499ef4e234ea8402a0f745 — Rahix 5 years ago 0c0ed04
Implement interrupts

This commit adds a new feature-flag `rt` which, when enabled, adds the
`#[interrupt]` procedural macro to define an interrupt handler.  Unlike
the implementation in cortex-m, this version needs an attribute which is
the name of the chip the interrupt is for.  In code, an interrupt
handler might look like this:

    #![feature(abi_avr_interrupt)]

    #[avr_device::interrupt(atmega32u4)]
    fn INT6() {
        // Do Something
    }

Closes #1.

Signed-off-by: Rahix <rahix@rahix.de>
8 files changed, 154 insertions(+), 2 deletions(-)

M .gitignore
M Cargo.toml
M Makefile
A gen-intr-lut.sh
A macros/Cargo.toml
A macros/src/.gitignore
A macros/src/lib.rs
M src/lib.rs
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;


Do not follow this link