If you’re making (or disassembling) anything complicated, you might yearn for a better view inside. An oscilloscope or logic analyzer can be a vital tool for digital electronics, and also surprisingly useful for embedded software. Your code can help too, via messages logged to file or serial port. But sometimes you really need an interactive view of your program’s internals, and on an embedded system this means you need an in-circuit debugger (ICD).
Some of you already know about source-level debugging from other contexts, like debugging desktop applications in Xcode or Visual Studio, using breakpoints and single-step functionality for scripts in a web browser’s console, or using a standalone debugger like the GNU Debugger, WinDbg, or LLDB. If you’re more used to Arduino, however, this kind of tool might be new to you.
Debugger generally refers to a system made up of debugging software on your PC, possibly software on the chip you’re testing, and usually some hardware both inside and outside the chip. Many microcontrollers have an on-chip debugger (OCD) baked into the silicon.
A debugger can step through code one line or instruction at a time while showing the contents of variables, and can view or edit memory. You can run your program at full speed until it hits a breakpoint — an intentional pause in your code — where it stops and the debugger resumes. With a debugger attached, you can also typically interact with a processor’s peripherals, load programs into flash memory, and read the flash contents (unless protections are in place to prevent this!).
What’s a Debug Server?
The debugger app on your PC keeps a detailed memory map for your code, but you also need some hardware — or software — on your microcontroller to provide for reading and writing memory, trapping breakpoints, and running instructions in a controlled way. One solution is a debug server.
The GNU Debugger (GDB) defines an especially simple server protocol for use over a serial port or network, including localhost. Implementations of this gdbserver exist for different operating systems. Traditionally this was a software-only component, but the protocol is now commonly used as a gateway to hardware devices or emulators.
Likewise, your IDE options now range far beyond a basic GDB command line, with tools like Eclipse, Visual Studio Code, and IDA Pro supporting the same GDB protocol.
Hardware Debug Ports
It would be convenient if the debug features built into silicon were directly compatible with the GDB protocol, but OCDs are optimized for minimum cost and impact on the overall processor design. Typically debug will be provided via the same port used for programming flash memory. Some chips use vendor-specific protocols, but two industry standards are worth knowing about: JTAG and SWD.
Whatever the protocol, some hardware is needed to convert back into USB. A wide range of adapters can be used as a debug server via the open source OpenOCD software, even hookups you may already have like Raspberry Pi GPIOs or an FTDI serial breakout. And the Black Magic Probe (see “Bug Squashers,” right) is an open hardware device that implements the debug server in firmware, providing a virtual serial port you attach directly to GDB.
JTAG: The original standard — It’s not a good name. Like JPEG, it doesn’t say what the standard does, just who designed it: the Joint Test Action Group. It was designed in the mid- to late 1980s and standardized in 1990, in response to complex circuit board assemblies too difficult to automatically test.
Electrically, the JTAG standard (IEEE 1149.1) is a series of shift registers that you can daisy-chain between devices. You might attach your debugger directly to a single device, or to a chain of devices within a single chip or across multiple chips. JTAG looks superficially like SPI, with a shared clock and one-way data input and output pins. But then you see a Test Mode Select (TMS) pin. Is it a chip-select? No. This is where JTAG starts to get especially low-level. It’s actually a bit pattern that drives a state machine specified by the standard, which chip makers build upon to create their own JTAG state machines.
The JTAG standard specifies states for selecting a device and reading its 32-bit ID code; and JTAG’s boundary scan protocol addresses pins on an IC for electrical testing of assembled PCBs. Beyond this, it gets device-specific: FPGAs and processors and memories each have their own protocols. This fragmentation mirrors the fragmentation you see in the whole embedded tools space!
With modern ARM processors, at least, the standards do us a favor. The ARM Debug Interface specification describes a standard JTAG Debug Port with a way to access memory, peripherals, and CPU state. From there, memory-mapped registers can modify breakpoints and control the CPU.
SWD: The newcomer — When ARM standardized a way of accessing memory and CPU debug features over JTAG, they also took the opportunity to develop a new alternative protocol: Serial Wire Debug (SWD), which uses a single bidirectional data pin and a modernized packet structure. The reduced pin count makes SWD ideal for smaller embedded processors like the popular ARM Cortex-M series, and it can share pins with JTAG when the processor supports both options.
That’s about all you need to know about SWD itself, if you only plan to wire up the debug port on a processor and run high-level tools like GDB. The details of configuring OpenOCD, GDB, and your specific debug adapter will differ by platform, so you’ll want to find the configuration file included with OpenOCD that most closely matches your situation as a starting point.
If you want to understand how debugging and even how processors work at a deeper level, the debug port is an excellent place to start digging around!