14.1.1.4. De firmware debuggen¶
Debuggen op hardware betekent dat je de processor stilzet, breakpoints in de C-broncode plaatst, stap voor stap doorloopt en variabelen, geheugen, registers en randapparaten inspecteert – allemaal vanuit VS Code. Hiervoor zijn drie dingen nodig: een debug-build, een SWD-debugprobe (een Segger J-Link) en de Cortex-Debug-extensie die arm-none-eabi-gdb aanstuurt tegen een J-Link GDB-server.
14.1.1.4.1. Bouwen om te debuggen¶
Bouw het target altijd opnieuw met DEBUG=1
make -j$(nproc) TARGET=<TARGET> DEBUG=1
Een release-image (DEBUG=0) wordt gecompileerd met -O2; in de debugger zie je voor veel variabelen <optimized out>, geïnlinede functies worden samengevoegd met hun aanroepers en het stappen springt onvoorspelbaar rond. DEBUG=1 bouwt met -Og -ggdb3, wat debugbaar is en toch nog opstart op de camera. De ELF waar je de debugger op richt is:
build/<TARGET>/bin/firmware.elf
(Voor de Alif AE3 debug je build/OPENMV_AE3/bin/firmware_M55_HP.elf – de high-performance core.)
14.1.1.4.2. De hardware: J-Link via SWD¶
Sluit een Segger J-Link aan op de SWD-pinnen van de camera (SWDIO, SWCLK, GND en target VCC als referentie; de camera krijgt zoals gebruikelijk voeding via USB). Een J-Link EDU / Base / Pro werken allemaal. Waar de debug-pinnen naar buiten komen verschilt per camera – veel boards hebben een speciale JTAG/SWD-connector, andere stellen SWD beschikbaar op de I/O-header of op testpads – controleer dus het pinout-diagram en het schema van dat board in de OpenMV-hardwaredocumentatie om te zien welke pinnen je moet aansluiten. Installeer het J-Link Software and Documentation Pack van segger.com op de machine waarop de probe fysiek is aangesloten. Houd het redelijk actueel – oudere J-Link-software kent de nieuwere apparaatnamen niet (STM32N6, MIMXRT, Alif).
Elke MCU heeft zijn exacte J-Link-apparaatnaam nodig zodat de probe de juiste flashloader en geheugenmap laadt:
Camera ( |
MCU |
J-Link |
|---|---|---|
|
STM32F427 |
|
|
STM32F765 |
|
|
STM32H743 |
|
|
STM32N657 |
|
|
MIMXRT1062 |
|
|
Alif Ensemble (M55-HP) |
|
|
STM32H747 |
|
14.1.1.4.3. VS Code Cortex-Debug-instelling¶
Maak .vscode/launch.json aan in de repository. Het eenvoudigste geval – VS Code, de J-Link en de build draaien allemaal op dezelfde Linux-/macOS-machine – gebruikt servertype: "jlink", waardoor Cortex-Debug zelf een J-Link GDB-server start:
{
"version": "0.2.0",
"configurations": [
{
"name": "OpenMV J-Link",
"type": "cortex-debug",
"request": "launch",
"cwd": "${workspaceFolder}",
"executable": "${workspaceFolder}/build/OPENMV4/bin/firmware.elf",
"servertype": "jlink",
"device": "STM32H743VI",
"interface": "swd",
"runToEntryPoint": "main",
"armToolchainPath": "${env:HOME}/openmv-sdk-1.6.0/gcc/bin",
"gdbPath": "${env:HOME}/openmv-sdk-1.6.0/gcc/bin/arm-none-eabi-gdb"
}
]
}
Pas executable en device aan voor jouw board (zie de tabel hierboven). Druk op F5 om te bouwen, flashen en uitvoeren tot main en daar te stoppen.
Tip
Om automatisch opnieuw te bouwen elke keer dat je begint met debuggen, voeg je een buildtaak toe aan .vscode/tasks.json en verwijs je ernaar vanuit de launch-config met "preLaunchTask". Bijvoorbeeld een taak die make -j$(nproc) TARGET=OPENMV4 DEBUG=1 uitvoert, met de naam "build-firmware", plus "preLaunchTask": "build-firmware" in de configuratie hierboven, zodat F5 in één stap opnieuw bouwt, flasht en de debugger start.
Waarschuwing
Cortex-Debug heeft arm-none-eabi-gdb nodig. Dit wordt meegeleverd in de SDK op ~/openmv-sdk-<version>/gcc/bin maar staat standaard niet in PATH, dus debuggen mislukt met “GDB executable ‘arm-none-eabi-gdb’ was not found”. Los dit op door armToolchainPath / gdbPath in te stellen zoals hierboven getoond, of door ~/openmv-sdk-<version>/gcc/bin aan je PATH toe te voegen (printenv PATH zou het dan moeten vermelden).
14.1.1.4.4. Weergave van randapparaatregisters (SVD)¶
Richt Cortex-Debug op een CMSIS-SVD-bestand om een gedecodeerde weergave van randapparaatregisters te krijgen (timers, DMA, de camera-interface, enz.) op naam en bitveld:
"svdFile": "/path/to/STM32H743.svd"
Voor STM32 en MIMXRT haal je de SVD uit de ST-/NXP-CMSIS-packs of het Cortex-Debug SVD-register. De Alif-SVD’s zijn meegeleverd in de firmware-repo op lib/micropython/lib/alif_ensemble-cmsis-dfp/Debug/SVD/ (gebruik de ..._CM55_HP_View.svd voor de AE3 HP-core).
14.1.1.4.5. Windows: de WSL ↔ Windows J-Link-brug¶
WSL 2 kan het USB-apparaat van de J-Link niet rechtstreeks zien, dus de opdeling is: Windows bedient de probe (waar deze is aangesloten) en VS Code + gdb draaien in WSL en bereiken die via TCP.
Op Windows installeer je het Segger J-Link-pack en sluit je de J-Link aan op een Windows-USB-poort.
Op Windows start je de J-Link Remote Server (deze wordt meegeleverd met het J-Link-pack): start hem met de J-Link aangesloten en klik op OK. Sta hem toe via de Windows-firewall wanneer daarom gevraagd wordt. Het venster toont het IP-adres waarop de probe wordt bediend – noteer dat.
In WSL bouw je met
DEBUG=1en zorg je datarm-none-eabi-gdbbereikbaar is (stelarmToolchainPathin zoals hierboven).In WSL VS Code houd je
servertype: "jlink"aan – de GDB-server draait in WSL en bereikt de probe via de Remote Server – en voeg jeserverpath+ipAddresstoe:{ "name": "OpenMV J-Link (Windows host)", "type": "cortex-debug", "request": "launch", "cwd": "${workspaceFolder}", "executable": "${workspaceFolder}/build/OPENMV4/bin/firmware.elf", "servertype": "jlink", "serverpath": "/opt/SEGGER/JLink/JLinkGDBServer", "ipAddress": "192.168.x.x", "device": "STM32H743VI", "interface": "swd", "runToEntryPoint": "main", "armToolchainPath": "${env:HOME}/openmv-sdk-1.6.0/gcc/bin" }
Stel
ipAddressin op het adres dat het Remote Server-venster toont. Dat is de hele brug.
Tip
Alternatief voor de GDB-serverbrug: usbipd-win. In plaats van een server op Windows te draaien kun je het USB-apparaat van de J-Link rechtstreeks in WSL koppelen met usbipd-win. Vanuit een PowerShell met beheerdersrechten:
winget install usbipd
usbipd list
usbipd bind --busid <busid>
usbipd attach --wsl --busid <busid>
(<busid> is de bus-ID van de J-Link uit usbipd list.) De probe verschijnt dan binnen WSL, en je gebruikt de gewone same-machine servertype: "jlink"-configuratie uit VS Code Cortex-Debug-instelling zonder IP-adres en zonder aparte Windows-server. De GDB-serverbrug vereist minder installatie voor incidenteel gebruik; usbipd-win is handiger voor routinematige ontwikkeling.
Tip
Gebruik "request": "attach" om de firmware te debuggen terwijl deze al draait zonder hem te resetten of opnieuw te flashen – ideaal om een hang in het veld te betrappen. Gebruik "request": "launch" om te resetten, de ELF te flashen en vers te beginnen bij runToEntryPoint.
14.1.1.4.6. Debuggen vanaf de opdrachtregel met gdbrunner¶
Met de hand een GDB-sessie opzetten tegen een embedded target is een dans in vijf stappen: start de J-Link-/ST-Link GDB-server in één venster met de juiste apparaat-, poort- en interfacevlaggen; wacht tot deze Waiting for GDB connection afdrukt; voer arm-none-eabi-gdb uit in een tweede venster; typ target remote localhost:<port>; richt gdb op de ELF. Wanneer de gdb-sessie eindigt, vergeet dan niet het servervenster af te sluiten. gdbrunner is een kleine CLI die dat alles samenvoegt tot één foreground-opdracht. Het wordt meegeleverd in de Python-omgeving van de OpenMV SDK, dus er valt niets te installeren; het gebruikelijke startpunt is het make debug-target van de firmware-repository:
make -j$(nproc) TARGET=<TARGET> DEBUG=1 debug
Dit voert gdbrunner uit met de debugger-argumenten uit de configuratie van het board – de J-Link-apparaatnaam en, waar nodig, de externe ST-Link-flashloader – met de arm-none-eabi-gdb van de SDK al in PATH. De standaardbackend is J-Link; make DEBUGGER=STLINK debug werkt in plaats daarvan met een ST-Link-probe.
gdbrunner kan ook rechtstreeks worden aangeroepen (buiten de SDK, pip install gdbrunner):
gdbrunner jlink --device STM32H743VI build/OPENMV4/bin/firmware.elf
gdbrunner stlink build/OPENMV4/bin/firmware.elf
gdbrunner qemu --machine mps2-an500 build/MPS2_AN500/bin/firmware.elf
Het eerste positionele argument kiest de serverbackend (jlink, stlink, qemu); de rest wordt doorgegeven aan die backend, met standaardwaarden die werken voor de OpenMV-camera’s. gdbrunner --help toont de volledige lijst met vlaggen per backend; de argumententabel van elke backend is JSON-gestuurd (src/gdbrunner/backends.json), zodat het toevoegen van een nieuwe server een configuratiewijziging is in plaats van code.
Wat gdbrunner doet voor werk vanaf de opdrachtregel:
Eén proces, schone levenscyclus. De server start, gdb koppelt zich aan wanneer de poort open is, de server wordt netjes beëindigd wanneer gdb afsluit. Geen verweesde
JLinkGDBServerdie de sessie overleeft, geen twee terminals om te beheren.Automatische detectie van STM32CubeProgrammer. De
stlink-backend doorzoekt de gebruikelijke installatielocaties (~/STM32CubeProgrammer/,/opt/st/, de STM32CubeIDE-pluginboom) naar de STM32CubeProgrammer-tools, zodat het lange--cube-prog-pad niet telkens hoeft te worden ingetypt. De SDK bundelt zijn eigen kopie op~/openmv-sdk-<version>/stcubeprog/bin– richt--cube-progdaarop als er geen systeeminstallatie bestaat.Per-project gdbinit gerespecteerd. Een
.gdbinitin de huidige map wordt geladen met-ix– waarmee de gebruikersbrede~/.gdbinitwordt overschreven – zodat per-project gdb-scripting (pretty-printers, board-specifieke macro’s, breakpointsets) eenvoudig wordt ingevoegd door aanwezig te zijn in de werkmap.make debugdraait vanuit de repository-root, dus een.gdbinitdaar is van toepassing.Droogtest.
--dryrundrukt de serveropdracht af zonder die uit te voeren, handig om de aanroep aan te passen aan een wrapperscript, om hem in een IDE-launcher-config te kopiëren, of gewoon om te controleren welke argumenten gdbrunner samenstelt.Serveruitvoer zichtbaar.
--show-outputhoudt de stdout / stderr van de server zichtbaar. De standaard onderdrukt die (zodat de UI van gdb schoon blijft); zet de vlag om wanneer de server zelf zich misdraagt.QEMU-backend.
qemu-system-armdebugt een firmware-build zonder dat er een board is aangesloten. HetMPS2_AN500-target selecteert deze backend in zijn boardconfiguratie, zodatmake TARGET=MPS2_AN500 DEBUG=1 debugbouwt voor demps2-an500-machine van QEMU en de platformonafhankelijke code stapsgewijs doorloopt – alles wat geen camera-specifieke randapparaten aanraakt – zonder hardware. (qemu-system-armis een host-installatie, geen onderdeel van de SDK.)
Voor stappen op bronniveau met breakpointmarges en een weergave van randapparaatregisters is de VS Code Cortex-Debug-instelling hierboven de betere tool; gdbrunner is de juiste voor alles wat zich op de opdrachtregel afspeelt.
14.1.1.4.7. De debugger gebruiken¶
Zodra een sessie draait (de processor stilgezet bij main):
Breakpoints – klik in de marge naast een C-regel, of in de Debug Console
break <file>:<line>/break <function>. Cortex-M-cores hebben een klein aantal hardware-breakpointcomparators (doorgaans 6–8 op M7 / H7, 8 op M55). Dit overschrijden voor code in flashgeheugen mislukt stilzwijgend – houd het aantal actieve breakpoints bescheiden.Stappen – F10 stap eroverheen (
next), F11 stap erin (step), Shift+F11 stap eruit (finish), F5 doorgaan. Stappen op instructieniveau gebeurt metstepi/nextiin de Debug Console.Variabelen / watch / call stack – de panelen Variables en Call Stack tonen lokale variabelen en de backtrace; voeg expressies toe aan Watch. Beweeg de muis over een variabele in de broncode om de waarde te zien. Alles wat
<optimized out>toont betekent dat je niet op eenDEBUG=1-build zit.Watchpoints (data-breakpoints) –
watch <expr>stopt wanneer een variabele wordt geschreven,rwatchbij lezen,awatchbij beide. De Cortex-M DWT-eenheid ondersteunt ~4 hardware-watchpoints – onmisbaar om te betrappen wie een variabele heeft beschadigd.Registers en randapparaten – de weergave Cortex Registers toont de core-registers; met
svdFileingesteld decodeert de weergave Peripherals elk randapparaatregister en bitveld (DMA, timers, de camera-/CSI-interface, XSPI, enz.) – de snelste manier om te zien waarom een driver zich misdraagt.Geheugen – gebruik de geheugenviewer van Cortex-Debug of gdb
x/om framebuffers, DMA-buffers en structuren rechtstreeks te inspecteren.printf zonder stil te zetten (SWO/RTT) – voor timinggevoelige problemen geven Segger RTT of SWO een
printfmet vrijwel nul overhead terwijl het target draait. Bouw metDEBUG_PRINTF=1en voeg derttConfig(RTT) ofswoConfig(SWO, vereist de core-klok) van Cortex-Debug toe. Dit is de juiste tool wanneer een breakpoint de timing zou veranderen die je probeert te observeren.Verbinding verbreken – Stop bij een
launch-sessie zet het target stil; Disconnect bij eenattach-sessie laat de camera draaien. Schakel de camera daarna uit en weer in om hem terug te brengen naar normale werking.
14.1.1.4.8. Valkuilen bij het debuggen¶
Wegge-optimaliseerde variabelen. Alles toont
<optimized out>– je hebt gebouwd metDEBUG=0. Bouw opnieuw metDEBUG=1.“GDB executable not found” – de
gcc/binvan de SDK staat niet inPATH; stelarmToolchainPath/gdbPathin.“Cannot connect” / verkeerde geheugenmap – verkeerde of ontbrekende
device-naam; gebruik de exacte string uit de tabel.Breakpoints worden stilzwijgend niet geraakt – te veel hardware-breakpoints op code die in flashgeheugen staat; verminder ze.
Bronpaden komen niet overeen (met Docker gebouwde ELF) – bouw met het Docker-
build-firmware-dev-target (zelfde absolute pad binnen en buiten de container) of stel gdbset substitute-pathin.