14.1.1.4. Depanarea firmware-ului¶
Depanarea pe hardware înseamnă oprirea procesorului, plasarea punctelor de întrerupere în codul sursă C, execuția pas cu pas și inspectarea variabilelor, a memoriei, a regiștrilor și a perifericelor – din interiorul VS Code. Pentru aceasta sunt necesare trei lucruri: o compilare de depanare, o sondă de depanare SWD (un Segger J-Link) și extensia Cortex-Debug care controlează arm-none-eabi-gdb printr-un server GDB J-Link.
14.1.1.4.1. Compilarea pentru depanare¶
Recompilați întotdeauna ținta cu DEBUG=1
make -j$(nproc) TARGET=<TARGET> DEBUG=1
O imagine de versiune finală (DEBUG=0) este compilată cu -O2; în depanator veți vedea <optimized out> pentru multe variabile, funcțiile inline se contopesc în apelanții lor, iar execuția pas cu pas sare imprevizibil. DEBUG=1 compilează cu -Og -ggdb3, ceea ce permite depanarea în timp ce firmware-ul pornește în continuare pe cameră. Fișierul ELF către care îndreptați depanatorul este:
build/<TARGET>/bin/firmware.elf
(Pentru Alif AE3, depanați build/OPENMV_AE3/bin/firmware_M55_HP.elf – nucleul de înaltă performanță.)
14.1.1.4.2. Hardware-ul: J-Link prin SWD¶
Conectați un Segger J-Link la pinii SWD ai camerei (SWDIO, SWCLK, GND și VCC al țintei ca referință; camera este alimentată prin USB ca de obicei). Funcționează un J-Link EDU / Base / Pro. Locul în care apar pinii de depanare diferă de la o cameră la alta – multe plăci au un conector JTAG/SWD dedicat, altele expun SWD pe antetul de I/O sau pe pad-uri de testare – așa că verificați diagrama de pinout și schema acelei plăci în documentația hardware OpenMV pentru a afla ce pini trebuie conectați. Instalați J-Link Software and Documentation Pack de pe segger.com pe mașina la care este conectată fizic sonda. Mențineți-l rezonabil de actualizat – versiunile mai vechi ale software-ului J-Link nu vor cunoaște numele dispozitivelor mai noi (STM32N6, MIMXRT, Alif).
Fiecare MCU are nevoie de numele de dispozitiv J-Link exact, astfel încât sonda să încarce încărcătorul de flash și harta de memorie corecte:
Cameră ( |
MCU |
J-Link |
|---|---|---|
|
STM32F427 |
|
|
STM32F765 |
|
|
STM32H743 |
|
|
STM32N657 |
|
|
MIMXRT1062 |
|
|
Alif Ensemble (M55-HP) |
|
|
STM32H747 |
|
14.1.1.4.3. Configurarea Cortex-Debug în VS Code¶
Creați .vscode/launch.json în depozit. Cazul cel mai simplu – VS Code, J-Link și compilarea se află toate pe aceeași mașină Linux / macOS – folosește servertype: "jlink", ceea ce face ca Cortex-Debug să pornească el însuși un server GDB J-Link:
{
"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"
}
]
}
Modificați executable și device pentru placa dumneavoastră (vedeți tabelul de mai sus). Apăsați F5 pentru a compila-flasha-și-rula până la main și a vă opri acolo.
Sfat
Pentru a recompila automat de fiecare dată când porniți depanarea, adăugați o sarcină de compilare în .vscode/tasks.json și faceți referire la ea din configurația de lansare prin "preLaunchTask". De exemplu, o sarcină care rulează make -j$(nproc) TARGET=OPENMV4 DEBUG=1, numită "build-firmware", plus "preLaunchTask": "build-firmware" în configurația de mai sus, astfel încât F5 să recompileze, să flasheze și să pornească depanatorul într-un singur pas.
Atenționare
Cortex-Debug are nevoie de arm-none-eabi-gdb. Acesta este livrat în SDK la ~/openmv-sdk-<version>/gcc/bin, dar nu se află pe PATH în mod implicit, astfel încât depanarea eșuează cu „GDB executable «arm-none-eabi-gdb» was not found”. Remediați acest lucru fie setând armToolchainPath / gdbPath așa cum se arată mai sus, fie adăugând ~/openmv-sdk-<version>/gcc/bin la PATH (printenv PATH ar trebui apoi să-l listeze).
14.1.1.4.4. Vizualizarea regiștrilor periferici (SVD)¶
Îndreptați Cortex-Debug către un fișier SVD CMSIS pentru a obține o vizualizare decodată a regiștrilor periferici (temporizatoare, DMA, interfața camerei etc.) după nume și câmp de biți:
"svdFile": "/path/to/STM32H743.svd"
Pentru STM32 și MIMXRT, obțineți fișierul SVD din pachetele CMSIS ale ST / NXP sau din registrul SVD al Cortex-Debug. Fișierele SVD pentru Alif sunt incluse în depozitul de firmware la lib/micropython/lib/alif_ensemble-cmsis-dfp/Debug/SVD/ (folosiți ..._CM55_HP_View.svd pentru nucleul HP al AE3).
14.1.1.4.5. Windows: puntea J-Link între WSL și Windows¶
WSL 2 nu poate vedea direct dispozitivul USB al J-Link, așa că împărțirea este: Windows servește sonda (acolo unde este conectată), iar VS Code + gdb rulează în WSL și o ating prin TCP.
Pe Windows, instalați pachetul Segger J-Link și conectați J-Link la un port USB Windows.
Pe Windows, porniți J-Link Remote Server (livrat împreună cu pachetul J-Link): lansați-l cu J-Link conectat și faceți clic pe OK. Permiteți-i trecerea prin firewall-ul Windows atunci când vi se solicită. Fereastra afișează adresa IP pe care servește sonda – notați-o.
În WSL, compilați cu
DEBUG=1și asigurați-vă căarm-none-eabi-gdbeste accesibil (setațiarmToolchainPathca mai sus).În VS Code din WSL, păstrați
servertype: "jlink"– serverul GDB rulează în WSL și ajunge la sondă prin Remote Server – și adăugațiserverpath+ipAddress{ "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" }
Setați
ipAddressla adresa pe care o afișează fereastra Remote Server. Aceasta este toată puntea.
Sfat
Alternativă la puntea cu server GDB: usbipd-win. În loc să rulați un server pe Windows, puteți atașa dispozitivul USB al J-Link direct în WSL cu usbipd-win. Dintr-un PowerShell de administrator:
winget install usbipd
usbipd list
usbipd bind --busid <busid>
usbipd attach --wsl --busid <busid>
(<busid> este ID-ul de magistrală al J-Link din usbipd list.) Sonda apare apoi în interiorul WSL, iar dumneavoastră folosiți configurația simplă pe aceeași mașină servertype: "jlink" din Configurarea Cortex-Debug în VS Code fără adresă IP și fără un server Windows separat. Puntea cu server GDB necesită mai puțină configurare pentru utilizare ocazională; usbipd-win este mai convenabil pentru dezvoltarea de rutină.
Sfat
Folosiți "request": "attach" pentru a depana firmware-ul în timp ce rulează deja fără a-l reseta sau reflasha – ideal pentru a surprinde o blocare pe teren. Folosiți "request": "launch" pentru a reseta, a flasha fișierul ELF și a porni de la zero la runToEntryPoint.
14.1.1.4.6. Depanare din linia de comandă cu gdbrunner¶
Configurarea manuală a unei sesiuni GDB împotriva unei ținte încorporate este un dans în cinci pași: porniți serverul GDB J-Link / ST-Link într-o fereastră cu opțiunile corecte de dispozitiv, port și interfață; așteptați să afișeze Waiting for GDB connection; rulați arm-none-eabi-gdb într-o a doua fereastră; tastați target remote localhost:<port>; îndreptați gdb către fișierul ELF. Când sesiunea gdb se încheie, nu uitați să închideți fereastra serverului. gdbrunner este un mic CLI care reduce toate acestea la o singură comandă în prim-plan. Este livrat în mediul Python al SDK-ului OpenMV, deci nu este nimic de instalat; punctul de intrare obișnuit este ținta make debug a depozitului de firmware:
make -j$(nproc) TARGET=<TARGET> DEBUG=1 debug
Aceasta rulează gdbrunner cu argumentele depanatorului din configurația plăcii – numele de dispozitiv J-Link și, acolo unde este necesar, încărcătorul de flash extern ST-Link – cu arm-none-eabi-gdb al SDK-ului deja pe PATH. Backend-ul implicit este J-Link; make DEBUGGER=STLINK debug funcționează în schimb cu o sondă ST-Link.
gdbrunner poate fi invocat și direct (în afara SDK-ului, 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
Primul argument pozițional alege backend-ul de server (jlink, stlink, qemu); restul sunt transmise acelui backend, cu valori implicite care funcționează pentru camerele OpenMV. gdbrunner --help listează lista completă de opțiuni per backend; tabelul de argumente al fiecărui backend este bazat pe JSON (src/gdbrunner/backends.json), astfel încât adăugarea unui server nou este o modificare de configurație, nu de cod.
Ce face gdbrunner pentru lucrul din linia de comandă:
Un singur proces, ciclu de viață curat. Serverul pornește, gdb se atașează când portul este deschis, serverul este terminat curat când gdb iese. Niciun
JLinkGDBServerorfan care să supraviețuiască sesiunii, niciun al doilea terminal de gestionat.Descoperire automată STM32CubeProgrammer. Backend-ul
stlinkcaută în locațiile uzuale de instalare (~/STM32CubeProgrammer/,/opt/st/, arborele de plugin-uri STM32CubeIDE) instrumentele STM32CubeProgrammer, astfel încât calea lungă--cube-progsă nu trebuiască tastată de fiecare dată. SDK-ul include propria copie la~/openmv-sdk-<version>/stcubeprog/bin– îndreptați--cube-progacolo dacă nu există o instalare la nivel de sistem.Fișierul gdbinit per proiect este respectat. Un fișier
.gdbinitdin directorul curent este încărcat cu-ix– suprascriind fișierul~/.gdbinitla nivel de utilizator – astfel încât scripturile gdb specifice proiectului (pretty-printer-e, macrouri specifice plăcii, seturi de puncte de întrerupere) intră în vigoare doar prin prezența lor în directorul de lucru.make debugrulează din rădăcina depozitului, deci un.gdbinitde acolo se aplică.Rulare de probă.
--dryrunafișează comanda serverului fără a o rula, util pentru adaptarea invocării la un script wrapper, pentru copierea ei într-o configurație de lansare a unui IDE sau pur și simplu pentru a verifica ce argumente compune gdbrunner.Ieșirea serverului vizibilă.
--show-outputmenține vizibile stdout / stderr ale serverului. Implicit acestea sunt suprimate (astfel încât interfața gdb să rămână curată); activați opțiunea atunci când serverul însuși este cel care se comportă greșit.Backend QEMU.
qemu-system-armdepanează o compilare de firmware fără nicio placă conectată. ȚintaMPS2_AN500selectează acest backend în configurația sa de placă, astfel încâtmake TARGET=MPS2_AN500 DEBUG=1 debugcompilează pentru mașinamps2-an500a QEMU și parcurge pas cu pas codul independent de platformă – tot ce nu atinge perifericele specifice camerei – în zbor. (qemu-system-armeste o instalare pe gazdă, nu face parte din SDK.)
Pentru execuția pas cu pas la nivel de sursă cu marcaje pentru punctele de întrerupere în marginea ferestrei și o vizualizare a regiștrilor periferici, configurația Cortex-Debug din VS Code de mai sus este instrumentul mai bun; gdbrunner este cel potrivit pentru tot ce trăiește în linia de comandă.
14.1.1.4.7. Utilizarea depanatorului¶
Odată ce o sesiune rulează (procesorul oprit la main):
Puncte de întrerupere – faceți clic în marginea de lângă o linie C sau, în Debug Console,
break <file>:<line>/break <function>. Nucleele Cortex-M au un număr mic de comparatoare hardware pentru puncte de întrerupere (de obicei 6–8 pe M7 / H7, 8 pe M55). Depășirea acestui număr pe cod aflat în flash eșuează în tăcere – mențineți modest numărul de puncte de întrerupere active.Execuție pas cu pas – F10 peste (
next), F11 în interior (step), Shift+F11 ieșire (finish), F5 continuare. Execuția pas cu pas la nivel de instrucțiune estestepi/nextiîn Debug Console.Variabile / urmărire / stivă de apeluri – panourile Variables și Call Stack afișează variabilele locale și urma de execuție; adăugați expresii în Watch. Treceți cu mouse-ul peste o variabilă din sursă pentru a-i vedea valoarea. Orice afișează
<optimized out>înseamnă că nu vă aflați pe o compilareDEBUG=1.Puncte de urmărire (puncte de întrerupere pe date) –
watch <expr>se oprește când o variabilă este scrisă,rwatchla citire,awatchla oricare dintre ele. Unitatea DWT a Cortex-M acceptă ~4 puncte de urmărire hardware – de neprețuit pentru a surprinde cine a corupt o variabilă.Regiștri și periferice – vizualizarea Cortex Registers afișează regiștrii nucleului; cu
svdFilesetat, vizualizarea Peripherals decodifică fiecare registru și câmp de biți periferic (DMA, temporizatoare, interfața camerei / CSI, XSPI etc.) – cea mai rapidă modalitate de a vedea de ce un driver se comportă greșit.Memorie – folosiți vizualizatorul de memorie Cortex-Debug sau
x/din gdb pentru a inspecta direct tampoanele de cadre, tampoanele DMA și structurile.printf fără oprire (SWO/RTT) – pentru probleme sensibile la sincronizare, RTT sau SWO de la Segger oferă
printfcu suprasarcină aproape nulă în timp ce ținta rulează. Compilați cuDEBUG_PRINTF=1și adăugațirttConfig(RTT) sauswoConfig(SWO, are nevoie de frecvența nucleului) din Cortex-Debug. Acesta este instrumentul potrivit atunci când un punct de întrerupere ar modifica sincronizarea pe care încercați să o observați.Deconectare – Stop într-o sesiune
launchoprește ținta; Disconnect într-o sesiuneattachlasă camera să ruleze. Reporniți alimentarea camerei pentru a o readuce ulterior la funcționarea normală.
14.1.1.4.8. Capcane în depanare¶
Variabile optimizate. Totul afișează
<optimized out>– ați compilat cuDEBUG=0. Recompilați cuDEBUG=1.„GDB executable not found” – directorul
gcc/binal SDK-ului nu se află pePATH; setațiarmToolchainPath/gdbPath.„Cannot connect” / hartă de memorie greșită – nume
devicegreșit sau lipsă; folosiți șirul exact din tabel.Puncte de întrerupere care nu sunt atinse în tăcere – prea multe puncte de întrerupere hardware pe cod rezident în flash; reduceți-le.
Căile sursă nu se potrivesc (ELF compilat cu Docker) – compilați cu ținta Docker
build-firmware-dev(aceeași cale absolută în interiorul și în afara containerului) sau setați în gdbset substitute-path.