From 0f139c386b17ed1bc925e1b482455a9c64b7a33b Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 12 May 2018 20:41:42 +0200 Subject: [PATCH] use published versions, doc up, update CHANGELOG --- .cargo/config | 23 +++ CHANGELOG.md | 20 ++- Cargo.toml | 31 ++-- examples/allocator.rs | 35 ++-- examples/crash.rs | 129 +++++++-------- examples/device.rs | 30 ++-- examples/exception.rs | 29 ++-- examples/hello.rs | 25 +-- examples/itm.rs | 39 +++-- examples/minimal.rs | 65 ++++---- examples/panic.rs | 38 +++-- gen-examples.sh | 4 +- interrupts.x | 1 - memory.x | 4 +- src/examples/_0_minimal.rs | 81 +++++----- src/examples/_1_hello.rs | 47 +++--- src/examples/_2_itm.rs | 61 ++++--- src/examples/_3_panic.rs | 56 ++++--- src/examples/_4_crash.rs | 151 +++++++++--------- src/examples/_5_exception.rs | 59 ++++--- .../{_7_allocator.rs => _6_allocator.rs} | 63 ++++---- src/examples/{_6_device.rs => _7_device.rs} | 66 ++++---- src/examples/mod.rs | 6 +- src/lib.rs | 40 ++--- 24 files changed, 548 insertions(+), 555 deletions(-) delete mode 100644 interrupts.x rename src/examples/{_7_allocator.rs => _6_allocator.rs} (66%) rename src/examples/{_6_device.rs => _7_device.rs} (76%) diff --git a/.cargo/config b/.cargo/config index 5999ced..337c283 100644 --- a/.cargo/config +++ b/.cargo/config @@ -3,6 +3,11 @@ runner = 'arm-none-eabi-gdb' rustflags = [ "-C", "link-arg=-Wl,-Tlink.x", "-C", "link-arg=-nostartfiles", + + # uncomment to use rustc LLD to link programs (a) + # "-C", "link-arg=-Tlink.x", + # "-C", "linker=lld", + # "-Z", "linker-flavor=ld.lld", ] [target.thumbv7m-none-eabi] @@ -10,6 +15,11 @@ runner = 'arm-none-eabi-gdb' rustflags = [ "-C", "link-arg=-Wl,-Tlink.x", "-C", "link-arg=-nostartfiles", + + # uncomment to use rustc LLD to link programs (a) + # "-C", "link-arg=-Tlink.x", + # "-C", "linker=lld", + # "-Z", "linker-flavor=ld.lld", ] [target.thumbv7em-none-eabi] @@ -17,6 +27,11 @@ runner = 'arm-none-eabi-gdb' rustflags = [ "-C", "link-arg=-Wl,-Tlink.x", "-C", "link-arg=-nostartfiles", + + # uncomment to use rustc LLD to link programs (a) + # "-C", "link-arg=-Tlink.x", + # "-C", "linker=lld", + # "-Z", "linker-flavor=ld.lld", ] [target.thumbv7em-none-eabihf] @@ -24,4 +39,12 @@ runner = 'arm-none-eabi-gdb' rustflags = [ "-C", "link-arg=-Wl,-Tlink.x", "-C", "link-arg=-nostartfiles", + + # uncomment to use rustc LLD to link programs (a) + # "-C", "link-arg=-Tlink.x", + # "-C", "linker=lld", + # "-Z", "linker-flavor=ld.lld", ] + +# (a) you also need to comment out the other two `link-arg` lines. But note that as of v0.6.0 LLD +# has a bug where it mislinks FFI calls and they up crashing the program at runtime \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a1cfc1..be91fe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,23 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.3.0] - 2018-05-12 + +### Changed + +- [breaking-change] `arm-none-eabi-gcc` is now a mandatory dependency as it's required by the + `cortex-m-rt` dependency and also the default linker. + +- Bumped the `cortex-m` and `cortex-m-rt` dependencies to v0.5.0. Updated all the examples to match + the new `cortex-m-rt` API. + +- Updated the `allocator` example to compile on a recent nightly. + +- Removed `opt-level = "s"` from `profile.release`. This flag is still unstable. + +- Set the number of codegen-units to 1 when compiling in release mode. This produces smaller and + faster binaries. + ## [v0.2.7] - 2018-04-24 ### Changed @@ -149,7 +166,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Initial release -[Unreleased]: https://github.com/japaric/cortex-m-quickstart/compare/v0.2.7...HEAD +[Unreleased]: https://github.com/japaric/cortex-m-quickstart/compare/v0.3.0...HEAD +[v0.3.0]: https://github.com/japaric/cortex-m-quickstart/compare/v0.2.7...v0.3.0 [v0.2.7]: https://github.com/japaric/cortex-m-quickstart/compare/v0.2.6...v0.2.7 [v0.2.6]: https://github.com/japaric/cortex-m-quickstart/compare/v0.2.5...v0.2.6 [v0.2.5]: https://github.com/japaric/cortex-m-quickstart/compare/v0.2.4...v0.2.5 diff --git a/Cargo.toml b/Cargo.toml index afc3268..bc60c97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,33 +6,24 @@ keywords = ["arm", "cortex-m", "template"] license = "MIT OR Apache-2.0" name = "cortex-m-quickstart" repository = "https://github.com/japaric/cortex-m-quickstart" -version = "0.2.7" +version = "0.3.0" [dependencies] -# cortex-m-rt = "0.5.0" -cortex-m-rt = { git = "https://github.com/japaric/cortex-m-rt", branch = "stable" } -panic-abort = "0.1.1" -# panic-semihosting = "0.1.1" -panic-semihosting = { git = "https://github.com/japaric/panic-semihosting", branch = "stable" } +cortex-m = "0.5.0" +cortex-m-rt = "0.5.0" +cortex-m-semihosting = "0.3.0" +panic-itm = "0.1.1" +panic-semihosting = "0.2.0" + # Uncomment for the allocator example. -#alloc-cortex-m = "0.3.3" - -[dependencies.cortex-m] -branch = "stable" -default-features = false -git = "https://github.com/japaric/cortex-m" -# version = "0.4.4" - -[dependencies.cortex-m-semihosting] -default-features = false -version = "0.2.1" +# alloc-cortex-m = "0.3.6" # Uncomment for the device example. # [dependencies.stm32f103xx] # features = ["rt"] -# version = "0.9.0" +# version = "0.10.0" [profile.release] +codegen-units = 1 # better optimizations debug = true -lto = true -opt-level = "s" # TODO remove; this flag requires nightly +lto = true \ No newline at end of file diff --git a/examples/allocator.rs b/examples/allocator.rs index f342e72..0d06669 100644 --- a/examples/allocator.rs +++ b/examples/allocator.rs @@ -11,6 +11,7 @@ #![feature(alloc)] #![feature(global_allocator)] +#![feature(lang_items)] #![no_main] #![no_std] @@ -22,7 +23,7 @@ extern crate cortex_m; #[macro_use] extern crate cortex_m_rt as rt; extern crate cortex_m_semihosting as sh; -extern crate panic_abort; +extern crate panic_semihosting; use core::fmt::Write; @@ -31,15 +32,16 @@ use cortex_m::asm; use rt::ExceptionFrame; use sh::hio; +// this is the allocator the application will use #[global_allocator] static ALLOCATOR: CortexMHeap = CortexMHeap::empty(); const HEAP_SIZE: usize = 1024; // in bytes -main!(main); +entry!(main); fn main() -> ! { - // Initialize the allocator + // Initialize the allocator BEFORE you use it unsafe { ALLOCATOR.init(rt::heap_start() as usize, HEAP_SIZE) } // Growable array allocated on the heap @@ -51,20 +53,23 @@ fn main() -> ! { loop {} } -exception!(DefaultHandler, dh); - -#[inline(always)] -fn dh(_nr: u8) { - asm::bkpt(); -} - -exception!(HardFault, hf); - -#[inline(always)] -fn hf(_ef: &ExceptionFrame) -> ! { +// define what happens in an Out Of Memory (OOM) condition +#[lang = "oom"] +#[no_mangle] +pub fn rust_oom() -> ! { asm::bkpt(); loop {} } -interrupts!(DefaultHandler); +exception!(HardFault, hard_fault); + +fn hard_fault(ef: &ExceptionFrame) -> ! { + panic!("HardFault at {:#?}", ef); +} + +exception!(*, default_handler); + +fn default_handler(irqn: i16) { + panic!("Unhandled exception (IRQn = {})", irqn); +} diff --git a/examples/crash.rs b/examples/crash.rs index 5c25e26..fafe436 100644 --- a/examples/crash.rs +++ b/examples/crash.rs @@ -1,11 +1,12 @@ //! Debugging a crash (exception) //! -//! The `cortex-m-rt` crate provides functionality for this through a default exception handler. -//! When an exception is hit, the default handler will trigger a breakpoint and in this debugging -//! context the stacked registers are accessible. +//! Most crash conditions trigger a hard fault exception, whose handler is defined via +//! `exception!(HardFault, ..)`. The `HardFault` handler has access to the exception frame, a +//! snapshot of the CPU registers at the moment of the exception. //! -//! In you run the example below, you'll be able to inspect the state of your program under the -//! debugger using these commands: +//! This program crashes and the `HardFault` handler prints to the console the contents of the +//! `ExceptionFrame` and then triggers a breakpoint. From that breakpoint one can see the backtrace +//! that led to the exception. //! //! ``` text //! (gdb) continue @@ -13,59 +14,66 @@ //! __bkpt () at asm/bkpt.s:3 //! 3 bkpt //! -//! (gdb) finish -//! Run till exit from #0 __bkpt () at asm/bkpt.s:3 -//! Note: automatically using hardware breakpoints for read-only addresses. -//! crash::hf (_ef=0x20004fa0) at examples/crash.rs:102 -//! 99 asm::bkpt(); -//! -//! (gdb) # Exception frame = program state during the crash -//! (gdb) print/x *_ef -//! $1 = cortex_m_rt::ExceptionFrame { -//! r0: 0x2fffffff, -//! r1: 0x2fffffff, -//! r2: 0x80006b0, -//! r3: 0x80006b0, -//! r12: 0x20000000, -//! lr: 0x800040f, -//! pc: 0x800066a, -//! xpsr: 0x61000000 -//! } -//! -//! (gdb) # Where did we come from? //! (gdb) backtrace -//! #0 crash::hf (_ef=0x20004fa0) at examples/crash.rs:102 -//! #1 0x080004ac in UserHardFault (ef=0x20004fa0) at :9 -//! #2 -//! #3 0x0800066a in core::ptr::read_volatile (src=0x2fffffff) at /checkout/src/libcore/ptr.rs:452 -//! #4 0x0800040e in crash::main () at examples/crash.rs:85 -//! #5 0x08000456 in main () at
:3 +//! #0 __bkpt () at asm/bkpt.s:3 +//! #1 0x080030b4 in cortex_m::asm::bkpt () at $$/cortex-m-0.5.0/src/asm.rs:19 +//! #2 rust_begin_unwind (args=..., file=..., line=99, col=5) at $$/panic-semihosting-0.2.0/src/lib.rs:87 +//! #3 0x08001d06 in core::panicking::panic_fmt () at libcore/panicking.rs:71 +//! #4 0x080004a6 in crash::hard_fault (ef=0x20004fa0) at examples/crash.rs:99 +//! #5 0x08000548 in UserHardFault (ef=0x20004fa0) at :10 +//! #6 0x0800093a in HardFault () at asm.s:5 +//! Backtrace stopped: previous frame identical to this frame (corrupt stack?) +//! ``` //! -//! (gdb) # Nail down the location of the crash -//! (gdb) disassemble/m _ef.pc +//! In the console output one will find the state of the Program Counter (PC) register at the time +//! of the exception. +//! +//! ``` text +//! panicked at 'HardFault at ExceptionFrame { +//! r0: 0x2fffffff, +//! r1: 0x2fffffff, +//! r2: 0x080051d4, +//! r3: 0x080051d4, +//! r12: 0x20000000, +//! lr: 0x08000435, +//! pc: 0x08000ab6, +//! xpsr: 0x61000000 +//! }', examples/crash.rs:106:5 +//! ``` +//! +//! This register contains the address of the instruction that caused the exception. In GDB one can +//! disassemble the program around this address to observe the instruction that caused the +//! exception. +//! +//! ``` text +//! (gdb) disassemble/m 0x08000ab6 //! Dump of assembler code for function core::ptr::read_volatile: -//! 451 pub unsafe fn read_volatile(src: *const T) -> T {} -//! 0x08000662 <+0>: sub sp, #16 -//! 0x08000664 <+2>: mov r1, r0 -//! 0x08000666 <+4>: str r0, [sp, #8] +//! 451 pub unsafe fn read_volatile(src: *const T) -> T { +//! 0x08000aae <+0>: sub sp, #16 +//! 0x08000ab0 <+2>: mov r1, r0 +//! 0x08000ab2 <+4>: str r0, [sp, #8] //! //! 452 intrinsics::volatile_load(src) -//! 0x08000668 <+6>: ldr r0, [sp, #8] -//! 0x0800066a <+8>: ldr r0, [r0, #0] -//! 0x0800066c <+10>: str r0, [sp, #12] -//! 0x0800066e <+12>: ldr r0, [sp, #12] -//! 0x08000670 <+14>: str r1, [sp, #4] -//! 0x08000672 <+16>: str r0, [sp, #0] -//! 0x08000674 <+18>: b.n 0x8000676 +//! 0x08000ab4 <+6>: ldr r0, [sp, #8] +//! -> 0x08000ab6 <+8>: ldr r0, [r0, #0] +//! 0x08000ab8 <+10>: str r0, [sp, #12] +//! 0x08000aba <+12>: ldr r0, [sp, #12] +//! 0x08000abc <+14>: str r1, [sp, #4] +//! 0x08000abe <+16>: str r0, [sp, #0] +//! 0x08000ac0 <+18>: b.n 0x8000ac2 //! //! 453 } -//! 0x08000676 <+20>: ldr r0, [sp, #0] -//! 0x08000678 <+22>: add sp, #16 -//! 0x0800067a <+24>: bx lr +//! 0x08000ac2 <+20>: ldr r0, [sp, #0] +//! 0x08000ac4 <+22>: add sp, #16 +//! 0x08000ac6 <+24>: bx lr //! //! End of assembler dump. //! ``` //! +//! `ldr r0, [r0, #0]` caused the exception. This instruction tried to load (read) a 32-bit word +//! from the address stored in the register `r0`. Looking again at the contents of `ExceptionFrame` +//! we see that the `r0` contained the address `0x2FFF_FFFF` when this instruction was executed. +//! //! --- #![no_main] @@ -74,38 +82,33 @@ extern crate cortex_m; #[macro_use] extern crate cortex_m_rt as rt; -extern crate panic_abort; +extern crate panic_semihosting; use core::ptr; -use cortex_m::asm; use rt::ExceptionFrame; -main!(main); +entry!(main); -#[inline(always)] fn main() -> ! { unsafe { + // read an address outside of the RAM region; causes a HardFault exception ptr::read_volatile(0x2FFF_FFFF as *const u32); } loop {} } -exception!(DefaultHandler, dh); +// define the hard fault handler +exception!(HardFault, hard_fault); -#[inline(always)] -fn dh(_nr: u8) { - asm::bkpt(); +fn hard_fault(ef: &ExceptionFrame) -> ! { + panic!("HardFault at {:#?}", ef); } -exception!(HardFault, hf); +// define the default exception handler +exception!(*, default_handler); -#[inline(always)] -fn hf(_ef: &ExceptionFrame) -> ! { - asm::bkpt(); - - loop {} +fn default_handler(irqn: i16) { + panic!("Unhandled exception (IRQn = {})", irqn); } - -interrupts!(DefaultHandler); diff --git a/examples/device.rs b/examples/device.rs index beb0883..e2c1a04 100644 --- a/examples/device.rs +++ b/examples/device.rs @@ -1,6 +1,6 @@ //! Using a device crate //! -//! Crates generated using [`svd2rust`] are referred to as device crates. These crates provides an +//! Crates generated using [`svd2rust`] are referred to as device crates. These crates provide an //! API to access the peripherals of a device. //! //! [`svd2rust`]: https://crates.io/crates/svd2rust @@ -19,9 +19,6 @@ //! version = "0.10.0" //! ``` //! -//! The `stm32f103xx` crate provides an `interrupts.x` file so you must remove the one in the root -//! of this crate. -//! //! --- #![no_main] @@ -33,17 +30,16 @@ extern crate cortex_m_rt as rt; extern crate cortex_m_semihosting as sh; #[macro_use] extern crate stm32f103xx; -extern crate panic_abort; +extern crate panic_semihosting; use core::fmt::Write; -use cortex_m::asm; use cortex_m::peripheral::syst::SystClkSource; use rt::ExceptionFrame; use sh::hio::{self, HStdout}; use stm32f103xx::Interrupt; -main!(main); +entry!(main); fn main() -> ! { let p = cortex_m::Peripherals::take().unwrap(); @@ -53,6 +49,7 @@ fn main() -> ! { nvic.enable(Interrupt::EXTI0); + // configure the system timer to wrap around every second syst.set_clock_source(SystClkSource::Core); syst.set_reload(8_000_000); // 1s syst.enable_counter(); @@ -66,6 +63,7 @@ fn main() -> ! { } } +// try commenting out this line: you'll end in `default_handler` instead of in `exti0` interrupt!(EXTI0, exti0, state: Option = None); fn exti0(state: &mut Option) { @@ -78,20 +76,14 @@ fn exti0(state: &mut Option) { } } -exception!(DefaultHandler, deh); +exception!(HardFault, hard_fault); -#[inline(always)] -fn deh(_nr: u8) { - asm::bkpt(); +fn hard_fault(ef: &ExceptionFrame) -> ! { + panic!("HardFault at {:#?}", ef); } -exception!(HardFault, hf); +exception!(*, default_handler); -#[inline(always)] -fn hf(_ef: &ExceptionFrame) -> ! { - asm::bkpt(); - - loop {} +fn default_handler(irqn: i16) { + panic!("Unhandled exception (IRQn = {})", irqn); } - -interrupts!(DefaultHandler); diff --git a/examples/exception.rs b/examples/exception.rs index 11ad73f..d62e6d4 100644 --- a/examples/exception.rs +++ b/examples/exception.rs @@ -14,30 +14,31 @@ extern crate cortex_m; #[macro_use] extern crate cortex_m_rt as rt; extern crate cortex_m_semihosting as sh; -extern crate panic_abort; +extern crate panic_semihosting; use core::fmt::Write; use cortex_m::peripheral::syst::SystClkSource; -use cortex_m::{asm, Peripherals}; +use cortex_m::Peripherals; use rt::ExceptionFrame; use sh::hio::{self, HStdout}; -main!(main); +entry!(main); fn main() -> ! { let p = Peripherals::take().unwrap(); let mut syst = p.SYST; + // configures the system timer to trigger a SysTick exception every second syst.set_clock_source(SystClkSource::Core); - syst.set_reload(8_000_000); // 1s + syst.set_reload(8_000_000); // period = 1s syst.enable_counter(); syst.enable_interrupt(); loop {} } -// try commenting out this line: you'll end in `deh` instead of in `sys_tick` +// try commenting out this line: you'll end in `default_handler` instead of in `sys_tick` exception!(SysTick, sys_tick, state: Option = None); fn sys_tick(state: &mut Option) { @@ -50,20 +51,14 @@ fn sys_tick(state: &mut Option) { } } -exception!(DefaultHandler, deh); +exception!(HardFault, hard_fault); -#[inline(always)] -fn deh(_nr: u8) { - asm::bkpt(); +fn hard_fault(ef: &ExceptionFrame) -> ! { + panic!("HardFault at {:#?}", ef); } -exception!(HardFault, hf); +exception!(*, default_handler); -#[inline(always)] -fn hf(_ef: &ExceptionFrame) -> ! { - asm::bkpt(); - - loop {} +fn default_handler(irqn: i16) { + panic!("Unhandled exception (IRQn = {})", irqn); } - -interrupts!(DefaultHandler); diff --git a/examples/hello.rs b/examples/hello.rs index aa4f12c..3c4b5ec 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -5,19 +5,17 @@ #![no_main] #![no_std] -extern crate cortex_m; #[macro_use] extern crate cortex_m_rt as rt; extern crate cortex_m_semihosting as sh; -extern crate panic_abort; +extern crate panic_semihosting; use core::fmt::Write; -use cortex_m::asm; use rt::ExceptionFrame; use sh::hio; -main!(main); +entry!(main); fn main() -> ! { let mut stdout = hio::hstdout().unwrap(); @@ -26,21 +24,14 @@ fn main() -> ! { loop {} } -exception!(DefaultHandler, dh); +exception!(HardFault, hard_fault); -#[inline(always)] -fn dh(_nr: u8) { - asm::bkpt(); +fn hard_fault(ef: &ExceptionFrame) -> ! { + panic!("HardFault at {:#?}", ef); } -exception!(HardFault, hf); +exception!(*, default_handler); -#[inline(always)] -fn hf(_ef: &ExceptionFrame) -> ! { - asm::bkpt(); - - loop {} +fn default_handler(irqn: i16) { + panic!("Unhandled exception (IRQn = {})", irqn); } - -// As we are not using interrupts, we just bind them all to the `DefaultHandler` exception handler -interrupts!(DefaultHandler); diff --git a/examples/itm.rs b/examples/itm.rs index 7b21cae..6fddea1 100644 --- a/examples/itm.rs +++ b/examples/itm.rs @@ -1,11 +1,13 @@ //! Sends "Hello, world!" through the ITM port 0 //! -//! **IMPORTANT** Not all Cortex-M chips support ITM. You'll have to connect the microcontroller's -//! SWO pin to the SWD interface. Note that some development boards don't provide this option. -//! //! ITM is much faster than semihosting. Like 4 orders of magnitude or so. //! -//! You'll need [`itmdump`] to receive the message on the host plus you'll need to uncomment the +//! **NOTE** Cortex-M0 chips don't support ITM. +//! +//! You'll have to connect the microcontroller's SWO pin to the SWD interface. Note that some +//! development boards don't provide this option. +//! +//! You'll need [`itmdump`] to receive the message on the host plus you'll need to uncomment two //! `monitor` commands in the `.gdbinit` file. //! //! [`itmdump`]: https://docs.rs/itm/0.2.1/itm/ @@ -19,37 +21,34 @@ extern crate cortex_m; #[macro_use] extern crate cortex_m_rt as rt; -extern crate panic_abort; // panicking behavior +extern crate panic_semihosting; use cortex_m::{asm, Peripherals}; use rt::ExceptionFrame; -main!(main); +entry!(main); -#[inline(always)] fn main() -> ! { let mut p = Peripherals::take().unwrap(); let stim = &mut p.ITM.stim[0]; iprintln!(stim, "Hello, world!"); - loop {} + loop { + asm::bkpt(); + } } -exception!(DefaultHandler, dh); +// define the hard fault handler +exception!(HardFault, hard_fault); -#[inline(always)] -fn dh(_nr: u8) { - asm::bkpt(); +fn hard_fault(ef: &ExceptionFrame) -> ! { + panic!("HardFault at {:#?}", ef); } -exception!(HardFault, hf); +// define the default exception handler +exception!(*, default_handler); -#[inline(always)] -fn hf(_ef: &ExceptionFrame) -> ! { - asm::bkpt(); - - loop {} +fn default_handler(irqn: i16) { + panic!("Unhandled exception (IRQn = {})", irqn); } - -interrupts!(DefaultHandler); diff --git a/examples/minimal.rs b/examples/minimal.rs index d27b761..db91840 100644 --- a/examples/minimal.rs +++ b/examples/minimal.rs @@ -1,66 +1,63 @@ //! Minimal Cortex-M program //! +//! When executed this program will hit the breakpoint set in `main`. +//! //! All Cortex-M programs need to: //! //! - Contain the `#![no_main]` and `#![no_std]` attributes. Embedded programs don't use the //! standard Rust `main` interface or the Rust standard (`std`) library. //! -//! - Define their entry point using `main!`. The entry point doesn't need to be called `main` and -//! it doesn't need to be in the root of the crate. +//! - Define their entry point using [`entry!`] macro. +//! +//! [`entry!`]: https://docs.rs/cortex-m-rt/~0.5/cortex_m_rt/macro.entry.html //! //! - Define their panicking behavior, i.e. what happens when `panic!` is called. The easiest way to -//! define a panicking behavior is to link to a [panic implementation crate][0] +//! define a panicking behavior is to link to a [panic handler crate][0] //! //! [0]: https://crates.io/keywords/panic-impl //! -//! - Define the `HardFault` handler. This function is called when a hard fault exception is raised -//! by the hardware. +//! - Define the `HardFault` handler using the [`exception!`] macro. This handler (function) is +//! called when a hard fault exception is raised by the hardware. //! -//! - Define a default handler. This function will be used to handle all interrupts and exceptions -//! which have not been assigned a specific handler. +//! [`exception!`]: https://docs.rs/cortex-m-rt/~0.5/cortex_m_rt/macro..html //! -//! - Define the device specific interrupt handlers. `interrupts!` can be used to create a generic -//! program that works for all Cortex-M devices by binding all the possible interrupt handlers to -//! the `DefaultHandler`. +//! - Define a default handler using the [`exception!`] macro. This function will be used to handle +//! all interrupts and exceptions which have not been assigned a specific handler. #![no_main] // <- IMPORTANT! #![no_std] extern crate cortex_m; -#[macro_use(main, exception, interrupts)] + +#[macro_use(entry, exception)] extern crate cortex_m_rt as rt; -extern crate panic_abort; // panicking behavior + +// makes `panic!` print messages to the host stderr using semihosting +extern crate panic_semihosting; use cortex_m::asm; use rt::ExceptionFrame; -// the program entry point -main!(main); +// the program entry point is ... +entry!(main); -#[inline(always)] +// ... this never ending function fn main() -> ! { - asm::bkpt(); - - loop {} -} - -// define the default exception handler -exception!(DefaultHandler, deh); - -#[inline(always)] -fn deh(_nr: u8) { - asm::bkpt(); + loop { + asm::bkpt(); + } } // define the hard fault handler -exception!(HardFault, hf); +exception!(HardFault, hard_fault); -#[inline(always)] -fn hf(_ef: &ExceptionFrame) -> ! { - asm::bkpt(); - - loop {} +fn hard_fault(ef: &ExceptionFrame) -> ! { + panic!("HardFault at {:#?}", ef); } -// bind all interrupts to the default exception handler -interrupts!(DefaultHandler); +// define the default exception handler +exception!(*, default_handler); + +fn default_handler(irqn: i16) { + panic!("Unhandled exception (IRQn = {})", irqn); +} diff --git a/examples/panic.rs b/examples/panic.rs index 951cc05..0ce22af 100644 --- a/examples/panic.rs +++ b/examples/panic.rs @@ -1,7 +1,6 @@ //! Changing the panic handler //! -//! The easiest way to change the panic handler is to use a different [panic implementation -//! crate][0]. +//! The easiest way to change the panic handler is to use a different [panic handler crate][0]. //! //! [0]: https://crates.io/keywords/panic-impl //! @@ -10,36 +9,35 @@ #![no_main] #![no_std] -extern crate cortex_m; #[macro_use] extern crate cortex_m_rt as rt; -// extern crate panic_abort; -extern crate panic_semihosting; // reports panic messages to the host stderr using semihosting -use cortex_m::asm; +// Pick one of these two panic handlers: + +// Reports panic messages to the host stderr using semihosting +extern crate panic_semihosting; + +// Logs panic messages using the ITM (Instrumentation Trace Macrocell) +// extern crate panic_itm; + use rt::ExceptionFrame; -main!(main); +entry!(main); -#[inline(always)] fn main() -> ! { panic!("Oops") } -exception!(DefaultHandler, deh); +// define the hard fault handler +exception!(HardFault, hard_fault); -#[inline(always)] -fn deh(_nr: u8) { - asm::bkpt(); +fn hard_fault(ef: &ExceptionFrame) -> ! { + panic!("HardFault at {:#?}", ef); } -exception!(HardFault, hf); +// define the default exception handler +exception!(*, default_handler); -#[inline(always)] -fn hf(_ef: &ExceptionFrame) -> ! { - asm::bkpt(); - - loop {} +fn default_handler(irqn: i16) { + panic!("Unhandled exception (IRQn = {})", irqn); } - -interrupts!(DefaultHandler); diff --git a/gen-examples.sh b/gen-examples.sh index a1f55d2..5676c01 100644 --- a/gen-examples.sh +++ b/gen-examples.sh @@ -11,8 +11,8 @@ main() { panic crash exception - device allocator + device ) rm -rf src/examples @@ -20,7 +20,7 @@ main() { mkdir src/examples cat >src/examples/mod.rs <<'EOF' -//! Examples +//! Examples sorted in increasing degree of complexity // Auto-generated. Do not modify. EOF diff --git a/interrupts.x b/interrupts.x deleted file mode 100644 index 65ab4a4..0000000 --- a/interrupts.x +++ /dev/null @@ -1 +0,0 @@ -/* Remove this file if you are linking to a device crate that provides this file */ diff --git a/memory.x b/memory.x index f407292..32879e1 100644 --- a/memory.x +++ b/memory.x @@ -2,8 +2,8 @@ MEMORY { /* NOTE K = KiBi = 1024 bytes */ /* TODO Adjust these memory regions to match your device memory layout */ - FLASH : ORIGIN = 0xBAAAAAAD, LENGTH = 0K - RAM : ORIGIN = 0xBAAAAAAD, LENGTH = 0K + FLASH : ORIGIN = 0x000BAAD0, LENGTH = 0K + RAM : ORIGIN = 0xBAAD0000, LENGTH = 0K } /* This is where the call stack will be allocated. */ diff --git a/src/examples/_0_minimal.rs b/src/examples/_0_minimal.rs index d73257d..8926e23 100644 --- a/src/examples/_0_minimal.rs +++ b/src/examples/_0_minimal.rs @@ -1,70 +1,67 @@ //! Minimal Cortex-M program //! +//! When executed this program will hit the breakpoint set in `main`. +//! //! All Cortex-M programs need to: //! //! - Contain the `#![no_main]` and `#![no_std]` attributes. Embedded programs don't use the //! standard Rust `main` interface or the Rust standard (`std`) library. //! -//! - Define their entry point using `main!`. The entry point doesn't need to be called `main` and -//! it doesn't need to be in the root of the crate. +//! - Define their entry point using [`entry!`] macro. +//! +//! [`entry!`]: https://docs.rs/cortex-m-rt/~0.5/cortex_m_rt/macro.entry.html //! //! - Define their panicking behavior, i.e. what happens when `panic!` is called. The easiest way to -//! define a panicking behavior is to link to a [panic implementation crate][0] +//! define a panicking behavior is to link to a [panic handler crate][0] //! //! [0]: https://crates.io/keywords/panic-impl //! -//! - Define the `HardFault` handler. This function is called when a hard fault exception is raised -//! by the hardware. +//! - Define the `HardFault` handler using the [`exception!`] macro. This handler (function) is +//! called when a hard fault exception is raised by the hardware. //! -//! - Define a default handler. This function will be used to handle all interrupts and exceptions -//! which have not been assigned a specific handler. +//! [`exception!`]: https://docs.rs/cortex-m-rt/~0.5/cortex_m_rt/macro..html //! -//! - Define the device specific interrupt handlers. `interrupts!` can be used to create a generic -//! program that works for all Cortex-M devices by binding all the possible interrupt handlers to -//! the `DefaultHandler`. +//! - Define a default handler using the [`exception!`] macro. This function will be used to handle +//! all interrupts and exceptions which have not been assigned a specific handler. //! //! ``` -//! +//! //! #![no_main] // <- IMPORTANT! //! #![no_std] -//! +//! //! extern crate cortex_m; -//! #[macro_use(main, exception, interrupts)] +//! +//! #[macro_use(entry, exception)] //! extern crate cortex_m_rt as rt; -//! extern crate panic_abort; // panicking behavior -//! +//! +//! // makes `panic!` print messages to the host stderr using semihosting +//! extern crate panic_semihosting; +//! //! use cortex_m::asm; //! use rt::ExceptionFrame; -//! -//! // the program entry point -//! main!(main); -//! -//! #[inline(always)] +//! +//! // the program entry point is ... +//! entry!(main); +//! +//! // ... this never ending function //! fn main() -> ! { -//! asm::bkpt(); -//! -//! loop {} +//! loop { +//! asm::bkpt(); +//! } //! } -//! -//! // define the default exception handler -//! exception!(DefaultHandler, deh); -//! -//! #[inline(always)] -//! fn deh(_nr: u8) { -//! asm::bkpt(); -//! } -//! +//! //! // define the hard fault handler -//! exception!(HardFault, hf); -//! -//! #[inline(always)] -//! fn hf(_ef: &ExceptionFrame) -> ! { -//! asm::bkpt(); -//! -//! loop {} +//! exception!(HardFault, hard_fault); +//! +//! fn hard_fault(ef: &ExceptionFrame) -> ! { +//! panic!("HardFault at {:#?}", ef); +//! } +//! +//! // define the default exception handler +//! exception!(*, default_handler); +//! +//! fn default_handler(irqn: i16) { +//! panic!("Unhandled exception (IRQn = {})", irqn); //! } -//! -//! // bind all interrupts to the default exception handler -//! interrupts!(DefaultHandler); //! ``` // Auto-generated. Do not modify. diff --git a/src/examples/_1_hello.rs b/src/examples/_1_hello.rs index badaa62..413c4fb 100644 --- a/src/examples/_1_hello.rs +++ b/src/examples/_1_hello.rs @@ -3,48 +3,39 @@ //! --- //! //! ``` -//! +//! //! #![no_main] //! #![no_std] -//! -//! extern crate cortex_m; +//! //! #[macro_use] //! extern crate cortex_m_rt as rt; //! extern crate cortex_m_semihosting as sh; -//! extern crate panic_abort; -//! +//! extern crate panic_semihosting; +//! //! use core::fmt::Write; -//! -//! use cortex_m::asm; +//! //! use rt::ExceptionFrame; //! use sh::hio; -//! -//! main!(main); -//! +//! +//! entry!(main); +//! //! fn main() -> ! { //! let mut stdout = hio::hstdout().unwrap(); //! writeln!(stdout, "Hello, world!").unwrap(); -//! +//! //! loop {} //! } -//! -//! exception!(DefaultHandler, dh); -//! -//! #[inline(always)] -//! fn dh(_nr: u8) { -//! asm::bkpt(); +//! +//! exception!(HardFault, hard_fault); +//! +//! fn hard_fault(ef: &ExceptionFrame) -> ! { +//! panic!("HardFault at {:#?}", ef); //! } -//! -//! exception!(HardFault, hf); -//! -//! #[inline(always)] -//! fn hf(_ef: &ExceptionFrame) -> ! { -//! asm::bkpt(); -//! -//! loop {} +//! +//! exception!(*, default_handler); +//! +//! fn default_handler(irqn: i16) { +//! panic!("Unhandled exception (IRQn = {})", irqn); //! } -//! -//! // As we are not using interrupts, we just bind them all to the `DefaultHandler` exception handler -//! interrupts!(DefaultHandler); //! ``` // Auto-generated. Do not modify. diff --git a/src/examples/_2_itm.rs b/src/examples/_2_itm.rs index 42abeff..568a467 100644 --- a/src/examples/_2_itm.rs +++ b/src/examples/_2_itm.rs @@ -1,11 +1,13 @@ //! Sends "Hello, world!" through the ITM port 0 //! -//! **IMPORTANT** Not all Cortex-M chips support ITM. You'll have to connect the microcontroller's -//! SWO pin to the SWD interface. Note that some development boards don't provide this option. -//! //! ITM is much faster than semihosting. Like 4 orders of magnitude or so. //! -//! You'll need [`itmdump`] to receive the message on the host plus you'll need to uncomment the +//! **NOTE** Cortex-M0 chips don't support ITM. +//! +//! You'll have to connect the microcontroller's SWO pin to the SWD interface. Note that some +//! development boards don't provide this option. +//! +//! You'll need [`itmdump`] to receive the message on the host plus you'll need to uncomment two //! `monitor` commands in the `.gdbinit` file. //! //! [`itmdump`]: https://docs.rs/itm/0.2.1/itm/ @@ -13,47 +15,44 @@ //! --- //! //! ``` -//! +//! //! #![no_main] //! #![no_std] -//! +//! //! #[macro_use] //! extern crate cortex_m; //! #[macro_use] //! extern crate cortex_m_rt as rt; -//! extern crate panic_abort; // panicking behavior -//! +//! extern crate panic_semihosting; +//! //! use cortex_m::{asm, Peripherals}; //! use rt::ExceptionFrame; -//! -//! main!(main); -//! -//! #[inline(always)] +//! +//! entry!(main); +//! //! fn main() -> ! { //! let mut p = Peripherals::take().unwrap(); //! let stim = &mut p.ITM.stim[0]; -//! +//! //! iprintln!(stim, "Hello, world!"); -//! -//! loop {} +//! +//! loop { +//! asm::bkpt(); +//! } //! } -//! -//! exception!(DefaultHandler, dh); -//! -//! #[inline(always)] -//! fn dh(_nr: u8) { -//! asm::bkpt(); +//! +//! // define the hard fault handler +//! exception!(HardFault, hard_fault); +//! +//! fn hard_fault(ef: &ExceptionFrame) -> ! { +//! panic!("HardFault at {:#?}", ef); //! } -//! -//! exception!(HardFault, hf); -//! -//! #[inline(always)] -//! fn hf(_ef: &ExceptionFrame) -> ! { -//! asm::bkpt(); -//! -//! loop {} +//! +//! // define the default exception handler +//! exception!(*, default_handler); +//! +//! fn default_handler(irqn: i16) { +//! panic!("Unhandled exception (IRQn = {})", irqn); //! } -//! -//! interrupts!(DefaultHandler); //! ``` // Auto-generated. Do not modify. diff --git a/src/examples/_3_panic.rs b/src/examples/_3_panic.rs index 7ad410f..4d6a7dd 100644 --- a/src/examples/_3_panic.rs +++ b/src/examples/_3_panic.rs @@ -1,49 +1,47 @@ //! Changing the panic handler //! -//! The easiest way to change the panic handler is to use a different [panic implementation -//! crate][0]. +//! The easiest way to change the panic handler is to use a different [panic handler crate][0]. //! //! [0]: https://crates.io/keywords/panic-impl //! //! --- //! //! ``` -//! +//! //! #![no_main] //! #![no_std] -//! -//! extern crate cortex_m; +//! //! #[macro_use] //! extern crate cortex_m_rt as rt; -//! // extern crate panic_abort; -//! extern crate panic_semihosting; // reports panic messages to the host stderr using semihosting -//! -//! use cortex_m::asm; +//! +//! // Pick one of these two panic handlers: +//! +//! // Reports panic messages to the host stderr using semihosting +//! extern crate panic_semihosting; +//! +//! // Logs panic messages using the ITM (Instrumentation Trace Macrocell) +//! // extern crate panic_itm; +//! //! use rt::ExceptionFrame; -//! -//! main!(main); -//! -//! #[inline(always)] +//! +//! entry!(main); +//! //! fn main() -> ! { //! panic!("Oops") //! } -//! -//! exception!(DefaultHandler, deh); -//! -//! #[inline(always)] -//! fn deh(_nr: u8) { -//! asm::bkpt(); +//! +//! // define the hard fault handler +//! exception!(HardFault, hard_fault); +//! +//! fn hard_fault(ef: &ExceptionFrame) -> ! { +//! panic!("HardFault at {:#?}", ef); //! } -//! -//! exception!(HardFault, hf); -//! -//! #[inline(always)] -//! fn hf(_ef: &ExceptionFrame) -> ! { -//! asm::bkpt(); -//! -//! loop {} +//! +//! // define the default exception handler +//! exception!(*, default_handler); +//! +//! fn default_handler(irqn: i16) { +//! panic!("Unhandled exception (IRQn = {})", irqn); //! } -//! -//! interrupts!(DefaultHandler); //! ``` // Auto-generated. Do not modify. diff --git a/src/examples/_4_crash.rs b/src/examples/_4_crash.rs index 861d161..f3957d1 100644 --- a/src/examples/_4_crash.rs +++ b/src/examples/_4_crash.rs @@ -1,11 +1,12 @@ //! Debugging a crash (exception) //! -//! The `cortex-m-rt` crate provides functionality for this through a default exception handler. -//! When an exception is hit, the default handler will trigger a breakpoint and in this debugging -//! context the stacked registers are accessible. +//! Most crash conditions trigger a hard fault exception, whose handler is defined via +//! `exception!(HardFault, ..)`. The `HardFault` handler has access to the exception frame, a +//! snapshot of the CPU registers at the moment of the exception. //! -//! In you run the example below, you'll be able to inspect the state of your program under the -//! debugger using these commands: +//! This program crashes and the `HardFault` handler prints to the console the contents of the +//! `ExceptionFrame` and then triggers a breakpoint. From that breakpoint one can see the backtrace +//! that led to the exception. //! //! ``` text //! (gdb) continue @@ -13,103 +14,105 @@ //! __bkpt () at asm/bkpt.s:3 //! 3 bkpt //! -//! (gdb) finish -//! Run till exit from #0 __bkpt () at asm/bkpt.s:3 -//! Note: automatically using hardware breakpoints for read-only addresses. -//! crash::hf (_ef=0x20004fa0) at examples/crash.rs:102 -//! 99 asm::bkpt(); -//! -//! (gdb) # Exception frame = program state during the crash -//! (gdb) print/x *_ef -//! $1 = cortex_m_rt::ExceptionFrame { -//! r0: 0x2fffffff, -//! r1: 0x2fffffff, -//! r2: 0x80006b0, -//! r3: 0x80006b0, -//! r12: 0x20000000, -//! lr: 0x800040f, -//! pc: 0x800066a, -//! xpsr: 0x61000000 -//! } -//! -//! (gdb) # Where did we come from? //! (gdb) backtrace -//! #0 crash::hf (_ef=0x20004fa0) at examples/crash.rs:102 -//! #1 0x080004ac in UserHardFault (ef=0x20004fa0) at :9 -//! #2 -//! #3 0x0800066a in core::ptr::read_volatile (src=0x2fffffff) at /checkout/src/libcore/ptr.rs:452 -//! #4 0x0800040e in crash::main () at examples/crash.rs:85 -//! #5 0x08000456 in main () at
:3 +//! #0 __bkpt () at asm/bkpt.s:3 +//! #1 0x080030b4 in cortex_m::asm::bkpt () at $$/cortex-m-0.5.0/src/asm.rs:19 +//! #2 rust_begin_unwind (args=..., file=..., line=99, col=5) at $$/panic-semihosting-0.2.0/src/lib.rs:87 +//! #3 0x08001d06 in core::panicking::panic_fmt () at libcore/panicking.rs:71 +//! #4 0x080004a6 in crash::hard_fault (ef=0x20004fa0) at examples/crash.rs:99 +//! #5 0x08000548 in UserHardFault (ef=0x20004fa0) at :10 +//! #6 0x0800093a in HardFault () at asm.s:5 +//! Backtrace stopped: previous frame identical to this frame (corrupt stack?) +//! ``` //! -//! (gdb) # Nail down the location of the crash -//! (gdb) disassemble/m _ef.pc +//! In the console output one will find the state of the Program Counter (PC) register at the time +//! of the exception. +//! +//! ``` text +//! panicked at 'HardFault at ExceptionFrame { +//! r0: 0x2fffffff, +//! r1: 0x2fffffff, +//! r2: 0x080051d4, +//! r3: 0x080051d4, +//! r12: 0x20000000, +//! lr: 0x08000435, +//! pc: 0x08000ab6, +//! xpsr: 0x61000000 +//! }', examples/crash.rs:106:5 +//! ``` +//! +//! This register contains the address of the instruction that caused the exception. In GDB one can +//! disassemble the program around this address to observe the instruction that caused the +//! exception. +//! +//! ``` text +//! (gdb) disassemble/m 0x08000ab6 //! Dump of assembler code for function core::ptr::read_volatile: -//! 451 pub unsafe fn read_volatile(src: *const T) -> T {} -//! 0x08000662 <+0>: sub sp, #16 -//! 0x08000664 <+2>: mov r1, r0 -//! 0x08000666 <+4>: str r0, [sp, #8] +//! 451 pub unsafe fn read_volatile(src: *const T) -> T { +//! 0x08000aae <+0>: sub sp, #16 +//! 0x08000ab0 <+2>: mov r1, r0 +//! 0x08000ab2 <+4>: str r0, [sp, #8] //! //! 452 intrinsics::volatile_load(src) -//! 0x08000668 <+6>: ldr r0, [sp, #8] -//! 0x0800066a <+8>: ldr r0, [r0, #0] -//! 0x0800066c <+10>: str r0, [sp, #12] -//! 0x0800066e <+12>: ldr r0, [sp, #12] -//! 0x08000670 <+14>: str r1, [sp, #4] -//! 0x08000672 <+16>: str r0, [sp, #0] -//! 0x08000674 <+18>: b.n 0x8000676 +//! 0x08000ab4 <+6>: ldr r0, [sp, #8] +//! -> 0x08000ab6 <+8>: ldr r0, [r0, #0] +//! 0x08000ab8 <+10>: str r0, [sp, #12] +//! 0x08000aba <+12>: ldr r0, [sp, #12] +//! 0x08000abc <+14>: str r1, [sp, #4] +//! 0x08000abe <+16>: str r0, [sp, #0] +//! 0x08000ac0 <+18>: b.n 0x8000ac2 //! //! 453 } -//! 0x08000676 <+20>: ldr r0, [sp, #0] -//! 0x08000678 <+22>: add sp, #16 -//! 0x0800067a <+24>: bx lr +//! 0x08000ac2 <+20>: ldr r0, [sp, #0] +//! 0x08000ac4 <+22>: add sp, #16 +//! 0x08000ac6 <+24>: bx lr //! //! End of assembler dump. //! ``` //! +//! `ldr r0, [r0, #0]` caused the exception. This instruction tried to load (read) a 32-bit word +//! from the address stored in the register `r0`. Looking again at the contents of `ExceptionFrame` +//! we see that the `r0` contained the address `0x2FFF_FFFF` when this instruction was executed. +//! //! --- //! //! ``` -//! +//! //! #![no_main] //! #![no_std] -//! +//! //! extern crate cortex_m; //! #[macro_use] //! extern crate cortex_m_rt as rt; -//! extern crate panic_abort; -//! +//! extern crate panic_semihosting; +//! //! use core::ptr; -//! -//! use cortex_m::asm; +//! //! use rt::ExceptionFrame; -//! -//! main!(main); -//! -//! #[inline(always)] +//! +//! entry!(main); +//! //! fn main() -> ! { //! unsafe { +//! // read an address outside of the RAM region; causes a HardFault exception //! ptr::read_volatile(0x2FFF_FFFF as *const u32); //! } -//! +//! //! loop {} //! } -//! -//! exception!(DefaultHandler, dh); -//! -//! #[inline(always)] -//! fn dh(_nr: u8) { -//! asm::bkpt(); +//! +//! // define the hard fault handler +//! exception!(HardFault, hard_fault); +//! +//! fn hard_fault(ef: &ExceptionFrame) -> ! { +//! panic!("HardFault at {:#?}", ef); //! } -//! -//! exception!(HardFault, hf); -//! -//! #[inline(always)] -//! fn hf(_ef: &ExceptionFrame) -> ! { -//! asm::bkpt(); -//! -//! loop {} +//! +//! // define the default exception handler +//! exception!(*, default_handler); +//! +//! fn default_handler(irqn: i16) { +//! panic!("Unhandled exception (IRQn = {})", irqn); //! } -//! -//! interrupts!(DefaultHandler); //! ``` // Auto-generated. Do not modify. diff --git a/src/examples/_5_exception.rs b/src/examples/_5_exception.rs index 49c98aa..f2a65f3 100644 --- a/src/examples/_5_exception.rs +++ b/src/examples/_5_exception.rs @@ -7,67 +7,62 @@ //! --- //! //! ``` -//! +//! //! #![deny(unsafe_code)] //! #![no_main] //! #![no_std] -//! +//! //! extern crate cortex_m; //! #[macro_use] //! extern crate cortex_m_rt as rt; //! extern crate cortex_m_semihosting as sh; -//! extern crate panic_abort; -//! +//! extern crate panic_semihosting; +//! //! use core::fmt::Write; -//! +//! //! use cortex_m::peripheral::syst::SystClkSource; -//! use cortex_m::{asm, Peripherals}; +//! use cortex_m::Peripherals; //! use rt::ExceptionFrame; //! use sh::hio::{self, HStdout}; -//! -//! main!(main); -//! +//! +//! entry!(main); +//! //! fn main() -> ! { //! let p = Peripherals::take().unwrap(); //! let mut syst = p.SYST; -//! +//! +//! // configures the system timer to trigger a SysTick exception every second //! syst.set_clock_source(SystClkSource::Core); -//! syst.set_reload(8_000_000); // 1s +//! syst.set_reload(8_000_000); // period = 1s //! syst.enable_counter(); //! syst.enable_interrupt(); -//! +//! //! loop {} //! } -//! -//! // try commenting out this line: you'll end in `deh` instead of in `sys_tick` +//! +//! // try commenting out this line: you'll end in `default_handler` instead of in `sys_tick` //! exception!(SysTick, sys_tick, state: Option = None); -//! +//! //! fn sys_tick(state: &mut Option) { //! if state.is_none() { //! *state = Some(hio::hstdout().unwrap()); //! } -//! +//! //! if let Some(hstdout) = state.as_mut() { //! hstdout.write_str(".").unwrap(); //! } //! } -//! -//! exception!(DefaultHandler, deh); -//! -//! #[inline(always)] -//! fn deh(_nr: u8) { -//! asm::bkpt(); +//! +//! exception!(HardFault, hard_fault); +//! +//! fn hard_fault(ef: &ExceptionFrame) -> ! { +//! panic!("HardFault at {:#?}", ef); //! } -//! -//! exception!(HardFault, hf); -//! -//! #[inline(always)] -//! fn hf(_ef: &ExceptionFrame) -> ! { -//! asm::bkpt(); -//! -//! loop {} +//! +//! exception!(*, default_handler); +//! +//! fn default_handler(irqn: i16) { +//! panic!("Unhandled exception (IRQn = {})", irqn); //! } -//! -//! interrupts!(DefaultHandler); //! ``` // Auto-generated. Do not modify. diff --git a/src/examples/_7_allocator.rs b/src/examples/_6_allocator.rs similarity index 66% rename from src/examples/_7_allocator.rs rename to src/examples/_6_allocator.rs index 071261d..c7bddcc 100644 --- a/src/examples/_7_allocator.rs +++ b/src/examples/_6_allocator.rs @@ -10,12 +10,13 @@ //! --- //! //! ``` -//! +//! //! #![feature(alloc)] //! #![feature(global_allocator)] +//! #![feature(lang_items)] //! #![no_main] //! #![no_std] -//! +//! //! // This is the allocator crate; you can use a different one //! extern crate alloc_cortex_m; //! #[macro_use] @@ -24,51 +25,55 @@ //! #[macro_use] //! extern crate cortex_m_rt as rt; //! extern crate cortex_m_semihosting as sh; -//! extern crate panic_abort; -//! +//! extern crate panic_semihosting; +//! //! use core::fmt::Write; -//! +//! //! use alloc_cortex_m::CortexMHeap; //! use cortex_m::asm; //! use rt::ExceptionFrame; //! use sh::hio; -//! +//! +//! // this is the allocator the application will use //! #[global_allocator] //! static ALLOCATOR: CortexMHeap = CortexMHeap::empty(); -//! +//! //! const HEAP_SIZE: usize = 1024; // in bytes -//! -//! main!(main); -//! +//! +//! entry!(main); +//! //! fn main() -> ! { -//! // Initialize the allocator +//! // Initialize the allocator BEFORE you use it //! unsafe { ALLOCATOR.init(rt::heap_start() as usize, HEAP_SIZE) } -//! +//! //! // Growable array allocated on the heap //! let xs = vec![0, 1, 2]; -//! +//! //! let mut stdout = hio::hstdout().unwrap(); //! writeln!(stdout, "{:?}", xs).unwrap(); -//! +//! //! loop {} //! } -//! -//! exception!(DefaultHandler, dh); -//! -//! #[inline(always)] -//! fn dh(_nr: u8) { +//! +//! // define what happens in an Out Of Memory (OOM) condition +//! #[lang = "oom"] +//! #[no_mangle] +//! pub fn rust_oom() -> ! { //! asm::bkpt(); -//! } -//! -//! exception!(HardFault, hf); -//! -//! #[inline(always)] -//! fn hf(_ef: &ExceptionFrame) -> ! { -//! asm::bkpt(); -//! +//! //! loop {} //! } -//! -//! interrupts!(DefaultHandler); +//! +//! exception!(HardFault, hard_fault); +//! +//! fn hard_fault(ef: &ExceptionFrame) -> ! { +//! panic!("HardFault at {:#?}", ef); +//! } +//! +//! exception!(*, default_handler); +//! +//! fn default_handler(irqn: i16) { +//! panic!("Unhandled exception (IRQn = {})", irqn); +//! } //! ``` // Auto-generated. Do not modify. diff --git a/src/examples/_6_device.rs b/src/examples/_7_device.rs similarity index 76% rename from src/examples/_6_device.rs rename to src/examples/_7_device.rs index 83656b1..7564284 100644 --- a/src/examples/_6_device.rs +++ b/src/examples/_7_device.rs @@ -1,6 +1,6 @@ //! Using a device crate //! -//! Crates generated using [`svd2rust`] are referred to as device crates. These crates provides an +//! Crates generated using [`svd2rust`] are referred to as device crates. These crates provide an //! API to access the peripherals of a device. //! //! [`svd2rust`]: https://crates.io/crates/svd2rust @@ -19,83 +19,75 @@ //! version = "0.10.0" //! ``` //! -//! The `stm32f103xx` crate provides an `interrupts.x` file so you must remove the one in the root -//! of this crate. -//! //! --- //! //! ``` -//! +//! //! #![no_main] //! #![no_std] -//! +//! //! extern crate cortex_m; //! #[macro_use] //! extern crate cortex_m_rt as rt; //! extern crate cortex_m_semihosting as sh; //! #[macro_use] //! extern crate stm32f103xx; -//! extern crate panic_abort; -//! +//! extern crate panic_semihosting; +//! //! use core::fmt::Write; -//! -//! use cortex_m::asm; +//! //! use cortex_m::peripheral::syst::SystClkSource; //! use rt::ExceptionFrame; //! use sh::hio::{self, HStdout}; //! use stm32f103xx::Interrupt; -//! -//! main!(main); -//! +//! +//! entry!(main); +//! //! fn main() -> ! { //! let p = cortex_m::Peripherals::take().unwrap(); -//! +//! //! let mut syst = p.SYST; //! let mut nvic = p.NVIC; -//! +//! //! nvic.enable(Interrupt::EXTI0); -//! +//! +//! // configure the system timer to wrap around every second //! syst.set_clock_source(SystClkSource::Core); //! syst.set_reload(8_000_000); // 1s //! syst.enable_counter(); -//! +//! //! loop { //! // busy wait until the timer wraps around //! while !syst.has_wrapped() {} -//! +//! //! // trigger the `EXTI0` interrupt //! nvic.set_pending(Interrupt::EXTI0); //! } //! } -//! +//! +//! // try commenting out this line: you'll end in `default_handler` instead of in `exti0` //! interrupt!(EXTI0, exti0, state: Option = None); -//! +//! //! fn exti0(state: &mut Option) { //! if state.is_none() { //! *state = Some(hio::hstdout().unwrap()); //! } -//! +//! //! if let Some(hstdout) = state.as_mut() { //! hstdout.write_str(".").unwrap(); //! } //! } -//! -//! exception!(DefaultHandler, deh); -//! -//! #[inline(always)] -//! fn deh(_nr: u8) { -//! asm::bkpt(); +//! +//! exception!(HardFault, hard_fault); +//! +//! fn hard_fault(ef: &ExceptionFrame) -> ! { +//! panic!("HardFault at {:#?}", ef); //! } -//! -//! exception!(HardFault, hf); -//! -//! #[inline(always)] -//! fn hf(_ef: &ExceptionFrame) -> ! { -//! asm::bkpt(); -//! -//! loop {} +//! +//! exception!(*, default_handler); +//! +//! fn default_handler(irqn: i16) { +//! panic!("Unhandled exception (IRQn = {})", irqn); //! } -//! -//! interrupts!(DefaultHandler); //! ``` // Auto-generated. Do not modify. diff --git a/src/examples/mod.rs b/src/examples/mod.rs index dc816c2..0358d78 100644 --- a/src/examples/mod.rs +++ b/src/examples/mod.rs @@ -1,4 +1,4 @@ -//! Examples +//! Examples sorted in increasing degree of complexity // Auto-generated. Do not modify. pub mod _0_minimal; pub mod _1_hello; @@ -6,5 +6,5 @@ pub mod _2_itm; pub mod _3_panic; pub mod _4_crash; pub mod _5_exception; -pub mod _6_device; -pub mod _7_allocator; +pub mod _6_allocator; +pub mod _7_device; diff --git a/src/lib.rs b/src/lib.rs index d2489e8..89dcc23 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,7 @@ //! 2) Clone this crate //! //! ``` text -//! $ git clone https://github.com/japaric/cortex-m-quickstart --branch less-unstable +//! $ git clone cortex-m-quickstart --vers 0.3.0 //! ``` //! //! 3) Change the crate name, author and version @@ -43,8 +43,8 @@ //! 4) Specify the memory layout of the target device //! //! **NOTE** board support crates sometimes provide this file for you (check the crate -//! documentation). If you are using one that does then remove *both* the `memory.x` and `build.rs` -//! files. +//! documentation). If you are using one that does then remove *both* `memory.x` and `build.rs` from +//! the root of this crate. //! //! ``` text //! $ cat >memory.x <<'EOF' @@ -57,7 +57,8 @@ //! EOF //! ``` //! -//! 5) Optionally, set a default build target +//! 5) Optionally, set a default build target. This way you don't have to pass `--target` to each +//! Cargo invocation. //! //! ``` text //! $ cat >>.cargo/config <<'EOF' @@ -113,7 +114,7 @@ //! Tag_ABI_FP_16bit_format: IEEE 754 //! ``` //! -//! 9) Flash the program +//! 9) Flash and debug the program //! //! ``` text //! $ # Launch OpenOCD on a terminal @@ -166,12 +167,14 @@ //! error: linking with `arm-none-eabi-ld` failed: exit code: 1 //! | //! = note: "arm-none-eabi-gcc" "-L" (..) +//! (..) +//! (..)/ld: region `FLASH' overflowed by XXX bytes //! ``` //! -//! Solution: Specify your device memory layout in the `memory.x` linker script. -//! See [Usage] section. +//! Solution: Specify your device memory layout in the `memory.x` linker script. See [Usage] +//! section. //! -//! ## Forgot to set a default build target +//! ## Didn't set a default build target and forgot to pass `--target` to Cargo //! //! Error message: //! @@ -183,9 +186,8 @@ //! error: aborting due to previous error //! ``` //! -//! Solution: Set a default build target in the `.cargo/config` file -//! (see [Usage] section), or call Cargo with `--target` flag: -//! `cargo build --target thumbv7em-none-eabi`. +//! Solution: Set a default build target in the `.cargo/config` file (see [Usage] section), or call +//! Cargo with `--target` flag: `cargo build --target thumbv7em-none-eabi`. //! //! ## Overwrote the original `.cargo/config` file //! @@ -209,11 +211,10 @@ //! collect2: error: ld returned 1 exit status //! ``` //! -//! Solution: You probably overwrote the original `.cargo/config` instead of -//! appending the default build target (e.g. `cat >` instead of `cat >>`). The -//! less error prone way to fix this is to remove the `.cargo` directory, clone -//! a new copy of the template and then copy the `.cargo` directory from that -//! fresh template into your current project. Don't forget to *append* the +//! Solution: You probably overwrote the original `.cargo/config` instead of appending the default +//! build target (e.g. `cat >` instead of `cat >>`). The less error prone way to fix this is to +//! remove the `.cargo` directory, clone a new copy of the template and then copy the `.cargo` +//! directory from that fresh template into your current project. Don't forget to *append* the //! default build target to `.cargo/config`. //! //! ## Called OpenOCD with wrong arguments @@ -228,9 +229,8 @@ //! in procedure 'ocd_bouncer' //! ``` //! -//! Solution: Correct the OpenOCD arguments. Check the -//! `/usr/share/openocd/scripts` directory (exact location varies per -//! distribution / OS) for a list of scripts that can be used. +//! Solution: Correct the OpenOCD arguments. Check the `/usr/share/openocd/scripts` directory (exact +//! location varies per distribution / OS) for a list of scripts that can be used. //! //! ## Forgot to install the `rust-std` component //! @@ -268,6 +268,8 @@ //! ``` text //! $ cargo build //! error[E0463]: can't find crate for `core` +//! | +//! = note: the `thumbv7em-none-eabihf` target may not be installed //! ``` //! //! Solution: We are not there yet! Switch to the nightly toolchain with `rustup default nightly`.