Skip to content

Commit fb7368e

Browse files
committed
implement #[interrupt]
1 parent 98ce8a1 commit fb7368e

File tree

11 files changed

+310
-6
lines changed

11 files changed

+310
-6
lines changed

cortex-m-rt/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ cortex-m-rt-macros = { path = "macros", version = "0.1.1" }
1818
cortex-m = "0.5.4"
1919
panic-abort = "0.3.0"
2020
panic-semihosting = "0.4.0"
21+
panic-halt = "0.2.0"
2122

2223
[dev-dependencies.rand]
2324
default-features = false

cortex-m-rt/ci/script.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ main() {
88
if [ $TARGET = x86_64-unknown-linux-gnu ]; then
99
( cd macros && cargo check && cargo test )
1010

11-
cargo test --test compiletest
11+
cargo test --features device --test compiletest
1212
fi
1313

1414
local examples=(

cortex-m-rt/macros/src/lib.rs

Lines changed: 143 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -411,10 +411,10 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
411411
let expr = var.expr;
412412

413413
quote!(
414-
static mut #ident_: #ty = #expr;
415-
#[allow(non_snake_case)]
416-
let #ident: &mut #ty = unsafe { &mut #ident_ };
417-
)
414+
static mut #ident_: #ty = #expr;
415+
#[allow(non_snake_case)]
416+
let #ident: &mut #ty = unsafe { &mut #ident_ };
417+
)
418418
}).collect::<Vec<_>>();
419419

420420
quote!(
@@ -435,6 +435,145 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
435435
}
436436
}
437437

438+
/// Attribute to declare an interrupt (AKA device-specific exception) handler
439+
///
440+
/// **IMPORTANT**: This attribute must be used on reachable items (i.e. there must be no private
441+
/// modules between the item and the root of the crate). If the item is in the root of the crate
442+
/// you'll be fine.
443+
///
444+
/// **NOTE**: This attribute is exposed by `cortex-m-rt` only when the `device` feature is enabled.
445+
/// However, that export is not meant to be used directly -- using it will result in a compilation
446+
/// error. You should instead use the device crate (usually generated using `svd2rust`) re-export of
447+
/// that attribute. You need to use the re-export to have the compiler check that the interrupt
448+
/// exists on the target device.
449+
///
450+
/// # Syntax
451+
///
452+
/// ``` ignore
453+
/// extern crate device;
454+
///
455+
/// // the attribute comes from the device crate not from cortex-m-rt
456+
/// use device::interrupt;
457+
///
458+
/// #[interrupt]
459+
/// fn USART1() {
460+
/// // ..
461+
/// }
462+
/// ```
463+
///
464+
/// where the name of the function must be one of the device interrupts.
465+
///
466+
/// # Usage
467+
///
468+
/// `#[interrupt] fn Name(..` overrides the default handler for the interrupt with the given `Name`.
469+
/// These handlers must have signature `[unsafe] fn() [-> !]`. It's possible to add state to these
470+
/// handlers by declaring `static mut` variables at the beginning of the body of the function. These
471+
/// variables will be safe to access from the function body.
472+
///
473+
/// If the interrupt handler has not been overridden it will be dispatched by the default exception
474+
/// handler (`DefaultHandler`).
475+
///
476+
/// # Properties
477+
///
478+
/// Interrupts handlers can only be called by the hardware. Other parts of the program can't refer
479+
/// to the interrupt handlers, much less invoke them as if they were functions.
480+
///
481+
/// `static mut` variables declared within an interrupt handler are safe to access and can be used
482+
/// to preserve state across invocations of the handler. The compiler can't prove this is safe so
483+
/// the attribute will help by making a transformation to the source code: for this reason a
484+
/// variable like `static mut FOO: u32` will become `let FOO: &mut u32;`.
485+
///
486+
/// # Examples
487+
///
488+
/// - Using state within an interrupt handler
489+
///
490+
/// ``` ignore
491+
/// extern crate device;
492+
///
493+
/// use device::interrupt;
494+
///
495+
/// #[interrupt]
496+
/// fn TIM2() {
497+
/// static mut COUNT: i32 = 0;
498+
///
499+
/// // `COUNT` is safe to access and has type `&mut i32`
500+
/// *COUNT += 1;
501+
///
502+
/// println!("{}", COUNT);
503+
/// }
504+
/// ```
505+
#[proc_macro_attribute]
506+
pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
507+
let f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function");
508+
509+
assert!(
510+
args.to_string() == "",
511+
"`interrupt` attribute must have no arguments"
512+
);
513+
514+
let ident = f.ident;
515+
let ident_s = ident.to_string();
516+
517+
// XXX should we blacklist other attributes?
518+
let attrs = f.attrs;
519+
let block = f.block;
520+
let stmts = block.stmts;
521+
522+
assert!(
523+
f.constness.is_none()
524+
&& f.vis == Visibility::Inherited
525+
&& f.abi.is_none()
526+
&& f.decl.inputs.is_empty()
527+
&& f.decl.generics.params.is_empty()
528+
&& f.decl.generics.where_clause.is_none()
529+
&& f.decl.variadic.is_none()
530+
&& match f.decl.output {
531+
ReturnType::Default => true,
532+
ReturnType::Type(_, ref ty) => match **ty {
533+
Type::Tuple(ref tuple) => tuple.elems.is_empty(),
534+
Type::Never(..) => true,
535+
_ => false,
536+
},
537+
},
538+
"`#[interrupt]` functions must have signature `[unsafe] fn() [-> !]`"
539+
);
540+
541+
let (statics, stmts) = extract_static_muts(stmts);
542+
543+
let vars = statics
544+
.into_iter()
545+
.map(|var| {
546+
let ident = var.ident;
547+
// `let` can't shadow a `static mut` so we must give the `static` a different
548+
// name. We'll create a new name by appending an underscore to the original name
549+
// of the `static`.
550+
let mut ident_ = ident.to_string();
551+
ident_.push('_');
552+
let ident_ = Ident::new(&ident_, Span::call_site());
553+
let ty = var.ty;
554+
let expr = var.expr;
555+
556+
quote!(
557+
static mut #ident_: #ty = #expr;
558+
#[allow(non_snake_case)]
559+
let #ident: &mut #ty = unsafe { &mut #ident_ };
560+
)
561+
}).collect::<Vec<_>>();
562+
563+
let hash = random_ident();
564+
quote!(
565+
#[export_name = #ident_s]
566+
#(#attrs)*
567+
pub extern "C" fn #hash() {
568+
interrupt::#ident;
569+
570+
#(#vars)*
571+
572+
#(#stmts)*
573+
}
574+
).into()
575+
}
576+
438577
/// Attribute to mark which function will be called at the beginning of the reset handler.
439578
///
440579
/// **IMPORTANT**: This attribute must be used once in the dependency graph and must be used on a

cortex-m-rt/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,8 @@ extern crate r0;
394394
use core::fmt;
395395
use core::sync::atomic::{self, Ordering};
396396

397+
#[cfg(feature = "device")]
398+
pub use macros::interrupt;
397399
pub use macros::{entry, exception, pre_init};
398400

399401
#[export_name = "error: cortex-m-rt appears more than once in the dependency graph"]
@@ -674,7 +676,7 @@ pub static __EXCEPTIONS: [Vector; 14] = [
674676

675677
// If we are not targeting a specific device we bind all the potential device specific interrupts
676678
// to the default handler
677-
#[cfg(all(not(feature = "device"), not(armv6m)))]
679+
#[cfg(all(any(not(feature = "device"), test), not(armv6m)))]
678680
#[doc(hidden)]
679681
#[link_section = ".vector_table.interrupts"]
680682
#[no_mangle]
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#![no_main]
2+
#![no_std]
3+
4+
extern crate cortex_m_rt;
5+
extern crate panic_halt;
6+
7+
use cortex_m_rt::{entry, interrupt};
8+
9+
#[entry]
10+
fn foo() -> ! {
11+
loop {}
12+
}
13+
14+
enum interrupt {
15+
USART1,
16+
}
17+
18+
#[interrupt(true)] //~ ERROR custom attribute panicked
19+
//~^ HELP `interrupt` attribute must have no arguments
20+
fn USART1() {}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#![no_main]
2+
#![no_std]
3+
4+
extern crate cortex_m_rt;
5+
extern crate panic_halt;
6+
7+
use cortex_m_rt::{entry, interrupt};
8+
9+
#[entry]
10+
fn foo() -> ! {
11+
loop {}
12+
}
13+
14+
enum interrupt {
15+
USART1,
16+
}
17+
18+
#[interrupt] //~ ERROR custom attribute panicked
19+
//~^ HELP `#[interrupt]` functions must have signature `[unsafe] fn() [-> !]`
20+
fn USART1(undef: i32) {}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#![no_main]
2+
#![no_std]
3+
4+
extern crate cortex_m_rt;
5+
extern crate panic_halt;
6+
7+
use cortex_m_rt::{entry, interrupt};
8+
9+
#[entry]
10+
fn foo() -> ! {
11+
loop {}
12+
}
13+
14+
enum interrupt {
15+
USART1,
16+
}
17+
18+
#[interrupt] //~ ERROR custom attribute panicked
19+
//~^ HELP `#[interrupt]` functions must have signature `[unsafe] fn() [-> !]`
20+
fn USART1() -> i32 {
21+
0
22+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![no_main]
2+
#![no_std]
3+
4+
extern crate cortex_m_rt;
5+
extern crate panic_halt;
6+
7+
use cortex_m_rt::{entry, interrupt};
8+
9+
#[entry]
10+
fn foo() -> ! {
11+
loop {}
12+
}
13+
14+
enum interrupt {
15+
USART1,
16+
}
17+
18+
// NOTE this looks a bit better when using a device crate:
19+
// "no variant named `foo` found for type `stm32f30x::Interrupt` in the current scope"
20+
#[interrupt] //~ ERROR no variant named `foo` found for type `interrupt` in the current scope
21+
fn foo() {}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#![no_main]
2+
#![no_std]
3+
4+
extern crate cortex_m_rt;
5+
extern crate panic_halt;
6+
7+
use cortex_m_rt::{entry, interrupt};
8+
9+
#[entry]
10+
fn foo() -> ! {
11+
loop {}
12+
}
13+
14+
#[interrupt] //~ ERROR failed to resolve. Use of undeclared type or module `interrupt`
15+
fn USART1() {}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#![no_main]
2+
#![no_std]
3+
4+
extern crate cortex_m_rt;
5+
extern crate panic_halt;
6+
7+
use cortex_m_rt::{entry, interrupt};
8+
9+
#[entry]
10+
fn foo() -> ! {
11+
loop {}
12+
}
13+
14+
enum interrupt {
15+
USART1,
16+
USART2,
17+
}
18+
19+
#[interrupt]
20+
fn USART1() {
21+
static mut COUNT: u64 = 0;
22+
23+
if *COUNT % 2 == 0 {
24+
*COUNT += 1;
25+
} else {
26+
*COUNT *= 2;
27+
}
28+
}
29+
30+
#[interrupt]
31+
fn USART2() {
32+
USART1(); //~ ERROR cannot find function `USART1` in this scope
33+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#![allow(non_camel_case_types)]
2+
#![no_main]
3+
#![no_std]
4+
5+
extern crate cortex_m_rt;
6+
extern crate panic_halt;
7+
8+
use cortex_m_rt::{entry, interrupt};
9+
10+
#[entry]
11+
fn foo() -> ! {
12+
loop {}
13+
}
14+
15+
enum interrupt {
16+
USART1,
17+
}
18+
19+
#[interrupt]
20+
fn USART1() {}
21+
22+
pub mod reachable {
23+
use cortex_m_rt::interrupt;
24+
25+
enum interrupt {
26+
USART1,
27+
}
28+
29+
#[interrupt] //~ ERROR symbol `USART1` is already defined
30+
fn USART1() {}
31+
}

0 commit comments

Comments
 (0)