aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cortex-m-rt/macros/src/lib.rs146
-rw-r--r--cortex-m-rt/src/lib.rs29
2 files changed, 128 insertions, 47 deletions
diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs
index 82457f1..5d6a603 100644
--- a/cortex-m-rt/macros/src/lib.rs
+++ b/cortex-m-rt/macros/src/lib.rs
@@ -9,7 +9,7 @@ extern crate syn;
use proc_macro2::Span;
use rand::Rng;
-use syn::{FnArg, Ident, Item, ItemFn, ReturnType, Stmt, Type, Visibility};
+use syn::{FnArg, Ident, Item, ItemFn, ItemStatic, ReturnType, Stmt, Type, Visibility};
use proc_macro::TokenStream;
@@ -24,13 +24,45 @@ use proc_macro::TokenStream;
///
/// The type of the specified function must be `fn() -> !` (never ending function)
///
+/// # Properties
+///
+/// The entry point will be called by the reset handler. The program can't reference to the entry
+/// point, much less invoke it.
+///
+/// `static mut` variables declared within the entry point are safe to access. The compiler can't
+/// prove this is safe so the attribute will help by making a transformation to the source code: for
+/// this reason a variable like `static mut FOO: u32` will become `let FOO: &'static mut u32;`. Note
+/// that `&'static mut` references have move semantics.
+///
/// # Examples
///
+/// - Simple entry point
+///
+/// ``` no_run
+/// # #![no_main]
+/// # use cortex_m_rt_macros::entry;
+/// #[entry]
+/// fn main() -> ! {
+/// loop {
+/// /* .. */
+/// }
+/// }
+/// ```
+///
+/// - `static mut` variables local to the entry point are safe to modify.
+///
/// ``` no_run
/// # #![no_main]
/// # use cortex_m_rt_macros::entry;
/// #[entry]
/// fn main() -> ! {
+/// static mut FOO: u32 = 0;
+///
+/// let foo: &'static mut u32 = FOO;
+/// assert_eq!(*foo, 0);
+/// *foo = 1;
+/// assert_eq!(*foo, 1);
+///
/// loop {
/// /* .. */
/// }
@@ -69,12 +101,36 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
// XXX should we blacklist other attributes?
let attrs = f.attrs;
let ident = f.ident;
- let block = f.block;
+ let (statics, stmts) = extract_static_muts(f.block.stmts);
+
+ let vars = statics
+ .into_iter()
+ .map(|var| {
+ let ident = var.ident;
+ // `let` can't shadow a `static mut` so we must give the `static` a different
+ // name. We'll create a new name by appending an underscore to the original name
+ // of the `static`.
+ let mut ident_ = ident.to_string();
+ ident_.push('_');
+ let ident_ = Ident::new(&ident_, Span::call_site());
+ let ty = var.ty;
+ let expr = var.expr;
+
+ quote!(
+ static mut #ident_: #ty = #expr;
+ #[allow(non_snake_case, unsafe_code)]
+ let #ident: &'static mut #ty = unsafe { &mut #ident_ };
+ )
+ }).collect::<Vec<_>>();
quote!(
#[export_name = "main"]
#(#attrs)*
- pub fn #ident() -> ! #block
+ pub fn #ident() -> ! {
+ #(#vars)*
+
+ #(#stmts)*
+ }
).into()
}
@@ -130,8 +186,15 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
/// mut` variables at the beginning of the body of the function. These variables will be safe to
/// access from the function body.
///
+/// # Properties
+///
/// Exception handlers can only be called by the hardware. Other parts of the program can't refer to
-/// the exception handler much less invoke them as if they were functions.
+/// the exception handlers, much less invoke them as if they were functions.
+///
+/// `static mut` variables declared within an exception handler are safe to access and can be used
+/// to preserve state across invocations of the handler. The compiler can't prove this is safe so
+/// the attribute will help by making a transformation to the source code: for this reason a
+/// variable like `static mut FOO: u32` will become `let FOO: &mut u32;`.
///
/// # Examples
///
@@ -215,17 +278,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
let block = f.block;
let stmts = block.stmts;
- let mut rng = rand::thread_rng();
- let hash = (0..16)
- .map(|i| {
- if i == 0 || rng.gen() {
- ('a' as u8 + rng.gen::<u8>() % 25) as char
- } else {
- ('0' as u8 + rng.gen::<u8>() % 10) as char
- }
- }).collect::<String>();
- let hash = Ident::new(&hash, Span::call_site());
-
+ let hash = random_ident();
match exn {
Exception::DefaultHandler => {
assert!(
@@ -336,27 +389,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
have signature `fn()`"
);
- // Collect all the `static mut` at the beginning of the function body. We'll make them
- // safe
- let mut istmts = stmts.into_iter();
-
- let mut statics = vec![];
- let mut stmts = vec![];
- while let Some(stmt) = istmts.next() {
- match stmt {
- Stmt::Item(Item::Static(var)) => if var.mutability.is_some() {
- statics.push(var);
- } else {
- stmts.push(Stmt::Item(Item::Static(var)));
- },
- _ => {
- stmts.push(stmt);
- break;
- }
- }
- }
-
- stmts.extend(istmts);
+ let (statics, stmts) = extract_static_muts(stmts);
let vars = statics
.into_iter()
@@ -455,3 +488,44 @@ pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream {
pub unsafe fn #ident() #block
).into()
}
+
+// Creates a random identifier
+fn random_ident() -> Ident {
+ let mut rng = rand::thread_rng();
+ Ident::new(
+ &(0..16)
+ .map(|i| {
+ if i == 0 || rng.gen() {
+ ('a' as u8 + rng.gen::<u8>() % 25) as char
+ } else {
+ ('0' as u8 + rng.gen::<u8>() % 10) as char
+ }
+ }).collect::<String>(),
+ Span::call_site(),
+ )
+}
+
+/// Extracts `static mut` vars from the beginning of the given statements
+fn extract_static_muts(stmts: Vec<Stmt>) -> (Vec<ItemStatic>, Vec<Stmt>) {
+ let mut istmts = stmts.into_iter();
+
+ let mut statics = vec![];
+ let mut stmts = vec![];
+ while let Some(stmt) = istmts.next() {
+ match stmt {
+ Stmt::Item(Item::Static(var)) => if var.mutability.is_some() {
+ statics.push(var);
+ } else {
+ stmts.push(Stmt::Item(Item::Static(var)));
+ },
+ _ => {
+ stmts.push(stmt);
+ break;
+ }
+ }
+ }
+
+ stmts.extend(istmts);
+
+ (statics, stmts)
+}
diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs
index 526b28b..17c4a63 100644
--- a/cortex-m-rt/src/lib.rs
+++ b/cortex-m-rt/src/lib.rs
@@ -14,9 +14,16 @@
//!
//! - Enabling the FPU before the program entry point if the target is `thumbv7em-none-eabihf`.
//!
-//! This crate also provides a mechanism to set exception handlers: see the [`exception!`] macro.
+//! This crate also provides the following attributes:
//!
-//! [`exception!`]: macro.exception.html
+//! - [`#[entry]`] to declare the entry point of the program
+//! - [`#[exception]`] to override an exception handler. If not overridden all exception handlers
+//! default to an infinite loop.
+//! - [`#[pre_init]`] to run code *before* `static` variables are initialized
+//!
+//! [`#[entry]`]: ../cortex_m_rt_macros/fn.entry.html
+//! [`#[exception]`]: ../cortex_m_rt_macros/fn.exception.html
+//! [`#[pre_init]`]: ../cortex_m_rt_macros/fn.pre_init.html
//!
//! # Requirements
//!
@@ -87,7 +94,7 @@
//! This section presents a minimal application built on top of `cortex-m-rt`. Apart from the
//! mandatory `memory.x` linker script describing the memory layout of the device, the hard fault
//! handler and the default exception handler must also be defined somewhere in the dependency
-//! graph (cf. [`exception!`]). In this example we define them in the binary crate:
+//! graph (see [`#[exception]`]). In this example we define them in the binary crate:
//!
//! ``` ignore
//! // IMPORTANT the standard `main` interface is not used because it requires nightly
@@ -191,15 +198,15 @@
//!
//! [`entry!`]: macro.entry.html
//!
-//! - `DefaultHandler`. This is the default handler. This function will contain, or call, the
-//! function you declared in the second argument of `exception!(*, ..)`.
+//! - `DefaultHandler`. This is the default handler. If not overridden using `#[exception] fn
+//! DefaultHandler(..` this will be an infinite loop.
//!
//! - `HardFault`. This is the hard fault handler. This function is simply a trampoline that jumps
-//! into the user defined hard fault handler: `UserHardFault`. The trampoline is required to set up
-//! the pointer to the stacked exception frame.
+//! into the user defined hard fault handler named `UserHardFault`. The trampoline is required to
+//! set up the pointer to the stacked exception frame.
//!
-//! - `UserHardFault`. This is the user defined hard fault handler. This function will contain, or
-//! call, the function you declared in the second argument of `exception!(HardFault, ..)`
+//! - `UserHardFault`. This is the user defined hard fault handler. If not overridden using
+//! `#[exception] fn HardFault(..` this will be an infinite loop.
//!
//! - `__STACK_START`. This is the first entry in the `.vector_table` section. This symbol contains
//! the initial value of the stack pointer; this is where the stack will be located -- the stack
@@ -340,8 +347,8 @@
//! PROVIDE(Bar = DefaultHandler);
//! ```
//!
-//! This weakly aliases both `Foo` and `Bar`. `DefaultHandler` is the default exception handler that
-//! the user provides via `exception!(*, ..)` and that the core exceptions use unless overridden.
+//! This weakly aliases both `Foo` and `Bar`. `DefaultHandler` is the default exception handler and
+//! that the core exceptions use unless overridden.
//!
//! Because this linker script is provided by a dependency of the final application the dependency
//! must contain build script that puts `device.x` somewhere the linker can find. An example of such