14.1.1.4. Felsöka den fasta programvaran

Felsökning på hårdvaran innebär att stoppa processorn, sätta brytpunkter i C-källkoden, stega instruktion för instruktion och inspektera variabler, minne, register och kringutrustning – direkt inifrån VS Code. Detta kräver tre saker: en felsökningsbygge, en SWD-felsökningssond (en Segger J-Link) och tillägget Cortex-Debug som driver arm-none-eabi-gdb mot en J-Link GDB-server.

14.1.1.4.1. Bygga för felsökning

Bygg alltid om målet med DEBUG=1

make -j$(nproc) TARGET=<TARGET> DEBUG=1

En release-avbild (DEBUG=0) kompileras med -O2; i felsökaren kommer du att se <optimized out> för många variabler, inlinade funktioner kollapsar in i sina anropare och stegningen hoppar omkring oförutsägbart. DEBUG=1 bygger med -Og -ggdb3, vilket är felsökbart samtidigt som det fortfarande startar på kameran. Den ELF du pekar felsökaren mot är:

build/<TARGET>/bin/firmware.elf

(För Alif AE3, felsök build/OPENMV_AE3/bin/firmware_M55_HP.elf – högprestandakärnan.)

14.1.1.4.3. Konfiguration av VS Code Cortex-Debug

Skapa .vscode/launch.json i arkivet. Det enklaste fallet – VS Code, J-Link och bygget finns alla på samma Linux-/macOS-maskin – använder servertype: "jlink", vilket får Cortex-Debug att själv starta en J-Link GDB-server:

{
  "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"
    }
  ]
}

Ändra executable och device för ditt kort (se tabellen ovan). Tryck på F5 för att bygga, flasha och köra till main och stoppa där.

Tips

För att bygga om automatiskt varje gång du startar felsökningen, lägg till en byggaktivitet i .vscode/tasks.json och referera till den från startkonfigurationen med "preLaunchTask". Till exempel en aktivitet som kör make -j$(nproc) TARGET=OPENMV4 DEBUG=1, med namnet "build-firmware", plus "preLaunchTask": "build-firmware" i konfigurationen ovan, så att F5 bygger om, flashar och startar felsökaren i ett enda steg.

Varning

Cortex-Debug behöver arm-none-eabi-gdb. Den medföljer i SDK:t i ~/openmv-sdk-<version>/gcc/bin men finns intePATH som standard, så felsökningen misslyckas med ”GDB executable ’arm-none-eabi-gdb’ was not found”. Åtgärda det antingen genom att ställa in armToolchainPath / gdbPath som visas ovan, eller genom att lägga till ~/openmv-sdk-<version>/gcc/bin i din PATH (printenv PATH bör då lista den).

14.1.1.4.4. Vy över kringutrustningens register (SVD)

Peka Cortex-Debug mot en CMSIS-SVD-fil för att få en avkodad vy över kringutrustningens register (timers, DMA, kameragränssnittet osv.) efter namn och bitfält:

"svdFile": "/path/to/STM32H743.svd"

För STM32 och MIMXRT hämtar du SVD:n från ST:s / NXP:s CMSIS-paket eller från Cortex-Debugs SVD-register. Alif-SVD:erna är inbakade i firmware-arkivet i lib/micropython/lib/alif_ensemble-cmsis-dfp/Debug/SVD/ (använd ..._CM55_HP_View.svd för AE3:s HP-kärna).

14.1.1.4.6. Felsökning från kommandoraden med gdbrunner

Att sätta upp en GDB-session mot ett inbyggt mål för hand är en dans i fem steg: starta J-Link- / ST-Link-GDB-servern i ett fönster med rätt enhet, port och gränssnittsflaggor; vänta tills den skriver ut Waiting for GDB connection; kör arm-none-eabi-gdb i ett andra fönster; skriv target remote localhost:<port>; peka gdb mot ELF:en. När gdb-sessionen avslutas, kom ihåg att stänga serverfönstret. gdbrunner är ett litet CLI som kollapsar allt detta till ett enda förgrundskommando. Det medföljer i OpenMV-SDK:ts Python-miljö, så det finns inget att installera; den vanliga ingångspunkten är firmware-arkivets make debug-mål:

make -j$(nproc) TARGET=<TARGET> DEBUG=1 debug

Detta kör gdbrunner med felsökningsargumenten från kortets konfiguration – J-Link-enhetsnamnet och, där det behövs, ST-Link:ens externa flash-laddare – med SDK:ts arm-none-eabi-gdb redan på PATH. Standardbackenden är J-Link; make DEBUGGER=STLINK debug fungerar med en ST-Link-sond i stället.

gdbrunner kan även anropas direkt (utanför SDK:t, 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

Det första positionsargumentet väljer serverbackenden (jlink, stlink, qemu); resten vidarebefordras till den backenden, med standardvärden som fungerar för OpenMV-kamerorna. gdbrunner --help listar den fullständiga listan med flaggor per backend; varje backends argumenttabell är JSON-driven (src/gdbrunner/backends.json), så att lägga till en ny server är en konfigurationsändring snarare än kod.

Vad gdbrunner gör för arbete på kommandoraden:

  • En process, ren livscykel. Servern startar, gdb ansluter när porten är öppen, servern avslutas rent när gdb avslutas. Ingen kvarvarande JLinkGDBServer som överlever sessionen, inga två terminaler att hantera.

  • Automatisk upptäckt av STM32CubeProgrammer. Backenden stlink söker på de vanliga installationsplatserna (~/STM32CubeProgrammer/, /opt/st/, STM32CubeIDE-pluginträdet) efter STM32CubeProgrammer-verktygen, så att den långa --cube-prog-sökvägen inte behöver skrivas varje gång. SDK:t levererar sin egen kopia i ~/openmv-sdk-<version>/stcubeprog/bin – peka --cube-prog dit om ingen systeminstallation finns.

  • Projektspecifik gdbinit respekteras. En .gdbinit i den aktuella katalogen laddas med -ix – vilket åsidosätter den användaromfattande ~/.gdbinit – så projektspecifika gdb-skript (pretty-printers, kortspecifika makron, brytpunktsuppsättningar) tillkommer bara genom att finnas i arbetskatalogen. make debug körs från arkivets rot, så en .gdbinit där gäller.

  • Torrkörning. --dryrun skriver ut serverkommandot utan att köra det, vilket är användbart för att anpassa anropet till ett omslagsskript, kopiera in det i en IDE-startkonfiguration eller bara kontrollera vilka argument gdbrunner sätter samman.

  • Serverutdata synlig. --show-output håller serverns stdout / stderr synlig. Standardvärdet undertrycker den (så att gdb:s gränssnitt förblir rent); slå om flaggan när det är servern själv som krånglar.

  • QEMU-backend. qemu-system-arm felsöker ett firmware-bygge utan något inkopplat kort. Målet MPS2_AN500 väljer denna backend i sin kortkonfiguration, så make TARGET=MPS2_AN500 DEBUG=1 debug bygger för QEMU:s mps2-an500-maskin och stegar igenom den plattformsoberoende koden – allt som inte rör kameraspecifik kringutrustning – på resande fot. (qemu-system-arm är en värdinstallation, inte en del av SDK:t.)

För stegning på källkodsnivå med brytpunktsmarginaler och en vy över kringutrustningens register är konfigurationen av VS Code Cortex-Debug ovan det bättre verktyget; gdbrunner är det rätta för allt som lever på kommandoraden.

14.1.1.4.7. Använda felsökaren

När en session väl körs (processorn stoppad vid main):

  • Brytpunkter – klicka i marginalen bredvid en C-rad, eller i Debug Console break <file>:<line> / break <function>. Cortex-M-kärnor har ett litet antal hårdvaru-brytpunktskomparatorer (vanligen 6–8 på M7 / H7, 8 på M55). Att överskrida det antalet på kod i flashminne misslyckas tyst – håll antalet aktiva brytpunkter måttligt.

  • StegningF10 stega över (next), F11 stega in i (step), Shift+F11 stega ut (finish), F5 fortsätt. Stegning på instruktionsnivå är stepi / nexti i Debug Console.

  • Variabler / bevakning / anropsstack – panelerna Variables och Call Stack visar lokala variabler och bakåtspårningen; lägg till uttryck i Watch. Hovra över en variabel i källkoden för att se dess värde. Allt som visar <optimized out> betyder att du inte är på ett DEBUG=1-bygge.

  • Bevakningspunkter (databrytpunkter)watch <expr> stoppar när en variabel skrivs, rwatch vid läsning, awatch vid endera. Cortex-M:s DWT-enhet stöder ~4 hårdvarubevakningspunkter – ovärderligt för att fånga vem som korrumperade en variabel.

  • Register och kringutrustning – vyn Cortex Registers visar kärnregister; med svdFile inställd avkodar vyn Peripherals varje register och bitfält i kringutrustningen (DMA, timers, kamera- / CSI-gränssnittet, XSPI osv.) – det snabbaste sättet att se varför en drivrutin krånglar.

  • Minne – använd Cortex-Debugs minnesvisare eller gdb x/ för att inspektera bildbuffertar, DMA-buffertar och strukturer direkt.

  • printf utan att stoppa (SWO/RTT) – för tidskänsliga problem ger Segger RTT eller SWO nära nog overhead-fri printf medan målet körs. Bygg med DEBUG_PRINTF=1 och lägg till Cortex-Debugs rttConfig (RTT) eller swoConfig (SWO, kräver kärnklockan). Detta är rätt verktyg när en brytpunkt skulle ändra den tidsstyrning du försöker observera.

  • Koppla frånStop på en launch-session stoppar målet; Disconnect på en attach-session lämnar kameran igång. Strömcykla kameran för att återställa den till normal drift efteråt.

14.1.1.4.8. Fallgropar vid felsökning

  • Bortoptimerade variabler. Allt visar <optimized out> – du byggde DEBUG=0. Bygg om med DEBUG=1.

  • ”GDB executable not found” – SDK:ts gcc/bin finns inte på PATH; ställ in armToolchainPath / gdbPath.

  • ”Cannot connect” / fel minneskarta – fel eller saknat device-namn; använd den exakta strängen från tabellen.

  • Brytpunkter träffas tyst inte – för många hårdvarubrytpunkter på flash-resident kod; minska dem.

  • Källsökvägar matchar inte (Docker-byggd ELF) – bygg med Docker-målet build-firmware-dev (samma absoluta sökväg innanför och utanför containern) eller ställ in gdb set substitute-path.