diff --git a/.cargo/config b/.cargo/config index 01b30cb..b9bbec9 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,4 +1,12 @@ +[target.thumbv7m-none-eabi] +# uncomment this to make `cargo run` execute programs on QEMU +# runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" + [target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# uncomment this to make `cargo run` start a GDB session +# NOTE: you may need to change `arm-none-eabi-gdb` +# runner = "arm-none-eabi-gdb -x openocd.gdb" + rustflags = [ # LLD (shipped with the Rust toolchain) is used as the default linker "-C", "link-arg=-Tlink.x", @@ -20,4 +28,4 @@ rustflags = [ # target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ target = "thumbv7m-none-eabi" # Cortex-M3 # target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) -# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) \ No newline at end of file +# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) diff --git a/Cargo.toml b/Cargo.toml index e4e5d8b..ebc43d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,17 @@ +# TODO remove +cargo-features = ["edition"] + [package] +edition = "2018" authors = ["{{authors}}"] name = "{{project-name}}" version = "0.1.0" [dependencies] -cortex-m = "0.5.6" -cortex-m-rt = "0.5.3" +cortex-m = "0.5.7" +cortex-m-rt = "0.6.1" cortex-m-semihosting = "0.3.1" -panic-semihosting = "0.4.0" +panic-halt = "0.1.3" # Uncomment for the panic example. # panic-itm = "0.3.0" @@ -16,11 +20,17 @@ panic-semihosting = "0.4.0" # alloc-cortex-m = "0.3.5" # Uncomment for the device example. -# [dependencies.stm32f103xx] +# [dependencies.stm32f30x] # features = ["rt"] -# version = "0.10.0" +# version = "0.7.1" + +# this lets you use `cargo fix`! +[[bin]] +name = "{{project-name}}" +test = false +bench = false [profile.release] codegen-units = 1 # better optimizations -debug = true +debug = true # symbols are nice and they don't increase the size on Flash lto = true # better optimizations diff --git a/README.md b/README.md index 3ce896f..14c6437 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ This project is developed and maintained by the [Cortex-M team][team]. -# [Documentation](https://rust-embedded.github.io/cortex-m-quickstart/cortex_m_quickstart) - ## Dependencies To build embedded programs using this template you'll need: -- Nightly Rust toolchain from 2018-08-28 or newer: `rustup default nightly` +- Rust 1.30-beta or nightly. `rustup default beta` + - The `cargo generate` subcommand: `cargo install cargo-generate` + - `rust-std` components (pre-compiled `core` crate) for the ARM Cortex-M targets. Run: @@ -19,337 +19,56 @@ To build embedded programs using this template you'll need: $ rustup target add thumbv6m-none-eabi thumbv7m-none-eabi thumbv7em-none-eabi thumbv7em-none-eabihf ``` -## Non-dependencies +## Using this template -This section list programs that are *not* required to build embedded programs -but are useful or necessary for embedded development. +**NOTE**: This is the very short version that only covers building programs. For +the long version check [the embedded Rust book][book]. -To flash (put the firmware on the target device) and debug embedded programs -you'll need these additional programs: +[book]: https://rust-embedded.github.io/book/blinky/blinky.html - - +0. Before we begin you need to identify some characteristics of the target + device as these will be used to configure the project: -- GDB with ARM support or LLDB, for debugging. -- QEMU with ARM support, for running embedded programs on the build machine. -- OpenOCD and similar, which make debugging possible at all. +- The ARM core. e.g. Cortex-M3. -To inspect the produced binaries you'll want [`cargo-binutils`]. - -[`cargo-binutils`]: https://crates.io/crates/cargo-binutils - -## Usage - -### First timers - -#### Building - -If you are already familiar with the process of building and debugging embedded -Rust programs feel free to skip this section! - -To get you familiar with building and debugging embedded Rust programs we'll use -the template with its default values to build a program for the LM3S6965, a -microcontroller with a Cortex-M3 core that QEMU can emulate. - -In this section we'll use a debugger (GDB or LLDB) and QEMU. Be sure to have -them installed! - -1. Initialize the template - -``` console -$ cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart -``` - -2. Build the example "Hello, world!" program - -``` rust -// example/hello.rs -#![no_main] -#![no_std] - -#[macro_use] -extern crate cortex_m_rt; -extern crate cortex_m_semihosting as sh; -extern crate panic_semihosting; - -use core::fmt::Write; - -use sh::hio; - -entry!(main); - -fn main() -> ! { - let mut stdout = hio::hstdout().unwrap(); - writeln!(stdout, "Hello, world!").unwrap(); - - loop {} -} -``` - -We'll cross compile the program for the `thumbv7m-none-eabi` target. This target -covers Cortex-M3 devices like the one we are targeting. - -``` console -$ cargo build --target thumbv7m-none-eabi --example hello -``` -You'll find the output binary in the following path: -`target/thumbv7m-none-eabi/debug/examples/hello`. The output is an ELF file. - -If you have `file` installed you can print the file type of the output to -confirm it's an ELF file: - -``` -$ ( cd target/thumbv7m-none-eabi/debug/examples && file hello ) -hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, with debug_info, not stripped -``` - -If you have `cargo-binutils` installed you can look at the ELF header of the -output: - -> NOTE `cargo-readelf` will be available in a *future* release of binutils -> (v0.1.3) - -``` console -$ cargo readelf --example hello -- --file-headers -``` - -``` text -ELF Header: - Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 - Class: ELF32 - Data: 2's complement, little endian - Version: 1 (current) - OS/ABI: UNIX - System V - ABI Version: 0x0 - Type: EXEC (Executable file) - Machine: ARM - Version: 0x1 - Entry point address: 0x23A3 - Start of program headers: 52 (bytes into file) - Start of section headers: 673340 (bytes into file) - Flags: 0x5000200 - Size of this header: 52 (bytes) - Size of program headers: 32 (bytes) - Number of program headers: 2 - Size of section headers: 40 (bytes) - Number of section headers: 21 - Section header string table index: 19 -``` - -If you look at the bottom of the Cargo configuration file (`.cargo/config`) -you'll see that the `thumbv7m-none-eabi` target has been set as the default -compilation target. This means that you do *not* need to pass the `--target` -flag to cross compile because it's already implied. So: - -``` console -$ cargo build --example hello -``` - -Does the same as before. - -3. There's no step 3! Your build is done, but for completeness let's look at how - to run this program on QEMU. - -#### Running the program on QEMU - -Execute this command and you are done. - -``` console -$ qemu-system-arm \ - -cpu cortex-m3 \ - -machine lm3s6965evb \ - -nographic \ - -semihosting-config enable=on,target=native \ - -kernel target/thumbv7m-none-eabi/debug/examples/hello -Hello, world! -``` - -The above command will block the terminal because the `hello` program never -ends! To terminate QEMU input this in your terminal: `Ctrl+A`, followed by `X`. - -Let me break down that long command for you: - -- `qemu-system-arm`. This is the QEMU emulator. There are a few variants of - these QEMU binaries; this one does full *system* emulation of *ARM* machines - -- hence the name. - -- `-cpu cortex-m3`. This tells QEMU to emulate a Cortex-M3 CPU. Specifying the - CPU model lets us catch some miscompilation errors: for example, running a - program compiled for the Cortex-M4F, which has a hardware FPU, will make QEMU - error during its execution. - -- `-machine lm3s6965evb`. This tells QEMU to emulate the LM3S6965EVB, a - evaluation board that contains a LM3S6965 microcontroller. - -- `-nographic`. This tells QEMU to not launch its GUI. - -- `-semihosting-config (..)`. This tells QEMU to enable semihosting. Semihosting - lets the emulated device, among other things, use the host stdout, stderr and - stdin and create files on the host. - -- `-kernel $file`. This tells QEMU which binary to flash (kind of) and run on - the emulated machine. - -#### Debugging - -First, we launch a QEMU instance: - -``` console -$ qemu-system-arm \ - -cpu cortex-m3 \ - -machine lm3s6965evb \ - -nographic \ - -semihosting-config enable=on,target=native \ - -gdb tcp::3333 \ - -S \ - -kernel target/thumbv7m-none-eabi/debug/examples/hello -``` - -Note that this command won't print anything to the console but will block the -terminal. We have passed two extra flags this time: - -- `-gdb tcp::3333`. This tells QEMU to wait for a GDB connection on TCP - port 3333. - -- `-S`. This tells QEMU to freeze the machine at startup. Without this the - program would have reached the end of `main` before we had a chance to launch - the debugger! - -Next, we start up LLDB in another terminal: - -``` console -$ lldb target/thumbv7m-none-eabi/debug/examples/hello -``` - -And tell it to connect to the GDB server that QEMU created: - -``` console -(lldb) gdb-remote 3333 -Process 1 stopped -* thread #1, stop reason = signal SIGTRAP - frame #0: 0x000023a2 hello`Reset at lib.rs:475 - 472 - 473 #[doc(hidden)] - 474 #[no_mangle] --> 475 pub unsafe extern "C" fn Reset() -> ! { - 476 extern "C" { - 477 // This symbol will be provided by the user via the `entry!` macro - 478 fn main() -> !; -``` - -You'll see that the process is halted and that the program counter is pointing -to a function named `Reset`. That is the reset handler: what Cortex-M cores -execute upon booting. - -This reset handler will eventually call our `main` function. Let's skip all the -way there using a breakpoint and the `continue` command: - -``` console -(lldb) breakpoint set -name hello::main -Breakpoint 1: where = hello`hello::main::h9cf19a1378cbd1b8 + 2 at hello.rs:20, address = 0x000006a8 - -(lldb) continue -Process 1 resuming -Process 1 stopped -* thread #1, stop reason = breakpoint 1.1 - frame #0: 0x000006a8 hello`hello::main::h9cf19a1378cbd1b8 at hello.rs:20 - 17 entry!(main); - 18 - 19 fn main() -> ! { --> 20 let mut stdout = hio::hstdout().unwrap(); - 21 writeln!(stdout, "Hello, world!").unwrap(); - 22 - 23 loop {} -``` - -We are now close to the code that prints "Hello, world!". Let's move the program -forward using the `next` command. - -``` console -(lldb) next -Process 1 stopped -* thread #1, stop reason = step over - frame #0: 0x000006be hello`hello::main::h9cf19a1378cbd1b8 at hello.rs:21 - 18 - 19 fn main() -> ! { - 20 let mut stdout = hio::hstdout().unwrap(); --> 21 writeln!(stdout, "Hello, world!").unwrap(); - 22 - 23 loop {} - 24 } - -(lldb) next -Process 1 stopped -* thread #1, stop reason = step over - frame #0: 0x000006f6 hello`hello::main::h9cf19a1378cbd1b8 at hello.rs:23 - 20 let mut stdout = hio::hstdout().unwrap(); - 21 writeln!(stdout, "Hello, world!").unwrap(); - 22 --> 23 loop {} - 24 } -``` - -At this point you should see "Hello, world!" printed on the terminal that's -running `qemu-system-arm`. - -``` console -$ qemu-system-arm (..) -Hello, world! -``` - -That's it! You can now exit `lldb`, which will also cause QEMU to terminate. - -``` -(lldb) exit -Quitting LLDB will kill one or more processes. Do you really want to proceed: [Y/n] y -``` - -### Not first timers - -#### Building - -This section explains how to configure this template to build programs for some -specific Cortex-M device. - -0. Before everything else, you need to know the characteristics of the target - device: - -- What's the ARM core? e.g. Cortex-M3. - -- Does the ARM core include an FPU? e.g. the Cortex-M4F does. +- Does the ARM core include an FPU? Cortex-M4**F** and Cortex-M7**F** cores do. - How much Flash memory and RAM does the target device has? e.g. 40 KB of RAM -- Where is Flash memory and RAM mapped in the address space? e.g. `0x2000_0000` - is common for RAM. +- Where are Flash memory and RAM mapped in the address space? e.g. RAM is + commonly located at address `0x2000_0000`. -You should be able to find this information in the data sheet and / or reference -manual of your device. +You can find this information in the data sheet or the reference manual of your +device. -As an example, we'll use the [STM32F303VCT6] microcontroller. This device has: +In this example we'll be using the STM32F3DISCOVERY. This board contains an +STM32F303VCT6 microcontroller. This microcontroller has: -[STM32F303VCT6]: https://www.st.com/en/microcontrollers/stm32f303vc.html +- A Cortex-M4F core that includes a single precision FPU -- a Cortex-M4F core that includes a single precision FPU +- 256 KB of Flash located at address 0x0800_0000. -- 256 KB of Flash located at address `0x0800_0000`. - -- 40 KB of RAM located at address `0x2000_0000`. (There's another RAM region but +- 40 KB of RAM located at address 0x2000_0000. (There's another RAM region but for simplicity we'll ignore it). -1. Initialize the template +1. Instantiate the template. ``` console $ cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart + Project Name: app + Creating project called `app`... + Done! New project created /tmp/app + +$ cd app ``` -2. Set the default compilation target in `.cargo/config`. +2. Set a default compilation target. There are four options as mentioned at the + bottom of `.cargo/config`. For the STM32F303VCT6, which has a Cortex-M4F + core, we'll pick the `thumbv7em-none-eabihf` target. -For the STM32F303VCT6, we pick the `thumbv7em-none-eabihf` target as that covers -the Cortex-M4F core. ``` console -$ tail .cargo/config +$ tail -n6 .cargo/config ``` ``` toml @@ -361,138 +80,25 @@ $ tail .cargo/config target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) ``` -3. Enter the memory region information into `memory.x`. - -As we mentioned before the STM32F303VCT6 has 40 KB of RAM located at address -`0x2000_0000` and 256 KB of Flash memory located at address `0x0800_0000`. +3. Enter the memory region information into the `memory.x` file. ``` console $ cat memory.x -``` - -``` text +/* Linker script for the STM32F303VCT6 */ MEMORY { - /* NOTE K = KiBi = 1024 bytes */ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ FLASH : ORIGIN = 0x08000000, LENGTH = 256K RAM : ORIGIN = 0x20000000, LENGTH = 40K } ``` -4. Build one of the examples: +4. Build the template application or one of the examples. ``` console -$ cargo build --example hello +$ cargo build ``` -You are done! You should be able to flash and run this example on your -device. - -#### Debugging - -Teaching you how to flash and debug this program on *your* device is out of -scope for this document due to the sheer variety of possible hardware / software -combinations. But the steps required to flash and debug this program on the -[STM32F3DISCOVERY] using OpenOCD are provided below as a reference. - -[STM32F3DISCOVERY]: https://www.st.com/en/evaluation-tools/stm32f3discovery.html - -On a terminal run `openocd` to connect to the ST-LINK on the discovery board. -Run this command from the root of the template; `openocd` will pick up the -`openocd.cfg` file which indicates which interface file and target file to use. - -``` console -$ cat openocd.cfg -``` - -``` text -source [find interface/stlink-v2-1.cfg] -source [find target/stm32f3x.cfg] -``` - -``` console -$ openocd -Open On-Chip Debugger 0.10.0 -Licensed under GNU GPL v2 -For bug reports, read - http://openocd.org/doc/doxygen/bugs.html -Info : auto-selecting first available session transport "hla_swd". To override use 'transport select '. -adapter speed: 1000 kHz -adapter_nsrst_delay: 100 -Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD -none separate -Info : Unable to match requested speed 1000 kHz, using 950 kHz -Info : Unable to match requested speed 1000 kHz, using 950 kHz -Info : clock speed 950 kHz -Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B -Info : using stlink api v2 -Info : Target voltage: 2.913879 -Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints -``` - -On another terminal run GDB, also from the root of the template. - -``` console -$ cat openocd.gdb -``` - -``` text -target remote :3333 - -# print demangled symbols -set print asm-demangle on - -# detect unhandled exceptions and hard faults -break DefaultHandler -break UserHardFault - -monitor arm semihosting enable - -load -``` - -``` console -$ arm-none-eabi-gdb -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello -(..) -Loading section .vector_table, size 0x400 lma 0x8000000 -Loading section .text, size 0x21dc lma 0x8000400 -Loading section .rodata, size 0x6a4 lma 0x80025e0 -Start address 0x800238c, load size 11392 -Transfer rate: 17 KB/sec, 3797 bytes/write. - -(gdb) list -470 #[no_mangle] -471 pub static __RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset; -472 -473 #[doc(hidden)] -474 #[no_mangle] -475 pub unsafe extern "C" fn Reset() -> ! { -476 extern "C" { -477 // This symbol will be provided by the user via the `entry!` macro -478 fn main() -> !; -479 -``` - -The `openocd.gdb` script will connect GDB to OpenOCD and then flash the program -into the device. After that you can do a normal debugging session. - -If you `continue` the program past the semihosting write operation you'll see -"Hello, world" printed on the OpenOCD console. - -``` console -(gdb) continue -``` - -``` console -$ openocd -(..) -Hello, world! -``` - -## Next steps - -> TODO point the reader to embedded-hal, awesome-embedded-rust, etc. - # License Licensed under either of diff --git a/examples/allocator.rs b/examples/allocator.rs index 5e242c8..5594e3c 100644 --- a/examples/allocator.rs +++ b/examples/allocator.rs @@ -11,27 +11,19 @@ #![feature(alloc)] #![feature(alloc_error_handler)] -#![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; -#[macro_use] -extern crate cortex_m_rt as rt; -extern crate cortex_m_semihosting as sh; -extern crate panic_semihosting; +extern crate panic_halt; use core::alloc::Layout; use core::fmt::Write; +use alloc::vec; use alloc_cortex_m::CortexMHeap; use cortex_m::asm; -use rt::ExceptionFrame; -use sh::hio; +use cortex_m_rt::entry; +use cortex_m_semihosting::hio; // this is the allocator the application will use #[global_allocator] @@ -39,11 +31,10 @@ static ALLOCATOR: CortexMHeap = CortexMHeap::empty(); const HEAP_SIZE: usize = 1024; // in bytes -entry!(main); - +#[entry] fn main() -> ! { // Initialize the allocator BEFORE you use it - unsafe { ALLOCATOR.init(rt::heap_start() as usize, HEAP_SIZE) } + unsafe { ALLOCATOR.init(cortex_m_rt::heap_start() as usize, HEAP_SIZE) } // Growable array allocated on the heap let xs = vec![0, 1, 2]; @@ -56,21 +47,8 @@ fn main() -> ! { // define what happens in an Out Of Memory (OOM) condition #[alloc_error_handler] -#[no_mangle] -pub fn alloc_error(_layout: Layout) -> ! { +fn alloc_error(_layout: Layout) -> ! { 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 fafe436..fecde7a 100644 --- a/examples/crash.rs +++ b/examples/crash.rs @@ -79,36 +79,18 @@ #![no_main] #![no_std] -extern crate cortex_m; -#[macro_use] -extern crate cortex_m_rt as rt; -extern crate panic_semihosting; +extern crate panic_halt; use core::ptr; -use rt::ExceptionFrame; - -entry!(main); +use cortex_m_rt::entry; +#[entry] fn main() -> ! { unsafe { - // read an address outside of the RAM region; causes a HardFault exception + // read an address outside of the RAM region; this 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); -} diff --git a/examples/device.rs b/examples/device.rs index e2c1a04..26e547a 100644 --- a/examples/device.rs +++ b/examples/device.rs @@ -24,23 +24,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; -#[macro_use] -extern crate stm32f103xx; -extern crate panic_semihosting; +#[allow(unused_extern_crates)] +extern crate panic_halt; use core::fmt::Write; use cortex_m::peripheral::syst::SystClkSource; -use rt::ExceptionFrame; -use sh::hio::{self, HStdout}; -use stm32f103xx::Interrupt; - -entry!(main); +use cortex_m_rt::entry; +use cortex_m_semihosting::hio::{self, HStdout}; +use stm32f30x::{interrupt, Interrupt}; +#[entry] fn main() -> ! { let p = cortex_m::Peripherals::take().unwrap(); @@ -75,15 +69,3 @@ fn exti0(state: &mut Option) { 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/exception.rs b/examples/exception.rs index d62e6d4..6b9b103 100644 --- a/examples/exception.rs +++ b/examples/exception.rs @@ -1,8 +1,8 @@ //! Overriding an exception handler //! -//! You can override an exception handler using the [`exception!`][1] macro. +//! You can override an exception handler using the [`#[exception]`][1] attribute. //! -//! [1]: https://docs.rs/cortex-m-rt/0.5.0/cortex_m_rt/macro.exception.html +//! [1]: https://rust-embedded.github.io/cortex-m-rt/0.6.1/cortex_m_rt_macros/fn.exception.html //! //! --- @@ -10,21 +10,16 @@ #![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; +extern crate panic_halt; use core::fmt::Write; use cortex_m::peripheral::syst::SystClkSource; use cortex_m::Peripherals; -use rt::ExceptionFrame; -use sh::hio::{self, HStdout}; - -entry!(main); +use cortex_m_rt::{entry, exception}; +use cortex_m_semihosting::hio::{self, HStdout}; +#[entry] fn main() -> ! { let p = Peripherals::take().unwrap(); let mut syst = p.SYST; @@ -38,27 +33,15 @@ fn main() -> ! { loop {} } -// try commenting out this line: you'll end in `default_handler` instead of in `sys_tick` -exception!(SysTick, sys_tick, state: Option = None); +#[exception] +fn SysTick() { + static mut STDOUT: Option = None; -fn sys_tick(state: &mut Option) { - if state.is_none() { - *state = Some(hio::hstdout().unwrap()); + if STDOUT.is_none() { + *STDOUT = Some(hio::hstdout().unwrap()); } - if let Some(hstdout) = state.as_mut() { + if let Some(hstdout) = STDOUT.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 5537379..c7af29b 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -1,24 +1,22 @@ -//! Prints "Hello, world!" on the OpenOCD console using semihosting -//! -//! --- +//! Prints "Hello, world!" on the host console using semihosting #![no_main] #![no_std] -#[macro_use] -extern crate cortex_m_rt; -extern crate cortex_m_semihosting as sh; -extern crate panic_semihosting; +extern crate panic_halt; use core::fmt::Write; -use sh::hio; - -entry!(main); +use cortex_m_rt::entry; +use cortex_m_semihosting::{debug, hio}; +#[entry] fn main() -> ! { let mut stdout = hio::hstdout().unwrap(); writeln!(stdout, "Hello, world!").unwrap(); + // exit QEMU or the debugger section + debug::exit(debug::EXIT_SUCCESS); + loop {} } diff --git a/examples/itm.rs b/examples/itm.rs index 6fddea1..f82f985 100644 --- a/examples/itm.rs +++ b/examples/itm.rs @@ -17,38 +17,17 @@ #![no_main] #![no_std] -#[macro_use] -extern crate cortex_m; -#[macro_use] -extern crate cortex_m_rt as rt; -extern crate panic_semihosting; +extern crate panic_halt; -use cortex_m::{asm, Peripherals}; -use rt::ExceptionFrame; - -entry!(main); +use cortex_m::{iprintln, Peripherals}; +use cortex_m_rt::entry; +#[entry] 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); + loop {} } diff --git a/examples/minimal.rs b/examples/minimal.rs deleted file mode 100644 index e573932..0000000 --- a/examples/minimal.rs +++ /dev/null @@ -1,40 +0,0 @@ -//! 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 - -#![no_main] // <- IMPORTANT! -#![no_std] - -extern crate cortex_m; - -#[macro_use(entry)] -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; - -// the program entry point is ... -entry!(main); - -// ... this never ending function -fn main() -> ! { - loop { - asm::bkpt(); - } -} diff --git a/examples/panic.rs b/examples/panic.rs index 0822156..a323396 100644 --- a/examples/panic.rs +++ b/examples/panic.rs @@ -1,44 +1,28 @@ -//! Changing the panic handler +//! Changing the panicking behavior //! -//! The easiest way to change the panic handler is to use a different [panic handler crate][0]. +//! The easiest way to change the panicking behavior 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 panic handlers: -// Pick one of these two panic handlers: +// `panic!` halts execution; the panic message is ignored +extern crate panic_halt; // Reports panic messages to the host stderr using semihosting -extern crate panic_semihosting; +// NOTE to use this you need to uncomment the `panic-semihosting` dependency in Cargo.toml +// 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); +use cortex_m_rt::entry; +#[entry] 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); -} diff --git a/memory.x b/memory.x index ee7e8a8..ade393b 100644 --- a/memory.x +++ b/memory.x @@ -1,6 +1,6 @@ MEMORY { - /* NOTE K = KiBi = 1024 bytes */ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ /* TODO Adjust these memory regions to match your device memory layout */ /* These values correspond to the LM3S6965, one of the few devices QEMU can emulate */ FLASH : ORIGIN = 0x00000000, LENGTH = 256K @@ -19,6 +19,3 @@ MEMORY /* This is required only on microcontrollers that store some configuration right after the vector table */ /* _stext = ORIGIN(FLASH) + 0x400; */ - -/* Size of the heap (in bytes) */ -/* _heap_size = 1024; */ diff --git a/openocd.gdb b/openocd.gdb index bacba86..55cf8ac 100644 --- a/openocd.gdb +++ b/openocd.gdb @@ -3,10 +3,14 @@ target remote :3333 # print demangled symbols set print asm-demangle on -# detect unhandled exceptions and hard faults +# detect unhandled exceptions, hard faults and panics break DefaultHandler break UserHardFault +break rust_begin_unwind monitor arm semihosting enable -load \ No newline at end of file +load + +# start the process but immediately halt the processor +stepi \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 23646c3..3142280 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,17 @@ -#![no_main] #![no_std] +#![no_main] -extern crate cortex_m; -#[macro_use] -extern crate cortex_m_rt; - -// TODO pick a panicking behavior +// pick a panicking behavior +extern crate panic_halt; // you can put a breakpoint on `rust_begin_unwind` to catch panics // extern crate panic_abort; // requires nightly -// extern crate panic_itm; // requires ITM support -// extern crate panic_semihosting; // requires a debugger +// extern crate panic_itm; // logs messages over ITM; requires ITM support +// extern crate panic_semihosting; // logs messages to the host stderr; requires a debugger -entry!(main); +use cortex_m_rt::entry; +#[entry] fn main() -> ! { loop { - // TODO your code goes here + // your code goes here } }