Since I have started to hold seminars and hands-on to newbie developers and engineering students, one of the most controversial things people don’t get used to is the debugging. Many of them often confuse between a bootloader and an on-chip debugger because both are used to test the firmware functional behavior.
Bootloader and on-chip debugger
A bootloader is a piece of software that loads the downloads the firmware from a host, usually a PC, into the microcontroller main memory. Typically, once the bootload procedure is over, the microcontroller starts to execute the freshly loaded code and the interaction between the host and the microcontroller terminates. The bootloader is a cheap solution often used when a debugger is not available and this was particularly true when embedded systems moved their first steps because of the high cost and low availability of commercial debuggers.
The task of the bootloader is basically to upload flash the code into the MCU memory and it does not offer any debug feature. In such a scenario, the functional test is often executed adding additional instructions not directly related to the user application with the scope to help the developer to understand the code behavior. For example, a user could decide to turn on a LED when a certain condition is verified to ensure that such condition happens. Because of this indirect correlation between the bootloader and the test of the functional behavior the bootloader if often confused with a debugger.
The on-chip debugger
An on-chip debugger is composed of software and hardware which as has the full capability to interact with a microcontroller at many levels acting as a monitor inside the microcontroller at run-time. The microcontroller which is probed by the debugger is often named as the target.
Let’s explore the capabilities of a debugger to understand how to exploit it.
The debugger can interact with the flash memory of a microcontroller allowing to:
- Flash\erase a firmware into it without using a bootloader and in a more efficient way
The debugger can interact directly with the CPU of a microcontroller and its registers. This allows to:
- Break the execution of the code at any moment (Stop\Resume)
- Break the execution of the code when the Program Counter reaches a certain address (Breakpoint)
- Execute the code on instruction at a time (Step by step execution)
- Change the content of CPU registers for debugging purpose
The debugger can interact directly with the RAM of a microcontroller allowing to:
- Break the execution of the code when a certain memory address changes its value (Watchpoint)
- Read\write static and automatic variables
- Read\write memory-mapped registers like, for example, MCU’s peripherals (TIMs, SPIs, ADCs, UARTs, and so on)
To do that actually the MCU should be designed to offer debugging features. This usually requires a specific piece of silicon inside the chip that is interfaced to the external world through a standard communication bus. For example, the ARM Cortex-M architecture specifies a debugging interface known as ADI a.k.a. ARM Debug Interface. Such a cell could be interfaced to a host (e.g. the PC) through a JTAG/SWD probe (e.g. the STLink or the Segger JLink or the Lauterbach PowerDebug)
Many IDE can be configured to integrate the debugging features. For example, Eclipse could be extended with a GNU Debugger plugin which provides an interface between the Eclipse engine and an external GDB server (e.g. OpenOCD). This allows placing breakpoints, executing the code step by step, exploring the memory, etc. acting on the Eclipse GUI.
The debugger requires an additional piece of hardware and some special configurations inside the IDE to become effective. Anyway, once it is configured, it could be a silver bullet to catch and solve any functional issue reducing the effort required by the development. It is actually almost mandatory when the development has to start from scratch for example while implementing the startup routines and the reset handler.
It worth to notice that actually almost every development kit based on ARM Cortex MCUs comes with a debugger. Plus, many IDEs are already preconfigured to provide fully debugging capabilities. For example, every development kit for the STM32 comes with the STLink debugger and there is plenty of IDE like ChibiStudio that are already configured to develop with this platform providing all the debugging features.
So if you come from Arduino and you are used to the bootloader and the LED-based debugging, it worth to spend some energy getting used to on-chip debuggers and run-time debugging.