14.1.1.4. A firmware hibakeresése

A hardveres hibakeresés azt jelenti, hogy megállítjuk a processzort, töréspontokat helyezünk el a C forráskódban, lépésenként haladunk, valamint változókat, memóriát, regisztereket és perifériákat vizsgálunk – mindezt a VS Code-on belül. Ehhez három dolog szükséges: egy debug build, egy SWD debug szonda (egy Segger J-Link) és a Cortex-Debug bővítmény, amely az arm-none-eabi-gdb eszközt vezérli egy J-Link GDB szerverrel szemben.

14.1.1.4.1. Build hibakereséshez

Mindig fordítsd újra a célt a DEBUG=1 kapcsolóval:

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

A release (DEBUG=0) image -O2 optimalizálással fordul; a hibakeresőben sok változónál <optimized out> jelenik meg, az inline függvények beolvadnak a hívóikba, a lépkedés pedig kiszámíthatatlanul ugrál. A DEBUG=1 -Og -ggdb3 beállítással fordít, ami hibakereshető, miközben még elindul a kamerán. Az ELF, amelyre a hibakeresőt irányítod, a következő:

build/<TARGET>/bin/firmware.elf

(Az Alif AE3 esetén a build/OPENMV_AE3/bin/firmware_M55_HP.elf fájlt kell hibakeresni – a nagy teljesítményű mag.)

14.1.1.4.3. VS Code Cortex-Debug beállítása

Hozz létre egy .vscode/launch.json fájlt a repository-ban. A legegyszerűbb eset – a VS Code, a J-Link és a build mind ugyanazon a Linux / macOS gépen van – a servertype: "jlink" beállítást használja, amely arra készteti a Cortex-Debug-ot, hogy maga indítson el egy J-Link GDB szervert:

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

Módosítsd az executable és device értékét a paneledhez (lásd a fenti táblázatot). Nyomd meg az F5 billentyűt a build-flash-és-futtatáshoz a main pontig, és ott állj meg.

Javaslat

Ahhoz, hogy minden hibakeresés indításakor automatikusan újraépüljön, adj hozzá egy build feladatot a .vscode/tasks.json fájlhoz, és hivatkozz rá a launch konfigurációból a "preLaunchTask" kulccsal. Például egy feladat, amely a make -j$(nproc) TARGET=OPENMV4 DEBUG=1 parancsot futtatja, "build-firmware" néven, plusz a "preLaunchTask": "build-firmware" a fenti konfigurációban, így az F5 egyetlen lépésben újraépít, flashel és elindítja a hibakeresőt.

Figyelem

A Cortex-Debug-nak szüksége van az arm-none-eabi-gdb eszközre. Ez az SDK-ban a ~/openmv-sdk-<version>/gcc/bin helyen érkezik, de alapértelmezetten nincs a PATH változón, ezért a hibakeresés a „GDB executable «arm-none-eabi-gdb» was not found” hibával hiúsul meg. Javítsd ki vagy az armToolchainPath / gdbPath fent bemutatott beállításával, vagy a ~/openmv-sdk-<version>/gcc/bin hozzáadásával a PATH változóhoz (a printenv PATH ekkor felsorolja).

14.1.1.4.4. Periféria-regiszter nézet (SVD)

Irányítsd a Cortex-Debug-ot egy CMSIS SVD fájlra, hogy dekódolt periféria-regiszter nézetet kapj (időzítők, DMA, a kamerainterfész stb.) név és bitmező szerint:

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

Az STM32 és MIMXRT esetén az SVD-t az ST / NXP CMSIS csomagokból vagy a Cortex-Debug SVD regisztertől szerezd be. Az Alif SVD-k a firmware repository-ban vannak elhelyezve a lib/micropython/lib/alif_ensemble-cmsis-dfp/Debug/SVD/ helyen (használd a ..._CM55_HP_View.svd fájlt az AE3 HP magjához).

14.1.1.4.6. Parancssori hibakeresés a gdbrunner segítségével

Egy beágyazott céllal szembeni GDB munkamenet kézi felállítása ötlépéses tánc: indítsd el a J-Link / ST-Link GDB szervert az egyik ablakban a megfelelő eszközzel, porttal és interfész kapcsolókkal; várd meg, amíg kiírja a Waiting for GDB connection üzenetet; futtasd az arm-none-eabi-gdb eszközt egy második ablakban; írd be a target remote localhost:<port> parancsot; irányítsd a gdb-t az ELF-re. Amikor a gdb munkamenet véget ér, ne feledd leállítani a szerver ablakát. A gdbrunner egy kis CLI, amely mindezt egyetlen előtér-parancsba sűríti. Az OpenMV SDK Python környezetében érkezik, így nincs mit telepíteni; a szokásos belépési pont a firmware repository make debug célja:

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

Ez a gdbrunner-t a panel konfigurációjából származó hibakereső argumentumokkal futtatja – a J-Link eszköznévvel és, ahol szükséges, az ST-Link külső flash betöltővel – úgy, hogy az SDK arm-none-eabi-gdb eszköze már a PATH változón van. Az alapértelmezett backend a J-Link; a make DEBUGGER=STLINK debug helyette egy ST-Link szondával működik.

A gdbrunner közvetlenül is meghívható (az SDK-n kívül, 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

Az első pozicionális argumentum választja ki a szerver backendet (jlink, stlink, qemu); a többit továbbítja az adott backendhez, olyan alapértelmezésekkel, amelyek működnek az OpenMV kamerákkal. A gdbrunner --help felsorolja a teljes, backendenkénti kapcsolólistát; az egyes backendek argumentumtáblázata JSON-vezérelt (src/gdbrunner/backends.json), így egy új szerver hozzáadása inkább konfigurációs szerkesztés, mint kódolás.

Mit tesz a gdbrunner a parancssori munkához:

  • Egy folyamat, tiszta életciklus. A szerver elindul, a gdb csatlakozik, amikor a port megnyílik, a szerver pedig tisztán leáll, amikor a gdb kilép. Nincs árva JLinkGDBServer, amely túléli a munkamenetet, nincs két kezelendő terminál.

  • STM32CubeProgrammer automatikus felismerés. Az stlink backend a szokásos telepítési helyeken (~/STM32CubeProgrammer/, /opt/st/, az STM32CubeIDE plugin fa) keresi az STM32CubeProgrammer eszközöket, így a hosszú --cube-prog útvonalat nem kell minden alkalommal beírni. Az SDK saját másolatot is tartalmaz a ~/openmv-sdk-<version>/stcubeprog/bin helyen – ide irányítsd a --cube-prog értékét, ha nincs rendszerszintű telepítés.

  • Projektenkénti gdbinit figyelembe vétele. Az aktuális könyvtárban lévő .gdbinit betöltődik a -ix kapcsolóval – felülírva a felhasználói szintű ~/.gdbinit fájlt – így a projektenkénti gdb szkriptelés (pretty-printerek, panelspecifikus makrók, töréspont-halmazok) egyszerűen a munkakönyvtárban való jelenlétével illeszkedik be. A make debug a repository gyökeréből fut, így az ott lévő .gdbinit érvényesül.

  • Próbafutás. A --dryrun kiírja a szerver parancsot anélkül, hogy futtatná, ami hasznos a meghívás wrapper szkripthez igazításához, IDE indító konfigurációba másolásához, vagy egyszerűen annak ellenőrzéséhez, milyen argumentumokat állít össze a gdbrunner.

  • Látható szerverkimenet. A --show-output láthatóan tartja a szerver stdout / stderr kimenetét. Az alapértelmezés elnyomja azt (hogy a gdb felülete tiszta maradjon); fordítsd meg a kapcsolót, amikor maga a szerver az, ami hibásan működik.

  • QEMU backend. A qemu-system-arm egy firmware buildet hibakeres anélkül, hogy bármilyen panel be lenne dugva. Az MPS2_AN500 cél a panel konfigurációjában ezt a backendet választja, így a make TARGET=MPS2_AN500 DEBUG=1 debug a QEMU mps2-an500 gépéhez épít, és lépteti a platformfüggetlen kódot – mindent, ami nem érinti a kameraspecifikus perifériákat – röptében. (A qemu-system-arm egy gazdagépes telepítés, nem része az SDK-nak.)

Forrás szintű lépkedéshez töréspont-jelzőkkel és periféria-regiszter nézettel a fenti VS Code Cortex-Debug beállítás a jobb eszköz; a gdbrunner a megfelelő mindenhez, ami a parancssorban él.

14.1.1.4.7. A hibakereső használata

Amint egy munkamenet fut (a processzor a main pontnál megállítva):

  • Töréspontok – kattints a jelzősávra egy C sor mellett, vagy a Debug Console-ban break <file>:<line> / break <function>. A Cortex-M magoknak kis számú hardveres töréspont-komparátoruk van (jellemzően 6–8 az M7 / H7 esetén, 8 az M55 esetén). Ennek túllépése flash-ben lévő kódon csendben meghiúsul – tartsd az aktív töréspontok számát szerényen.

  • LépkedésF10 átlépés (next), F11 belépés (step), Shift+F11 kilépés (finish), F5 folytatás. Az utasítás szintű lépkedés a stepi / nexti a Debug Console-ban.

  • Változók / figyelés / hívási verem – a Variables és Call Stack panelek a lokális változókat és a visszanyomon követést mutatják; adj hozzá kifejezéseket a Watch panelhez. Vidd a kurzort egy változó fölé a forrásban az értékének megtekintéséhez. Bármi, ami <optimized out> értéket mutat, azt jelenti, hogy nem DEBUG=1 buildet használsz.

  • Figyelőpontok (adat-töréspontok) – a watch <expr> megáll, amikor egy változóba írnak, az rwatch olvasáskor, az awatch pedig bármelyik esetén. A Cortex-M DWT egység ~4 hardveres figyelőpontot támogat – felbecsülhetetlen annak elkapásához, ki rontott el egy változót.

  • Regiszterek és perifériák – a Cortex Registers nézet a magregisztereket mutatja; az svdFile beállításával a Peripherals nézet dekódol minden periféria-regisztert és bitmezőt (DMA, időzítők, a kamera / CSI interfész, XSPI stb.) – a leggyorsabb módja annak, hogy lássuk, miért működik hibásan egy driver.

  • Memória – használd a Cortex-Debug memórianézegetőt vagy a gdb x/ parancsát a képkocka-pufferek, DMA pufferek és struktúrák közvetlen vizsgálatához.

  • printf megállítás nélkül (SWO/RTT) – időzítésérzékeny problémáknál a Segger RTT vagy SWO szinte nulla többletterheléssel ad printf lehetőséget, miközben a cél fut. Építs a DEBUG_PRINTF=1 kapcsolóval, és add hozzá a Cortex-Debug rttConfig (RTT) vagy swoConfig (SWO, amihez a magórajel szükséges) beállítását. Ez a megfelelő eszköz, amikor egy töréspont megváltoztatná az időzítést, amelyet megfigyelni próbálsz.

  • Lecsatlakozás – a Stop egy launch munkameneten megállítja a célt; a Disconnect egy attach munkameneten futni hagyja a kamerát. Indítsd újra a kamerát a táp ki-be kapcsolásával, hogy utána visszatérjen a normál működéshez.

14.1.1.4.8. Hibakeresési buktatók

  • Optimalizálással eltávolított változók. Minden <optimized out> értéket mutat – DEBUG=0 beállítással építettél. Építsd újra DEBUG=1 beállítással.

  • „GDB executable not found” – az SDK gcc/bin nincs a PATH változón; állítsd be az armToolchainPath / gdbPath értékét.

  • „Cannot connect” / rossz memóriatérkép – rossz vagy hiányzó device név; használd a táblázatban szereplő pontos szöveget.

  • A töréspontok csendben nem érvényesülnek – túl sok hardveres töréspont a flash-rezidens kódon; csökkentsd a számukat.

  • A forrásútvonalak nem egyeznek (Docker-ban épített ELF) – építs a Docker build-firmware-dev céllal (ugyanaz az abszolút útvonal a konténeren belül és kívül) vagy állítsd be a gdb set substitute-path értékét.