As your embedded applications get more complicated an On-Chip Debugger will save you a lot of time when things don’t run quite right. On-Chip Debugging (OCD) is just what it sounds like — a way to run your program on the target chip that lets you pause execution to examine values and change them if need be. The Arduino has no built-in method of using OCD, but the AVR chips used by the boards do. The caveat is that you need a proper AVR programmer to access the Debug Wire protocol, or a JTAG interface for some of the larger chips. In this case I’m going to be using an STM32 Discovery Board to give you an overview of OCD. But this will work the same way for any chip that has hardware debugging capabilities. Many IDE’s have debugging support built right in so that you can use a nice GUI as you work. But often these are just a front end for the command line tools I’ll be using. Join me after the break and we’ll get started.
Why use On-Chip Debugging?
Recently I was programming a game of Snake on an ARM chip. I had it working and could play for a few minutes but then things would go very wrong. It was most likely a bug in the code, but it could have been a memory problem cause by the startup or linker scripts, a typo, or a bunch of other things. Using OCD I was able to locate and fix the bug in under five minutes. As with software debugging all it took was to put a break point at a part of the program that was my best guess of the problem. This narrowed things down immediately and all I had to do was scan about twenty lines of code to pinpoint my mistake.
In order to do this type of debugging you need three things: a program that controls the chip’s debugging mode, a way to interface with it, and a program which has been compiled with debugging information.
The interface part is easy, we’ll be using GDB (GNU Debugger). In almost every case this program is included in the same toolchain as the GCC compiler. Since GDB is used in almost universal circumstances it’s well worth your time to learn how to use. If you’re a software person it’s likely you already have.
In my case I’m using OpenOCD to control my ARM processor’s debugging features. It provides a port through which GDB connects. The platform supports a huge range of chips so a little searching should lead you to the commands/config files necessary to talk to different chips. In my case the STM32-Discovery F0 board has support built-in.
To compile with debugging information simply add the ‘-g’ compiler flag when using GCC. For me this merely affects the .elf file so I always include this flag in my makefile.
Getting Everything Running
Prerequisites: I won’t cover installing a toolchain or OpenOCD. I did some work putting together a template for developing STM32 F0 projects on Linux. There are instructions in the readme that detail acquiring a toolchain and compiling OpenOCD with ST-LINK support.
Critical Prerequisite: Whenever debugging you need to make sure you have compiled your program using the ‘-g’ flag. This rolls the information GDB needs to associate your C code with the machine code running on the chip.
With the debugging hardware connected (in the case of a Discovery board just plug it into a USB port) I start OpenOCD, specifying the board specific configuration to use. OpenOCD comes with a lot of board files. It’s going to be very rare for you to be the first one debugging any type of hardware so a ready-made configuration file should be available. Here’s the command I use for the Discovery board:
openocd -f /usr/share/openocd/scripts/board/stm32f0discovery.cfg
OpenOCD should now be running, time to launch GDB.
Launch GDB and connect to OpenOCD
When launching GDB it’s a good idea to tell it which debugging file to use. This will be the ‘.elf’ file generated when you compiled code with the ‘-g’ flag. It is very important that you do not use the simple command ‘gdb’. This will indeed launch the GNU Debugger, but not the one necessary for ARM debugging. For that we need to use the command for the cross compiled toolchain used to compile the ARM code:
Once GDB starts up we need to tell it to connect to our target chip. This is done with a couple of keywords, and a port on which OpenOCD has been listening:
target remote :3333
Now we are connected and can use the GDB commands to start debugging.
Theoretically you can make changes to your program and flash it onto the chip while debugging. The software tools I have don’t offer that capability for this hardware (this is a newer chipset and it’s been a long time since I compiled OpenOCD so this may have changed). Here’s some general commands which should work with most chips:
- file – tell GDB which file to use for debugging (I passed this as an argument when launching GDB)
- load – flash the binary onto the chip (doesn’t work for me with the STM32F0Discovery board)
- monitor – send special commands to the server (OpenOCD)
I use the monitor command to control the chip. To reset the chip (after setting break points, etc) the command ‘monitor reset halt’ tells OpenOCD to reset the ARM processor but do not start program execution yet.
The basic commands you will use while debugging include:
- continue – start program execution and don’t stop unless a breakpoint/watchpoint/etc. is reached
- break – add a breakpoint. This can be specified as the name of a function, or a line of code. When execution reaches this point the processor will be halted and you can check on your data
- print – prints the value of a variable or memory address. Use this to see what’s up with your code
- display – cause the ‘print’ command to be executed on a specified variable every time a break point is reached
There are just way too many commands to outline here. The majority of GDB commands are platform-agnostic, so searching for tricks and tips should be pretty simple. I find the best tool is this GDB Refcard (PDF). I’ll let you take a look at it yourself, but one important thing to note is the difference between the ‘next’ and ‘step’ commands. Next will let the processor run until the next line of code is reached, honoring function calls if they occur. Step will do the same but it will not follow any function calls.
The best bet is to watch the demo video, then give debugging a try for yourself. Help is just a Google search away, and the tricks of the trade are easy to learn. Good luck!