diff options
-rw-r--r-- | cortex-m-rt/macros/src/lib.rs | 146 | ||||
-rw-r--r-- | cortex-m-rt/src/lib.rs | 29 |
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 |