Semihosting
Semihosting is a mechanism that lets embedded devices do I/O on the host and ismainly used to log messages to the host console. Semihosting requires a debugsession and pretty much nothing else (no extra wires!) so it's super convenientto use. The downside is that it's super slow: each write operation can takeseveral milliseconds depending on the hardware debugger (e.g. ST-Link) you use.
The cortex-m-semihosting
crate provides an API to do semihosting operationson Cortex-M devices. The program below is the semihosting version of "Hello,world!":
#![no_main]
#![no_std]
extern crate panic_halt;
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!" messagewithin the OpenOCD logs.
$ openocd
(..)
Hello, world!
(..)
You do need to enable semihosting in OpenOCD from GDB first:
(gdb) monitor arm semihosting enable
semihosting is enabled
QEMU understands semihosting operations so the above program will also work withqemu-system-arm
without having to start a debug session. Note that you'llneed to pass the -semihosting-config
flag to QEMU to enable semihostingsupport; these flags are already included in the .cargo/config
file of thetemplate.
$ # 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 theQEMU process. Important: do not use debug::exit
on hardware; this functioncan corrupt your OpenOCD session and you will not be able to debug more programsuntil you restart it.
#![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 {}
}
$ cargo run
Running `qemu-system-arm (..)
$ echo $?
1
One last tip: you can set the panicking behavior to exit(EXIT_FAILURE)
. Thiswill 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 whenenabled invokes exit(EXIT_FAILURE)
after logging the panic message to the hoststderr.
#![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 {}
}
$ cargo run
Running `qemu-system-arm (..)
panicked at 'assertion failed: `(left == right)`
left: `"blue"`,
right: `"red"`', examples/hello.rs:15:5
$ echo $?
1
NOTE: To enable this feature on panic-semihosting
, edit yourCargo.toml
dependencies section where panic-semihosting
is specified with:
panic-semihosting = { version = "VERSION", features = ["exit"] }
where VERSION
is the version desired. For more information on dependenciesfeatures check the specifying dependencies
section of the Cargo book.