14.1.1.4. Laiteohjelmiston virheenjäljitys

Laitteistotason virheenjäljitys tarkoittaa suorittimen pysäyttämistä, keskeytyspisteiden asettamista C-lähdekoodiin, askeltamista ja muuttujien, muistin, rekisterien sekä oheislaitteiden tarkastelua – suoraan VS Coden sisältä. Tähän tarvitaan kolme asiaa: virheenjäljityskäännös, SWD-virheenjäljitysanturi (Segger J-Link) ja Cortex-Debug-laajennus, joka ohjaa arm-none-eabi-gdb-työkalua J-Linkin GDB-palvelinta vasten.

14.1.1.4.1. Käännös virheenjäljitystä varten

Käännä kohde aina uudelleen asetuksella DEBUG=1

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

Julkaisukäännös (DEBUG=0) käännetään asetuksella -O2; virheenjäljittimessä monille muuttujille näkyy <optimized out>, sisäänliitetyt funktiot sulautuvat kutsujiinsa ja askellus hyppii arvaamattomasti. DEBUG=1 kääntää asetuksilla -Og -ggdb3, mikä on jäljitettävissä mutta käynnistyy silti kamerassa. ELF-tiedosto, johon osoitat virheenjäljittimen, on:

build/<TARGET>/bin/firmware.elf

(Alif AE3:lla jäljitä build/OPENMV_AE3/bin/firmware_M55_HP.elf – suorituskykyydin.)

14.1.1.4.3. VS Code Cortex-Debug -asetukset

Luo .vscode/launch.json repositorioon. Yksinkertaisin tapaus – VS Code, J-Link ja käännös ovat kaikki samassa Linux- / macOS-koneessa – käyttää asetusta servertype: "jlink", jolloin Cortex-Debug käynnistää itse J-Linkin GDB-palvelimen:

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

Vaihda kortillesi sopivat executable ja device (katso yllä oleva taulukko). Paina F5 kääntääksesi, ohjelmoidaksesi ja suorittaaksesi pisteeseen main ja pysähtyäksesi siihen.

Vihje

Jotta uudelleenkäännös tapahtuu automaattisesti aina kun aloitat virheenjäljityksen, lisää käännöstehtävä tiedostoon .vscode/tasks.json ja viittaa siihen käynnistyskonfiguraatiosta asetuksella "preLaunchTask". Esimerkiksi tehtävä, joka ajaa make -j$(nproc) TARGET=OPENMV4 DEBUG=1 ja jolle on annettu nimi "build-firmware", sekä "preLaunchTask": "build-firmware" yllä olevassa konfiguraatiossa, jolloin F5 kääntää uudelleen, ohjelmoi ja käynnistää virheenjäljittimen yhdellä askeleella.

Varoitus

Cortex-Debug tarvitsee arm-none-eabi-gdb-työkalun. Se toimitetaan SDK:ssa polussa ~/openmv-sdk-<version>/gcc/bin mutta ei ole oletuksena PATH-muuttujassa, joten virheenjäljitys epäonnistuu viestillä ”GDB executable ’arm-none-eabi-gdb’ was not found”. Korjaa se joko asettamalla armToolchainPath / gdbPath kuten yllä on esitetty, tai lisäämällä ~/openmv-sdk-<version>/gcc/bin PATH-muuttujaan (printenv PATH listaa sen tällöin).

14.1.1.4.4. Oheislaiterekisterien näkymä (SVD)

Osoita Cortex-Debug CMSIS SVD-tiedostoon saadaksesi puretun oheislaiterekisterinäkymän (ajastimet, DMA, kameraliitäntä jne.) nimen ja bittikentän mukaan:

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

STM32:lle ja MIMXRT:lle hanki SVD ST:n / NXP:n CMSIS-paketeista tai Cortex-Debugin SVD-rekisteristä. Alifin SVD:t toimitetaan laiteohjelmistorepositorion mukana polussa lib/micropython/lib/alif_ensemble-cmsis-dfp/Debug/SVD/ (käytä tiedostoa ..._CM55_HP_View.svd AE3:n HP-ytimelle).

14.1.1.4.6. Komentorivivirheenjäljitys gdbrunnerilla

GDB-istunnon pystyttäminen sulautettua kohdetta vasten käsin on viisivaiheinen tanssi: käynnistä J-Link- / ST-Link-GDB-palvelin yhdessä ikkunassa oikein laite-, portti- ja liitäntälipuilla; odota, että se tulostaa Waiting for GDB connection; aja arm-none-eabi-gdb toisessa ikkunassa; kirjoita target remote localhost:<port>; osoita gdb ELF-tiedostoon. Kun gdb-istunto päättyy, muista lopettaa palvelinikkuna. gdbrunner on pieni CLI, joka tiivistää kaiken tuon yhdeksi etualan komennoksi. Se toimitetaan OpenMV SDK:n Python-ympäristössä, joten mitään ei tarvitse asentaa; tavallinen aloituskohta on laiteohjelmistorepositorion make debug -kohde:

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

Tämä ajaa gdbrunnerin kortin konfiguraatiosta saaduilla virheenjäljitysargumenteilla – J-Linkin laitenimellä ja tarvittaessa ST-Linkin ulkoisen flash-latainin kanssa – siten että SDK:n arm-none-eabi-gdb on jo PATH-muuttujassa. Oletustaustajärjestelmä on J-Link; make DEBUGGER=STLINK debug toimii sen sijaan ST-Link-anturin kanssa.

gdbrunner voidaan myös kutsua suoraan (SDK:n ulkopuolella, 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

Ensimmäinen sijaintiargumentti valitsee palvelimen taustajärjestelmän (jlink, stlink, qemu); loput välitetään kyseiselle taustajärjestelmälle oletuksilla, jotka toimivat OpenMV-kameroilla. gdbrunner --help listaa täydellisen taustajärjestelmäkohtaisen lippuluettelon; kunkin taustajärjestelmän argumenttitaulukko on JSON-ohjattu (src/gdbrunner/backends.json), joten uuden palvelimen lisääminen on konfiguraatiomuokkaus eikä koodimuutos.

Mitä gdbrunner tekee komentorivityöskentelyä varten:

  • Yksi prosessi, siisti elinkaari. Palvelin käynnistyy, gdb liittyy kun portti on auki, ja palvelin lopetetaan siististi gdb:n päättyessä. Ei istunnosta selviävää orpoa JLinkGDBServer-prosessia, ei kahta hallittavaa päätettä.

  • STM32CubeProgrammerin automaattinen löytäminen. stlink-taustajärjestelmä etsii STM32CubeProgrammer-työkaluja tavanomaisista asennuspaikoista (~/STM32CubeProgrammer/, /opt/st/, STM32CubeIDE-lisäosapuu), joten pitkää --cube-prog-polkua ei tarvitse kirjoittaa joka kerta. SDK sisältää oman kopionsa polussa ~/openmv-sdk-<version>/stcubeprog/bin – osoita --cube-prog sinne, jos järjestelmäasennusta ei ole.

  • Projektikohtainen gdbinit huomioidaan. Nykyhakemiston .gdbinit ladataan lipulla -ix – ohittaen käyttäjälaajuisen ~/.gdbinit – joten projektikohtainen gdb-skriptaus (pretty-printerit, korttikohtaiset makrot, keskeytyspistejoukot) tulee käyttöön pelkästään olemalla työhakemistossa. make debug ajetaan repositorion juuresta, joten siellä oleva .gdbinit on voimassa.

  • Kuivaharjoitus. --dryrun tulostaa palvelinkomennon ajamatta sitä, mikä on hyödyllistä kutsun mukauttamiseen wrapper-skriptiin, sen kopioimiseen IDE:n käynnistyskonfiguraatioon tai vain sen tarkistamiseen, mitä argumentteja gdbrunner kokoaa.

  • Palvelimen tuloste näkyvissä. --show-output pitää palvelimen stdout-/stderr-tulosteen näkyvissä. Oletus vaimentaa sen (jotta gdb:n käyttöliittymä pysyy siistinä); käännä lippu, kun palvelin itse on se, joka käyttäytyy huonosti.

  • QEMU-taustajärjestelmä. qemu-system-arm jäljittää laiteohjelmistokäännöstä ilman, että korttia on kytkettynä. MPS2_AN500-kohde valitsee tämän taustajärjestelmän korttikonfiguraatiossaan, joten make TARGET=MPS2_AN500 DEBUG=1 debug kääntää QEMU:n mps2-an500-koneelle ja askeltaa alustariippumatonta koodia – kaikkea, mikä ei kosketa kamerakohtaisia oheislaitteita – lennossa. (qemu-system-arm on isäntäasennus, ei osa SDK:ta.)

Lähdetason askellukseen keskeytyspistereunuksineen ja oheislaiterekisterinäkymineen yllä oleva VS Code Cortex-Debug -asetus on parempi työkalu; gdbrunner on oikea kaikkeen, mikä elää komentorivillä.

14.1.1.4.7. Virheenjäljittimen käyttö

Kun istunto on käynnissä (suoritin pysäytettynä kohdassa main):

  • Keskeytyspisteet – napsauta reunusta C-rivin vieressä, tai Debug Consolessa break <file>:<line> / break <function>. Cortex-M-ytimillä on pieni määrä laitteistopohjaisia keskeytyspistevertailijoita (tyypillisesti 6–8 M7:llä / H7:llä, 8 M55:llä). Tämän ylittäminen flash-muistissa olevassa koodissa epäonnistuu hiljaisesti – pidä aktiivisten keskeytyspisteiden määrä maltillisena.

  • AskellusF10 askella yli (next), F11 askella sisään (step), Shift+F11 askella ulos (finish), F5 jatka. Käskytason askellus on stepi / nexti Debug Consolessa.

  • Muuttujat / vahti / kutsupinoVariables- ja Call Stack -ruudut näyttävät paikalliset muuttujat ja kutsupinon; lisää lausekkeita Watch-ruutuun. Vie hiiri muuttujan päälle lähdekoodissa nähdäksesi sen arvon. Kaikki, mikä näyttää <optimized out>, tarkoittaa, ettet ole DEBUG=1 -käännöksessä.

  • Vahtipisteet (datakeskeytyspisteet)watch <expr> pysähtyy, kun muuttujaan kirjoitetaan, rwatch lukemisessa, awatch kummassakin. Cortex-M:n DWT-yksikkö tukee noin 4:ää laitteistopohjaista vahtipistettä – korvaamaton sen nappaamiseen, kuka turmeli muuttujan.

  • Rekisterit ja oheislaitteetCortex Registers -näkymä näyttää ydinrekisterit; kun svdFile on asetettu, Peripherals-näkymä purkaa jokaisen oheislaiterekisterin ja bittikentän (DMA, ajastimet, kamera- / CSI-liitäntä, XSPI jne.) – nopein tapa nähdä, miksi ajuri käyttäytyy huonosti.

  • Muisti – käytä Cortex-Debugin muistinkatselinta tai gdb:n x/-komentoa tarkastellaksesi kehyspuskureita, DMA-puskureita ja rakenteita suoraan.

  • printf ilman pysäytystä (SWO/RTT) – ajoituksen suhteen herkkiin ongelmiin Seggerin RTT tai SWO antaa lähes nollakustannuksisen printf-tulosteen kohteen ollessa käynnissä. Käännä asetuksella DEBUG_PRINTF=1 ja lisää Cortex-Debugin rttConfig (RTT) tai swoConfig (SWO, tarvitsee ydinkellon). Tämä on oikea työkalu silloin, kun keskeytyspiste muuttaisi ajoitusta, jota yrität tarkkailla.

  • Yhteyden katkaiseminenStop launch-istunnossa pysäyttää kohteen; Disconnect attach-istunnossa jättää kameran käyntiin. Käynnistä kamera virtakatkolla uudelleen palauttaaksesi sen normaaliin toimintaan jälkeenpäin.

14.1.1.4.8. Virheenjäljityksen sudenkuopat

  • Optimoidut pois -muuttujat. Kaikki näyttää <optimized out> – käänsit DEBUG=0. Käännä uudelleen asetuksella DEBUG=1.

  • ”GDB executable not found” – SDK:n gcc/bin ei ole PATH-muuttujassa; aseta armToolchainPath / gdbPath.

  • ”Cannot connect” / väärä muistikartta – väärä tai puuttuva device-nimi; käytä taulukon tarkkaa merkkijonoa.

  • Keskeytyspisteisiin ei osuta hiljaisesti – liian monta laitteistopohjaista keskeytyspistettä flash-muistissa olevassa koodissa; vähennä niitä.

  • Lähdepolut eivät täsmää (Dockerissa käännetty ELF) – käännä Dockerin build-firmware-dev -kohteella (sama absoluuttinen polku säiliön sisällä ja ulkona) tai aseta gdb:ssä set substitute-path.