14.1.1.4. Depuração do firmware¶
A depuração em hardware significa parar o processador, definir pontos de interrupção no código-fonte C, executar passo a passo e inspecionar variáveis, memória, registos e periféricos – a partir do VS Code. Para isso são necessários três elementos: uma compilação de depuração, uma sonda de depuração SWD (um Segger J-Link) e a extensão Cortex-Debug a conduzir o arm-none-eabi-gdb contra um servidor GDB do J-Link.
14.1.1.4.1. Compilar para depuração¶
Recompile sempre o alvo com DEBUG=1
make -j$(nproc) TARGET=<TARGET> DEBUG=1
Uma imagem de lançamento (DEBUG=0) é compilada com -O2; no depurador verá <optimized out> para muitas variáveis, as funções inline colapsar-se-ão nos seus chamadores e a execução passo a passo salta de forma imprevisível. O DEBUG=1 compila com -Og -ggdb3, que é depurável mas ainda arranca na câmara. O ELF que se aponta ao depurador é:
build/<TARGET>/bin/firmware.elf
(Para o Alif AE3, depure build/OPENMV_AE3/bin/firmware_M55_HP.elf – o núcleo de alto desempenho.)
14.1.1.4.2. O hardware: J-Link via SWD¶
Ligue um Segger J-Link aos pinos SWD da câmara (SWDIO, SWCLK, GND e VCC alvo para referência; a câmara é alimentada via USB como habitualmente). Funcionam um J-Link EDU / Base / Pro. Os pinos de depuração variam consoante a câmara – muitas placas têm um conector JTAG/SWD dedicado, outras expõem o SWD no cabeçalho de I/O ou em pads de teste – portanto consulte o diagrama de pinos e o esquemático dessa placa na documentação de hardware do OpenMV para saber que pinos ligar. Instale o J-Link Software and Documentation Pack em segger.com na máquina onde a sonda está fisicamente ligada. Mantenha-o relativamente atualizado – software J-Link mais antigo não conhece os nomes de dispositivos mais recentes (STM32N6, MIMXRT, Alif).
Cada MCU precisa do seu nome de dispositivo J-Link exato para que a sonda carregue o flash loader e o mapa de memória corretos:
Câmara ( |
MCU |
J-Link |
|---|---|---|
|
STM32F427 |
|
|
STM32F765 |
|
|
STM32H743 |
|
|
STM32N657 |
|
|
MIMXRT1062 |
|
|
Alif Ensemble (M55-HP) |
|
|
STM32H747 |
|
14.1.1.4.3. Configuração do Cortex-Debug no VS Code¶
Crie .vscode/launch.json no repositório. O caso mais simples – VS Code, o J-Link e a compilação estão todos na mesma máquina Linux / macOS – utiliza servertype: "jlink", o que faz o Cortex-Debug iniciar ele próprio um servidor GDB do 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"
}
]
}
Altere executable e device para a sua placa (consulte a tabela acima). Prima F5 para compilar, flashar e executar até main e parar aí.
Dica
Para recompilar automaticamente sempre que inicia a depuração, adicione uma tarefa de compilação a .vscode/tasks.json e referencie-a na configuração de lançamento com "preLaunchTask". Por exemplo, uma tarefa que execute make -j$(nproc) TARGET=OPENMV4 DEBUG=1, com o nome "build-firmware", mais "preLaunchTask": "build-firmware" na configuração acima, para que F5 recompile, flashe e inicie o depurador num único passo.
Aviso
O Cortex-Debug precisa do arm-none-eabi-gdb. É fornecido no SDK em ~/openmv-sdk-<version>/gcc/bin, mas não está no PATH por defeito, pelo que a depuração falha com «GDB executable “arm-none-eabi-gdb” was not found». Corrija isto definindo armToolchainPath / gdbPath conforme mostrado acima, ou adicionando ~/openmv-sdk-<version>/gcc/bin ao seu PATH (printenv PATH deverá então listá-lo).
14.1.1.4.4. Vista de registos de periféricos (SVD)¶
Aponte o Cortex-Debug para um ficheiro SVD CMSIS para obter uma vista descodificada dos registos de periféricos (temporizadores, DMA, a interface da câmara, etc.) por nome e campo de bits:
"svdFile": "/path/to/STM32H743.svd"
Para STM32 e MIMXRT, obtenha o SVD dos packs CMSIS ST / NXP ou do registo SVD do Cortex-Debug. Os SVDs do Alif estão incluídos no repositório do firmware em lib/micropython/lib/alif_ensemble-cmsis-dfp/Debug/SVD/ (utilize o ..._CM55_HP_View.svd para o núcleo HP do AE3).
14.1.1.4.5. Windows: a ponte J-Link WSL ↔ Windows¶
O WSL 2 não consegue ver diretamente o dispositivo USB do J-Link, por isso a divisão é: o Windows serve a sonda (onde está ligada) e o VS Code + gdb correm no WSL e acedem a ela via TCP.
No Windows, instale o pack Segger J-Link e ligue o J-Link a uma porta USB do Windows.
No Windows, inicie o J-Link Remote Server (é fornecido com o pack J-Link): lance-o com o J-Link ligado e clique em OK. Permita-o através da firewall do Windows quando solicitado. A janela mostra o endereço IP em que está a servir a sonda – tome nota dele.
No WSL, compile com
DEBUG=1e certifique-se de que oarm-none-eabi-gdbé acessível (definaarmToolchainPathconforme acima).No VS Code do WSL, mantenha
servertype: "jlink"– o servidor GDB corre no WSL e acede à sonda através do Remote Server – e adicioneserverpath+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" }
Defina
ipAddresspara o endereço que a janela do Remote Server mostra. Esta é toda a ponte.
Dica
Alternativa à ponte do servidor GDB: usbipd-win. Em vez de executar um servidor no Windows, pode ligar o dispositivo USB do J-Link diretamente ao WSL com usbipd-win. A partir de um PowerShell de administrador:
winget install usbipd
usbipd list
usbipd bind --busid <busid>
usbipd attach --wsl --busid <busid>
(<busid> é o ID de barramento do J-Link obtido de usbipd list.) A sonda aparece então dentro do WSL, e utiliza a configuração servertype: "jlink" da mesma máquina, descrita em VS Code Cortex-Debug setup, sem endereço IP e sem servidor Windows separado. A ponte do servidor GDB requer menos configuração para uso ocasional; o usbipd-win é mais conveniente para desenvolvimento rotineiro.
Dica
Utilize "request": "attach" para depurar o firmware enquanto já está em execução sem reiniciar nem reflashear – ideal para detetar um bloqueio em campo. Utilize "request": "launch" para reiniciar, flashear o ELF e começar do início em runToEntryPoint.
14.1.1.4.6. Depuração em linha de comandos com gdbrunner¶
Configurar uma sessão GDB contra um alvo embebido manualmente é um processo de cinco etapas: iniciar o servidor GDB do J-Link / ST-Link numa janela com os flags corretos de dispositivo, porta e interface; aguardar que imprima Waiting for GDB connection; executar arm-none-eabi-gdb numa segunda janela; digitar target remote localhost:<port>; apontar o gdb ao ELF. Quando a sessão gdb termina, lembrar de terminar a janela do servidor. O gdbrunner é um pequeno CLI que condensa tudo isto num único comando em primeiro plano. É fornecido no ambiente Python do SDK do OpenMV, pelo que não há nada a instalar; o ponto de entrada habitual é o alvo make debug do repositório do firmware:
make -j$(nproc) TARGET=<TARGET> DEBUG=1 debug
Isto executa o gdbrunner com os argumentos do depurador da configuração da placa – o nome de dispositivo J-Link e, quando necessário, o flash loader externo ST-Link – com o arm-none-eabi-gdb do SDK já no PATH. O backend padrão é J-Link; make DEBUGGER=STLINK debug funciona com uma sonda ST-Link.
O gdbrunner também pode ser invocado diretamente (fora do 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
O primeiro argumento posicional escolhe o backend do servidor (jlink, stlink, qemu); os restantes são repassados a esse backend, com valores padrão que funcionam para as câmaras OpenMV. gdbrunner --help lista a lista completa de flags por backend; a tabela de argumentos de cada backend é controlada por JSON (src/gdbrunner/backends.json), pelo que adicionar um novo servidor é uma edição de configuração e não de código.
O que o gdbrunner faz para trabalho em linha de comandos:
Um processo, ciclo de vida limpo. O servidor inicia, o gdb liga-se quando a porta está aberta, o servidor é terminado de forma limpa quando o gdb sai. Sem
JLinkGDBServerórfão a sobreviver à sessão, sem dois terminais para gerir.Descoberta automática do STM32CubeProgrammer. O backend
stlinkprocura nas localizações de instalação habituais (~/STM32CubeProgrammer/,/opt/st/, a árvore de plugins do STM32CubeIDE) as ferramentas do STM32CubeProgrammer, pelo que o longo caminho--cube-prognão precisa de ser digitado de cada vez. O SDK inclui a sua própria cópia em~/openmv-sdk-<version>/stcubeprog/bin– aponte--cube-progpara lá se não existir uma instalação do sistema.Honra o gdbinit do projeto. Um
.gdbinitno diretório atual é carregado com-ix– substituindo o~/.gdbinitglobal do utilizador – para que scripts gdb por projeto (pretty-printers, macros específicas de placa, conjuntos de pontos de interrupção) sejam aplicados simplesmente por estar presentes no diretório de trabalho. Omake debugexecuta a partir da raiz do repositório, pelo que um.gdbinitaí aplica-se.Execução de teste.
--dryrunimprime o comando do servidor sem o executar, útil para adaptar a invocação a um script de wrapper, copiá-lo para uma configuração de lançador de IDE ou simplesmente verificar que argumentos o gdbrunner está a compor.Saída do servidor visível.
--show-outputmantém o stdout / stderr do servidor visível. O padrão suprime-o (para que a interface do gdb se mantenha limpa); inverta o flag quando o problema está no próprio servidor.Backend QEMU.
qemu-system-armdepura uma compilação de firmware sem nenhuma placa ligada. O alvoMPS2_AN500seleciona este backend na sua configuração de placa, pelo quemake TARGET=MPS2_AN500 DEBUG=1 debugcompila para a máquinamps2-an500do QEMU e executa passo a passo o código independente de plataforma – tudo o que não toca em periféricos específicos da câmara – em voo. (qemu-system-armé uma instalação do sistema anfitrião, não faz parte do SDK.)
Para execução passo a passo ao nível do código-fonte com indicadores de ponto de interrupção e vista de registos de periféricos, a configuração do Cortex-Debug no VS Code descrita acima é a melhor ferramenta; o gdbrunner é a certa para tudo o que vive na linha de comandos.
14.1.1.4.7. Utilizar o depurador¶
Uma vez que uma sessão esteja a decorrer (o processador parado em main):
Pontos de interrupção – clique na margem junto a uma linha C, ou na Consola de Depuração
break <file>:<line>/break <function>. Os núcleos Cortex-M têm um número reduzido de hardware breakpoint comparators (tipicamente 6–8 no M7 / H7, 8 no M55). Exceder esse número em código em flash falha silenciosamente – mantenha o número de pontos de interrupção ativos modesto.Execução passo a passo – F10 passo por cima (
next), F11 passo para dentro (step), Shift+F11 passo para fora (finish), F5 continuar. A execução passo a passo ao nível de instrução éstepi/nextina Consola de Depuração.Variáveis / vigiar / pilha de chamadas – os painéis Variables e Call Stack mostram variáveis locais e o backtrace; adicione expressões a Watch. Passe o rato sobre uma variável no código-fonte para ver o seu valor. Qualquer coisa que mostre
<optimized out>significa que não está numa compilaçãoDEBUG=1.Watchpoints (pontos de interrupção de dados) –
watch <expr>para quando uma variável é escrita,rwatchna leitura,awatchem ambos. A unidade DWT do Cortex-M suporta ~4 watchpoints de hardware – inestimável para detetar quem corrompeu uma variável.Registos e periféricos – a vista Cortex Registers mostra os registos do núcleo; com
svdFiledefinido, a vista Peripherals descodifica todos os registos e campos de bits de periféricos (DMA, temporizadores, a interface câmara / CSI, XSPI, etc.) – a forma mais rápida de ver porque é que um driver está a funcionar mal.Memória – utilize o visualizador de memória do Cortex-Debug ou o
x/do gdb para inspecionar diretamente buffers de fotograma, buffers DMA e estruturas.printf sem parar (SWO/RTT) – para problemas sensíveis ao tempo, o RTT ou SWO da Segger fornece
printfcom sobrecarga quase nula enquanto o alvo está em execução. Compile comDEBUG_PRINTF=1e adicionerttConfig(RTT) ouswoConfig(SWO, precisa do clock do núcleo) do Cortex-Debug. Esta é a ferramenta certa quando um ponto de interrupção alteraria o tempo que está a tentar observar.Desligar – Stop numa sessão
launchpara o alvo; Disconnect numa sessãoattachdeixa a câmara a correr. Desligue e ligue a câmara novamente para a devolver ao funcionamento normal.
14.1.1.4.8. Armadilhas de depuração¶
Variáveis otimizadas para fora. Tudo mostra
<optimized out>– compilou comDEBUG=0. Recompile comDEBUG=1.«GDB executable not found» – o
gcc/bindo SDK não está noPATH; definaarmToolchainPath/gdbPath.«Cannot connect» / mapa de memória errado – nome
deviceerrado ou em falta; utilize a string exata da tabela.Pontos de interrupção silenciosamente não ativados – demasiados pontos de interrupção de hardware em código residente em flash; reduza-os.
Caminhos de código-fonte não coincidem (ELF compilado com Docker) – compile com o alvo
build-firmware-devdo Docker (mesmo caminho absoluto dentro e fora do contentor) ou definaset substitute-pathno gdb.