14.1.1.4. Débogage du micrologiciel¶
Le débogage sur matériel consiste à arrêter le processeur, à poser des points d’arrêt dans le code source C, à exécuter pas à pas et à inspecter les variables, la mémoire, les registres et les périphériques – le tout depuis VS Code. Cela nécessite trois éléments : une version de débogage, une sonde de débogage SWD (une Segger J-Link) et l’extension Cortex-Debug pilotant arm-none-eabi-gdb face à un serveur GDB J-Link.
14.1.1.4.1. Compiler pour le débogage¶
Recompilez toujours la cible avec DEBUG=1
make -j$(nproc) TARGET=<TARGET> DEBUG=1
Une image de production (DEBUG=0) est compilée en -O2 ; dans le débogueur, vous verrez <optimized out> pour de nombreuses variables, les fonctions en ligne se fondent dans leurs appelants et l’exécution pas à pas saute de façon imprévisible. Les versions DEBUG=1 sont compilées en -Og -ggdb3, ce qui les rend débogables tout en démarrant correctement sur la caméra. L’ELF que vous indiquez au débogueur est
build/<TARGET>/bin/firmware.elf
(Pour l’Alif AE3, déboguez build/OPENMV_AE3/bin/firmware_M55_HP.elf – le cœur haute performance.)
14.1.1.4.2. Le matériel : J-Link via SWD¶
Connectez une Segger J-Link aux broches SWD de la caméra (SWDIO, SWCLK, GND, et le VCC de la cible comme référence ; la caméra est alimentée par USB comme d’habitude). Une J-Link EDU / Base / Pro fonctionnent toutes. L’emplacement des broches de débogage diffère selon la caméra – de nombreuses cartes disposent d’un connecteur JTAG/SWD dédié, d’autres exposent le SWD sur l’embase d’E/S ou sur des plots de test – consultez donc le schéma de brochage et le schéma électrique de la carte concernée dans la documentation matérielle d’OpenMV pour savoir quelles broches câbler. Installez le J-Link Software and Documentation Pack depuis segger.com sur la machine où la sonde est physiquement branchée. Gardez-le raisonnablement à jour – les anciennes versions du logiciel J-Link ne connaîtront pas les noms de périphériques plus récents (STM32N6, MIMXRT, Alif).
Chaque MCU a besoin de son nom de périphérique J-Link exact afin que la sonde charge le bon chargeur de mémoire flash et le bon plan mémoire :
Caméra ( |
MCU |
|
|---|---|---|
|
STM32F427 |
|
|
STM32F765 |
|
|
STM32H743 |
|
|
STM32N657 |
|
|
MIMXRT1062 |
|
|
Alif Ensemble (M55-HP) |
|
|
STM32H747 |
|
14.1.1.4.3. Configuration de Cortex-Debug dans VS Code¶
Créez .vscode/launch.json dans le dépôt. Le cas le plus simple – VS Code, la J-Link et la version compilée sont tous sur la même machine Linux / macOS – utilise servertype: "jlink", ce qui fait que Cortex-Debug démarre lui-même un serveur 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"
}
]
}
Modifiez executable et device selon votre carte (voir le tableau ci-dessus). Appuyez sur F5 pour compiler-flasher-et-exécuter jusqu’à main et vous y arrêter.
Astuce
Pour recompiler automatiquement à chaque démarrage du débogage, ajoutez une tâche de compilation à .vscode/tasks.json et référencez-la depuis la configuration de lancement avec "preLaunchTask". Par exemple une tâche exécutant make -j$(nproc) TARGET=OPENMV4 DEBUG=1, nommée "build-firmware", plus "preLaunchTask": "build-firmware" dans la configuration ci-dessus, afin que F5 recompile, flashe et démarre le débogueur en une seule étape.
Avertissement
Cortex-Debug a besoin de arm-none-eabi-gdb. Il est fourni dans le SDK sous ~/openmv-sdk-<version>/gcc/bin mais ne figure pas dans le PATH par défaut, si bien que le débogage échoue avec « GDB executable “arm-none-eabi-gdb” was not found ». Corrigez cela soit en définissant armToolchainPath / gdbPath comme indiqué ci-dessus, soit en ajoutant ~/openmv-sdk-<version>/gcc/bin à votre PATH (printenv PATH devrait alors le lister).
14.1.1.4.4. Vue des registres de périphériques (SVD)¶
Faites pointer Cortex-Debug vers un fichier SVD CMSIS pour obtenir une vue décodée des registres de périphériques (minuteurs, DMA, l’interface caméra, etc.) par nom et par champ de bits
"svdFile": "/path/to/STM32H743.svd"
Pour STM32 et MIMXRT, récupérez le SVD depuis les packs CMSIS de ST / NXP ou depuis le registre SVD de Cortex-Debug. Les SVD Alif sont fournis dans le dépôt du micrologiciel sous lib/micropython/lib/alif_ensemble-cmsis-dfp/Debug/SVD/ (utilisez le ..._CM55_HP_View.svd pour le cœur HP de l’AE3).
14.1.1.4.5. Windows : le pont J-Link WSL ↔ Windows¶
WSL 2 ne peut pas voir directement le périphérique USB de la J-Link ; la répartition est donc la suivante : Windows sert la sonde (là où elle est branchée) tandis que VS Code + gdb s’exécutent dans WSL et l’atteignent via TCP.
Sous Windows, installez le pack Segger J-Link et branchez la J-Link sur un port USB de Windows.
Sous Windows, démarrez le J-Link Remote Server (fourni avec le pack J-Link) : lancez-le avec la J-Link branchée et cliquez sur OK. Autorisez-le à travers le pare-feu Windows lorsque vous y êtes invité. La fenêtre affiche l”adresse IP sur laquelle elle sert la sonde – notez-la.
Dans WSL, compilez avec
DEBUG=1et assurez-vous quearm-none-eabi-gdbest accessible (définissezarmToolchainPathcomme ci-dessus).Dans VS Code sous WSL, conservez
servertype: "jlink"– le serveur GDB s’exécute dans WSL et atteint la sonde via le Remote Server – et ajoutezserverpath+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" }
Réglez
ipAddresssur l’adresse affichée par la fenêtre du Remote Server. C’est tout le pont.
Astuce
Alternative au pont serveur GDB : usbipd-win. Au lieu d’exécuter un serveur sous Windows, vous pouvez rattacher directement le périphérique USB de la J-Link dans WSL avec usbipd-win. Depuis une invite PowerShell d’administrateur
winget install usbipd
usbipd list
usbipd bind --busid <busid>
usbipd attach --wsl --busid <busid>
(<busid> est l’identifiant de bus de la J-Link, obtenu via usbipd list.) La sonde apparaît alors à l’intérieur de WSL, et vous utilisez la configuration ordinaire en machine unique servertype: "jlink" issue de Configuration de Cortex-Debug dans VS Code sans adresse IP ni serveur Windows distinct. Le pont serveur GDB nécessite moins de configuration pour un usage occasionnel ; usbipd-win est plus pratique pour le développement courant.
Astuce
Utilisez "request": "attach" pour déboguer le micrologiciel tel qu’il est déjà en cours d’exécution sans le réinitialiser ni le reflasher – idéal pour intercepter un blocage sur le terrain. Utilisez "request": "launch" pour réinitialiser, flasher l’ELF et repartir de zéro à runToEntryPoint.
14.1.1.4.6. Débogage en ligne de commande avec gdbrunner¶
Mettre en place une session GDB face à une cible embarquée à la main est une chorégraphie en cinq étapes : démarrer le serveur GDB J-Link / ST-Link dans une fenêtre avec les bons indicateurs de périphérique, de port et d’interface ; attendre qu’il affiche Waiting for GDB connection ; lancer arm-none-eabi-gdb dans une deuxième fenêtre ; taper target remote localhost:<port> ; faire pointer gdb vers l’ELF. À la fin de la session gdb, ne pas oublier de fermer la fenêtre du serveur. gdbrunner est un petit utilitaire en ligne de commande qui condense tout cela en une seule commande au premier plan. Il est fourni dans l’environnement Python du SDK OpenMV, il n’y a donc rien à installer ; le point d’entrée habituel est la cible make debug du dépôt du micrologiciel
make -j$(nproc) TARGET=<TARGET> DEBUG=1 debug
Cela exécute gdbrunner avec les arguments du débogueur issus de la configuration de la carte – le nom de périphérique J-Link et, lorsque c’est nécessaire, le chargeur de mémoire flash externe ST-Link – avec le arm-none-eabi-gdb du SDK déjà dans le PATH. Le backend par défaut est J-Link ; make DEBUGGER=STLINK debug fonctionne plutôt avec une sonde ST-Link.
gdbrunner peut aussi être invoqué directement (en dehors du 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
Le premier argument positionnel sélectionne le backend serveur (jlink, stlink, qemu) ; le reste est transmis à ce backend, avec des valeurs par défaut qui conviennent aux caméras OpenMV. gdbrunner --help liste l’ensemble des indicateurs par backend ; le tableau d’arguments de chaque backend est piloté par JSON (src/gdbrunner/backends.json), de sorte qu’ajouter un nouveau serveur relève d’une modification de configuration plutôt que de code.
Ce que gdbrunner fait pour le travail en ligne de commande :
Un seul processus, cycle de vie propre. Le serveur démarre, gdb s’y attache lorsque le port est ouvert, le serveur est arrêté proprement à la sortie de gdb. Aucun
JLinkGDBServerorphelin ne survit à la session, aucun deuxième terminal à gérer.Découverte automatique de STM32CubeProgrammer. Le backend
stlinkrecherche les emplacements d’installation habituels (~/STM32CubeProgrammer/,/opt/st/, l’arborescence du greffon STM32CubeIDE) pour trouver les outils STM32CubeProgrammer, de sorte que le long chemin--cube-progn’a pas à être saisi à chaque fois. Le SDK fournit sa propre copie sous~/openmv-sdk-<version>/stcubeprog/bin– faites pointer--cube-progvers cet emplacement s’il n’existe aucune installation système.Prise en compte du gdbinit par projet. Un fichier
.gdbinitsitué dans le répertoire courant est chargé avec-ix– ce qui remplace le~/.gdbinitglobal de l’utilisateur – de sorte qu’un script gdb propre au projet (pretty-printers, macros spécifiques à la carte, jeux de points d’arrêt) s’intègre simplement par sa présence dans le répertoire de travail.make debugs’exécute depuis la racine du dépôt, un.gdbinitprésent à cet endroit s’applique donc.Exécution à blanc.
--dryrunaffiche la commande du serveur sans l’exécuter, utile pour adapter l’invocation à un script enveloppant, la copier dans la configuration de lancement d’un IDE, ou simplement vérifier quels arguments gdbrunner compose.Sortie du serveur visible.
--show-outputconserve la visibilité de la sortie stdout / stderr du serveur. Le comportement par défaut la supprime (afin que l’interface de gdb reste épurée) ; activez l’indicateur lorsque c’est le serveur lui-même qui se comporte mal.Backend QEMU.
qemu-system-armdébogue une version compilée du micrologiciel sans aucune carte branchée. La cibleMPS2_AN500sélectionne ce backend dans sa configuration de carte, de sorte quemake TARGET=MPS2_AN500 DEBUG=1 debugcompile pour la machinemps2-an500de QEMU et exécute pas à pas le code indépendant de la plateforme – tout ce qui ne touche pas aux périphériques spécifiques à la caméra – en plein vol. (qemu-system-armest une installation côté hôte, ne faisant pas partie du SDK.)
Pour une exécution pas à pas au niveau du code source avec marges de points d’arrêt et une vue des registres de périphériques, la configuration Cortex-Debug dans VS Code décrite ci-dessus est le meilleur outil ; gdbrunner est l’outil idéal pour tout ce qui se passe en ligne de commande.
14.1.1.4.7. Utiliser le débogueur¶
Une fois qu’une session est en cours d’exécution (le processeur étant arrêté à main) :
Points d’arrêt – cliquez dans la marge à côté d’une ligne C, ou dans la console de débogage
break <file>:<line>/break <function>. Les cœurs Cortex-M disposent d’un petit nombre de comparateurs de points d’arrêt matériels (typiquement 6 à 8 sur M7 / H7, 8 sur M55). Dépasser ce nombre sur du code en mémoire flash échoue silencieusement – gardez un nombre de points d’arrêt actifs modeste.Exécution pas à pas – F10 pas par-dessus (
next), F11 pas à l’intérieur (step), Shift+F11 sortie de la fonction (finish), F5 continuer. L’exécution pas à pas au niveau instruction se fait avecstepi/nextidans la console de débogage.Variables / surveillance / pile d’appels – les volets Variables et Call Stack affichent les variables locales et la trace d’appels ; ajoutez des expressions à Watch. Survolez une variable dans le code source pour voir sa valeur. Tout ce qui affiche
<optimized out>signifie que vous n’êtes pas sur une versionDEBUG=1.Points de surveillance (points d’arrêt sur données) –
watch <expr>s’arrête lorsqu’une variable est écrite,rwatchà la lecture,awatchdans les deux cas. L’unité DWT des Cortex-M prend en charge environ 4 points de surveillance matériels – précieux pour déterminer qui a corrompu une variable.Registres et périphériques – la vue Cortex Registers affiche les registres du cœur ; lorsque
svdFileest défini, la vue Peripherals décode chaque registre de périphérique et chaque champ de bits (DMA, minuteurs, l’interface caméra / CSI, XSPI, etc.) – le moyen le plus rapide de comprendre pourquoi un pilote se comporte mal.Mémoire – utilisez la visionneuse de mémoire de Cortex-Debug ou la commande
x/de gdb pour inspecter directement les tampons d’image, les tampons DMA et les structures.printf sans arrêt (SWO/RTT) – pour les problèmes sensibles au timing, le RTT ou le SWO de Segger fournissent un
printfà surcharge quasi nulle pendant que la cible s’exécute. Compilez avecDEBUG_PRINTF=1et ajoutez lerttConfig(RTT) ou leswoConfig(SWO, qui nécessite l’horloge du cœur) de Cortex-Debug. C’est l’outil adéquat lorsqu’un point d’arrêt modifierait le timing que vous cherchez à observer.Déconnexion – Stop sur une session
launcharrête la cible ; Disconnect sur une sessionattachlaisse la caméra en fonctionnement. Effectuez ensuite un cycle d’alimentation de la caméra pour la ramener à un fonctionnement normal.
14.1.1.4.8. Pièges du débogage¶
Variables optimisées. Tout affiche
<optimized out>– vous avez compilé enDEBUG=0. Recompilez avecDEBUG=1.« GDB executable not found » – le
gcc/bindu SDK n’est pas dans lePATH; définissezarmToolchainPath/gdbPath.« Cannot connect » / plan mémoire erroné – nom de
deviceerroné ou manquant ; utilisez la chaîne exacte issue du tableau.Points d’arrêt silencieusement non atteints – trop de points d’arrêt matériels sur du code résidant en mémoire flash ; réduisez-les.
Les chemins source ne correspondent pas (ELF compilé via Docker) – compilez avec la cible Docker
build-firmware-dev(même chemin absolu à l’intérieur et à l’extérieur du conteneur) ou définissezset substitute-pathdans gdb.