aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bors[bot] <bors[bot]@users.noreply.github.com> 2018-05-12 16:01:33 +0000
committerGravatar bors[bot] <bors[bot]@users.noreply.github.com> 2018-05-12 16:01:33 +0000
commite047a5cfd6f569e101f12e5ed81ad882584b5610 (patch)
tree780583be0fbdcb7bb51189048ba92835dc391534
parent17b72ec698240ff70e6a4f64158eefdf58df598a (diff)
parent09013442e31a44634b44fa92c48f2134bf1388e9 (diff)
downloadcortex-m-e047a5cfd6f569e101f12e5ed81ad882584b5610.tar.gz
cortex-m-e047a5cfd6f569e101f12e5ed81ad882584b5610.tar.zst
cortex-m-e047a5cfd6f569e101f12e5ed81ad882584b5610.zip
Merge #69
69: compile on stable r=japaric a=japaric with these changes this crate compiles on stable Co-authored-by: Jorge Aparicio <jorge@japaric.io>
-rw-r--r--cortex-m-rt/.travis.yml85
-rw-r--r--cortex-m-rt/CHANGELOG.md37
-rw-r--r--cortex-m-rt/Cargo.toml14
-rw-r--r--cortex-m-rt/asm.s5
-rw-r--r--cortex-m-rt/bors.toml3
-rw-r--r--cortex-m-rt/build.rs51
-rw-r--r--cortex-m-rt/ci/install.sh9
-rw-r--r--cortex-m-rt/ci/script.sh34
-rw-r--r--cortex-m-rt/device.x3
-rw-r--r--cortex-m-rt/examples/device.rs50
-rw-r--r--cortex-m-rt/examples/main.rs28
-rw-r--r--cortex-m-rt/examples/minimal.rs31
-rw-r--r--cortex-m-rt/examples/state.rs38
-rw-r--r--cortex-m-rt/link.x125
-rw-r--r--cortex-m-rt/link.x.in204
-rw-r--r--cortex-m-rt/memory.x19
-rw-r--r--cortex-m-rt/src/lang_items.rs39
-rw-r--r--cortex-m-rt/src/lib.rs1131
18 files changed, 1287 insertions, 619 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/CHANGELOG.md b/cortex-m-rt/CHANGELOG.md
index 3dbed87..dc07408 100644
--- a/cortex-m-rt/CHANGELOG.md
+++ b/cortex-m-rt/CHANGELOG.md
@@ -7,6 +7,43 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
+## [v0.5.0] - 2018-05-12
+
+### Added
+
+- An `entry!` macro to set the entry point of the program.
+
+- A `heap_start` function that returns a pointer into the start of the heap region.
+
+- A `device` feature. When disabled this crate provides the interrupt vectors; when enabled the
+ interrupt vectors are expected to be provided by another crate. Read the documentation for
+ details.
+
+### Changed
+
+- This crate now compiles on the beta and stable channels.
+
+- [breaking-change] this crate now requires `arm-none-eabi-gcc` to be installed and available in
+ `$PATH` to compile.
+
+- [breaking-change] the `start` lang item has been removed. The standard `main` interface won't
+ work. Instead use `#![no_main]` and the `entry!` macro. See documentation for details.
+
+- [breaking-change] the `default_handler!` macro has been merged into the `exception!` macro. Use
+ `exception!(*, ..)` to set the default exception handler.
+
+- [breaking-change] there's no weak default handler so a default handler must be defined by the
+ application, or one of its dependencies.
+
+- [breaking-change] the syntax of the third argument of the `exception!` handler has changed. See
+ the documentation of the macro for details.
+
+- [breaking-change] the exception names that the `exception!` macro accepts has changed to match the
+ CMSIS specification. See the documentation of the macro for the list of names it accepts.
+
+- [breaking-change] The number of symbol interfaces has been reduced. Check the advanced section of
+ the documentation for details.
+
## [v0.4.0] - 2018-04-09
### Added
diff --git a/cortex-m-rt/Cargo.toml b/cortex-m-rt/Cargo.toml
index cd31b2d..460917b 100644
--- a/cortex-m-rt/Cargo.toml
+++ b/cortex-m-rt/Cargo.toml
@@ -7,8 +7,16 @@ keywords = ["arm", "cortex-m", "runtime", "startup"]
license = "MIT OR Apache-2.0"
name = "cortex-m-rt"
repository = "https://github.com/japaric/cortex-m-rt"
-version = "0.4.0"
+version = "0.5.0"
+
+[build-dependencies]
+cc = "1.0.10"
[dependencies]
-cortex-m = "0.3.0"
-r0 = "0.2.1" \ No newline at end of file
+r0 = "0.2.1"
+
+[dev-dependencies]
+panic-semihosting = "0.2.0"
+
+[features]
+device = []
diff --git a/cortex-m-rt/asm.s b/cortex-m-rt/asm.s
new file mode 100644
index 0000000..2937be8
--- /dev/null
+++ b/cortex-m-rt/asm.s
@@ -0,0 +1,5 @@
+ .global HardFault
+ .thumb_func
+HardFault:
+ mrs r0, MSP
+ bl UserHardFault
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 1d0160c..1b5c3d1 100644
--- a/cortex-m-rt/build.rs
+++ b/cortex-m-rt/build.rs
@@ -1,3 +1,5 @@
+extern crate cc;
+
use std::env;
use std::fs::File;
use std::io::Write;
@@ -7,14 +9,48 @@ 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");
+ }
// Put the linker script somewhere the linker can find it
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
- File::create(out.join("link.x"))
- .unwrap()
- .write_all(include_bytes!("link.x"))
- .unwrap();
+ 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!(
+ f,
+ r#"
+/* Provides weak aliases (cf. PROVIDED) for device specific interrupt handlers */
+/* This will usually be provided by a device crate generated using svd2rust (see `device.x`) */
+INCLUDE device.x"#
+ ).unwrap();
+ f.write_all(link_x).unwrap();
+ f
+ } else {
+ 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");
@@ -27,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
deleted file mode 100644
index 7e398de..0000000
--- a/cortex-m-rt/link.x
+++ /dev/null
@@ -1,125 +0,0 @@
-INCLUDE memory.x
-
-/* With multiple codegen units the rlib produced for this crate has several object files in it. */
-/* Because the linker is Smart it may not look into all the object files and not pick up the */
-/* .vector_table.exceptions section. But we want it to! To workaround the problem we create an */
-/* undefined reference to the EXCEPTIONS symbol (located in .vector_table.exceptions); this way the */
-/* linker will look at all the object of the rlib and pick up our EXCEPTIONS symbol */
-EXTERN(EXCEPTIONS);
-
-/* Create an undefined reference to the INTERRUPTS symbol. This is required to
- force the linker to *not* drop the INTERRUPTS symbol if it comes from an
- object file that's passed to the linker *before* this crate */
-EXTERN(INTERRUPTS);
-
-PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM));
-
-SECTIONS
-{
- .vector_table ORIGIN(FLASH) : ALIGN(4)
- {
- /* Vector table */
- _svector_table = .;
- LONG(_stack_start);
-
- KEEP(*(.vector_table.reset_vector));
-
- KEEP(*(.vector_table.exceptions));
- _eexceptions = .;
-
- KEEP(*(.vector_table.interrupts));
- _einterrupts = .;
- } > FLASH
-
- PROVIDE(_stext = _einterrupts);
-
- .text _stext : ALIGN(4)
- {
- /* Put reset handler first in .text section so it ends up as the entry */
- /* point of the program. */
- KEEP(*(.reset_handler));
-
- *(.text .text.*);
- } > FLASH
-
- .rodata : ALIGN(4)
- {
- *(.rodata .rodata.*);
- . = ALIGN(4);
- } > FLASH
-
- PROVIDE(_sbss = ORIGIN(RAM));
- .bss _sbss : ALIGN(4)
- {
- *(.bss .bss.*);
- . = ALIGN(4);
- _ebss = .;
- } > RAM AT > FLASH
- /* NOTE(AT > FLASH) without this LLD v6 produces a binary that crashes OpenOCD whereas LLD v7
- emits a ".rodata and .bss sections overlap" error ... This hacky workaround doesn't increase
- the binary size AFAICT */
-
- .data : ALIGN(4)
- {
- _sidata = LOADADDR(.data);
- _sdata = .;
- *(.data .data.*);
- . = ALIGN(4);
- _edata = .;
- } > RAM AT > FLASH
-
- /* The heap starts right after the .bss + .data section ends */
- _sheap = _edata;
-
- /* fake output .got section */
- /* Dynamic relocations are unsupported. This section is only used to detect
- relocatable code in the input files and raise an error if relocatable code
- is found */
- .got :
- {
- _sgot = .;
- KEEP(*(.got .got.*));
- _egot = .;
- } > RAM AT > FLASH
-
- /DISCARD/ :
- {
- *(.ARM.exidx.*);
- }
-}
-
-/* Do not exceed this mark in the error messages below | */
-ASSERT(_eexceptions - ORIGIN(FLASH) > 8, "
-The exception handlers are missing. This is likely a cortex-m-rt bug.
-Please file a bug report at:
-https://github.com/japaric/cortex-m-rt/issues");
-
-ASSERT(_eexceptions - ORIGIN(FLASH) == 0x40, "
-Invalid '.vector_table.exceptions' section. This is likely a
-cortex-m-rt bug. Please file a bug report at:
-https://github.com/japaric/cortex-m-rt/issues");
-
-ASSERT(_einterrupts - _eexceptions > 0, "
-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'");
-
-ASSERT(_stext < ORIGIN(FLASH) + LENGTH(FLASH), "
-The '.text' section must be placed inside the FLASH memory
-Set '_stext' to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)");
-
-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 `gcc` crate
-then modify your build script to compile the C code _without_ the
--fPIC flag. See the documentation of the `gcc::Config.fpic` method for
-details.");
diff --git a/cortex-m-rt/link.x.in b/cortex-m-rt/link.x.in
new file mode 100644
index 0000000..9b99427
--- /dev/null
+++ b/cortex-m-rt/link.x.in
@@ -0,0 +1,204 @@
+/* # Developer notes
+
+- Symbols that start with a double underscore (__) are considered "private"
+
+- Symbols that start with a single underscore (_) are considered "semi-public"; they can be
+ overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" {
+ static mut __sbss }`).
+
+- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a
+ symbol if not dropped if it appears in or near the front of the linker arguments and "it's not
+ needed" by any of the preceding objects (linker arguments)
+
+- `PROVIDE` is used to provide default values that can be overridden by a user linker script
+
+- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and*
+ the LMA of .data are all 4-byte aligned. These alignments are assumed by the RAM initialization
+ routine. There's also a second benefit: 4-byte aligned boundaries means that you won't see
+ "Address (..) is out of bounds" in the disassembly produced by `objdump`.
+*/
+
+/* Provides information about the memory layout of the device */
+/* This will be provided by the user (see `memory.x`) or by a Board Support Crate */
+INCLUDE memory.x
+
+/* # Entry point = reset vector */
+ENTRY(Reset);
+EXTERN(__RESET_VECTOR); /* depends on the `Reset` symbol */
+
+/* # Exception vectors */
+/* This is effectively weak aliasing at the linker level */
+/* The user can override any of these aliases by defining the corresponding symbol themselves (cf.
+ the `exception!` macro) */
+EXTERN(__EXCEPTIONS); /* depends on all the these PROVIDED symbols */
+
+EXTERN(DefaultHandler);
+
+PROVIDE(NonMaskableInt = DefaultHandler);
+EXTERN(HardFault);
+PROVIDE(MemoryManagement = DefaultHandler);
+PROVIDE(BusFault = DefaultHandler);
+PROVIDE(UsageFault = DefaultHandler);
+PROVIDE(SecureFault = DefaultHandler);
+PROVIDE(SVCall = DefaultHandler);
+PROVIDE(DebugMonitor = DefaultHandler);
+PROVIDE(PendSV = DefaultHandler);
+PROVIDE(SysTick = DefaultHandler);
+
+/* # Interrupt vectors */
+EXTERN(__INTERRUPTS); /* `static` variable similar to `__EXCEPTIONS` */
+
+/* # User overridable symbols I */
+/* Lets the user place the stack in a different RAM region */
+PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM));
+
+/* # Sections */
+SECTIONS
+{
+ /* ## Sections in FLASH */
+ /* ### Vector table */
+ .vector_table ORIGIN(FLASH) : ALIGN(4)
+ {
+ /* Initial Stack Pointer (SP) value */
+ __STACK_START = .; /* Just to get a nicer name in the disassembly */
+ LONG(_stack_start);
+
+ /* Reset vector */
+ KEEP(*(.vector_table.reset_vector)); /* this is `__RESET_VECTOR` symbol */
+ __reset_vector = ABSOLUTE(.);
+
+ /* Exceptions */
+ KEEP(*(.vector_table.exceptions)); /* this is `__EXCEPTIONS` symbol */
+ __eexceptions = ABSOLUTE(.);
+
+ /* Device specific interrupts */
+ KEEP(*(.vector_table.interrupts)); /* this is `__INTERRUPTS` symbol */
+ __einterrupts = ABSOLUTE(.);
+ } > FLASH
+
+ /* ### .text */
+ .text _stext :
+ {
+ *(.text .text.*);
+ __etext = ABSOLUTE(.);
+ } > FLASH
+
+ /* ### .rodata */
+ .rodata :
+ {
+ . = ALIGN(4); /* 4-byte align the start (VMA) of this section */
+ /* __srodata = ABSOLUTE(.); */
+
+ *(.rodata .rodata.*);
+
+ . = ALIGN(4); /* 4-byte align the end (VMA) of this section */
+ __erodata = ABSOLUTE(.);
+ } > FLASH
+
+ /* ## Sections in RAM */
+ /* ### .data */
+ .data : AT(__erodata) /* LMA */
+ {
+ . = ALIGN(4); /* 4-byte align the start (VMA) of this section */
+ __sdata = ABSOLUTE(.);
+
+ *(.data .data.*);
+
+ . = ALIGN(4); /* 4-byte align the end (VMA) of this section */
+ __edata = ABSOLUTE(.);
+ } > RAM
+
+ /* ### .bss */
+ .bss :
+ {
+ . = ALIGN(4); /* 4-byte align the start (VMA) of this section */
+ __sbss = ABSOLUTE(.);
+
+ *(.bss .bss.*);
+
+ . = ALIGN(4); /* 4-byte align the end (VMA) of this section */
+ __ebss = ABSOLUTE(.);
+ } > RAM
+
+ /* ## Fake output .got section */
+ /* Dynamic relocations are unsupported. This section is only used to detect relocatable code in
+ the input files and raise an error if relocatable code is found */
+ .got :
+ {
+ __sgot = ABSOLUTE(.);
+ KEEP(*(.got .got.*));
+ __egot = ABSOLUTE(.);
+ } > FLASH
+
+ /* ## Discarded sections */
+ /DISCARD/ :
+ {
+ /* Unused exception related info that only wastes space */
+ *(.ARM.exidx.*);
+ }
+}
+
+/* # User overridable symbols II */
+/* (The user overridable symbols are split in two parts because LLD demands that the RHS of PROVIDE
+ to be defined before the PROVIDE invocation) */
+/* Lets the user override this to place .text a bit further than the vector table. Required by
+microcontrollers that store their configuration right after the vector table. */
+PROVIDE(_stext = __einterrupts);
+
+/* # Hardcoded symbols */
+/* Place `.bss` at the start of the RAM region */
+__sidata = LOADADDR(.data);
+/* Place the heap right after `.bss` and `.data` */
+__sheap = __ebss;
+
+/* # Sanity checks */
+
+/* Do not exceed this mark in the error messages below | */
+ASSERT(__reset_vector == ORIGIN(FLASH) + 0x8, "
+cortex-m-rt: The reset vector is missing. This is a bug in cortex-m-rt. Please file a bug
+report at: https://github.com/japaric/cortex-m-rt/issues");
+
+ASSERT(__eexceptions - ORIGIN(FLASH) == 0x40, "
+cortex-m-rt: The exception vectors are missing. This is a bug in cortex-m-rt. Please file
+a bug report at: https://github.com/japaric/cortex-m-rt/issues");
+
+ASSERT(__sheap >= __ebss, "
+cortex-m-rt: The heap overlaps with the .bss section. This is a bug in cortex-m-rt. Please
+file a bug report at: https://github.com/japaric/cortex-m-rt/issues");
+
+ASSERT(__sheap >= __edata, "
+cortex-m-rt: The heap overlaps with the .data section. This is a bug in cortex-m-rt.
+Please file a bug report at: https://github.com/japaric/cortex-m-rt/issues");
+
+ASSERT(__einterrupts - __eexceptions > 0, "
+cortex-m-rt: The interrupt vectors are missing. Possible solutions, from most likely to
+less likely:
+- Link to a device crate
+- Disable the 'device' feature of cortex-m-rt to build a generic application (a dependency
+ may be enabling it)
+- Supply the interrupt handlers yourself. Check the documentation for details.");
+
+ASSERT(__einterrupts <= _stext, "
+cortex-m-rt: The '.text' section can't be placed inside the '.vector_table' section. Set
+'_stext' to an address greater than '__einterrupts' (cf. `nm` output)");
+
+ASSERT(_stext < ORIGIN(FLASH) + LENGTH(FLASH), "
+cortex-m-rt The '.text' section must be placed inside the FLASH memory. Set '_stext' to an
+address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)");
+
+/* This has been temporarily omitted because it's not supported by LLD */
+/* ASSERT(__sbss % 4 == 0 && __ebss % 4 == 0, " */
+/* .bss is not 4-byte aligned at its boundaries. This is a cortex-m-rt bug."); */
+
+/* ASSERT(__sdata % 4 == 0 && __edata % 4 == 0, " */
+/* .data is not 4-byte aligned at its boundaries. This is a cortex-m-rt bug."); */
+
+/* ASSERT(__sidata % 4 == 0, " */
+/* __sidata is not 4-byte aligned. This is a cortex-m-rt bug."); */
+
+ASSERT(__sgot == __egot, "
+.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
new file mode 100644
index 0000000..3d97414
--- /dev/null
+++ b/cortex-m-rt/memory.x
@@ -0,0 +1,19 @@
+/* Device specific memory layout */
+
+MEMORY
+{
+ /* FLASH and RAM are mandatory memory regions */
+ FLASH : ORIGIN = 0x08000000, LENGTH = 64K
+ RAM : ORIGIN = 0x20000000, LENGTH = 20K
+
+ /* More memory regions can declared: for example this is a second RAM region */
+ /* CCRAM : ORIGIN = 0x10000000, LENGTH = 8K */
+}
+
+/* The location of the stack can be overridden using the `_stack_start` symbol.
+ By default it will be placed at the end of the RAM region */
+/* _stack_start = ORIGIN(CCRAM) + LENGTH(CCRAM); */
+
+/* The location of the .text section can be overridden using the `_stext` symbol.
+ By default it will place after .vector_table */
+/* _stext = ORIGIN(FLASH) + 0x40c; */
diff --git a/cortex-m-rt/src/lang_items.rs b/cortex-m-rt/src/lang_items.rs
deleted file mode 100644
index 571a8a5..0000000
--- a/cortex-m-rt/src/lang_items.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-// Lang item required to make the normal `main` work in applications
-//
-// This is how the `start` lang item works:
-// When `rustc` compiles a binary crate, it creates a `main` function that looks
-// like this:
-//
-// ```
-// #[export_name = "main"]
-// pub extern "C" fn rustc_main(argc: isize, argv: *const *const u8) -> isize {
-// start(main, argc, argv)
-// }
-// ```
-//
-// Where `start` is this function and `main` is the binary crate's `main`
-// function.
-//
-// The final piece is that the entry point of our program, the reset handler,
-// has to call `rustc_main`. That's covered by the `reset_handler` function in
-// root of this crate.
-#[lang = "start"]
-extern "C" fn start<T>(main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
-where
- T: Termination,
-{
- main();
-
- 0
-}
-
-#[lang = "termination"]
-pub trait Termination {
- fn report(self) -> i32;
-}
-
-impl Termination for () {
- fn report(self) -> i32 {
- 0
- }
-}
diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs
index 7028cce..eaefbbc 100644
--- a/cortex-m-rt/src/lib.rs
+++ b/cortex-m-rt/src/lib.rs
@@ -1,615 +1,854 @@
//! Minimal startup / runtime for Cortex-M microcontrollers
//!
+//! This crate contains all the required parts to build a `no_std` application (binary crate) that
+//! targets a Cortex-M microcontroller.
+//!
//! # Features
//!
-//! This crate provides
+//! This crates takes care of:
+//!
+//! - The memory layout of the program. In particular, it populates the vector table so the device
+//! can boot correctly, and properly dispatch exceptions and interrupts.
+//!
+//! - Initializing `static` variables before the program entry point.
+//!
+//! - Enabling the FPU before the program entry point if the target is `thumbv7em-none-eabihf`.
//!
-//! - Before main initialization of the `.bss` and `.data` sections.
+//! This crate also provides a mechanism to set exception handlers: see the [`exception!`] macro.
//!
-//! - Before main initialization of the FPU (for targets that have a FPU).
+//! [`exception!`]: macro.exception.html
//!
-//! - A minimal `start` lang item to support the standard `fn main()`
-//! interface. (The processor goes to sleep (`loop { asm!("wfi") }`) after
-//! returning from `main`)
+//! # Requirements
//!
-//! - A linker script that encodes the memory layout of a generic Cortex-M
-//! microcontroller. This linker script is missing some information that must
-//! be supplied through a `memory.x` file (see example below).
+//! ## `arm-none-eabi-gcc`
//!
-//! - A default exception handler tailored for debugging that lets you inspect
-//! what was the state of the processor at the time of the exception. By
-//! default, all exceptions are serviced by this handler but each exception
-//! can be individually overridden using the
-//! [`exception!`](macro.exception.html) macro. The default exception handler
-//! itself can also be overridden using the
-//! [`default_handler!`](macro.default_handler.html) macro.
+//! This crate requires `arm-none-eabi-gcc` to be installed and available in `$PATH`.
//!
-//! - A `_sheap` symbol at whose address you can locate a heap.
+//! ## `memory.x`
+//!
+//! This crate expects the user, or some other crate, to provide the memory layout of the target
+//! device via a linker script named `memory.x`. This section covers the contents of `memory.x`
//!
-//! # Example
+//! ### `MEMORY`
//!
-//! Creating a new bare metal project. (I recommend you use the
-//! [`cortex-m-quickstart`](https://docs.rs/cortex-m-quickstart/0.2.0/cortex_m_quickstart/) template
-//! as it takes of all the boilerplate shown here)
+//! The linker script must specify the memory available in the device as, at least, two `MEMORY`
+//! regions: one named `FLASH` and one named `RAM`. The `.text` and `.rodata` sections of the
+//! program will be placed in the `FLASH` region, whereas the `.bss` and `.data` sections, as well
+//! as the heap,will be placed in the `RAM` region.
//!
//! ``` text
-//! $ cargo new --bin app && cd $_
+//! /* Linker script for the STM32F103C8T6 */
+//! MEMORY
+//! {
+//! FLASH : ORIGIN = 0x08000000, LENGTH = 64K
+//! RAM : ORIGIN = 0x20000000, LENGTH = 20K
+//! }
+//! ```
//!
-//! $ # add this crate as a dependency
-//! $ cargo add cortex-m-rt --vers 0.4.0
+//! ### `_stack_start`
//!
-//! $ # select a panicking behavior (look for the panic-impl keyword on crates.io)
-//! $ cargo add panic-abort
+//! This optional symbol can be used to indicate where the call stack of the program should be
+//! placed. If this symbol is not used then the stack will be placed at the *end* of the `RAM`
+//! region -- the stack grows downwards towards smaller address. This symbol can be used to place
+//! the stack in a different memory region, for example:
//!
-//! $ # memory layout of the device
-//! $ $EDITOR memory.x && cat $_
+//! ``` text
+//! /* Linker script for the STM32F303VCT6 */
//! MEMORY
//! {
-//! /* NOTE K = KiBi = 1024 bytes */
-//! FLASH : ORIGIN = 0x08000000, LENGTH = 128K
-//! RAM : ORIGIN = 0x20000000, LENGTH = 8K
+//! FLASH : ORIGIN = 0x08000000, LENGTH = 256K
+//!
+//! /* .bss, .data and the heap go in this region */
+//! RAM : ORIGIN = 0x20000000, LENGTH = 40K
+//!
+//! /* Core coupled (faster) RAM dedicated to hold the stack */
+//! CCRAM : ORIGIN = 0x10000000, LENGTH = 8K
//! }
//!
-//! $ $EDITOR src/main.rs && cat $_
+//! _stack_start = ORIGIN(CCRAM) + LENGTH(CCRAM);
+//! ```
+//!
+//! ### `_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;
+//! for these devices one must place the `.text` section after this configuration section --
+//! `_stext` can be used for this purpose.
+//!
+//! ``` text
+//! MEMORY
+//! {
+//! /* .. */
+//! }
+//!
+//! /* The device stores Flash configuration in 0x400-0x40C so we place .text after that */
+//! _stext = ORIGIN(FLASH) + 0x40C
//! ```
//!
-//! ``` ignore,no_run
-//! #![feature(used)]
+//! # An example
+//!
+//! 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:
+//!
+//! ``` ignore
+//! // IMPORTANT the standard `main` interface is not used because it requires nightly
+//! #![no_main]
//! #![no_std]
//!
-//! extern crate cortex_m_rt;
-//! extern crate panic_abort; // panicking behavior
+//! #[macro_use(entry, exception)]
+//! extern crate cortex_m_rt as rt;
//!
-//! fn main() {
-//! // do something here
+//! // makes `panic!` print messages to the host stderr using semihosting
+//! extern crate panic_semihosting;
+//!
+//! use rt::ExceptionFrame;
+//!
+//! // use `main` as the entry point of this application
+//! entry!(main);
+//!
+//! // `main` is not allowed to return
+//! fn main() -> ! {
+//! // initialization
+//!
+//! loop {
+//! // application logic
+//! }
//! }
//!
-//! // As we are not using interrupts, we just register a dummy catch all
-//! // handler
-//! #[link_section = ".vector_table.interrupts"]
-//! #[used]
-//! static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240];
+//! // define the hard fault handler
+//! exception!(HardFault, hard_fault);
//!
-//! extern "C" fn default_handler() {
-//! loop {}
+//! fn hard_fault(ef: &ExceptionFrame) -> ! {
+//! panic!("{:#?}", ef);
+//! }
+//!
+//! // define the default exception handler
+//! exception!(*, default_handler);
+//!
+//! fn default_handler(irqn: i16) {
+//! panic!("unhandled exception (IRQn={})", irqn);
//! }
//! ```
//!
+//! To actually build this program you need to place a `memory.x` linker script somewhere the linker
+//! can find it, e.g. in the current directory; and then link the program using `cortex-m-rt`'s
+//! linker script: `link.x`. The required steps are shown below:
+//!
//! ``` text
-//! $ rustup target add thumbv7m-none-eabi
+//! $ cat > memory.x <<EOF
+//! /* Linker script for the STM32F103C8T6 */
+//! MEMORY
+//! {
+//! FLASH : ORIGIN = 0x08000000, LENGTH = 64K
+//! RAM : ORIGIN = 0x20000000, LENGTH = 20K
+//! }
+//! EOF
//!
//! $ cargo rustc --target thumbv7m-none-eabi -- \
-//! -C link-arg=-Tlink.x -C linker=arm-none-eabi-ld -Z linker-flavor=ld
+//! -C link-arg=-nostartfiles -C link-arg=-Tlink.x
//!
-//! $ arm-none-eabi-objdump -Cd $(find target -name app) | head
+//! $ file target/thumbv7m-none-eabi/debug/app
+//! app: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, (..)
+//! ```
//!
-//! Disassembly of section .text:
+//! # Optional features
//!
-//! 08000400 <cortex_m_rt::reset_handler>:
-//! 8000400: b580 push {r7, lr}
-//! 8000402: 466f mov r7, sp
-//! 8000404: b084 sub sp, #8
+//! ## `device`
//!
+//! If this feature is disabled then this crate populates the whole vector table. All the interrupts
+//! in the vector table, even the ones unused by the target device, will be bound to the default
+//! exception handler. This makes the final application device agnostic: you will be able to run it
+//! on any Cortex-M device -- provided that you correctly specified its memory layout in `memory.x`
+//! -- without hitting undefined behavior.
//!
-//! $ arm-none-eabi-size -Ax $(find target -name app) | head
-//! target/thumbv7m-none-eabi/debug/app :
-//! section size addr
-//! .vector_table 0x400 0x8000000
-//! .text 0x24a 0x8000400
-//! .rodata 0x0 0x800064c
-//! .bss 0x0 0x20000000
-//! .data 0x0 0x20000000
-//! ```
+//! If this feature is enabled then the interrupts section of the vector table is left unpopulated
+//! and some other crate, or the user, will have to populate it. This mode is meant to be used in
+//! conjunction with crates generated using `svd2rust`. Those *device crates* will populate the
+//! missing part of the vector table when their `"rt"` feature is enabled.
//!
-//! # Symbol interfaces
+//! # Inspection
//!
-//! This crate makes heavy use of symbols, linker sections and linker scripts to
-//! provide most of its functionality. Below are described the main symbol
-//! interfaces.
+//! This section covers how to inspect a binary that builds on top of `cortex-m-rt`.
//!
-//! ## `DEFAULT_HANDLER`
+//! ## Sections (`size`)
//!
-//! This weak symbol can be overridden to override the default exception handler
-//! that this crate provides. It's recommended that you use the
-//! `default_handler!` to do the override, but below is shown how to manually
-//! override the symbol:
+//! `cortex-m-rt` uses standard sections like `.text`, `.rodata`, `.bss` and `.data` as one would
+//! expect. `cortex-m-rt` separates the vector table in its own section, named `.vector_table`. This
+//! lets you distinguish how much space is taking the vector table in Flash vs how much is being
+//! used by actual instructions (`.text`) and constants (`.rodata`).
//!
-//! ``` ignore,no_run
-//! #[no_mangle]
-//! pub extern "C" fn DEFAULT_HANDLER() {
-//! // do something here
-//! }
+//! ```
+//! $ size -Ax target/thumbv7m-none-eabi/examples/app
+//! target/thumbv7m-none-eabi/release/examples/app :
+//! section size addr
+//! .vector_table 0x400 0x8000000
+//! .text 0x88 0x8000400
+//! .rodata 0x0 0x8000488
+//! .data 0x0 0x20000000
+//! .bss 0x0 0x20000000
//! ```
//!
-//! ## `.vector_table.interrupts`
+//! Without the `-A` argument `size` reports the sum of the sizes of `.text`, `.rodata` and
+//! `.vector_table` under "text".
//!
-//! This linker section is used to register interrupt handlers in the vector
-//! table. The recommended way to use this section is to populate it, once, with
-//! an array of *weak* functions that just call the `DEFAULT_HANDLER` symbol.
-//! Then the user can override them by name.
+//! ```
+//! $ size target/thumbv7m-none-eabi/examples/app
+//! text data bss dec hex filename
+//! 1160 0 0 1660 67c target/thumbv7m-none-eabi/release/app
+//! ```
//!
-//! ### Example
+//! ## Symbols (`objdump`, `nm`)
//!
-//! Populating the vector table
+//! One will always find the following (unmangled) symbols in `cortex-m-rt` applications:
//!
-//! ``` ignore,no_run
-//! // Number of interrupts the device has
-//! const N: usize = 60;
+//! - `Reset`. This is the reset handler. The microcontroller will executed this function upon
+//! booting. This function will call the user program entry point (cf. [`entry!`]) using the `main`
+//! symbol so you may also find that symbol in your program; if you do, `main` will contain your
+//! application code. Some other times `main` gets inlined into `Reset` so you won't find it.
//!
-//! // Default interrupt handler that just calls the `DEFAULT_HANDLER`
-//! #[linkage = "weak"]
-//! #[naked]
-//! #[no_mangle]
-//! extern "C" fn WWDG() {
-//! unsafe {
-//! asm!("b DEFAULT_HANDLER" :::: "volatile");
-//! core::intrinsics::unreachable();
-//! }
-//! }
+//! [`entry!`]: macro.entry.html
//!
-//! // You need one function per interrupt handler
-//! #[linkage = "weak"]
-//! #[naked]
-//! #[no_mangle]
-//! extern "C" fn PVD() {
-//! unsafe {
-//! asm!("b DEFAULT_HANDLER" :::: "volatile");
-//! core::intrinsics::unreachable();
-//! }
-//! }
+//! - `DefaultHandler`. This is the default handler. This function will contain, or call, the
+//! function you declared in the second argument of `exception!(*, ..)`.
//!
-//! // ..
+//! - `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.
//!
-//! // Use `None` for reserved spots in the vector table
-//! #[link_section = ".vector_table.interrupts"]
-//! #[no_mangle]
-//! #[used]
-//! static INTERRUPTS: [Option<extern "C" fn()>; N] = [
-//! Some(WWDG),
-//! Some(PVD),
-//! // ..
-//! ];
-//! ```
+//! - `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, ..)`
//!
-//! Overriding an interrupt (this can be in a different crate)
+//! - `__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
+//! grows downwards towards smaller addresses.
//!
-//! ``` ignore,no_run
-//! // the name must match the name of one of the weak functions used to
-//! // populate the vector table.
-//! #[no_mangle]
-//! pub extern "C" fn WWDG() {
-//! // do something here
-//! }
-//! ```
+//! - `__RESET_VECTOR`. This is the reset vector, a pointer into the `Reset` handler. This vector is
+//! located in the `.vector_table` section after `__STACK_START`.
//!
-//! ## `memory.x`
+//! - `__EXCEPTIONS`. This is the core exceptions portion of the vector table; it's an array of 14
+//! exception vectors, which includes exceptions like `HardFault` and `SysTick`. This array is
+//! located after `__RESET_VECTOR` in the `.vector_table` section.
//!
-//! This file supplies the information about the device to the linker.
+//! - `__EXCEPTIONS`. This is the device specific interrupt portion of the vector table; its exact
+//! size depends on the target device but if the `"device"` feature has not been enabled it will
+//! have a size of 32 vectors (on ARMv6-M) or 240 vectors (on ARMv7-M). This array is located after
+//! `__EXCEPTIONS` in the `.vector_table` section.
//!
-//! ### `MEMORY`
+//! If you override any exception handler you'll find it as an unmangled symbol, e.g. `SysTick` or
+//! `SVCall`, in the output of `objdump`,
//!
-//! The main information that this file must provide is the memory layout of
-//! the device in the form of the `MEMORY` command. The command is documented
-//! [here](https://sourceware.org/binutils/docs/ld/MEMORY.html), but at a minimum you'll want to
-//! create two memory regions: one for Flash memory and another for RAM.
+//! 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`.
//!
-//! The program instructions (the `.text` section) will be stored in the memory
-//! region named FLASH, and the program `static` variables (the sections `.bss`
-//! and `.data`) will be allocated in the memory region named RAM.
+//! # Advanced usage
//!
-//! ### `_stack_start`
+//! ## Setting the program entry point
//!
-//! This symbol provides the address at which the call stack will be allocated.
-//! The call stack grows downwards so this address is usually set to the highest
-//! valid RAM address plus one (this *is* an invalid address but the processor
-//! will decrement the stack pointer *before* using its value as an address).
+//! 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.
//!
-//! If omitted this symbol value will default to `ORIGIN(RAM) + LENGTH(RAM)`.
+//! 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:
//!
-//! #### Example
+//! ``` ignore
+//! entry!(path::to::main);
//!
-//! Allocating the call stack on a different RAM region.
+//! // expands into
//!
-//! ``` ignore
-//! MEMORY
-//! {
-//! /* call stack will go here */
-//! CCRAM : ORIGIN = 0x10000000, LENGTH = 8K
-//! FLASH : ORIGIN = 0x08000000, LENGTH = 256K
-//! /* static variables will go here */
-//! RAM : ORIGIN = 0x20000000, LENGTH = 40K
-//! }
+//! #[export_name = "main"]
+//! pub extern "C" fn __impl_main() -> ! {
+//! // validate the signature of the program entry point
+//! let f: fn() -> ! = path::to::main;
//!
-//! _stack_start = ORIGIN(CCRAM) + LENGTH(CCRAM);
+//! f()
+//! }
//! ```
//!
-//! ### `_stext`
+//! The unmangled `main` symbol must have signature `extern "C" fn() -> !` or its invocation from
+//! `Reset` will result in undefined behavior.
//!
-//! This symbol indicates where the `.text` section will be located. If not
-//! specified in the `memory.x` file it will default to right after the vector
-//! table -- the vector table is always located at the start of the FLASH
-//! region.
+//! ## Incorporating device specific interrupts
//!
-//! The main use of this symbol is leaving some space between the vector table
-//! and the `.text` section unused. This is required on some microcontrollers
-//! that store some configuration information right after the vector table.
+//! 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
+//! interested in how device crates generated using `svd2rust` integrate with `cortex-m-rt` read on.
//!
-//! #### Example
+//! The information in this section applies when the `"device"` feature has been enabled.
//!
-//! Locate the `.text` section 1024 bytes after the start of the FLASH region.
+//! ### `__INTERRUPTS`
//!
-//! ``` ignore
-//! _stext = ORIGIN(FLASH) + 0x400;
-//! ```
+//! 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`
+//! section of its object file.
//!
-//! ### `_sheap`
+//! This `static` variable will be placed at `ORIGIN(FLASH) + 0x40`. This address corresponds to the
+//! spot where IRQ0 (IRQ number 0) is located.
//!
-//! This symbol is located in RAM right after the `.bss` and `.data` sections.
-//! You can use the address of this symbol as the start address of a heap
-//! region. This symbol is 4 byte aligned so that address will be a multiple of 4.
+//! To conform to the Cortex-M ABI `__INTERRUPTS` must be an array of function pointers; some spots
+//! in this array may need to be set to 0 if they are marked as *reserved* in the data sheet /
+//! reference manual. We recommend using a `union` to set the reserved spots to `0`; `None`
+//! (`Option<fn()>`) may also work but it's not guaranteed that the `None` variant will *always* be
+//! represented by the value `0`.
//!
-//! #### Example
+//! Let's illustrate with an artificial example where a device only has two interrupt: `Foo`, with
+//! IRQ number = 2, and `Bar`, with IRQ number = 4.
//!
//! ``` ignore
-//! extern crate some_allocator;
-//!
-//! // Size of the heap in bytes
-//! const SIZE: usize = 1024;
+//! union Vector {
+//! handler: extern "C" fn(),
+//! reserved: usize,
+//! }
//!
//! extern "C" {
-//! static mut _sheap: u8;
+//! fn Foo();
+//! fn Bar();
//! }
//!
+//! #[link_section = ".vector_table.interrupts"]
+//! #[no_mangle]
+//! pub static __INTERRUPTS: [Vector; 5] = [
+//! // 0-1: Reserved
+//! Vector { reserved: 0 },
+//! Vector { reserved: 0 },
+//!
+//! // 2: Foo
+//! Vector { handler: Foo },
+//!
+//! // 3: Reserved
+//! Vector { reserved: 0 },
+//!
+//! // 4: Bar
+//! Vector { handler: Bar },
+//! ];
+//! ```
+//!
+//! ### `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
+//! reference"` errors.
+//!
+//! We want to provide a default handler for all the interrupts while still letting the user
+//! individually override each interrupt handler. In C projects, this is usually accomplished using
+//! weak aliases declared in external assembly files. In Rust, we could achieve something similar
+//! using `global_asm!`, but that's an unstable feature.
+//!
+//! A solution that doesn't require `global_asm!` or external assembly files is to use the `PROVIDE`
+//! command in a linker script to create the weak aliases. This is the approach that `cortex-m-rt`
+//! uses; when the `"device"` feature is enabled `cortex-m-rt`'s linker script (`link.x`) depends on
+//! a linker script named `device.x`. The crate that provides `__INTERRUPTS` must also provide this
+//! file.
+//!
+//! For our running example the `device.x` linker script looks like this:
+//!
+//! ``` text
+//! /* device.x */
+//! PROVIDE(Foo = DefaultHandler);
+//! 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.
+//!
+//! 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
+//! build script is shown below:
+//!
+//! ``` ignore
+//! use std::env;
+//! use std::fs::File;
+//! use std::io::Write;
+//! use std::path::PathBuf;
+//!
//! fn main() {
-//! unsafe {
-//! let start_address = &mut _sheap as *mut u8;
-//! some_allocator::initialize(start_address, SIZE);
-//! }
+//! // Put the linker script somewhere the linker can find it
+//! let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
+//! File::create(out.join("device.x"))
+//! .unwrap()
+//! .write_all(include_bytes!("device.x"))
+//! .unwrap();
+//! println!("cargo:rustc-link-search={}", out.display());
//! }
//! ```
-//!
-//! [1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html
-//! [qs]: https://docs.rs/cortex-m-quickstart/0.2.0/cortex_m_quickstart/
-//! [2]: https://sourceware.org/binutils/docs/ld/MEMORY.html
+
+// # Developer notes
+//
+// - `link_section` is used to place symbols in specific places of the final binary. The names used
+// here will appear in the linker script (`link.x`) in conjunction with the `KEEP` command.
#![deny(missing_docs)]
#![deny(warnings)]
-#![feature(asm)]
-#![feature(core_intrinsics)]
-#![feature(global_asm)]
-#![feature(lang_items)]
-#![feature(linkage)]
-#![feature(naked_functions)]
-#![feature(used)]
#![no_std]
-#[cfg(target_arch = "arm")]
-extern crate cortex_m;
-#[cfg(target_arch = "arm")]
extern crate r0;
-#[cfg(not(test))]
-mod lang_items;
+use core::fmt;
-#[cfg(target_arch = "arm")]
-use core::intrinsics;
+/// Registers stacked (pushed into the stack) during an exception
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct ExceptionFrame {
+ /// (General purpose) Register 0
+ pub r0: u32,
-#[cfg(target_arch = "arm")]
-use cortex_m::asm;
-#[cfg(target_arch = "arm")]
-use cortex_m::exception::ExceptionFrame;
+ /// (General purpose) Register 1
+ pub r1: u32,
-extern "C" {
- // NOTE `rustc` forces this signature on us. See `src/lang_items.rs`
- #[cfg(target_arch = "arm")]
- fn main(argc: isize, argv: *const *const u8) -> isize;
+ /// (General purpose) Register 2
+ pub r2: u32,
+
+ /// (General purpose) Register 3
+ pub r3: u32,
- // Boundaries of the .bss section
- static mut _ebss: u32;
- static mut _sbss: u32;
+ /// (General purpose) Register 12
+ pub r12: u32,
- // Boundaries of the .data section
- static mut _edata: u32;
- static mut _sdata: u32;
+ /// Linker Register
+ pub lr: u32,
- // Initial values of the .data section (stored in Flash)
- static _sidata: u32;
+ /// Program Counter
+ pub pc: u32,
+
+ /// Program Status Register
+ pub xpsr: u32,
}
-#[cfg(target_arch = "arm")]
-#[link_section = ".vector_table.reset_vector"]
-#[used]
-static RESET_VECTOR: unsafe extern "C" fn() -> ! = reset_handler;
+impl fmt::Debug for ExceptionFrame {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ struct Hex(u32);
+ impl fmt::Debug for Hex {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "0x{:08x}", self.0)
+ }
+ }
+ f.debug_struct("ExceptionFrame")
+ .field("r0", &Hex(self.r0))
+ .field("r1", &Hex(self.r1))
+ .field("r2", &Hex(self.r2))
+ .field("r3", &Hex(self.r3))
+ .field("r12", &Hex(self.r12))
+ .field("lr", &Hex(self.lr))
+ .field("pc", &Hex(self.pc))
+ .field("xpsr", &Hex(self.xpsr))
+ .finish()
+ }
+}
-/// The reset handler
+/// Returns a pointer to the start of the heap
///
-/// This is the entry point of all programs
-#[cfg(target_arch = "arm")]
-#[link_section = ".reset_handler"]
-unsafe extern "C" fn reset_handler() -> ! {
- r0::zero_bss(&mut _sbss, &mut _ebss);
- r0::init_data(&mut _sdata, &mut _edata, &_sidata);
+/// The returned pointer is guaranteed to be 4-byte aligned.
+#[inline]
+pub fn heap_start() -> *mut u32 {
+ extern "C" {
+ static mut __sheap: u32;
+ }
+
+ unsafe { &mut __sheap }
+}
+
+/* Entry point */
+#[doc(hidden)]
+#[link_section = ".vector_table.reset_vector"]
+#[no_mangle]
+pub static __RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset;
+
+#[doc(hidden)]
+#[no_mangle]
+pub unsafe extern "C" fn Reset() -> ! {
+ extern "C" {
+ // This symbol will be provided by the user via the `entry!` macro
+ fn main() -> !;
+
+ // These symbols come from `link.x`
+ static mut __sbss: u32;
+ static mut __ebss: u32;
+
+ static mut __sdata: u32;
+ static mut __edata: u32;
+ static __sidata: u32;
+ }
+
+ // Initialize RAM
+ r0::zero_bss(&mut __sbss, &mut __ebss);
+ r0::init_data(&mut __sdata, &mut __edata, &__sidata);
match () {
#[cfg(not(has_fpu))]
- () => {
- // Neither `argc` or `argv` make sense in bare metal context so we
- // just stub them
- main(0, ::core::ptr::null());
- }
+ () => main(),
#[cfg(has_fpu)]
() => {
- // NOTE(safe) no exception / interrupt that also accesses the FPU
- // can occur here
- let scb = &*cortex_m::peripheral::SCB.get();
- scb.enable_fpu();
-
- // Make sure the user main function never gets inlined into this
- // function as that may cause FPU related instructions like vpush to
- // be executed *before* enabling the FPU and that would generate an
- // exception
+ // We redefine these here to avoid pulling the `cortex-m` crate as a dependency
+ const SCB_CPACR: *mut u32 = 0xE000_ED88 as *mut u32;
+ const SCB_CPACR_FPU_ENABLE: u32 = 0b01_01 << 20;
+ const SCB_CPACR_FPU_USER: u32 = 0b10_10 << 20;
+
+ // enable the FPU
+ core::ptr::write_volatile(
+ SCB_CPACR,
+ *SCB_CPACR | SCB_CPACR_FPU_ENABLE | SCB_CPACR_FPU_USER,
+ );
+
+ // this is used to prevent the compiler from inlining the user `main` into the reset
+ // handler. Inlining can cause the FPU instructions in the user `main` to be executed
+ // before enabling the FPU, and that would produce a hard to diagnose hard fault at
+ // runtime.
#[inline(never)]
- fn main() {
- unsafe {
- ::main(0, ::core::ptr::null());
- }
+ #[export_name = "ResetTrampoline"]
+ fn trampoline() -> ! {
+ unsafe { main() }
}
- main()
+ trampoline()
}
}
+}
- // If `main` returns, then we go into "reactive" mode and simply attend
- // interrupts as they occur.
- loop {
- asm!("wfi" :::: "volatile");
- }
+/// Macro to define the entry point of the program
+///
+/// **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
+/// is called.
+///
+/// The signature of the specified function must be `fn() -> !` (never ending function)
+#[macro_export]
+macro_rules! entry {
+ ($path:path) => {
+ #[export_name = "main"]
+ pub extern "C" fn __impl_main() -> ! {
+ // validate the signature of the program entry point
+ let f: fn() -> ! = $path;
+
+ f()
+ }
+ };
}
-#[cfg(target_arch = "arm")]
-global_asm!(
- r#"
-.weak NMI
-NMI = DEFAULT_HANDLER
+/* Exceptions */
+#[doc(hidden)]
+pub enum Exception {
+ NonMaskableInt,
-.weak HARD_FAULT
-HARD_FAULT = DEFAULT_HANDLER
+ // Not overridable
+ // HardFault,
+ #[cfg(not(armv6m))]
+ MemoryManagement,
-.weak MEM_MANAGE
-MEM_MANAGE = DEFAULT_HANDLER
+ #[cfg(not(armv6m))]
+ BusFault,
-.weak BUS_FAULT
-BUS_FAULT = DEFAULT_HANDLER
+ #[cfg(not(armv6m))]
+ UsageFault,
-.weak USAGE_FAULT
-USAGE_FAULT = DEFAULT_HANDLER
+ #[cfg(armv8m)]
+ SecureFault,
-.weak SVCALL
-SVCALL = DEFAULT_HANDLER
+ SVCall,
-.weak PENDSV
-PENDSV = DEFAULT_HANDLER
+ #[cfg(not(armv6m))]
+ DebugMonitor,
-.weak SYS_TICK
-SYS_TICK = DEFAULT_HANDLER
-"#
-);
+ PendSV,
-#[cfg(not(armv6m))]
-global_asm!(
- r#"
-.weak DEBUG_MONITOR
-DEBUG_MONITOR = DEFAULT_HANDLER
-"#
-);
+ SysTick,
+}
-#[cfg(target_arch = "arm")]
extern "C" {
- fn NMI();
- fn HARD_FAULT();
- fn MEM_MANAGE();
- fn BUS_FAULT();
- fn USAGE_FAULT();
- fn SVCALL();
+ fn NonMaskableInt();
+
+ fn HardFault();
+
+ #[cfg(not(armv6m))]
+ fn MemoryManagement();
+
+ #[cfg(not(armv6m))]
+ fn BusFault();
+
+ #[cfg(not(armv6m))]
+ fn UsageFault();
+
+ #[cfg(armv8m)]
+ fn SecureFault();
+
+ fn SVCall();
+
#[cfg(not(armv6m))]
- fn DEBUG_MONITOR();
- fn PENDSV();
- fn SYS_TICK();
+ fn DebugMonitor();
+
+ fn PendSV();
+
+ fn SysTick();
+}
+
+#[doc(hidden)]
+pub union Vector {
+ handler: unsafe extern "C" fn(),
+ reserved: usize,
}
-#[allow(private_no_mangle_statics)]
-#[cfg(target_arch = "arm")]
#[doc(hidden)]
#[link_section = ".vector_table.exceptions"]
#[no_mangle]
-#[used]
-pub static EXCEPTIONS: [Option<unsafe extern "C" fn()>; 14] = [
- Some(NMI),
- Some(HARD_FAULT),
- Some(MEM_MANAGE),
- Some(BUS_FAULT),
- Some(USAGE_FAULT),
- None,
- None,
- None,
- None,
- Some(SVCALL),
+pub static __EXCEPTIONS: [Vector; 14] = [
+ // Exception 2: Non Maskable Interrupt.
+ Vector {
+ handler: NonMaskableInt,
+ },
+ // Exception 3: Hard Fault Interrupt.
+ Vector { handler: HardFault },
+ // Exception 4: Memory Management Interrupt [not on Cortex-M0 variants].
+ #[cfg(not(armv6m))]
+ Vector {
+ handler: MemoryManagement,
+ },
+ #[cfg(armv6m)]
+ Vector { reserved: 0 },
+ // Exception 5: Bus Fault Interrupt [not on Cortex-M0 variants].
+ #[cfg(not(armv6m))]
+ Vector { handler: BusFault },
#[cfg(armv6m)]
- None,
+ Vector { reserved: 0 },
+ // Exception 6: Usage Fault Interrupt [not on Cortex-M0 variants].
#[cfg(not(armv6m))]
- Some(DEBUG_MONITOR),
- None,
- Some(PENDSV),
- Some(SYS_TICK),
+ Vector {
+ handler: UsageFault,
+ },
+ #[cfg(armv6m)]
+ Vector { reserved: 0 },
+ // Exception 7: Secure Fault Interrupt [only on Armv8-M].
+ #[cfg(armv8m)]
+ Vector {
+ handler: SecureFault,
+ },
+ #[cfg(not(armv8m))]
+ Vector { reserved: 0 },
+ // 8-10: Reserved
+ Vector { reserved: 0 },
+ Vector { reserved: 0 },
+ Vector { reserved: 0 },
+ // Exception 11: SV Call Interrupt.
+ Vector { handler: SVCall },
+ // Exception 12: Debug Monitor Interrupt [not on Cortex-M0 variants].
+ #[cfg(not(armv6m))]
+ Vector {
+ handler: DebugMonitor,
+ },
+ #[cfg(armv6m)]
+ Vector { reserved: 0 },
+ // 13: Reserved
+ Vector { reserved: 0 },
+ // Exception 14: Pend SV Interrupt [not on Cortex-M0 variants].
+ Vector { handler: PendSV },
+ // Exception 15: System Tick Interrupt.
+ Vector { handler: SysTick },
];
-/// `ef` points to the exception frame
-///
-/// That exception frame is a snapshot of the program state right before the
-/// exception occurred.
-#[allow(unused_variables)]
-#[cfg(target_arch = "arm")]
-extern "C" fn default_handler(ef: &ExceptionFrame) -> ! {
- asm::bkpt();
-
- loop {}
-
- #[export_name = "DEFAULT_HANDLER"]
- #[linkage = "weak"]
- #[naked]
- extern "C" fn trampoline() -> ! {
- unsafe {
- asm!("mrs r0, MSP
- b $0"
- :
- : "i"(default_handler as extern "C" fn(&ExceptionFrame) -> !)
- :
- : "volatile");
-
- intrinsics::unreachable()
- }
+// If we are not targeting a specific device we bind all the potential device specific interrupts
+// to the default handler
+#[cfg(all(not(feature = "device"), not(armv6m)))]
+#[doc(hidden)]
+#[link_section = ".vector_table.interrupts"]
+#[no_mangle]
+pub static __INTERRUPTS: [unsafe extern "C" fn(); 240] = [{
+ extern "C" {
+ fn DefaultHandler();
}
- #[used]
- static KEEP: extern "C" fn() -> ! = trampoline;
-}
+ DefaultHandler
+}; 240];
-// make sure the compiler emits the DEFAULT_HANDLER symbol so the linker can
-// find it!
-#[cfg(target_arch = "arm")]
-#[used]
-static KEEP: extern "C" fn(&ExceptionFrame) -> ! = default_handler;
+// ARMv6-M can only have a maximum of 32 device specific interrupts
+#[cfg(all(not(feature = "device"), armv6m))]
+#[doc(hidden)]
+#[link_section = ".vector_table.interrupts"]
+#[no_mangle]
+pub static __INTERRUPTS: [unsafe extern "C" fn(); 32] = [{
+ extern "C" {
+ fn DefaultHandler();
+ }
+
+ DefaultHandler
+}; 32];
-/// This macro lets you override the default exception handler
+/// Macro to set or override a processor core exception handler
///
-/// The first and only argument to this macro is the path to the function that
-/// will be used as the default handler. That function must have signature
-/// `fn()`
+/// **NOTE** This macro must be invoked from an accessible module, ideally from the root of the
+/// crate.
///
-/// # Examples
+/// # Syntax
///
/// ``` ignore
-/// default_handler!(foo::bar);
+/// exception!(
+/// // Name of the exception
+/// $Name:ident,
///
-/// mod foo {
-/// pub fn bar() {
-/// ::cortex_m::asm::bkpt();
-/// loop {}
-/// }
-/// }
+/// // Path to the exception handler (a function)
+/// $handler:path,
+///
+/// // Optional, state preserved across invocations of the handler
+/// state: $State:ty = $initial_state:expr,
+/// );
/// ```
-#[macro_export]
-macro_rules! default_handler {
- ($path:path) => {
- #[allow(non_snake_case)]
- #[doc(hidden)]
- #[no_mangle]
- pub unsafe extern "C" fn DEFAULT_HANDLER() {
- // type checking
- let f: fn() = $path;
- f();
- }
- }
-}
-
-/// Fault and system exceptions
-#[allow(non_camel_case_types)]
-#[doc(hidden)]
-pub enum Exception {
- /// Non-maskable interrupt
- NMI,
- /// All class of fault.
- HARD_FAULT,
- /// Memory management.
- MEN_MANAGE,
- /// Pre-fetch fault, memory access fault.
- BUS_FAULT,
- /// Undefined instruction or illegal state.
- USAGE_FAULT,
- /// System service call via SWI instruction
- SVCALL,
- /// Debug monitor
- #[cfg(not(armv6m))]
- DEBUG_MONITOR,
- /// Pendable request for system service
- PENDSV,
- /// System tick timer
- SYS_TICK,
-}
-
-/// Assigns a handler to an exception
///
-/// This macro takes two arguments: the name of an exception and the path to the
-/// function that will be used as the handler of that exception. That function
-/// must have signature `fn()`.
+/// where `$Name` can be one of:
///
-/// Optionally, a third argument may be used to declare exception local data.
-/// The handler will have exclusive access to these *local* variables on each
-/// invocation. If the third argument is used then the signature of the handler
-/// function must be `fn(&mut $NAME::Locals)` where `$NAME` is the first
-/// argument passed to the macro.
+/// - `*`
+/// - `NonMaskableInt`
+/// - `HardFault`
+/// - `MemoryManagement` (a)
+/// - `BusFault` (a)
+/// - `UsageFault` (a)
+/// - `SecureFault` (b)
+/// - `SVCall`
+/// - `DebugMonitor` (a)
+/// - `PendSV`
+/// - `SysTick`
///
-/// # Example
+/// (a) Not available on Cortex-M0 variants (`thumbv6m-none-eabi`)
///
-/// ``` ignore
-/// exception!(MEM_MANAGE, mpu_fault);
+/// (b) Only available on ARMv8-M
+///
+/// # Usage
+///
+/// `exception!(HardFault, ..)` sets the hard fault handler. The handler must have signature
+/// `fn(&ExceptionFrame) -> !`. This handler is not allowed to return as that can cause undefined
+/// behavior. It's mandatory to set the `HardFault` handler somewhere in the dependency graph of an
+/// application.
///
-/// fn mpu_fault() {
-/// panic!("Oh no! Something went wrong");
+/// `exception!(*, ..)` sets the *default* handler. All exceptions which have not been assigned a
+/// handler will be serviced by this handler. This handler must have signature `fn(irqn: i16)`.
+/// `irqn` is the IRQ number (cf. CMSIS); `irqn` will be a negative number when the handler is
+/// servicing a core exception; `irqn` will be a positive number when the handler is servicing a
+/// device specific exception (interrupt). It's mandatory to set the default handler somewhere
+/// in the dependency graph of an application.
+///
+/// `exception!($Exception, ..)` overrides the default handler for `$Exception`. All exceptions,
+/// except for `HardFault`, can be assigned some `$State`.
+///
+/// # Examples
+///
+/// - Setting the `HardFault` handler
+///
+/// ```
+/// #[macro_use(exception)]
+/// extern crate cortex_m_rt as rt;
+///
+/// use rt::ExceptionFrame;
+///
+/// exception!(HardFault, hard_fault);
+///
+/// fn hard_fault(ef: &ExceptionFrame) -> ! {
+/// // prints the exception frame as a panic message
+/// panic!("{:#?}", ef);
/// }
///
-/// exception!(SYS_TICK, periodic, locals: {
-/// counter: u32 = 0;
-/// });
+/// # fn main() {}
+/// ```
+///
+/// - Setting the default handler
+///
+/// ```
+/// #[macro_use(exception)]
+/// extern crate cortex_m_rt as rt;
+///
+/// exception!(*, default_handler);
///
-/// fn periodic(locals: &mut SYS_TICK::Locals) {
-/// locals.counter += 1;
-/// println!("This function has been called {} times", locals.counter);
+/// fn default_handler(irqn: i16) {
+/// println!("IRQn = {}", irqn);
/// }
+///
+/// # fn main() {}
+/// ```
+///
+/// - Overriding the `SysTick` handler
+///
+/// ```
+/// #[macro_use(exception)]
+/// extern crate cortex_m_rt as rt;
+///
+/// exception!(SysTick, sys_tick, state: u32 = 0);
+///
+/// fn sys_tick(count: &mut u32) {
+/// println!("count = {}", *count);
+///
+/// *count += 1;
+/// }
+///
+/// # fn main() {}
/// ```
#[macro_export]
macro_rules! exception {
- ($NAME:ident, $path:path, locals: {
- $($lvar:ident:$lty:ident = $lval:expr;)+
- }) => {
- #[allow(non_snake_case)]
- mod $NAME {
- pub struct Locals {
- $(
- pub $lvar: $lty,
- )+
- }
+ (* , $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 DefaultHandler() {
+ extern crate core;
+
+ // validate the signature of the user provided handler
+ let f: fn(i16) = $handler;
+
+ const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32;
+
+ // NOTE not volatile so the compiler can opt the load operation away if the value is
+ // unused
+ f(core::ptr::read(SCB_ICSR) as u8 as i16 - 16)
}
+ };
- #[allow(non_snake_case)]
- #[doc(hidden)]
+ (HardFault, $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 the handler exists
- let _ = $crate::Exception::$NAME;
-
- static mut LOCALS: self::$NAME::Locals = self::$NAME::Locals {
- $(
- $lvar: $lval,
- )*
- };
-
- // type checking
- let f: fn(&mut self::$NAME::Locals) = $path;
- f(&mut LOCALS);
+ pub unsafe extern "C" fn UserHardFault(ef: &$crate::ExceptionFrame) {
+ // validate the signature of the user provided handler
+ let f: fn(&$crate::ExceptionFrame) -> ! = $handler;
+
+ f(ef)
}
};
- ($NAME:ident, $path:path) => {
- #[allow(non_snake_case)]
- #[doc(hidden)]
+
+ ($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() {
- // check that the handler exists
- let _ = $crate::Exception::$NAME;
+ pub unsafe extern "C" fn $Name() {
+ static mut STATE: $State = $initial_state;
- // type checking
- let f: fn() = $path;
- f();
+ // check that this exception exists
+ let _ = $crate::Exception::$Name;
+
+ // validate the signature of the user provided handler
+ let f: fn(&mut $State) = $handler;
+
+ f(&mut STATE)
}
- }
+ };
+
+ ($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
+ let _ = $crate::Exception::$Name;
+
+ // validate the signature of the user provided handler
+ let f: fn() = $handler;
+
+ f()
+ }
+ };
}