When it comes to debugging embedded code, we actually have a lot of options (contrary to popular belief)!
- SWV protocol through SWO line, data tracing (printf-style debugging). Very minimal time/memory required
- Stepping: single stepping, stepping over, stepping out - walk through the code step by step, checking that the result at each point matches with the expected result
- Hardware breakpoints: triggers for stopping the processor
- Disassembly: convert your high level C code to assembly for the target to more closely inspect
- Call stack: ?
- Expression, variable windows: write expressions to evaluate values and addresses of variables and functions
- Memory browser: examine memory of your MCU, e.g. flash, SRAM (kind of like an unceremonious data dump)
- Data watch-points: also halt processor when some data condition is met?
Disassembly
Window > Show View > Disassembly
Window > Show View > Registers
To show opcodes (what?) we can write click on the address in the left address column. We can also view the values stored in the chip’s registers!
Steps Step into: “step into” the function; this could be a C function or an assembly subroutine. We can execute our code statements line by line. A single line of C code could be multiple lines of assembly instructions (disassembly). Stepping into a for loop may mean waiting for a long time! Step over: evaluate a function in its entirety in one go; for simple functions that contain lots of repetitive computations, we may wish to “step over” its processing. This is only useful when you’re on a line with a function call and have not “stepped in” into it yet. Step return: done with the parts you wanted to debug in a function, but don’t want to walk through the rest of the thing? Simply “step return” out of it!
**Useful Buttons
- Reset chip and restart debug session
- Instruction stepping mode
Breakpoints
Window > Show View > Breakpoints
Run > Toggle Breakpoint
Alternatively: click on the left side of a code line to set a breakpoint
Just like it says on the tin, breakpoints are what we use to halt execution at a certain instruction address. Breakpoints are realised through hardware address comparators inside the processor debug unit; hence, the target's CPU must also support breakpoints. The address comparator is like a “counter” that keeps track of the address of the current instruction during runtime. When the counter’s address matches the address of an instruction where a breakpoint was placed, the processor stops. Note that due to hardware limitations, we may be limited to a certain number of breakpoints (e.g. a processor may support only 8 comparators, so we can only have 8 breakpoints simultaneously).
As they rely on the hardware unit of the processor, those are referred to as hardware breakpoints. QUESTION: What is the difference between software, hardware breakpoints?
We can use an IDE to set, delete, and skip breakpoints as desired.
Expression and variable windows (use with breakpoint)
Window > Show View > Variables or mouse hover over a variable during runtime
Window > Show View > Expressions, then drag a variable to the window
We might wonder what the value of a variable is during our debugging process. Maybe we’re used to printing everything to the console, but what if we didn’t need that? The Variables window lets us see the value of variables in the stack frame of the current function (so not other functions!). We can also create small expressions (e.g. arithmetic, dereferencing a pointer) for those variables.
Memory peeking
Window > Show View > Memory Browser
If there are variables we need to see which are outside of our current function’s stack frame, we can drag the variables to our Expression window instead. Then, using the memory browser, type in any memory address - flash, ROM, RAM memory address - to view.
Call stack and memory analysers
Window > Show View > Fault Analyser
Left window is stack frame analysis, which you can walk through one by one.
Data watchpoints
Window > Show View > Breakpoints > ... > Watchpoints
We may wonder as to which part in our code is changing a variable. Maybe a global variable is being changed (corrupted?) without our knowledge or consent, either by someone else or by the design of the stack!
We can add either the name or address of variables. Press resume to walk through the code. The debugger will pause at points where it thinks the variable is being manipulated.
Special Function Registers (SFRs)
Window > Show Views > SFRS
(not Registers as that is processor registers, not microcontroller registers!)