In general the debug options for ARM CORTEX CPUs are confusing to the newcomer. The embedded world expects everyone to already be an expert, with the end result that you’ve got to be living in it for a fairly significant length of time before the fog finally starts to clear.
I’m assuming that anyone reading this stuff has already got their head around I/O bits and serial ports, so lets concentrate on SemiHosting as our first entry into this wonderful world. This is obviously just an intro, you should look at the ARM documents when you want the real lowdown. I should state upfront that I don’t generally use SemiHosting, I find other techniques more suitable, but this should give you enough of a foothold to start using it if it looks like it floats your boat.
SemiHosting has been around since the 1990s. It allows the application running on your Target (embedded CPU) to access the Input and Output capabilities of the Host that is connected over the debug link. It does this by ‘tunneling’ the I/O requests over that link for various file descriptors. You’ll recall that file descriptors 0 and 1 are stdin and stdout in the Unix world, so one of the things you get with SemiHosting in addition to file access is remote screen and keyboard for your target application. Bargain.
It’s important to be aware that when an app is compiled with SemiHosting it will not work without the debugger connected. This is a big restriction. It also switches the CPU into Debug mode while it’s active, where it doesn’t play nicely with interrupts and stuff. Let’s be honest, SemiHosting is really useful for testing routines that take chunks of data in or throw chunks of data out because that’s where the file handling bit comes in. It’s not great for realtime oriented stuff either because it’s not a particularly fast technique. Its big advantages are that its properly bidirectional and it integrates cleanly, with no (or very little) glue with the filesystem on the host.
So, how does it work? Turns out the implementation is slightly different depending on if you’re on a ARMv6-M or ARMv7-M (M0 or M3/4 etc.) as distinct from any other ARM family CPU. In the former case the BKPT (Breakpoint) instruction is used, other ARM CPUs use SVC (Service) calls…that distinction doesn’t really matter though unless you’re stepping through machine code trying to figure out what’s going on….so lets stick with the CORTEX-M case.
When the application on the target wants to perform a SemiHosting call in regular code it performs a BKPT 0xAB instruction with the operation to be performed in R0, and parameters in other registers. A few examples of ARM-set standard actions are;
2 – SYS_CLOSE: You can figure this one out
3 – SYS_WRITEC: Write a single character
5 – SYS_WRITE: Write a block
6 – SYS_READ: ...and so it goes on
Obviously each of these calls needs parameters and returns results. The reference above gives you all the info you need on what those actually are…although in reality you mostly use libraries to realise a SemiHosting implementation so you don’t need this level of detail. One question I always had was why SemiHosting was implemented with BKPT/SVC and not just a library…well, if you think about it an exception-based calling routine will work anywhere with any language and from any processor state (pushing the CPU into a Debug state), so it’s much cleaner implementation than the alternatives that you might dream up.
So, we’ve reached the BPKT/SVC handler, and we’ve got our marching orders in the various registers….how does this get conveyed to the connected debugger? That depends on the compiler and debugger you’re using, but let’s stay in a GCC/GDB world where everything is documented and transparent.
In that case the handler marshals everything and sends it over the GDB link. That’s all documented in the Remote Protocol section of the GDB manual, and specifically the File I/O Remote Protocol Extension. I’m not going to regurgitate all of that stuff here for the purposes of padding a blog, but suffice to say that requests from the target eventually pop up at the host end where GDB (or, if you’re using something like Segger or OpenOCD, initially the debug driver) handles it and returns the results back to the target.
OK, so that’s the mechanics, and you understand the limitations, so how to use it in the real world? Turns out it’s pretty straightforward, just add the magic incantation
to your linker options (replace that with
when you want to turn SemiHosting off). That will load up the BKPT/SVC handling routines and allow you to use printf/scanf and all the file handling stuff in your application. One thing that folks do forget is an initialisation call that’s needed at the start of main (or leastways, before you do any SemiHostery) if you’re not running newlib;
You’ll probably need to switch on the semihosting options on your host side debug stub, and the MCUOnEclipse site has good info on doing that.
You don’t need to do anything extra if you’re running Blackmagic probe…one of it’s big advantages is that it’s all handled natively.
So, there you have it. Zero to SemiHosting-competent in ten minutes, but if you can cope with an output-only channel though there are better, faster, more flexible options. More to follow.