The Cortex-M4 processor from ARM includes several debug registers that allow developers to debug and trace code execution. These registers give visibility into the processor’s internal state during debugging sessions. Understanding how to properly utilize the debug registers is key to effectively debugging Cortex-M4-based systems.
Overview of Cortex-M4 Debug Architecture
The Cortex-M4 debug architecture is based on the ARM CoreSight technology. It provides real-time access to the processor’s internal state via the Debug Access Port (DAP). The DAP uses a JTAG or SWD interface to communicate with the debug probe. This allows tools like GDB or commercial IDEs to control code execution and inspect registers/memory during debugging.
In addition to the DAP interface, the Cortex-M4 includes several important debug components:
- Debug Control and Configuration Registers – Manage the debug logic
- Breakpoint Unit – Set code breakpoints
- Watchpoint Unit – Set data watchpoints
- Instrumentation Trace Macrocell (ITM) – Logs printf and other trace data
- Embedded Trace Macrocell (ETM) – Traces program flow in real-time (optional)
The debugger interacts with these components through the DAP interface. This provides all the visibility tools need to debug code running on the Cortex-M4 processor.
Debug Control and Configuration Registers
The Debug Control and Configuration Registers manage the overall debug logic built into the Cortex-M4. These registers control features like halting debug events, monitor exceptions, and debug state entry/exit. The registers include:
- DHCSR – Debug Halting Control and Status Register
- DCRSR – Debug Core Register Selector Register
- DCRDR – Debug Core Register Data Register
- DEMCR – Debug Exception and Monitor Control Register
The DHCSR register is one of the main controls over the debug logic. It contains bits like C_DEBUGEN to enable debug, C_HALT to halt the core, and S_REGRDY to indicate the register bank is ready. The debugger writes to this register to start a debug session and halt the core.
The DCRSR register selects which core register to read/write via the DCRDR data register. For example, writing 0x4 to DCRSR selects R4 as the current register, then DCRDR can read/write the value in R4. This provides access to the general purpose registers during a debug halt.
The DEMCR register configures options like enabling the trace unit and generating debug monitor exceptions. Monitor exceptions force a debug halt when enabled debug events occur like BKPT instructions or watchpoint matches.
Breakpoint Unit Registers
The breakpoint unit handles hardware breakpoints inserted into program code. Up to 16 breakpoints are supported concurrently. The breakpoint registers include:
- FP_CTRL – Flash Patch Control Register
- FP_COMPx – Flash Patch Comparator Registers
- BP_CTRL – Breakpoint Control Register
- BP_COMPx – Breakpoint Comparator Registers
The FP_CTRL register manages breakpoints in flash memory. Flash breakpoints use comparators that check instruction addresses fetched from flash. If an address matches, a breakpoint is triggered.
The FP_COMPx registers define the comparator values for flash breakpoints. Up to 4 flash breakpoints are available. The BP_CTRL and BP_COMPx registers are similar, but define breakpoints in RAM instead of flash.
To set a breakpoint, the debugger writes the address to a BP_COMPx or FP_COMPx register. It then enables that breakpoint in the control register. When a match occurs between a comparator and the program counter, the Cortex-M4 halts execution and enters debug state.
Watchpoint Unit Registers
Watchpoints monitor data accesses, rather than instruction fetches like breakpoints. The Cortex-M4 watchpoint unit has 4 comparators to match addresses and/or data values during load/store operations. The watchpoint registers include:
- DWT_CTRL – Control register for the Data Watchpoint and Trace unit
- DWT_COMPx – Watchpoint comparators
- DWT_MASKx – Watchpoint masks
To define a watchpoint, the debugger writes an address to DWT_COMPx and enables that comparator in DWT_CTRL. When the Cortex-M4 performs a data access that matches the address, the watchpoint triggers.
Masks in DWT_MASKx add flexibility to watchpoints. The mask allows ignoring parts of the data address or value when evaluating watchpoint matches. For example, use a mask to trigger on accesses to a range of addresses instead of a single exact address.
Instrumentation Trace Macrocell (ITM)
The ITM module is used to transmit trace data from the Cortex-M4 to the debugger. Trace provides logging and profiling information to understand program flow and performance. The ITM registers include:
- ITM_TCR – ITM Trace Control Register
- ITM_TPR – ITM Trace Privilege Register
- ITM_TER – ITM Trace Enable Register
- ITM_STIMx – Stimulus Port Registers
The TER enables tracing on stimulus ports (up to 32). Stimulus ports are assigned to trace sources like printf() to identify the trace data. The TPR selects privilege levels allowed to generate trace data.
During execution, trace data is written to the STIMx registers. The ITM module packs the trace into packets and transmits it to the debugger via the DAP. This provides efficient streaming of trace without halting the processor.
Embedded Trace Macrocell (ETM)
The ETM module provides instruction trace capabilities. This traces program flow by recording executed branches to reconstruct exact execution paths and program counters. The ETM includes:
- ETM_CR – ETM Control Register
- ETM_TRACESRC – Defines tracing modes (address, data, timestamp, etc)
- ETM_TRACEINIT – Controls trace initialization
- ETM_TRACEID – Traces thread ID information
To start an ETM trace, the debugger configures the tracing mode in ETM_TRACESRC, initializes it via ETM_TRACEINIT, then enables the tracer in ETM_CR. The ETM will then reconstruct program flow, including pipelines and speculation, and output the trace packets to the debugger.
ETM trace provides significant visibility into how code executes. However, it also has a high performance impact. The tracer consumes additional cycles to record and transmit all branches. This detailed tracing is best suited for short captures rather than always-on tracing.
Using Debug Registers in Cortex-M4 Debugging
Properly utilizing the Cortex-M4 debug registers is key to taking advantage of the processor’s capabilities. Here are some examples of how debug registers enable effective debugging:
- Halting execution – The debugger writes DHCSR to enter debug state and halt the core. This pauses execution to inspect register/memory values.
- Reading registers – DCRSR and DCRDR give access to core registers like R0-R12 and special registers.
- Setting breakpoints – The debugger programs breakpoint registers to halt execution at specific code locations.
- Monitoring data accesses – Watchpoints halt the processor when data addresses and/or values match.
- Tracing – ITM and ETM send program trace to the debugger for profiling and reconstruction.
Proper configuration of the debug registers enables powerful visibility into program execution without impacting performance. Debug registers are controlled through the DAP interface, allowing debug tools to leverage these capabilities.
In summary, the Cortex-M4 debug registers provide extensive control over debugging and tracing. Mastering these registers is essential to unleash the full potential of Cortex-M4 debug capabilities.