Jeder Embedded-Entwickler nutzt es täglich, aber selten wird es systematisch erklärt: das Debug-Interface zwischen Hardware und Software. Dieser Post geht von der Hobbyisten-Werkzeugkette (ST-Link/J-Link
OpenOCD/GDB) bis zum professionellen Lauterbach-TRACE32-Debugger, wie er in der automotive/AUTOSAR-Entwicklung Industriestandard ist.

JTAG vs. SWD

Beide sind Debug-Zugriffsprotokolle auf den ARM-Debug-Port, unterscheiden sich aber deutlich in der Pin-Anzahl und im Anwendungsfall:

JTAGSWD

Pins

TDI, TDO, TCK, TMS (+ optional TRST) — mind. 4-5

SWDIO, SWCLK — nur 2

Multi-Device-Chains

Ja, mehrere Chips in einer Kette (Daisy-Chain)

Nein, ein Ziel pro Verbindung (aber SWD-Multi-Drop bei manchen Cortex-M-Varianten)

Verbreitung bei Cortex-M

Seltener, meist zusätzlich zu SWD vorhanden

Standard — die meisten Cortex-M-Boards brechen nur SWD-Pins heraus

Bei Cortex-M ist SWD in der Praxis fast immer die richtige Wahl: weniger Pins auf der Platine, gleicher Funktionsumfang für Single-Core-Debugging. JTAG wird vor allem dann relevant, wenn mehrere Chips in einer Kette hängen oder ein SoC explizit keinen SWD-Port anbietet — bei größeren automotive SoCs mit mehreren Cores/Domains ist das keine Seltenheit.

Debug-Probe-Hardware im Überblick

Debug-Probe-Landschaft: Hobbyist vs. Professionell
EbeneWerkzeug

Hobbyist/Prototyping

ST-Link (günstig, STM32-fokussiert), J-Link (SEGGER, breite Chip-Unterstützung, teurer), CMSIS-DAP (offener Standard, oft auf Eval-Boards integriert)

Professionell/Automotive

Lauterbach TRACE32 (PowerDebug/PowerTrace-Hardware) — deutlich verbreiteter im professionellen Umfeld als die Hobbyisten-Werkzeugketten, u. a. wegen Multicore-Debugging, Trace-Fähigkeiten und der PRACTICE-Skriptsprache

GDB-Server-Workflow als Einstieg

Der klassische Einstieg läuft über einen GDB-Server, der zwischen GDB und der Debug-Probe vermittelt:

# OpenOCD als GDB-Server starten (Beispiel: ST-Link + STM32F4)
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg

# In einem zweiten Terminal: GDB verbinden
arm-none-eabi-gdb firmware.elf
(gdb) target remote localhost:3333
(gdb) load
(gdb) break main
(gdb) continue

pyOCD ist eine Python-basierte Alternative zu OpenOCD mit besonders guter CMSIS-DAP-Unterstützung — funktional für den Einstieg vergleichbar, aber mit einfacherer Board-Konfiguration über eine eingebaute Chip-Datenbank statt manueller .cfg-Dateien.

Dieser Workflow reicht für die meisten Cortex-M-Projekte mit einem Core vollkommen aus.

Lauterbach TRACE32 im Detail

Architektur

TRACE32 besteht aus drei Teilen: der PowerDebug/PowerTrace-Hardware (das eigentliche Debug-Probe-Äquivalent, aber mit zusätzlichem Trace-Speicher), der PowerView-GUI und der PRACTICE-Skriptsprache, die beides steuert.

Debug-Session-Ablauf: Breakpoint bis Hard-Fault-Analyse

Multicore- und Multi-Chip-Debugging

Automotive-SoCs haben typischerweise mehrere Cores (z. B. ein Lockstep-Cortex-R-Paar für Safety-Funktionen plus ein Cortex-A-Cluster für Infotainment/High-Level-Software) — teils sogar über mehrere physische Chips verteilt (z. B. ein separater Security-Controller). TRACE32 kann alle Cores/Chips gleichzeitig anhalten (synchroner Stopp über alle Cores hinweg — wichtig, wenn ein Fehler durch das Zusammenspiel zweier Cores entsteht) und in einer einzigen PowerView-Instanz mit mehreren Fenstern gleichzeitig debuggen.

On-Chip-Trace (ETM/ITM)

Der entscheidende Unterschied zu einem reinen GDB-Setup: TRACE32 kann über ETM (Embedded Trace Macrocell) oder ITM (Instrumentation Trace Macrocell) eine lückenlose Ablaufverfolgung aufzeichnen — jeder ausgeführte Sprung, nicht nur der Zustand an einem Breakpoint. Ein GDB-only-Setup zeigt immer nur eine Momentaufnahme beim Anhalten; ETM/ITM zeigt den vollständigen Pfad dorthin, inklusive sporadischer Fehlerbedingungen, die sich nicht zuverlässig reproduzieren lassen.

Ein einfaches PRACTICE-Skript

PRACTICE (.cmm-Dateien) automatisiert wiederkehrende Debug-Abläufe:

; breakpoint_check.cmm — Breakpoint setzen, anhalten lassen, Register auswerten
Break.Set main.c\42
Go
WAIT !STATE.RUN()
PRINT "PC:  " R(PC)
PRINT "SP:  " R(SP)
PRINT "CFSR:" D.Long(0xE000ED28)
IF D.Long(0xE000ED28)!=0
(
  PRINT "Fault-Status ungleich Null — Hard-Fault-Analyse nötig"
)
ENDDO

Dieses Skript setzt einen Breakpoint, lässt die Ausführung laufen, wartet auf den Stopp und wertet automatisch das Fault-Status-Register (CFSR) aus — nützlich für automatisierte Regressionsläufe, bei denen ein sporadischer Fehler über viele Durchläufe hinweg gesucht wird.

Wann OpenOCD/GDB genügt, wann sich TRACE32 lohnt

KriteriumEntscheidung

Single-Core, kleines Projekt

OpenOCD/GDB reicht

Mehrere Cores/Chips, die synchron angehalten werden müssen

TRACE32 — GDB-Multi-Target-Setups sind hier deutlich fragiler

Sporadischer Fehler, der sich nicht reproduzieren lässt

TRACE32 mit ETM/ITM-Trace — GDB zeigt nur den Zustand beim Stopp, nicht den Weg dorthin

Budget

TRACE32-Hardware kostet ein Vielfaches eines ST-Link/J-Link — der Aufpreis lohnt sich erst ab dem oben genannten Bedarf

Praktisches Debugging: Breakpoints, Register, Hard-Fault-Analyse

Am Beispiel eines NULL-Pointer-Zugriffs, der einen Hard Fault auslöst — einmal mit GDB, einmal mit TRACE32/PRACTICE:

# GDB: nach einem Hard Fault
(gdb) info registers
(gdb) x/8xw $sp        # Stack-Frame am Fault-Zeitpunkt inspizieren
(gdb) print/x *(uint32_t*)0xE000ED28   # CFSR lesen
; TRACE32/PRACTICE: äquivalent
PRINT R(PC),R(LR),R(SP)
Data.dump SP++32
PRINT D.Long(0xE000ED28)

Der gestapelte Stack-Frame beim Hard Fault (R0-R3, R12, LR, PC, xPSR) verrät, an welcher Instruktion der Fehler auftrat — der entscheidende Schritt danach ist die Einordnung:

Zeigt die gefaulte PC-Adresse auf eigenen Anwendungscode (z. B. ein Dereferenzieren eines uninitialisierten Zeigers), ist es typischerweise ein Software-Bug. Zeigt sie auf einen Peripherie-Register-Zugriff oder tritt der Fehler nur unter Last/bestimmtem Timing auf, deutet das eher auf ein Hardware-/Timing-Problem hin — hier greift die allgemeine Bring-up-Methodik (systematische Eingrenzung vom Multimeter zum Protokoll-Analyzer).

Zusammenfassung

AspektKernaussage

JTAG vs. SWD

SWD ist bei Cortex-M fast immer die richtige Wahl — weniger Pins, gleicher Funktionsumfang für Single-Core

Hobbyist-Werkzeugkette

ST-Link/J-Link/CMSIS-DAP + OpenOCD/pyOCD genügt für Single-Core-Projekte vollkommen

TRACE32

Lohnt sich bei Multicore/Multi-Chip-Debugging, sporadischen Fehlern (ETM/ITM-Trace) und automatisierter Analyse per PRACTICE

Hard-Fault-Analyse

Stack-Frame am Fault-Zeitpunkt auswerten, dann einordnen: Anwendungscode (Software) oder Peripherie/Timing (Hardware) — beide Debugger liefern dieselben Rohdaten, nur mit unterschiedlichem Komfort