diff options
-rw-r--r-- | cortex-m-rt/.travis.yml | 85 | ||||
-rw-r--r-- | cortex-m-rt/Cargo.toml | 9 | ||||
-rw-r--r-- | cortex-m-rt/bors.toml | 3 | ||||
-rw-r--r-- | cortex-m-rt/build.rs | 35 | ||||
-rw-r--r-- | cortex-m-rt/ci/install.sh | 9 | ||||
-rw-r--r-- | cortex-m-rt/ci/script.sh | 34 | ||||
-rw-r--r-- | cortex-m-rt/device.x | 3 | ||||
-rw-r--r-- | cortex-m-rt/examples/device.rs | 50 | ||||
-rw-r--r-- | cortex-m-rt/examples/main.rs | 28 | ||||
-rw-r--r-- | cortex-m-rt/examples/minimal.rs | 31 | ||||
-rw-r--r-- | cortex-m-rt/examples/state.rs | 38 | ||||
-rw-r--r-- | cortex-m-rt/link.x.in (renamed from cortex-m-rt/link.x) | 20 | ||||
-rw-r--r-- | cortex-m-rt/memory.x | 2 | ||||
-rw-r--r-- | cortex-m-rt/src/lib.rs | 78 |
14 files changed, 382 insertions, 43 deletions
diff --git a/cortex-m-rt/.travis.yml b/cortex-m-rt/.travis.yml new file mode 100644 index 0000000..518037d --- /dev/null +++ b/cortex-m-rt/.travis.yml @@ -0,0 +1,85 @@ +language: rust + +matrix: + include: + - env: TARGET=x86_64-unknown-linux-gnu + + - env: TARGET=thumbv6m-none-eabi + rust: beta + addons: + apt: + packages: + - gcc-arm-none-eabi + + - env: TARGET=thumbv7m-none-eabi + rust: beta + addons: + apt: + packages: + - gcc-arm-none-eabi + + - env: TARGET=thumbv7em-none-eabi + rust: beta + addons: + apt: + packages: + - gcc-arm-none-eabi + + - env: TARGET=thumbv7em-none-eabihf + rust: beta + addons: + apt: + packages: + - gcc-arm-none-eabi + + - env: TARGET=thumbv6m-none-eabi + rust: nightly + addons: + apt: + packages: + - gcc-arm-none-eabi + + - env: TARGET=thumbv7m-none-eabi + rust: nightly + addons: + apt: + packages: + - gcc-arm-none-eabi + + - env: TARGET=thumbv7em-none-eabi + rust: nightly + addons: + apt: + packages: + - gcc-arm-none-eabi + + - env: TARGET=thumbv7em-none-eabihf + rust: nightly + addons: + apt: + packages: + - gcc-arm-none-eabi + +before_install: set -e + +install: + - bash ci/install.sh + +script: + - bash ci/script.sh + +after_script: set +e + +cache: cache + +before_cache: + - chmod -R a+r $HOME/.cargo; + +branches: + only: + - staging + - trying + +notifications: + email: + on_success: never diff --git a/cortex-m-rt/Cargo.toml b/cortex-m-rt/Cargo.toml index 51269af..460917b 100644 --- a/cortex-m-rt/Cargo.toml +++ b/cortex-m-rt/Cargo.toml @@ -9,11 +9,14 @@ name = "cortex-m-rt" repository = "https://github.com/japaric/cortex-m-rt" version = "0.5.0" +[build-dependencies] +cc = "1.0.10" + [dependencies] r0 = "0.2.1" -[build-dependencies] -cc = "1.0.10" +[dev-dependencies] +panic-semihosting = "0.2.0" [features] -device = []
\ No newline at end of file +device = [] diff --git a/cortex-m-rt/bors.toml b/cortex-m-rt/bors.toml new file mode 100644 index 0000000..5ccee21 --- /dev/null +++ b/cortex-m-rt/bors.toml @@ -0,0 +1,3 @@ +status = [ + "continuous-integration/travis-ci/push", +]
\ No newline at end of file diff --git a/cortex-m-rt/build.rs b/cortex-m-rt/build.rs index 1098dca..1b5c3d1 100644 --- a/cortex-m-rt/build.rs +++ b/cortex-m-rt/build.rs @@ -9,7 +9,7 @@ fn main() { let target = env::var("TARGET").unwrap(); has_fpu(&target); - is_armv6m(&target); + let is_armv6m = is_armv6m(&target); if target.starts_with("thumbv") { cc::Build::new().file("asm.s").compile("asm"); @@ -17,8 +17,8 @@ fn main() { // Put the linker script somewhere the linker can find it let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); - let link_x = include_bytes!("link.x"); - if env::var_os("CARGO_FEATURE_DEVICE").is_some() { + let link_x = include_bytes!("link.x.in"); + let mut f = if env::var_os("CARGO_FEATURE_DEVICE").is_some() { let mut f = File::create(out.join("link.x")).unwrap(); writeln!( @@ -29,12 +29,28 @@ fn main() { INCLUDE device.x"# ).unwrap(); f.write_all(link_x).unwrap(); + f } else { - File::create(out.join("link.x")) - .unwrap() - .write_all(link_x) - .unwrap(); + let mut f = File::create(out.join("link.x")).unwrap(); + f.write_all(link_x).unwrap(); + f }; + + let max_int_handlers = if is_armv6m { 32 } else { 240 }; + + // checking the size of the interrupts portion of the vector table is sub-architecture dependent + writeln!( + f, + r#" +ASSERT(__einterrupts - __eexceptions <= 0x{:x}, " +There can't be more than {} interrupt handlers. This may be a bug in +your device crate, or you may have registered more than 240 interrupt +handlers."); +"#, + max_int_handlers * 4, + max_int_handlers + ).unwrap(); + println!("cargo:rustc-link-search={}", out.display()); println!("cargo:rerun-if-changed=build.rs"); @@ -47,8 +63,11 @@ fn has_fpu(target: &str) { } } -fn is_armv6m(target: &str) { +fn is_armv6m(target: &str) -> bool { if target.starts_with("thumbv6m-") { println!("cargo:rustc-cfg=armv6m"); + true + } else { + false } } diff --git a/cortex-m-rt/ci/install.sh b/cortex-m-rt/ci/install.sh new file mode 100644 index 0000000..3c41921 --- /dev/null +++ b/cortex-m-rt/ci/install.sh @@ -0,0 +1,9 @@ +set -euxo pipefail + +main() { + if [ $TARGET != x86_64-unknown-linux-gnu ]; then + rustup target add $TARGET + fi +} + +main diff --git a/cortex-m-rt/ci/script.sh b/cortex-m-rt/ci/script.sh new file mode 100644 index 0000000..221386d --- /dev/null +++ b/cortex-m-rt/ci/script.sh @@ -0,0 +1,34 @@ +set -euxo pipefail + +main() { + cargo check --target $TARGET + + cargo check --target $TARGET --features device + + local examples=( + minimal + main + state + ) + if [ $TRAVIS_RUST_VERSION = nightly ]; then + for ex in "${examples[@]}"; do + cargo rustc --target $TARGET --example $ex -- \ + -C link-arg=-nostartfiles \ + -C link-arg=-Wl,-Tlink.x + + cargo rustc --target $TARGET --example $ex --release -- \ + -C link-arg=-nostartfiles \ + -C link-arg=-Wl,-Tlink.x + done + + cargo rustc --target $TARGET --example device --features device -- \ + -C link-arg=-nostartfiles \ + -C link-arg=-Wl,-Tlink.x + + cargo rustc --target $TARGET --example device --features device --release -- \ + -C link-arg=-nostartfiles \ + -C link-arg=-Wl,-Tlink.x + fi +} + +main diff --git a/cortex-m-rt/device.x b/cortex-m-rt/device.x new file mode 100644 index 0000000..28f975e --- /dev/null +++ b/cortex-m-rt/device.x @@ -0,0 +1,3 @@ +/* Sample device.x file */ +PROVIDE(WWDG = DefaultHandler); +PROVIDE(PVD = DefaultHandler); diff --git a/cortex-m-rt/examples/device.rs b/cortex-m-rt/examples/device.rs new file mode 100644 index 0000000..cf91f21 --- /dev/null +++ b/cortex-m-rt/examples/device.rs @@ -0,0 +1,50 @@ +//! Manually create the interrupts portion of the vector table + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +#[macro_use(entry, exception)] +extern crate cortex_m_rt as rt; +extern crate panic_semihosting; + +use rt::ExceptionFrame; + +// the program entry point +entry!(main); + +fn main() -> ! { + loop {} +} + +// the hard fault handler +exception!(HardFault, hard_fault); + +fn hard_fault(_ef: &ExceptionFrame) -> ! { + loop {} +} + +// the default exception handler +exception!(*, default_handler); + +fn default_handler(_irqn: i16) {} + +// interrupts portion of the vector table +pub union Vector { + handler: unsafe extern "C" fn(), + reserved: usize, +} + +extern "C" { + fn WWDG(); + fn PVD(); +} + +#[link_section = ".vector_table.interrupts"] +#[no_mangle] +pub static __INTERRUPTS: [Vector; 3] = [ + Vector { handler: WWDG }, + Vector { reserved: 0 }, + Vector { handler: PVD }, +]; diff --git a/cortex-m-rt/examples/main.rs b/cortex-m-rt/examples/main.rs new file mode 100644 index 0000000..d319249 --- /dev/null +++ b/cortex-m-rt/examples/main.rs @@ -0,0 +1,28 @@ +//! Directly plug a `main` symbol instead of using `entry!` + +#![deny(warnings)] +#![no_main] +#![no_std] + +#[macro_use(exception)] +extern crate cortex_m_rt as rt; +extern crate panic_semihosting; + +use rt::ExceptionFrame; + +#[no_mangle] +pub unsafe extern "C" fn main() -> ! { + loop {} +} + +// the hard fault handler +exception!(HardFault, hard_fault); + +fn hard_fault(_ef: &ExceptionFrame) -> ! { + loop {} +} + +// the default exception handler +exception!(*, default_handler); + +fn default_handler(_irqn: i16) {} diff --git a/cortex-m-rt/examples/minimal.rs b/cortex-m-rt/examples/minimal.rs new file mode 100644 index 0000000..c12d12d --- /dev/null +++ b/cortex-m-rt/examples/minimal.rs @@ -0,0 +1,31 @@ +//! Minimal `cortex-m-rt` based program + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +#[macro_use(entry, exception)] +extern crate cortex_m_rt as rt; +extern crate panic_semihosting; + +use rt::ExceptionFrame; + +// the program entry point +entry!(main); + +fn main() -> ! { + loop {} +} + +// the hard fault handler +exception!(HardFault, hard_fault); + +fn hard_fault(_ef: &ExceptionFrame) -> ! { + loop {} +} + +// the default exception handler +exception!(*, default_handler); + +fn default_handler(_irqn: i16) {} diff --git a/cortex-m-rt/examples/state.rs b/cortex-m-rt/examples/state.rs new file mode 100644 index 0000000..0b5eeeb --- /dev/null +++ b/cortex-m-rt/examples/state.rs @@ -0,0 +1,38 @@ +//! Preserving state across executions of an exception handler + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +#[macro_use(entry, exception)] +extern crate cortex_m_rt as rt; +extern crate panic_semihosting; + +use rt::ExceptionFrame; + +// the program entry point +entry!(main); + +fn main() -> ! { + loop {} +} + +// exception handler with state +exception!(SysTick, sys_tick, state: u32 = 0); + +fn sys_tick(state: &mut u32) { + *state += 1; +} + +// the hard fault handler +exception!(HardFault, hard_fault); + +fn hard_fault(_ef: &ExceptionFrame) -> ! { + loop {} +} + +// the default exception handler +exception!(*, default_handler); + +fn default_handler(_irqn: i16) {} diff --git a/cortex-m-rt/link.x b/cortex-m-rt/link.x.in index 44c427d..0e68199 100644 --- a/cortex-m-rt/link.x +++ b/cortex-m-rt/link.x.in @@ -155,7 +155,7 @@ __sheap = __edata; /* Do not exceed this mark in the error messages below | */ ASSERT(__reset_vector == ORIGIN(FLASH) + 0x8, -"Reset vector is missing. This is a cortex-m-rt bug. +"The reset vector is missing. This is a cortex-m-rt bug. Please file a bug report at: https://github.com/japaric/cortex-m-rt/issues"); @@ -169,14 +169,9 @@ The interrupt handlers are missing. If you are not linking to a device crate then you supply the interrupt handlers yourself. Check the documentation."); -ASSERT(__einterrupts - __eexceptions <= 0x3c0, " -There can't be more than 240 interrupt handlers. This may be a bug in -your device crate, or you may have registered more than 240 interrupt -handlers."); - ASSERT(__einterrupts <= _stext, " The '.text' section can't be placed inside '.vector_table' section. -Set '_stext' to an address greater than '__einterrupts'"); +Set '_stext' to an address greater than '__einterrupts' (cf. `nm`)"); ASSERT(_stext < ORIGIN(FLASH) + LENGTH(FLASH), " The '.text' section must be placed inside the FLASH memory @@ -193,8 +188,9 @@ Set '_stext' to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)"); /* __sidata is not 4-byte aligned. This is a cortex-m-rt bug."); */ ASSERT(__sgot == __egot, " -.got section detected in the input files. Dynamic relocations are not -supported. If you are linking to C code compiled using the `cc` crate -then modify your build script to compile the C code _without_ the --fPIC flag. See the documentation of the `cc::Build.pic` method for -details."); +.got section detected in the input object files. Dynamic relocations +are not supported. If you are linking to C code compiled using the +`cc` crate then modify your build script to compile the C code +_without_ the -fPIC flag. See the documentation of the +`cc::Build.pic` method for details."); +/* Do not exceed this mark in the error messages above | */ diff --git a/cortex-m-rt/memory.x b/cortex-m-rt/memory.x index 8027efc..3d97414 100644 --- a/cortex-m-rt/memory.x +++ b/cortex-m-rt/memory.x @@ -16,4 +16,4 @@ MEMORY /* The location of the .text section can be overridden using the `_stext` symbol. By default it will place after .vector_table */ -/* _stext = ORIGIN(FLASH) + 0x100; */ +/* _stext = ORIGIN(FLASH) + 0x40c; */ diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs index 4bb5fe1..e9aacf7 100644 --- a/cortex-m-rt/src/lib.rs +++ b/cortex-m-rt/src/lib.rs @@ -21,7 +21,7 @@ //! # `memory.x` //! //! This crate expects the user, or some other crate, to provide the memory layout of the target -//! device via a linker named `memory.x`. This section covers the contents of `memory.x` +//! device via a linker script named `memory.x`. This section covers the contents of `memory.x` //! //! ## `MEMORY` //! @@ -65,8 +65,8 @@ //! ## `_stext` //! //! This optional symbol can be used to control where the `.text` section is placed. If omitted the -//! `.text` section will be placed right after the vector table (which is placed at the beginning of -//! `FLASH`). Some devices store settings like Flash configuration right after the vector table; +//! `.text` section will be placed right after the vector table, which is placed at the beginning of +//! `FLASH`. Some devices store settings like Flash configuration right after the vector table; //! for these devices one must place the `.text` section after this configuration section -- //! `_stext` can be used for this purpose. //! @@ -218,7 +218,42 @@ //! If you override any exception handler you'll find it as an unmangled symbol, e.g. `SysTick` or //! `SVCall`, in the output of `objdump`, //! -//! # Incorporating device specific interrupts +//! If you are targeting the `thumbv7em-none-eabihf` target you'll also see a `ResetTrampoline` +//! symbol in the output. To avoid the compiler placing FPU instructions before the FPU has been +//! enabled (cf. `vpush`) `Reset` calls the function `ResetTrampoline` which is marked as +//! `#[inline(never)]` and `ResetTrampoline` calls `main`. The compiler is free to inline `main` +//! into `ResetTrampoline` but it can't inline `ResetTrampoline` into `Reset` -- the FPU is enabled +//! in `Reset`. +//! +//! # Advanced usage +//! +//! ## Setting the program entry point +//! +//! This section describes how `entry!` is implemented. This information is useful to developers who +//! want to provide an alternative to `entry!` that provides extra guarantees. +//! +//! The `Reset` handler will call a symbol named `main` (unmangled) *after* initializing `.bss` and +//! `.data`, and enabling the FPU (if the target is `thumbv7em-none-eabihf`). `entry!` provides this +//! symbol in its expansion: +//! +//! ``` ignore +//! entry!(path::to::main); +//! +//! // expands into +//! +//! #[export_name = "main"] +//! pub extern "C" fn __impl_main() -> ! { +//! // validate the signature of the program entry point +//! let f: fn() -> ! = path::to::main; +//! +//! f() +//! } +//! ``` +//! +//! The unmangled `main` symbol must have signature `extern "C" fn() -> !` or its invocation from +//! `Reset` will result in undefined behavior. +//! +//! ## Incorporating device specific interrupts //! //! This section covers how an external crate can insert device specific interrupt handlers into the //! vector table. Most users don't need to concern themselves with these details, but if you are @@ -226,7 +261,7 @@ //! //! The information in this section applies when the `"device"` feature has been enabled. //! -//! ## `__INTERRUPTS` +//! ### `__INTERRUPTS` //! //! The external crate must provide the interrupts portion of the vector table via a `static` //! variable named`__INTERRUPTS` (unmangled) that must be placed in the `.vector_table.interrupts` @@ -273,7 +308,7 @@ //! ]; //! ``` //! -//! ## `device.x` +//! ### `device.x` //! //! Linking in `__INTERRUPTS` creates a bunch of undefined references. If the user doesn't set a //! handler for *all* the device specific interrupts then linking will fail with `"undefined @@ -425,9 +460,12 @@ pub unsafe extern "C" fn Reset() -> ! { } } -/// Macro to define the user entry point of a program +/// Macro to define the entry point of the program /// -/// Usage: `entry!($path::to::user::entry)` +/// **NOTE** This macro must be invoked once and must be invoked from an accessible module, ideally +/// from the root of the crate. +/// +/// Usage: `entry!(path::to::entry::point)` /// /// The specified function will be called by the reset handler *after* RAM has been initialized. In /// the case of the `thumbv7em-none-eabihf` target the FPU will also be enabled before the function @@ -439,7 +477,7 @@ macro_rules! entry { ($path:path) => { #[export_name = "main"] pub extern "C" fn __impl_main() -> ! { - // validate the signature of the user provide `main` + // validate the signature of the program entry point let f: fn() -> ! = $path; f() @@ -561,10 +599,7 @@ pub static __EXCEPTIONS: [Vector; 14] = [ // 13: Reserved Vector { reserved: 0 }, // Exception 14: Pend SV Interrupt [not on Cortex-M0 variants]. - #[cfg(not(armv6m))] Vector { handler: PendSV }, - #[cfg(armv6m)] - Vector { reserved: 0 }, // Exception 15: System Tick Interrupt. Vector { handler: SysTick }, ]; @@ -598,6 +633,9 @@ pub static __INTERRUPTS: [unsafe extern "C" fn(); 32] = [{ /// Macro to set or override a processor core exception handler /// +/// **NOTE** This macro must be invoked from an accessible module, ideally from the root of the +/// crate. +/// /// # Syntax /// /// ``` ignore @@ -613,7 +651,7 @@ pub static __INTERRUPTS: [unsafe extern "C" fn(); 32] = [{ /// ); /// ``` /// -/// `$Name` can be one of: +/// where `$Name` can be one of: /// /// - `*` /// - `NonMaskableInt` @@ -701,9 +739,9 @@ pub static __INTERRUPTS: [unsafe extern "C" fn(); 32] = [{ macro_rules! exception { (* , $handler:path) => { #[allow(unsafe_code)] - #[allow(non_snake_case)] - #[export_name = "DefaultHandler"] - pub unsafe extern "C" fn __impl_DefaultHandler() { + #[deny(private_no_mangle_fns)] // raise an error if this item is not accessible + #[no_mangle] + pub unsafe extern "C" fn DefaultHandler() { extern crate core; // validate the signature of the user provided handler @@ -719,9 +757,9 @@ macro_rules! exception { (HardFault, $handler:path) => { #[allow(unsafe_code)] - #[allow(non_snake_case)] - #[export_name = "UserHardFault"] - pub unsafe extern "C" fn __impl_UserHardFault(ef: &$crate::ExceptionFrame) { + #[deny(private_no_mangle_fns)] // raise an error if this item is not accessible + #[no_mangle] + pub unsafe extern "C" fn UserHardFault(ef: &$crate::ExceptionFrame) { // validate the signature of the user provided handler let f: fn(&$crate::ExceptionFrame) -> ! = $handler; @@ -731,6 +769,7 @@ macro_rules! exception { ($Name:ident, $handler:path,state: $State:ty = $initial_state:expr) => { #[allow(unsafe_code)] + #[deny(private_no_mangle_fns)] // raise an error if this item is not accessible #[no_mangle] pub unsafe extern "C" fn $Name() { static mut STATE: $State = $initial_state; @@ -747,6 +786,7 @@ macro_rules! exception { ($Name:ident, $handler:path) => { #[allow(unsafe_code)] + #[deny(private_no_mangle_fns)] // raise an error if this item is not accessible #[no_mangle] pub unsafe extern "C" fn $Name() { // check that this exception exists |