diff --git a/.cargo/config b/.cargo/config index 2f4d0ad..337c283 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,39 +1,50 @@ [target.thumbv6m-none-eabi] runner = 'arm-none-eabi-gdb' rustflags = [ - "-C", "link-arg=-Tlink.x", - "-C", "linker=lld", - "-Z", "linker-flavor=ld.lld", - # "-C", "linker=arm-none-eabi-ld", - # "-Z", "linker-flavor=ld", + "-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] runner = 'arm-none-eabi-gdb' rustflags = [ - "-C", "link-arg=-Tlink.x", - "-C", "linker=lld", - "-Z", "linker-flavor=ld.lld", - # "-C", "linker=arm-none-eabi-ld", - # "-Z", "linker-flavor=ld", + "-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] runner = 'arm-none-eabi-gdb' rustflags = [ - "-C", "link-arg=-Tlink.x", - "-C", "linker=lld", - "-Z", "linker-flavor=ld.lld", - # "-C", "linker=arm-none-eabi-ld", - # "-Z", "linker-flavor=ld", + "-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] runner = 'arm-none-eabi-gdb' rustflags = [ - "-C", "link-arg=-Tlink.x", - "-C", "linker=lld", - "-Z", "linker-flavor=ld.lld", - # "-C", "linker=arm-none-eabi-ld", - # "-Z", "linker-flavor=ld", + "-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/.gitignore b/.gitignore index 6cda3bd..59a4524 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ **/*.rs.bk +.#* .gdb_history Cargo.lock target/ diff --git a/.travis.yml b/.travis.yml index b939699..ec6158e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,37 +6,29 @@ matrix: rust: nightly addons: apt: - sources: - - debian-sid packages: - - binutils-arm-none-eabi + - gcc-arm-none-eabi - env: TARGET=thumbv7m-none-eabi rust: nightly addons: apt: - sources: - - debian-sid packages: - - binutils-arm-none-eabi + - gcc-arm-none-eabi - env: TARGET=thumbv7em-none-eabi rust: nightly addons: apt: - sources: - - debian-sid packages: - - binutils-arm-none-eabi + - gcc-arm-none-eabi - env: TARGET=thumbv7em-none-eabihf rust: nightly addons: apt: - sources: - - debian-sid packages: - - binutils-arm-none-eabi + - gcc-arm-none-eabi before_install: set -e 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 3249b67..a1f9148 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,23 +6,26 @@ 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 = "0.4.0" -cortex-m-rt = "0.4.0" -cortex-m-semihosting = "0.2.0" -panic-abort = "0.1.1" -panic-semihosting = "0.1.0" +cortex-m = "0.5.0" +cortex-m-rt = "0.5.0" +cortex-m-semihosting = "0.3.0" +panic-semihosting = "0.2.0" + +# Uncomment for the panic example. +# panic-itm = "0.1.1" + # Uncomment for the allocator example. -#alloc-cortex-m = "0.3.3" +# alloc-cortex-m = "0.3.4" # 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" diff --git a/ci/script.sh b/ci/script.sh index 82d2e0f..d2195a2 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -9,7 +9,6 @@ main() { cat >memory.x <<'EOF' MEMORY { - /* NOTE K = KiBi = 1024 bytes */ FLASH : ORIGIN = 0x08000000, LENGTH = 256K RAM : ORIGIN = 0x20000000, LENGTH = 40K } @@ -17,8 +16,9 @@ EOF local examples=( crash + exception hello - override-exception-handler + minimal panic ) for ex in "${examples[@]}"; do @@ -33,17 +33,15 @@ EOF cargo build --target $TARGET --example $ex --release examples+=( $ex ) - fi # Allocator example needs an extra dependency cat >>Cargo.toml <<'EOF' [dependencies.alloc-cortex-m] -version = "0.3.3" +version = "0.3.4" EOF local ex=allocator - cargo build --target $TARGET --example $ex cargo build --target $TARGET --example $ex --release examples+=( $ex ) @@ -53,7 +51,7 @@ EOF cat >>Cargo.toml <<'EOF' [dependencies.stm32f103xx] features = ["rt"] -version = "0.9.0" +version = "0.10.0" EOF local ex=device diff --git a/examples/allocator.rs b/examples/allocator.rs index 01d2a62..0d06669 100644 --- a/examples/allocator.rs +++ b/examples/allocator.rs @@ -11,7 +11,8 @@ #![feature(alloc)] #![feature(global_allocator)] -#![feature(used)] +#![feature(lang_items)] +#![no_main] #![no_std] // This is the allocator crate; you can use a different one @@ -19,42 +20,56 @@ extern crate alloc_cortex_m; #[macro_use] extern crate alloc; extern crate cortex_m; -extern crate cortex_m_rt; -extern crate cortex_m_semihosting; -extern crate panic_abort; // panicking behavior +#[macro_use] +extern crate cortex_m_rt as rt; +extern crate cortex_m_semihosting as sh; +extern crate panic_semihosting; use core::fmt::Write; use alloc_cortex_m::CortexMHeap; use cortex_m::asm; -use cortex_m_semihosting::hio; +use rt::ExceptionFrame; +use sh::hio; +// this is the allocator the application will use #[global_allocator] static ALLOCATOR: CortexMHeap = CortexMHeap::empty(); -extern "C" { - static mut _sheap: u32; -} - const HEAP_SIZE: usize = 1024; // in bytes -fn main() { - // Initialize the allocator - let start = unsafe { &mut _sheap as *mut u32 as usize }; - unsafe { ALLOCATOR.init(start, HEAP_SIZE) } +entry!(main); + +fn main() -> ! { + // 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(); -} -// 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]; + loop {} +} -extern "C" fn default_handler() { +// define what happens in an Out Of Memory (OOM) condition +#[lang = "oom"] +#[no_mangle] +pub fn rust_oom() -> ! { asm::bkpt(); + + loop {} +} + +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 e5cd545..fafe436 100644 --- a/examples/crash.rs +++ b/examples/crash.rs @@ -1,85 +1,114 @@ //! 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) # Exception frame = program state during the crash -//! (gdb) print/x *ef -//! $1 = cortex_m::exception::ExceptionFrame { -//! r0 = 0x2fffffff, -//! r1 = 0x2fffffff, -//! r2 = 0x0, -//! r3 = 0x0, -//! r12 = 0x0, -//! lr = 0x8000481, -//! pc = 0x8000460, -//! xpsr = 0x61000000, -//! } +//! (gdb) continue +//! Program received signal SIGTRAP, Trace/breakpoint trap. +//! __bkpt () at asm/bkpt.s:3 +//! 3 bkpt //! -//! (gdb) # Where did we come from? //! (gdb) backtrace -//! #0 cortex_m_rt::default_handler (ef=0x20004f54) at (..) -//! #1 -//! #2 0x08000460 in core::ptr::read_volatile (src=0x2fffffff) at (..) -//! #3 0x08000480 in crash::main () at examples/crash.rs:68 +//! #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?) +//! ``` +//! +//! In the console output one will find the state of the Program Counter (PC) register at the time +//! of the exception. //! -//! (gdb) # Nail down the location of the crash -//! (gdb) disassemble/m ef.pc -//! Dump of assembler code for function core::ptr::read_volatile: -//! 408 pub unsafe fn read_volatile(src: *const T) -> T { -//! 0x08000454 <+0>: sub sp, #20 -//! 0x08000456 <+2>: mov r1, r0 -//! 0x08000458 <+4>: str r0, [sp, #8] -//! 0x0800045a <+6>: ldr r0, [sp, #8] -//! 0x0800045c <+8>: str r0, [sp, #12] +//! ``` 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 { +//! 0x08000aae <+0>: sub sp, #16 +//! 0x08000ab0 <+2>: mov r1, r0 +//! 0x08000ab2 <+4>: str r0, [sp, #8] //! -//! 409 intrinsics::volatile_load(src) -//! 0x0800045e <+10>: ldr r0, [sp, #12] -//! 0x08000460 <+12>: ldr r0, [r0, #0] -//! 0x08000462 <+14>: str r0, [sp, #16] -//! 0x08000464 <+16>: ldr r0, [sp, #16] -//! 0x08000466 <+18>: str r1, [sp, #4] -//! 0x08000468 <+20>: str r0, [sp, #0] -//! 0x0800046a <+22>: b.n 0x800046c +24> +//! 452 intrinsics::volatile_load(src) +//! 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 //! -//! 410 } -//! 0x0800046c <+24>: ldr r0, [sp, #0] -//! 0x0800046e <+26>: add sp, #20 -//! 0x08000470 <+28>: bx lr +//! 453 } +//! 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. +//! //! --- -#![feature(used)] +#![no_main] #![no_std] extern crate cortex_m; -extern crate cortex_m_rt; -extern crate panic_abort; // panicking behavior +#[macro_use] +extern crate cortex_m_rt as rt; +extern crate panic_semihosting; use core::ptr; -use cortex_m::asm; +use rt::ExceptionFrame; + +entry!(main); -fn main() { - // Read an invalid memory address +fn main() -> ! { unsafe { + // read an address outside of the RAM region; causes a HardFault exception ptr::read_volatile(0x2FFF_FFFF as *const u32); } + + loop {} +} + +// define the hard fault handler +exception!(HardFault, hard_fault); + +fn hard_fault(ef: &ExceptionFrame) -> ! { + panic!("HardFault at {:#?}", ef); } -// 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 default exception handler +exception!(*, default_handler); -extern "C" fn default_handler() { - asm::bkpt(); +fn default_handler(irqn: i16) { + panic!("Unhandled exception (IRQn = {})", irqn); } diff --git a/examples/device.rs b/examples/device.rs index 91e6332..e2c1a04 100644 --- a/examples/device.rs +++ b/examples/device.rs @@ -1,12 +1,12 @@ //! Using a device crate //! -//! Crates generated using [`svd2rust`] are referred to as device crates. These crates provides an -//! API to access the peripherals of a device. When you depend on one of these crates and the "rt" -//! feature is enabled you don't need link to the cortex-m-rt crate. +//! 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 //! -//! Device crates also provide an `interrupt!` macro to register interrupt handlers. +//! Device crates also provide an `interrupt!` macro (behind the "rt" feature) to register interrupt +//! handlers. //! //! This example depends on the [`stm32f103xx`] crate so you'll have to add it to your Cargo.toml. //! @@ -16,80 +16,74 @@ //! $ edit Cargo.toml && tail $_ //! [dependencies.stm32f103xx] //! features = ["rt"] -//! version = "0.9.0" +//! version = "0.10.0" //! ``` //! //! --- -#![deny(warnings)] -#![feature(const_fn)] +#![no_main] #![no_std] extern crate cortex_m; -// extern crate cortex_m_rt; // included in the device crate -extern crate cortex_m_semihosting; -#[macro_use(exception, interrupt)] +#[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; // panicking behavior +extern crate panic_semihosting; -use core::cell::RefCell; use core::fmt::Write; -use cortex_m::interrupt::{self, Mutex}; use cortex_m::peripheral::syst::SystClkSource; -use cortex_m_semihosting::hio::{self, HStdout}; +use rt::ExceptionFrame; +use sh::hio::{self, HStdout}; use stm32f103xx::Interrupt; -static HSTDOUT: Mutex>> = Mutex::new(RefCell::new(None)); +entry!(main); -static NVIC: Mutex>> = Mutex::new(RefCell::new(None)); +fn main() -> ! { + let p = cortex_m::Peripherals::take().unwrap(); -fn main() { - let global_p = cortex_m::Peripherals::take().unwrap(); - interrupt::free(|cs| { - let hstdout = HSTDOUT.borrow(cs); - if let Ok(fd) = hio::hstdout() { - *hstdout.borrow_mut() = Some(fd); - } + let mut syst = p.SYST; + let mut nvic = p.NVIC; - let mut nvic = global_p.NVIC; - nvic.enable(Interrupt::TIM2); - *NVIC.borrow(cs).borrow_mut() = Some(nvic); + nvic.enable(Interrupt::EXTI0); - let mut syst = global_p.SYST; - syst.set_clock_source(SystClkSource::Core); - syst.set_reload(8_000_000); // 1s - syst.enable_counter(); - syst.enable_interrupt(); - }); + // 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); + } } -exception!(SYS_TICK, tick); +// try commenting out this line: you'll end in `default_handler` instead of in `exti0` +interrupt!(EXTI0, exti0, state: Option = None); -fn tick() { - interrupt::free(|cs| { - let hstdout = HSTDOUT.borrow(cs); - if let Some(hstdout) = hstdout.borrow_mut().as_mut() { - writeln!(*hstdout, "Tick").ok(); - } +fn exti0(state: &mut Option) { + if state.is_none() { + *state = Some(hio::hstdout().unwrap()); + } - if let Some(nvic) = NVIC.borrow(cs).borrow_mut().as_mut() { - nvic.set_pending(Interrupt::TIM2); - } - }); + if let Some(hstdout) = state.as_mut() { + hstdout.write_str(".").unwrap(); + } } -interrupt!(TIM2, tock, locals: { - tocks: u32 = 0; -}); +exception!(HardFault, hard_fault); + +fn hard_fault(ef: &ExceptionFrame) -> ! { + panic!("HardFault at {:#?}", ef); +} -fn tock(l: &mut TIM2::Locals) { - l.tocks += 1; +exception!(*, default_handler); - interrupt::free(|cs| { - let hstdout = HSTDOUT.borrow(cs); - if let Some(hstdout) = hstdout.borrow_mut().as_mut() { - writeln!(*hstdout, "Tock ({})", l.tocks).ok(); - } - }); +fn default_handler(irqn: i16) { + panic!("Unhandled exception (IRQn = {})", irqn); } diff --git a/examples/exception.rs b/examples/exception.rs new file mode 100644 index 0000000..d62e6d4 --- /dev/null +++ b/examples/exception.rs @@ -0,0 +1,64 @@ +//! Overriding an exception handler +//! +//! You can override an exception handler using the [`exception!`][1] macro. +//! +//! [1]: https://docs.rs/cortex-m-rt/0.5.0/cortex_m_rt/macro.exception.html +//! +//! --- + +#![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_semihosting; + +use core::fmt::Write; + +use cortex_m::peripheral::syst::SystClkSource; +use cortex_m::Peripherals; +use rt::ExceptionFrame; +use sh::hio::{self, HStdout}; + +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); // period = 1s + syst.enable_counter(); + syst.enable_interrupt(); + + loop {} +} + +// 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!(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/hello.rs b/examples/hello.rs index 3d9d8c0..3c4b5ec 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -2,29 +2,36 @@ //! //! --- -#![feature(used)] +#![no_main] #![no_std] -extern crate cortex_m; -extern crate cortex_m_rt; -extern crate cortex_m_semihosting; -extern crate panic_abort; // panicking behavior +#[macro_use] +extern crate cortex_m_rt as rt; +extern crate cortex_m_semihosting as sh; +extern crate panic_semihosting; use core::fmt::Write; -use cortex_m::asm; -use cortex_m_semihosting::hio; +use rt::ExceptionFrame; +use sh::hio; -fn main() { +entry!(main); + +fn main() -> ! { let mut stdout = hio::hstdout().unwrap(); writeln!(stdout, "Hello, world!").unwrap(); + + loop {} +} + +exception!(HardFault, hard_fault); + +fn hard_fault(ef: &ExceptionFrame) -> ! { + panic!("HardFault at {:#?}", ef); } -// 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]; +exception!(*, default_handler); -extern "C" fn default_handler() { - asm::bkpt(); +fn default_handler(irqn: i16) { + panic!("Unhandled exception (IRQn = {})", irqn); } diff --git a/examples/itm.rs b/examples/itm.rs index f5b9f17..6fddea1 100644 --- a/examples/itm.rs +++ b/examples/itm.rs @@ -1,39 +1,54 @@ //! 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/ //! //! --- -#![feature(used)] +#![no_main] #![no_std] #[macro_use] extern crate cortex_m; -extern crate cortex_m_rt; -extern crate panic_abort; // panicking behavior +#[macro_use] +extern crate cortex_m_rt as rt; +extern crate panic_semihosting; use cortex_m::{asm, Peripherals}; +use rt::ExceptionFrame; + +entry!(main); + +fn main() -> ! { + let mut p = Peripherals::take().unwrap(); + let stim = &mut p.ITM.stim[0]; + + iprintln!(stim, "Hello, world!"); + + loop { + asm::bkpt(); + } +} -fn main() { - let p = Peripherals::take().unwrap(); - let mut itm = p.ITM; +// define the hard fault handler +exception!(HardFault, hard_fault); - iprintln!(&mut itm.stim[0], "Hello, world!"); +fn hard_fault(ef: &ExceptionFrame) -> ! { + panic!("HardFault at {:#?}", ef); } -// 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 default exception handler +exception!(*, default_handler); -extern "C" fn default_handler() { - asm::bkpt(); +fn default_handler(irqn: i16) { + panic!("Unhandled exception (IRQn = {})", irqn); } diff --git a/examples/minimal.rs b/examples/minimal.rs new file mode 100644 index 0000000..db91840 --- /dev/null +++ b/examples/minimal.rs @@ -0,0 +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 [`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 handler crate][0] +//! +//! [0]: https://crates.io/keywords/panic-impl +//! +//! - Define the `HardFault` handler using the [`exception!`] macro. This handler (function) is +//! called when a hard fault exception is raised by the hardware. +//! +//! [`exception!`]: https://docs.rs/cortex-m-rt/~0.5/cortex_m_rt/macro..html +//! +//! - 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(entry, exception)] +extern crate cortex_m_rt as rt; + +// 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 is ... +entry!(main); + +// ... this never ending function +fn main() -> ! { + loop { + asm::bkpt(); + } +} + +// define the hard fault handler +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); +} diff --git a/examples/override-exception-handler.rs b/examples/override-exception-handler.rs deleted file mode 100644 index 43f3080..0000000 --- a/examples/override-exception-handler.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! Overriding an exception handler -//! -//! You can override an exception handler using the [`exception!`][1] macro. -//! -//! [1]: https://docs.rs/cortex-m-rt/0.3.2/cortex_m_rt/macro.exception.html -//! -//! The default exception handler can be overridden using the [`default_handler!`][2] macro -//! -//! [2]: https://docs.rs/cortex-m-rt/0.3.2/cortex_m_rt/macro.default_handler.html -//! -//! --- - -#![feature(used)] -#![no_std] - -extern crate cortex_m; -#[macro_use(exception)] -extern crate cortex_m_rt; -extern crate panic_abort; // panicking behavior - -use core::ptr; - -use cortex_m::asm; - -fn main() { - unsafe { - // Invalid memory access - ptr::read_volatile(0x2FFF_FFFF as *const u32); - } -} - -exception!(HARD_FAULT, handler); - -fn handler() { - // You'll hit this breakpoint rather than the one in cortex-m-rt - asm::bkpt() -} - -// As we are not using interrupts, we just register a dummy catch all handler -#[allow(dead_code)] -#[used] -#[link_section = ".vector_table.interrupts"] -static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240]; - -extern "C" fn default_handler() { - asm::bkpt(); -} diff --git a/examples/panic.rs b/examples/panic.rs index c3e8002..0822156 100644 --- a/examples/panic.rs +++ b/examples/panic.rs @@ -1,31 +1,44 @@ //! 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 //! //! --- -#![feature(used)] +#![no_main] #![no_std] -extern crate cortex_m; -extern crate cortex_m_rt; -// extern crate panic_abort; -extern crate panic_semihosting; // reports panic messages to the host stderr using semihosting +#[macro_use] +extern crate cortex_m_rt as rt; -use cortex_m::asm; +// Pick one of these two panic handlers: -fn main() { - panic!("Oops"); +// Reports panic messages to the host stderr using semihosting +extern crate panic_semihosting; + +// Logs panic messages using the ITM (Instrumentation Trace Macrocell) +// NOTE to use this you need to uncomment the `panic-itm` dependency in Cargo.toml +// extern crate panic_itm; + +use rt::ExceptionFrame; + +entry!(main); + +fn main() -> ! { + panic!("Oops") +} + +// define the hard fault handler +exception!(HardFault, hard_fault); + +fn hard_fault(ef: &ExceptionFrame) -> ! { + panic!("HardFault at {:#?}", ef); } -// 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 default exception handler +exception!(*, default_handler); -extern "C" fn default_handler() { - asm::bkpt(); +fn default_handler(irqn: i16) { + panic!("Unhandled exception (IRQn = {})", irqn); } diff --git a/gen-examples.sh b/gen-examples.sh index 660607b..5676c01 100644 --- a/gen-examples.sh +++ b/gen-examples.sh @@ -5,13 +5,14 @@ set -ex main() { local examples=( + minimal hello itm panic crash - override-exception-handler - device + exception allocator + device ) rm -rf src/examples @@ -19,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/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_hello.rs b/src/examples/_0_hello.rs deleted file mode 100644 index d6c70bf..0000000 --- a/src/examples/_0_hello.rs +++ /dev/null @@ -1,34 +0,0 @@ -//! Prints "Hello, world!" on the OpenOCD console using semihosting -//! -//! --- -//! -//! ``` -//! -//! #![feature(used)] -//! #![no_std] -//! -//! extern crate cortex_m; -//! extern crate cortex_m_rt; -//! extern crate cortex_m_semihosting; -//! extern crate panic_abort; // panicking behavior -//! -//! use core::fmt::Write; -//! -//! use cortex_m::asm; -//! use cortex_m_semihosting::hio; -//! -//! fn main() { -//! let mut stdout = hio::hstdout().unwrap(); -//! writeln!(stdout, "Hello, world!").unwrap(); -//! } -//! -//! // 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]; -//! -//! extern "C" fn default_handler() { -//! asm::bkpt(); -//! } -//! ``` -// Auto-generated. Do not modify. diff --git a/src/examples/_0_minimal.rs b/src/examples/_0_minimal.rs new file mode 100644 index 0000000..8926e23 --- /dev/null +++ b/src/examples/_0_minimal.rs @@ -0,0 +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 [`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 handler crate][0] +//! +//! [0]: https://crates.io/keywords/panic-impl +//! +//! - Define the `HardFault` handler using the [`exception!`] macro. This handler (function) is +//! called when a hard fault exception is raised by the hardware. +//! +//! [`exception!`]: https://docs.rs/cortex-m-rt/~0.5/cortex_m_rt/macro..html +//! +//! - 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(entry, exception)] +//! extern crate cortex_m_rt as rt; +//! +//! // 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 is ... +//! entry!(main); +//! +//! // ... this never ending function +//! fn main() -> ! { +//! loop { +//! asm::bkpt(); +//! } +//! } +//! +//! // define the hard fault handler +//! 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); +//! } +//! ``` +// Auto-generated. Do not modify. diff --git a/src/examples/_1_hello.rs b/src/examples/_1_hello.rs new file mode 100644 index 0000000..413c4fb --- /dev/null +++ b/src/examples/_1_hello.rs @@ -0,0 +1,41 @@ +//! Prints "Hello, world!" on the OpenOCD console using semihosting +//! +//! --- +//! +//! ``` +//! +//! #![no_main] +//! #![no_std] +//! +//! #[macro_use] +//! extern crate cortex_m_rt as rt; +//! extern crate cortex_m_semihosting as sh; +//! extern crate panic_semihosting; +//! +//! use core::fmt::Write; +//! +//! use rt::ExceptionFrame; +//! use sh::hio; +//! +//! entry!(main); +//! +//! fn main() -> ! { +//! let mut stdout = hio::hstdout().unwrap(); +//! writeln!(stdout, "Hello, world!").unwrap(); +//! +//! loop {} +//! } +//! +//! 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/_1_itm.rs b/src/examples/_1_itm.rs deleted file mode 100644 index 6793f70..0000000 --- a/src/examples/_1_itm.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! 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 -//! `monitor` commands in the `.gdbinit` file. -//! -//! [`itmdump`]: https://docs.rs/itm/0.2.1/itm/ -//! -//! --- -//! -//! ``` -//! -//! #![feature(used)] -//! #![no_std] -//! -//! #[macro_use] -//! extern crate cortex_m; -//! extern crate cortex_m_rt; -//! extern crate panic_abort; // panicking behavior -//! -//! use cortex_m::{asm, Peripherals}; -//! -//! fn main() { -//! let p = Peripherals::take().unwrap(); -//! let mut itm = p.ITM; -//! -//! iprintln!(&mut itm.stim[0], "Hello, world!"); -//! } -//! -//! // 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]; -//! -//! extern "C" fn default_handler() { -//! asm::bkpt(); -//! } -//! ``` -// Auto-generated. Do not modify. diff --git a/src/examples/_2_itm.rs b/src/examples/_2_itm.rs new file mode 100644 index 0000000..568a467 --- /dev/null +++ b/src/examples/_2_itm.rs @@ -0,0 +1,58 @@ +//! Sends "Hello, world!" through the ITM port 0 +//! +//! ITM is much faster than semihosting. Like 4 orders of magnitude or so. +//! +//! **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/ +//! +//! --- +//! +//! ``` +//! +//! #![no_main] +//! #![no_std] +//! +//! #[macro_use] +//! extern crate cortex_m; +//! #[macro_use] +//! extern crate cortex_m_rt as rt; +//! extern crate panic_semihosting; +//! +//! use cortex_m::{asm, Peripherals}; +//! use rt::ExceptionFrame; +//! +//! entry!(main); +//! +//! fn main() -> ! { +//! let mut p = Peripherals::take().unwrap(); +//! let stim = &mut p.ITM.stim[0]; +//! +//! iprintln!(stim, "Hello, world!"); +//! +//! loop { +//! asm::bkpt(); +//! } +//! } +//! +//! // define the hard fault handler +//! 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); +//! } +//! ``` +// Auto-generated. Do not modify. diff --git a/src/examples/_2_panic.rs b/src/examples/_2_panic.rs deleted file mode 100644 index 0ea182b..0000000 --- a/src/examples/_2_panic.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Changing the panic handler -//! -//! The easiest way to change the panic handler is to use a different [panic implementation -//! crate][0]. -//! -//! [0]: https://crates.io/keywords/panic-impl -//! -//! --- -//! -//! ``` -//! -//! #![feature(used)] -//! #![no_std] -//! -//! extern crate cortex_m; -//! extern crate cortex_m_rt; -//! // extern crate panic_abort; -//! extern crate panic_semihosting; // reports panic messages to the host stderr using semihosting -//! -//! use cortex_m::asm; -//! -//! fn main() { -//! panic!("Oops"); -//! } -//! -//! // 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]; -//! -//! extern "C" fn default_handler() { -//! asm::bkpt(); -//! } -//! ``` -// Auto-generated. Do not modify. diff --git a/src/examples/_3_crash.rs b/src/examples/_3_crash.rs deleted file mode 100644 index 96b1516..0000000 --- a/src/examples/_3_crash.rs +++ /dev/null @@ -1,89 +0,0 @@ -//! 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. -//! -//! In you run the example below, you'll be able to inspect the state of your program under the -//! debugger using these commands: -//! -//! ``` text -//! (gdb) # Exception frame = program state during the crash -//! (gdb) print/x *ef -//! $1 = cortex_m::exception::ExceptionFrame { -//! r0 = 0x2fffffff, -//! r1 = 0x2fffffff, -//! r2 = 0x0, -//! r3 = 0x0, -//! r12 = 0x0, -//! lr = 0x8000481, -//! pc = 0x8000460, -//! xpsr = 0x61000000, -//! } -//! -//! (gdb) # Where did we come from? -//! (gdb) backtrace -//! #0 cortex_m_rt::default_handler (ef=0x20004f54) at (..) -//! #1 -//! #2 0x08000460 in core::ptr::read_volatile (src=0x2fffffff) at (..) -//! #3 0x08000480 in crash::main () at examples/crash.rs:68 -//! -//! (gdb) # Nail down the location of the crash -//! (gdb) disassemble/m ef.pc -//! Dump of assembler code for function core::ptr::read_volatile: -//! 408 pub unsafe fn read_volatile(src: *const T) -> T { -//! 0x08000454 <+0>: sub sp, #20 -//! 0x08000456 <+2>: mov r1, r0 -//! 0x08000458 <+4>: str r0, [sp, #8] -//! 0x0800045a <+6>: ldr r0, [sp, #8] -//! 0x0800045c <+8>: str r0, [sp, #12] -//! -//! 409 intrinsics::volatile_load(src) -//! 0x0800045e <+10>: ldr r0, [sp, #12] -//! 0x08000460 <+12>: ldr r0, [r0, #0] -//! 0x08000462 <+14>: str r0, [sp, #16] -//! 0x08000464 <+16>: ldr r0, [sp, #16] -//! 0x08000466 <+18>: str r1, [sp, #4] -//! 0x08000468 <+20>: str r0, [sp, #0] -//! 0x0800046a <+22>: b.n 0x800046c +24> -//! -//! 410 } -//! 0x0800046c <+24>: ldr r0, [sp, #0] -//! 0x0800046e <+26>: add sp, #20 -//! 0x08000470 <+28>: bx lr -//! -//! End of assembler dump. -//! ``` -//! -//! --- -//! -//! ``` -//! -//! #![feature(used)] -//! #![no_std] -//! -//! extern crate cortex_m; -//! extern crate cortex_m_rt; -//! extern crate panic_abort; // panicking behavior -//! -//! use core::ptr; -//! -//! use cortex_m::asm; -//! -//! fn main() { -//! // Read an invalid memory address -//! unsafe { -//! ptr::read_volatile(0x2FFF_FFFF as *const u32); -//! } -//! } -//! -//! // 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]; -//! -//! extern "C" fn default_handler() { -//! asm::bkpt(); -//! } -//! ``` -// Auto-generated. Do not modify. diff --git a/src/examples/_3_panic.rs b/src/examples/_3_panic.rs new file mode 100644 index 0000000..4d6a7dd --- /dev/null +++ b/src/examples/_3_panic.rs @@ -0,0 +1,47 @@ +//! Changing the panic handler +//! +//! 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] +//! +//! #[macro_use] +//! extern crate cortex_m_rt as rt; +//! +//! // 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; +//! +//! entry!(main); +//! +//! fn main() -> ! { +//! panic!("Oops") +//! } +//! +//! // define the hard fault handler +//! 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); +//! } +//! ``` +// Auto-generated. Do not modify. diff --git a/src/examples/_4_crash.rs b/src/examples/_4_crash.rs new file mode 100644 index 0000000..f3957d1 --- /dev/null +++ b/src/examples/_4_crash.rs @@ -0,0 +1,118 @@ +//! Debugging a crash (exception) +//! +//! 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. +//! +//! 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 +//! Program received signal SIGTRAP, Trace/breakpoint trap. +//! __bkpt () at asm/bkpt.s:3 +//! 3 bkpt +//! +//! (gdb) backtrace +//! #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?) +//! ``` +//! +//! 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 { +//! 0x08000aae <+0>: sub sp, #16 +//! 0x08000ab0 <+2>: mov r1, r0 +//! 0x08000ab2 <+4>: str r0, [sp, #8] +//! +//! 452 intrinsics::volatile_load(src) +//! 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 } +//! 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_semihosting; +//! +//! use core::ptr; +//! +//! use rt::ExceptionFrame; +//! +//! 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 {} +//! } +//! +//! // define the hard fault handler +//! 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); +//! } +//! ``` +// Auto-generated. Do not modify. diff --git a/src/examples/_4_override_exception_handler.rs b/src/examples/_4_override_exception_handler.rs deleted file mode 100644 index a7df0c1..0000000 --- a/src/examples/_4_override_exception_handler.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! Overriding an exception handler -//! -//! You can override an exception handler using the [`exception!`][1] macro. -//! -//! [1]: https://docs.rs/cortex-m-rt/0.3.2/cortex_m_rt/macro.exception.html -//! -//! The default exception handler can be overridden using the [`default_handler!`][2] macro -//! -//! [2]: https://docs.rs/cortex-m-rt/0.3.2/cortex_m_rt/macro.default_handler.html -//! -//! --- -//! -//! ``` -//! -//! #![feature(used)] -//! #![no_std] -//! -//! extern crate cortex_m; -//! #[macro_use(exception)] -//! extern crate cortex_m_rt; -//! extern crate panic_abort; // panicking behavior -//! -//! use core::ptr; -//! -//! use cortex_m::asm; -//! -//! fn main() { -//! unsafe { -//! // Invalid memory access -//! ptr::read_volatile(0x2FFF_FFFF as *const u32); -//! } -//! } -//! -//! exception!(HARD_FAULT, handler); -//! -//! fn handler() { -//! // You'll hit this breakpoint rather than the one in cortex-m-rt -//! asm::bkpt() -//! } -//! -//! // As we are not using interrupts, we just register a dummy catch all handler -//! #[allow(dead_code)] -//! #[used] -//! #[link_section = ".vector_table.interrupts"] -//! static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240]; -//! -//! extern "C" fn default_handler() { -//! asm::bkpt(); -//! } -//! ``` -// Auto-generated. Do not modify. diff --git a/src/examples/_5_device.rs b/src/examples/_5_device.rs deleted file mode 100644 index 06b1723..0000000 --- a/src/examples/_5_device.rs +++ /dev/null @@ -1,99 +0,0 @@ -//! Using a device crate -//! -//! Crates generated using [`svd2rust`] are referred to as device crates. These crates provides an -//! API to access the peripherals of a device. When you depend on one of these crates and the "rt" -//! feature is enabled you don't need link to the cortex-m-rt crate. -//! -//! [`svd2rust`]: https://crates.io/crates/svd2rust -//! -//! Device crates also provide an `interrupt!` macro to register interrupt handlers. -//! -//! This example depends on the [`stm32f103xx`] crate so you'll have to add it to your Cargo.toml. -//! -//! [`stm32f103xx`]: https://crates.io/crates/stm32f103xx -//! -//! ``` -//! $ edit Cargo.toml && tail $_ -//! [dependencies.stm32f103xx] -//! features = ["rt"] -//! version = "0.9.0" -//! ``` -//! -//! --- -//! -//! ``` -//! -//! #![deny(warnings)] -//! #![feature(const_fn)] -//! #![no_std] -//! -//! extern crate cortex_m; -//! // extern crate cortex_m_rt; // included in the device crate -//! extern crate cortex_m_semihosting; -//! #[macro_use(exception, interrupt)] -//! extern crate stm32f103xx; -//! extern crate panic_abort; // panicking behavior -//! -//! use core::cell::RefCell; -//! use core::fmt::Write; -//! -//! use cortex_m::interrupt::{self, Mutex}; -//! use cortex_m::peripheral::syst::SystClkSource; -//! use cortex_m_semihosting::hio::{self, HStdout}; -//! use stm32f103xx::Interrupt; -//! -//! static HSTDOUT: Mutex>> = Mutex::new(RefCell::new(None)); -//! -//! static NVIC: Mutex>> = Mutex::new(RefCell::new(None)); -//! -//! fn main() { -//! let global_p = cortex_m::Peripherals::take().unwrap(); -//! interrupt::free(|cs| { -//! let hstdout = HSTDOUT.borrow(cs); -//! if let Ok(fd) = hio::hstdout() { -//! *hstdout.borrow_mut() = Some(fd); -//! } -//! -//! let mut nvic = global_p.NVIC; -//! nvic.enable(Interrupt::TIM2); -//! *NVIC.borrow(cs).borrow_mut() = Some(nvic); -//! -//! let mut syst = global_p.SYST; -//! syst.set_clock_source(SystClkSource::Core); -//! syst.set_reload(8_000_000); // 1s -//! syst.enable_counter(); -//! syst.enable_interrupt(); -//! }); -//! } -//! -//! exception!(SYS_TICK, tick); -//! -//! fn tick() { -//! interrupt::free(|cs| { -//! let hstdout = HSTDOUT.borrow(cs); -//! if let Some(hstdout) = hstdout.borrow_mut().as_mut() { -//! writeln!(*hstdout, "Tick").ok(); -//! } -//! -//! if let Some(nvic) = NVIC.borrow(cs).borrow_mut().as_mut() { -//! nvic.set_pending(Interrupt::TIM2); -//! } -//! }); -//! } -//! -//! interrupt!(TIM2, tock, locals: { -//! tocks: u32 = 0; -//! }); -//! -//! fn tock(l: &mut TIM2::Locals) { -//! l.tocks += 1; -//! -//! interrupt::free(|cs| { -//! let hstdout = HSTDOUT.borrow(cs); -//! if let Some(hstdout) = hstdout.borrow_mut().as_mut() { -//! writeln!(*hstdout, "Tock ({})", l.tocks).ok(); -//! } -//! }); -//! } -//! ``` -// Auto-generated. Do not modify. diff --git a/src/examples/_5_exception.rs b/src/examples/_5_exception.rs new file mode 100644 index 0000000..f2a65f3 --- /dev/null +++ b/src/examples/_5_exception.rs @@ -0,0 +1,68 @@ +//! Overriding an exception handler +//! +//! You can override an exception handler using the [`exception!`][1] macro. +//! +//! [1]: https://docs.rs/cortex-m-rt/0.5.0/cortex_m_rt/macro.exception.html +//! +//! --- +//! +//! ``` +//! +//! #![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_semihosting; +//! +//! use core::fmt::Write; +//! +//! use cortex_m::peripheral::syst::SystClkSource; +//! use cortex_m::Peripherals; +//! use rt::ExceptionFrame; +//! use sh::hio::{self, HStdout}; +//! +//! 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); // period = 1s +//! syst.enable_counter(); +//! syst.enable_interrupt(); +//! +//! loop {} +//! } +//! +//! // 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!(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_allocator.rs b/src/examples/_6_allocator.rs index f85a7b5..c7bddcc 100644 --- a/src/examples/_6_allocator.rs +++ b/src/examples/_6_allocator.rs @@ -10,55 +10,70 @@ //! --- //! //! ``` -//! +//! //! #![feature(alloc)] //! #![feature(global_allocator)] -//! #![feature(used)] +//! #![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] //! extern crate alloc; //! extern crate cortex_m; -//! extern crate cortex_m_rt; -//! extern crate cortex_m_semihosting; -//! extern crate panic_abort; // panicking behavior -//! +//! #[macro_use] +//! extern crate cortex_m_rt as rt; +//! extern crate cortex_m_semihosting as sh; +//! extern crate panic_semihosting; +//! //! use core::fmt::Write; -//! +//! //! use alloc_cortex_m::CortexMHeap; //! use cortex_m::asm; -//! use cortex_m_semihosting::hio; -//! +//! use rt::ExceptionFrame; +//! use sh::hio; +//! +//! // this is the allocator the application will use //! #[global_allocator] //! static ALLOCATOR: CortexMHeap = CortexMHeap::empty(); -//! -//! extern "C" { -//! static mut _sheap: u32; -//! } -//! +//! //! const HEAP_SIZE: usize = 1024; // in bytes -//! -//! fn main() { -//! // Initialize the allocator -//! let start = unsafe { &mut _sheap as *mut u32 as usize }; -//! unsafe { ALLOCATOR.init(start, HEAP_SIZE) } -//! +//! +//! entry!(main); +//! +//! fn main() -> ! { +//! // 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 {} //! } -//! -//! // 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]; -//! -//! extern "C" fn default_handler() { +//! +//! // define what happens in an Out Of Memory (OOM) condition +//! #[lang = "oom"] +//! #[no_mangle] +//! pub fn rust_oom() -> ! { //! asm::bkpt(); +//! +//! loop {} +//! } +//! +//! 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/_7_device.rs b/src/examples/_7_device.rs new file mode 100644 index 0000000..7564284 --- /dev/null +++ b/src/examples/_7_device.rs @@ -0,0 +1,93 @@ +//! Using a device crate +//! +//! 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 +//! +//! Device crates also provide an `interrupt!` macro (behind the "rt" feature) to register interrupt +//! handlers. +//! +//! This example depends on the [`stm32f103xx`] crate so you'll have to add it to your Cargo.toml. +//! +//! [`stm32f103xx`]: https://crates.io/crates/stm32f103xx +//! +//! ``` +//! $ edit Cargo.toml && tail $_ +//! [dependencies.stm32f103xx] +//! features = ["rt"] +//! version = "0.10.0" +//! ``` +//! +//! --- +//! +//! ``` +//! +//! #![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_semihosting; +//! +//! use core::fmt::Write; +//! +//! use cortex_m::peripheral::syst::SystClkSource; +//! use rt::ExceptionFrame; +//! use sh::hio::{self, HStdout}; +//! use stm32f103xx::Interrupt; +//! +//! 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!(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/mod.rs b/src/examples/mod.rs index fd0e6e4..0358d78 100644 --- a/src/examples/mod.rs +++ b/src/examples/mod.rs @@ -1,9 +1,10 @@ -//! Examples +//! Examples sorted in increasing degree of complexity // Auto-generated. Do not modify. -pub mod _0_hello; -pub mod _1_itm; -pub mod _2_panic; -pub mod _3_crash; -pub mod _4_override_exception_handler; -pub mod _5_device; +pub mod _0_minimal; +pub mod _1_hello; +pub mod _2_itm; +pub mod _3_panic; +pub mod _4_crash; +pub mod _5_exception; pub mod _6_allocator; +pub mod _7_device; diff --git a/src/lib.rs b/src/lib.rs index dca15de..89dcc23 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,9 +4,9 @@ //! //! - Nightly Rust toolchain newer than `nightly-2018-04-08`: `rustup default nightly` //! - Cargo `clone` subcommand: `cargo install cargo-clone` +//! - ARM toolchain: `sudo apt-get install gcc-arm-none-eabi` (on Ubuntu) //! - GDB: `sudo apt-get install gdb-arm-none-eabi` (on Ubuntu) //! - OpenOCD: `sudo apt-get install OpenOCD` (on Ubuntu) -//! - [Optional] ARM linker: `sudo apt-get install binutils-arm-none-eabi` (on Ubuntu) //! - [Optional] Cargo `add` subcommand: `cargo install cargo-edit` //! //! # Usage @@ -27,7 +27,7 @@ //! 2) Clone this crate //! //! ``` text -//! $ cargo clone cortex-m-quickstart && cd $_ +//! $ 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,27 +114,7 @@ //! Tag_ABI_FP_16bit_format: IEEE 754 //! ``` //! -//! **NOTE** By default Cargo will use the LLD linker shipped with the Rust toolchain. If you -//! encounter any linking error try to switch to the GNU linker by modifying the `.cargo/config` -//! file as shown below: -//! -//! ``` text -//! runner = 'arm-none-eabi-gdb' -//! rustflags = [ -//! "-C", "link-arg=-Tlink.x", -//! - "-C", "linker=lld", -//! - "-Z", "linker-flavor=ld.lld", -//! - # "-C", "linker=arm-none-eabi-ld", -//! - # "-Z", "linker-flavor=ld", -//! + # "-C", "linker=lld", -//! + # "-Z", "linker-flavor=ld.lld", -//! + "-C", "linker=arm-none-eabi-ld", -//! + "-Z", "linker-flavor=ld", -//! "-Z", "thinlto=no", -//! ] -//! ``` -//! -//! 9) Flash the program +//! 9) Flash and debug the program //! //! ``` text //! $ # Launch OpenOCD on a terminal @@ -185,34 +166,28 @@ //! Compiling demo v0.1.0 (file:///home/japaric/tmp/demo) //! error: linking with `arm-none-eabi-ld` failed: exit code: 1 //! | -//! = note: "lld" "-L" (..) -//! = note: arm-none-eabi-ld: address 0xbaaab838 of hello section `.text' is .. -//! arm-none-eabi-ld: address 0xbaaab838 of hello section `.text' is .. -//! arm-none-eabi-ld: -//! Invalid '.rodata.exceptions' section. -//! Make sure to place a static with type `cortex_m::exception::Handlers` -//! in that section (cf. #[link_section]) ONLY ONCE. +//! = 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: //! //! ``` text //! $ cargo build //! (..) -//! Compiling cortex-m-semihosting v0.2.0 -//! error[E0463]: can't find crate for `std` +//! error: language item required, but not found: `eh_personality` //! //! 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 //! @@ -236,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 @@ -255,9 +229,21 @@ //! 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 +//! +//! Error message: +//! +//! ``` text +//! $ cargo build +//! error[E0463]: can't find crate for `core` +//! | +//! = note: the `thumbv7m-none-eabi` target may not be installed +//! ``` +//! +//! Solution: call `rustup target add thumbv7m-none-eabi` but with the name of your target //! //! ## Used an old nightly //! @@ -281,12 +267,12 @@ //! //! ``` text //! $ cargo build -//! error: failed to run `rustc` to learn about target-specific information -//! -//! To learn more, run the command again with --verbose. +//! error[E0463]: can't find crate for `core` +//! | +//! = note: the `thumbv7em-none-eabihf` target may not be installed //! ``` //! -//! Solution: Switch to the nightly toolchain with `rustup default nightly`. +//! Solution: We are not there yet! Switch to the nightly toolchain with `rustup default nightly`. //! //! ## Used `gdb` instead of `arm-none-eabi-gdb` //!