diff --git a/src/SUMMARY.md b/src/SUMMARY.md index db6fce2..13b51d5 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -22,6 +22,7 @@ more information and coordination - [QEMU](./start/qemu.md) - [Hardware](./start/hardware.md) - [Memory-mapped Registers](./start/registers.md) + - [Semihosting](./start/semihosting.md) - [Panicking](./start/panicking.md) - [Exceptions](./start/exceptions.md) - [IO](./start/io.md) diff --git a/src/start/semihosting.md b/src/start/semihosting.md new file mode 100644 index 0000000..6ebf25e --- /dev/null +++ b/src/start/semihosting.md @@ -0,0 +1,128 @@ +# Semihosting + +Semihosting is a mechanism that lets embedded devices do I/O on the host and is +mainly used to log messages to the host console. Semihosting requires a debug +session and pretty much nothing else (no extra wires!) so it's super convenient +to use. The downside is that it's super slow: each write operation can take +several milliseconds depending on the hardware debugger (e.g. ST-Link) you use. + +The [`cortex-m-semihosting`] crate provides an API to do semihosting operations +on Cortex-M devices. The program below is the semihosting version of "Hello, +world!": + +[`cortex-m-semihosting`]: https://crates.io/crates/cortex-m-semihosting + +``` rust +#![no_main] +#![no_std] + +extern crate panic_halt; + +use core::fmt::Write; + +use cortex_m_rt::entry; +use cortex_m_semihosting::hprintln; + +#[entry] +fn main() -> ! { + hprintln!("Hello, world!").unwrap(); + + loop {} +} +``` + +If you run this program on hardware you'll see the "Hello, world!" message +within the OpenOCD logs. + +``` console +$ openocd +(..) +Hello, world! +(..) +``` + +QEMU understands semihosting operations so the above program will also work with +`qemu-system-arm` without having to start a debug session. Note that you'll +need to pass the `-semihosting-config` flag to QEMU to enable semihosting +support; these flags are already included in the `.cargo/config` file of the +template. + +``` console +$ # this program will block the terminal +$ cargo run + Running `qemu-system-arm (..) +Hello, world! +``` + +There's also an `exit` semihosting operation that can be used to terminate the +QEMU process. Important: do **not** use `debug::exit` on hardware; this function +can corrupt your OpenOCD session and you will not be able to debug more programs +until you restart it. + +``` rust +#![no_main] +#![no_std] + +extern crate panic_halt; + +use cortex_m_rt::entry; +use cortex_m_semihosting::debug; + +#[entry] +fn main() -> ! { + let roses = "blue"; + + if roses == "red" { + debug::exit(debug::EXIT_SUCCESS); + } else { + debug::exit(debug::EXIT_FAILURE); + } + + loop {} +} +``` + +``` console +$ cargo run + Running `qemu-system-arm (..) + +$ echo $? +1 +``` + +One last tip: you can set the panicking behavior to `exit(EXIT_FAILURE)`. This +will let you write `no_std` run-pass tests that you can run on QEMU. + +For convenience, the `panic-semihosting` crate has an "exit" feature that when +enabled invokes `exit(EXIT_FAILURE)` after logging the panic message to the host +stderr. + +``` rust +#![no_main] +#![no_std] + +extern crate panic_semihosting; // features = ["exit"] + +use cortex_m_rt::entry; +use cortex_m_semihosting::debug; + +#[entry] +fn main() -> ! { + let roses = "blue"; + + assert_eq!(roses, "red"); + + loop {} +} +``` + +``` console +$ cargo run + Running `qemu-system-arm (..) +panicked at 'assertion failed: `(left == right)` + left: `"blue"`, + right: `"red"`', examples/hello.rs:15:5 + +$ echo $? +1 +```