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:
| JTAG | SWD | |
|---|---|---|
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
| Ebene | Werkzeug |
|---|---|
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) continuepyOCD 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.
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"
)
ENDDODieses 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
| Kriterium | Entscheidung |
|---|---|
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
| Aspekt | Kernaussage |
|---|---|
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 |