14.1.1.4. Ladění firmwaru

Ladění přímo na hardwaru znamená zastavení procesoru, nastavování breakpointů v C kódu, krokování a kontrolu proměnných, paměti, registrů a periferií – to vše z prostředí VS Code. K tomu jsou potřeba tři věci: ladicí build, SWD ladicí sonda (Segger J-Link) a rozšíření Cortex-Debug, které řídí arm-none-eabi-gdb proti J-Link GDB serveru.

14.1.1.4.1. Build pro ladění

Vždy znovu sestavte cíl s DEBUG=1

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

Release verze (DEBUG=0) se kompiluje s -O2; v ladicím nástroji uvidíte u mnoha proměnných <optimized out>, vložené (inlined) funkce splynou se svými volajícími a krokování skáče nepředvídatelně. DEBUG=1 sestavuje s -Og -ggdb3, což je laditelné a přitom se to na kameře stále nabootuje. ELF, na který ladicí nástroj nasměrujete, je:

build/<TARGET>/bin/firmware.elf

(Pro Alif AE3 laďte build/OPENMV_AE3/bin/firmware_M55_HP.elf – vysoce výkonné jádro.)

14.1.1.4.3. Nastavení Cortex-Debug ve VS Code

V repozitáři vytvořte .vscode/launch.json. Nejjednodušší případ – VS Code, J-Link i build jsou všechny na stejném stroji s Linuxem / macOS – používá servertype: "jlink", díky čemuž si Cortex-Debug sám spustí 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"
    }
  ]
}

Změňte executable a device podle své desky (viz tabulka výše). Stiskněte F5, čímž se provede build, flash a spuštění až do main, kde se zastaví.

Tip

Aby se build automaticky provedl pokaždé, když spustíte ladění, přidejte build úlohu do .vscode/tasks.json a odkažte se na ni z konfigurace spouštění pomocí "preLaunchTask". Například úloha spouštějící make -j$(nproc) TARGET=OPENMV4 DEBUG=1 pojmenovaná "build-firmware" plus "preLaunchTask": "build-firmware" ve výše uvedené konfiguraci, takže F5 znovu sestaví, naflashuje a spustí ladicí nástroj v jediném kroku.

Varování

Cortex-Debug potřebuje arm-none-eabi-gdb. Dodává se v SDK v ~/openmv-sdk-<version>/gcc/bin, ale ve výchozím nastavení není v PATH, takže ladění selže s hláškou „GDB executable ‚arm-none-eabi-gdb‘ was not found“. Opravíte to buď nastavením armToolchainPath / gdbPath jak je ukázáno výše, nebo přidáním ~/openmv-sdk-<version>/gcc/bin do svého PATH (printenv PATH by jej pak měl vypisovat).

14.1.1.4.4. Zobrazení registrů periferií (SVD)

Nasměrujte Cortex-Debug na CMSIS soubor SVD, abyste získali dekódované zobrazení registrů periferií (časovače, DMA, rozhraní kamery atd.) podle názvu a bitového pole:

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

Pro STM32 a MIMXRT získáte SVD z CMSIS balíčků ST / NXP nebo z registru SVD pro Cortex-Debug. SVD soubory pro Alif jsou součástí firmware repozitáře v lib/micropython/lib/alif_ensemble-cmsis-dfp/Debug/SVD/ (pro jádro AE3 HP použijte ..._CM55_HP_View.svd).

14.1.1.4.6. Ladění z příkazové řádky pomocí gdbrunner

Ruční nastavení relace GDB proti vestavěnému cíli je pětikrokový tanec: v jednom okně spustíte J-Link / ST-Link GDB server se správným zařízením, portem a příznaky rozhraní; počkáte, až vypíše Waiting for GDB connection; v druhém okně spustíte arm-none-eabi-gdb; zadáte target remote localhost:<port>; nasměrujete gdb na ELF. Když relace gdb skončí, nezapomeňte ukončit okno serveru. gdbrunner je malé CLI, které tohle všechno sloučí do jediného popředního příkazu. Dodává se v Python prostředí OpenMV SDK, takže není co instalovat; obvyklým vstupním bodem je cíl make debug ve firmware repozitáři:

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

Tím se spustí gdbrunner s argumenty ladicího nástroje z konfigurace desky – názvem zařízení J-Link a, kde je to potřeba, externím flash loaderem ST-Link – přičemž arm-none-eabi-gdb z SDK je již v PATH. Výchozím backendem je J-Link; make DEBUGGER=STLINK debug funguje místo toho se sondou ST-Link.

gdbrunner lze také vyvolat přímo (mimo 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

První poziční argument vybírá backend serveru (jlink, stlink, qemu); zbytek se předává tomuto backendu s výchozími hodnotami, které fungují pro kamery OpenMV. gdbrunner --help vypíše úplný seznam příznaků pro jednotlivé backendy; tabulka argumentů každého backendu je řízena JSONem (src/gdbrunner/backends.json), takže přidání nového serveru je úprava konfigurace, nikoli kódu.

Co gdbrunner dělá pro práci z příkazové řádky:

  • Jeden proces, čistý životní cyklus. Server se spustí, gdb se připojí, jakmile je port otevřený, server se po ukončení gdb čistě ukončí. Žádný osiřelý JLinkGDBServer přežívající relaci, žádné dva terminály ke spravování.

  • Automatické vyhledání STM32CubeProgrammer. Backend stlink prohledá obvyklá instalační umístění (~/STM32CubeProgrammer/, /opt/st/, strom pluginů STM32CubeIDE) a najde nástroje STM32CubeProgrammer, takže se dlouhá cesta --cube-prog nemusí psát pokaždé. SDK přibaluje vlastní kopii v ~/openmv-sdk-<version>/stcubeprog/bin – nasměrujte --cube-prog sem, pokud žádná systémová instalace neexistuje.

  • Respektování gdbinit pro jednotlivé projekty. Soubor .gdbinit v aktuálním adresáři se načte pomocí -ix – přebíjí celouživatelský ~/.gdbinit – takže gdb skriptování pro daný projekt (pretty-printery, makra specifická pro desku, sady breakpointů) se zapojí jen tím, že je přítomno v pracovním adresáři. make debug běží z kořene repozitáře, takže se uplatní tamní .gdbinit.

  • Suchý běh. --dryrun vypíše příkaz serveru, aniž by jej spustil, což je užitečné pro přizpůsobení volání obalovacímu skriptu, zkopírování do konfigurace spouštěče v IDE nebo prostě pro kontrolu, jaké argumenty gdbrunner skládá.

  • Viditelný výstup serveru. --show-output ponechá stdout / stderr serveru viditelný. Výchozí nastavení jej potlačuje (aby zůstalo UI gdb čisté); přepněte tento příznak, když je problémem samotný server.

  • QEMU backend. qemu-system-arm ladí firmware build bez připojené desky. Cíl MPS2_AN500 ve své konfiguraci desky volí tento backend, takže make TARGET=MPS2_AN500 DEBUG=1 debug sestaví pro QEMU stroj mps2-an500 a krokuje kód nezávislý na platformě – vše, co se nedotýká periferií specifických pro kameru – za letu. (qemu-system-arm je instalace na hostiteli, není součástí SDK.)

Pro krokování na úrovni zdrojového kódu s vyznačením breakpointů na okraji a se zobrazením registrů periferií je lepším nástrojem výše popsané nastavení Cortex-Debug ve VS Code; gdbrunner je ten správný pro vše, co žije na příkazové řádce.

14.1.1.4.7. Použití ladicího nástroje

Jakmile relace běží (procesor zastavený na main):

  • Breakpointy – klikněte na okraj vedle řádku v C, nebo v Debug Console break <file>:<line> / break <function>. Jádra Cortex-M mají malý počet hardwarových komparátorů breakpointů (typicky 6–8 na M7 / H7, 8 na M55). Jejich překročení u kódu ve flash paměti tiše selže – udržujte počet aktivních breakpointů střídmý.

  • KrokováníF10 přeskočit (next), F11 vstoupit do (step), Shift+F11 vystoupit z (finish), F5 pokračovat. Krokování na úrovni instrukcí je stepi / nexti v Debug Console.

  • Proměnné / watch / zásobník volání – panely Variables a Call Stack zobrazují lokální proměnné a backtrace; výrazy přidejte do Watch. Najetím na proměnnou ve zdrojovém kódu zobrazíte její hodnotu. Cokoli, co zobrazuje <optimized out>, znamená, že nejste na buildu DEBUG=1.

  • Watchpointy (datové breakpointy)watch <expr> zastaví při zápisu do proměnné, rwatch při čtení, awatch při obojím. Jednotka DWT v Cortex-M podporuje ~4 hardwarové watchpointy – neocenitelné pro zachycení toho, kdo proměnnou poškodil.

  • Registry a periferie – zobrazení Cortex Registers ukazuje jádrové registry; s nastaveným svdFile zobrazení Peripherals dekóduje každý registr a bitové pole periferie (DMA, časovače, rozhraní kamery / CSI, XSPI atd.) – nejrychlejší způsob, jak zjistit, proč se ovladač chová špatně.

  • Paměť – pomocí prohlížeče paměti Cortex-Debug nebo gdb x/ zkoumejte přímo snímkové buffery (frame buffers), DMA buffery a struktury.

  • printf bez zastavení (SWO/RTT) – pro problémy citlivé na časování poskytuje Segger RTT nebo SWO printf s téměř nulovou režií, zatímco cíl běží. Sestavte s DEBUG_PRINTF=1 a přidejte rttConfig (RTT) nebo swoConfig (SWO, potřebuje hodiny jádra) z Cortex-Debug. Toto je správný nástroj, když by breakpoint změnil časování, které se snažíte pozorovat.

  • OdpojeníStop v relaci launch zastaví cíl; Disconnect v relaci attach nechá kameru běžet. Poté kameru vypněte a zapněte, abyste ji vrátili do normálního provozu.

14.1.1.4.8. Úskalí ladění

  • Optimalizací odstraněné proměnné. Vše zobrazuje <optimized out> – sestavili jste DEBUG=0. Sestavte znovu s DEBUG=1.

  • „GDB executable not found“gcc/bin z SDK není v PATH; nastavte armToolchainPath / gdbPath.

  • „Cannot connect“ / chybná mapa paměti – chybný nebo chybějící název device; použijte přesný řetězec z tabulky.

  • Breakpointy tiše nezasáhnou – příliš mnoho hardwarových breakpointů na kódu rezidentním ve flash paměti; snižte jejich počet.

  • Cesty ke zdrojům nesouhlasí (ELF sestavený v Dockeru) – sestavte s cílem build-firmware-dev v Dockeru (stejná absolutní cesta uvnitř i vně kontejneru) nebo nastavte gdb set substitute-path.