The Debug Toolbox
Embedded debugging goes wrong in a particular way. You have a symptom ā the board hangs after eleven minutes, the sensor returns zeros every hundredth read, the current draw is four times what the datasheet promises ā and you reach for the instrument you are most comfortable with rather than the one that can answer the question. Then you measure the wrong thing very carefully for a day.
SWD, JTAG, and GDB
The thing that surprises people about on-chip debugging is how little of it is software. GDB does not single-step your firmware; the processor single-steps it, because Armv7-M defines a halting-debug mode with its own registers, comparator hardware for breakpoints, and comparator hardware for watchpoints. GDB is a client. OpenOCD is a translator. The debug capability is silicon that was on the die before you wrote anything, and it is finite in a way that software breakpoints on a hosted system are not.
Logging Without Breaking Timing
Here is the observation this page exists to explain. You have a fault that happens once every few minutes ā a corrupted buffer, a missed deadline, a state machine that ends up somewhere it cannot reach. You add a printf to narrow it down. The fault stops happening. You remove the printf and it comes back.
HardFault Forensics
A HardFault is not an error message. It is the processor announcing that it has already stopped being able to run your program, and that everything it knows about why is sitting in four registers and one stack frame. Nothing is printed, nothing is logged, and the default handler in every startup file ever shipped is b . ā an infinite loop that discards all of it.
Postmortem Debugging
Every technique in HardFault Forensics assumes something that is only true on your bench: a debugger attached at the moment of the fault, watching CFSR before anything clears it, holding the stack frame before the stack is reused. A device in the field has none of that. It faults, and unless you decided in advance what to do about it, the only evidence is a b . loop nobody is watching, or a reset that erases everything and starts the firmware running again as if nothing happened.
Logic Analyzer Workflows
"The bus doesn't work" is not a bug report a logic analyzer can answer by itself, and the gap between clipping on four wires and actually learning something is where most of a session goes. Lab Equipment and What It Answers makes the case for reaching for the analyzer first and works one I²C bug through it end to end; this page assumes you have already made that choice and is about running the capture well enough that the answer it gives you is true.
The Oscilloscope for Firmware Engineers
A logic analyzer and an oscilloscope look like they answer the same question ā "what happened on this wire" ā and firmware engineers who own one tend to reach for it for everything, because a protocol decode is easier to read than a wobbly trace. They are not the same instrument. A logic analyzer's front end does one thing: at every sample instant, compare the voltage to a threshold and emit a 0 or a 1. Everything about how the signal got to that voltage ā how fast, how far past it, whether it wobbled on the way ā is discarded before the decoder ever sees a bit. An oscilloscope keeps that information. It is not a better logic analyzer; it answers a different class of question, and it is the only instrument in this folder that can.
Instruction and Event Tracing
There is a category of bug that a breakpoint destroys by looking at it, and a log line destroys almost as thoroughly. A race between two interrupts, an occasional priority inversion, a state machine that takes a wrong branch once in ten thousand iterations ā halting the core to inspect any of these changes exactly the timing that produced them, and a printf line inserted to catch them costs enough cycles to close the window it was supposed to observe. The Debug Toolbox's perturbation table already ranks every instrument in this folder by how much it disturbs the thing it is measuring; trace is the answer at the bottom of that table, the one built specifically to be close to zero.
Unit Testing Firmware
The ordinary embedded development loop is: change a line, build, flash, power-cycle or reset, and watch a UART or an LED to see whether the change did what you meant. On a fast board with a fast probe that loop is a few seconds; on a slow one, or one shared with a bench full of other work, it is tens of seconds, and every one of those seconds is spent re-verifying code paths that had nothing to do with the line you just changed. A state machine with a dozen transitions and a handful of edge cases does not get thoroughly exercised at that cadence ā it gets exercised until the one case someone thought to try passes, and the rest ride along untested until a customer finds them.
Mocking Hardware
Unit Testing Firmware drew a clean line around the logic that needs nothing from the hardware at all ā a parser, a state machine, a checksum ā and pointed at this page for the harder case: logic whose whole job is to talk to a peripheral. A driver cannot be tested by giving it inputs and checking outputs the way a parser can, because its inputs and outputs are register writes and register reads, and there is no register to read from on a laptop. Testing it on the host requires something on the other end of the bus that behaves like the real device without being it.
Static Analysis and Sanitizers
There is a tool that finds real defects, requires no new install, no configuration file, and no CI integration beyond a flag already accepted by the compiler you are already invoking on every build ā and most projects run it in a mode that throws its findings away. That tool is the compiler itself. arm-none-eabi-gcc already builds a fully type-checked internal representation of your program to generate code from; a warning is analysis it already did, offered back to you, and the default build configuration in a great many embedded projects is -Wall at best, or nothing at all, which means the cheapest static analysis available is routinely left switched off.
Simulation and Emulation
Every technique in the rest of this folder needs a board on the desk, or at minimum a physical binary running somewhere real. Unit Testing Firmware and Mocking Hardware sidestep that by pulling logic out from behind a seam and running it on the host ā a deliberate, narrow substitute for one driver's register interface. Simulation is a different move entirely: instead of extracting the logic, it models the chip, and runs the actual, unmodified cross-compiled firmware.elf against that model ā the same instruction stream, the same startup code, the same register pokes, executing against a virtual STM32 instead of a real one.