Summary
SWO (Serial Wire Output) provides a low-cost, single-pin trace channel for debug output via the ITM/TPIU. The hardware infrastructure exists in the codebase but is incomplete — the core trace peripherals are defined and the cortex::swo::emit() API works, but no initialization code connects them to the physical pin.
This issue covers enabling SWO on both the STM32H7 and STM32F4 families used in this project.
Current State
The following already exists (family-independent):
modules/cortex/include/cortex/swo.hpp — Public API (cortex::swo::emit() for ports 0–1)
modules/cortex/source/swo.cpp — Implementation (blocks on FIFO ready, writes ITM stimulus port)
modules/cortex/include/cortex/peripherals/InstructionTraceMacrocell.hpp — Full register maps for ITM, DWT, and TPIU, with extern instances linked in peripherals.cpp
modules/cortex/include/cortex/peripherals/DebugSystem.hpp — DEMCR register definition with enable_trace bit (TRCENA)
What's Missing
1. Family-specific DBGMCU register headers
No STM32 DBGMCU register header exists for either family. The key registers and fields differ:
STM32H7 (DBGMCU_CR at 0x5C001004):
| Bit |
Field |
Purpose |
| 20 |
TRACECLKEN |
Enable trace port clock |
| 21 |
D1DBGCKEN |
Enable D1 debug clock (trace runs in D1 domain) |
| 28 |
TRGOEN |
External trigger output (optional) |
Currently reads 0x00000007 — only debug-sleep/stop/standby bits are set.
STM32F4 (DBGMCU_CR at 0xE0042004):
| Bit |
Field |
Purpose |
| 5 |
TRACE_IOEN |
Enable trace port I/Os |
| 6:7 |
TRACE_MODE |
0b00=async, 0b01=async SWO/NRZ, 0b10=async SWO/Manchester, 0b11=parallel |
| 20 |
TRACECLKEN |
Enable trace clock (some F4 variants) |
Both are defined in their respective SVD files under modules/stm32/scripts/.
2. SWO pin configuration
The SWO pin is the same on both families:
- Pin: PB3, Alternate Function AF0 (TRACESWO)
- Mode: Alternate Function
- Output Type: Push-Pull
- Speed: Very High
- Pull: None
On the Nucleo-H753ZI, PB3 is also used as SPI1_SCL on the Arduino header (solder-bridge SB63/SB64 isolates it). On some STM32F4 packages PB3 may also serve as JTDO — board-specific handling may be needed.
3. System-level trace initialization sequence
A new cortex::initialize::trace() function that performs:
- Core: Set
DEMCR.TRCENA via DebugSystem::ExceptionMonitorControl.enable_trace
- Family-specific DBGMCU:
- H7:
DBGMCU_CR.TRACECLKEN = 1, D1DBGCKEN = 1
- F4:
DBGMCU_CR.TRACE_IOEN = 1, TRACE_MODE = 0b01 (async NRZ)
- GPIO: Configure PB3 as AF0 push-pull very high speed
- TPIU:
TPIU_ACPR.SWOSCALER = (trace_ref_clock / desired_baud) - 1
TPIU_SPPR.Protocol = AsyncNRZ (preferred over Manchester)
- DWT:
DWT_CTRL.CYCCNTENA = 1 (cycle counter)
DWT_CTRL.PCSAMPLENA = 1 (periodic PC sampling)
- ITM:
ITM_TCR.ITMENA = 1, SWOENA = 1, DWTENA = 1, TSENA = 1
ITM_TER = enable stimulus ports 0 and 1
- Unlock via
ITM_LAR = 0xC5ACCE55
4. Clock frequency plumbing
The TPIU SWO baud prescaler formula is the same for both:
SWOSCALER = (trace_ref_clock / desired_baud) - 1
| Family |
Trace ref clock |
Typical |
2 MHz SWO prescaler |
| H7 |
rcc_hclk6 |
200 MHz |
99 |
| F4 |
HCLK |
168 MHz |
83 |
The clock tree must be initialized before trace init, since the prescaler depends on the measured frequency.
Measured Live Register Values (from J-Link on H753)
| Register |
Value |
Key bits |
| DBGMCU_CR (H7) |
0x00000007 |
TRACECLKEN=0, TRGOEN=0 |
| DBGMCU_CR (F4) |
TBD |
TBD |
| ITM_TCR |
0x00000001 |
ITMENA=1, SWOENA=0, DWTENA=0, TSENA=0 |
| ITM_TER |
0x00000003 |
ports 0,1 enabled — but no output path |
| TPIU_ACPR |
0x00000000 |
prescaler=0 — not configured |
| TPIU_SPPR |
0x00000000 |
protocol=Manchester — fallback |
| DEMCR |
0x01000000 |
TRCENA=1 — core-level trace enabled |
| DWT_CTRL |
0x400003FF |
CYCCNTENA=1, cycle counter running |
| DWT_CYCCNT |
15742475 |
free-running |
The ITM and DWT are partially initialized (likely by debugger or reset defaults), but the system-level path to the pin is gated off.
Acceptance Criteria
Summary
SWO (Serial Wire Output) provides a low-cost, single-pin trace channel for debug output via the ITM/TPIU. The hardware infrastructure exists in the codebase but is incomplete — the core trace peripherals are defined and the
cortex::swo::emit()API works, but no initialization code connects them to the physical pin.This issue covers enabling SWO on both the STM32H7 and STM32F4 families used in this project.
Current State
The following already exists (family-independent):
modules/cortex/include/cortex/swo.hpp— Public API (cortex::swo::emit()for ports 0–1)modules/cortex/source/swo.cpp— Implementation (blocks on FIFO ready, writes ITM stimulus port)modules/cortex/include/cortex/peripherals/InstructionTraceMacrocell.hpp— Full register maps for ITM, DWT, and TPIU, withexterninstances linked inperipherals.cppmodules/cortex/include/cortex/peripherals/DebugSystem.hpp— DEMCR register definition withenable_tracebit (TRCENA)What's Missing
1. Family-specific DBGMCU register headers
No STM32 DBGMCU register header exists for either family. The key registers and fields differ:
STM32H7 (
DBGMCU_CRat0x5C001004):Currently reads
0x00000007— only debug-sleep/stop/standby bits are set.STM32F4 (
DBGMCU_CRat0xE0042004):Both are defined in their respective SVD files under
modules/stm32/scripts/.2. SWO pin configuration
The SWO pin is the same on both families:
On the Nucleo-H753ZI, PB3 is also used as SPI1_SCL on the Arduino header (solder-bridge SB63/SB64 isolates it). On some STM32F4 packages PB3 may also serve as JTDO — board-specific handling may be needed.
3. System-level trace initialization sequence
A new
cortex::initialize::trace()function that performs:DEMCR.TRCENAviaDebugSystem::ExceptionMonitorControl.enable_traceDBGMCU_CR.TRACECLKEN = 1,D1DBGCKEN = 1DBGMCU_CR.TRACE_IOEN = 1,TRACE_MODE = 0b01(async NRZ)TPIU_ACPR.SWOSCALER=(trace_ref_clock / desired_baud) - 1TPIU_SPPR.Protocol=AsyncNRZ(preferred over Manchester)DWT_CTRL.CYCCNTENA = 1(cycle counter)DWT_CTRL.PCSAMPLENA = 1(periodic PC sampling)ITM_TCR.ITMENA = 1,SWOENA = 1,DWTENA = 1,TSENA = 1ITM_TER= enable stimulus ports 0 and 1ITM_LAR = 0xC5ACCE554. Clock frequency plumbing
The TPIU SWO baud prescaler formula is the same for both:
The clock tree must be initialized before trace init, since the prescaler depends on the measured frequency.
Measured Live Register Values (from J-Link on H753)
The ITM and DWT are partially initialized (likely by debugger or reset defaults), but the system-level path to the pin is gated off.
Acceptance Criteria
cortex::initialize::trace()function handles both H7 and F4modules/stm32/include/stm32/cortex::system::main()in normal startup