14.1.1.4. Отладка прошивки¶
Аппаратная отладка означает остановку процессора, установку точек останова в исходном коде на C, пошаговое выполнение и просмотр переменных, памяти, регистров и периферийных устройств – прямо из VS Code. Для этого нужны три вещи: отладочная сборка, отладочный зонд SWD (Segger J-Link) и расширение Cortex-Debug, управляющее arm-none-eabi-gdb через GDB-сервер J-Link.
14.1.1.4.1. Сборка для отладки¶
Всегда пересобирайте целевую прошивку с DEBUG=1:
make -j$(nproc) TARGET=<TARGET> DEBUG=1
Релизный образ (DEBUG=0) компилируется с -O2; в отладчике вы будете видеть <optimized out> для многих переменных, встраиваемые функции сворачиваются в свои вызывающие функции, а пошаговое выполнение перескакивает непредсказуемо. Сборки с DEBUG=1 используют -Og -ggdb3, что обеспечивает отлаживаемость, при этом прошивка всё ещё загружается на камере. ELF-файл, на который вы направляете отладчик, это:
build/<TARGET>/bin/firmware.elf
(Для Alif AE3 отлаживайте build/OPENMV_AE3/bin/firmware_M55_HP.elf – высокопроизводительное ядро.)
14.1.1.4.2. Оборудование: J-Link через SWD¶
Подключите Segger J-Link к выводам SWD камеры (SWDIO, SWCLK, GND и целевой VCC для опорного напряжения; камера, как обычно, питается через USB). Подойдут J-Link EDU / Base / Pro. Расположение отладочных выводов различается у разных камер – на многих платах есть выделенный разъём JTAG/SWD, на других SWD выведен на колодку ввода-вывода или на контактные площадки – поэтому проверьте схему расположения выводов и принципиальную схему конкретной платы в документации по оборудованию OpenMV, чтобы понять, какие выводы подключать. Установите J-Link Software and Documentation Pack с сайта segger.com на ту машину, к которой физически подключён зонд. Поддерживайте его достаточно свежим – более старое ПО J-Link не будет знать имена новых устройств (STM32N6, MIMXRT, Alif).
Каждому микроконтроллеру нужно его точное имя устройства J-Link, чтобы зонд загрузил правильный загрузчик флеш-памяти и карту памяти:
Камера ( |
MCU |
J-Link |
|---|---|---|
|
STM32F427 |
|
|
STM32F765 |
|
|
STM32H743 |
|
|
STM32N657 |
|
|
MIMXRT1062 |
|
|
Alif Ensemble (M55-HP) |
|
|
STM32H747 |
|
14.1.1.4.3. Настройка Cortex-Debug в VS Code¶
Создайте .vscode/launch.json в репозитории. Простейший случай – VS Code, J-Link и сборка находятся на одной машине Linux / macOS – использует servertype: "jlink", что заставляет Cortex-Debug самостоятельно запускать 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"
}
]
}
Измените executable и device под вашу плату (см. таблицу выше). Нажмите F5, чтобы собрать, прошить, запустить до main и остановиться там.
Совет
Чтобы автоматически выполнять пересборку при каждом запуске отладки, добавьте задачу сборки в .vscode/tasks.json и сошлитесь на неё из конфигурации запуска через "preLaunchTask". Например, задача, выполняющая make -j$(nproc) TARGET=OPENMV4 DEBUG=1, с именем "build-firmware", плюс "preLaunchTask": "build-firmware" в приведённой выше конфигурации, чтобы F5 пересобирал, прошивал и запускал отладчик за один шаг.
Предупреждение
Cortex-Debug требует arm-none-eabi-gdb. Он поставляется в составе SDK по пути ~/openmv-sdk-<version>/gcc/bin, но по умолчанию не добавлен в PATH, поэтому отладка завершается ошибкой «GDB executable „arm-none-eabi-gdb“ was not found». Исправьте это либо установив armToolchainPath / gdbPath, как показано выше, либо добавив ~/openmv-sdk-<version>/gcc/bin в ваш PATH (тогда printenv PATH должен его перечислять).
14.1.1.4.4. Просмотр регистров периферии (SVD)¶
Направьте Cortex-Debug на файл CMSIS SVD, чтобы получить декодированный просмотр регистров периферии (таймеры, DMA, интерфейс камеры и т.д.) по имени и битовым полям:
"svdFile": "/path/to/STM32H743.svd"
Для STM32 и MIMXRT получите SVD из пакетов CMSIS ST / NXP или из реестра SVD Cortex-Debug. SVD-файлы Alif включены в репозиторий прошивки по пути lib/micropython/lib/alif_ensemble-cmsis-dfp/Debug/SVD/ (используйте ..._CM55_HP_View.svd для HP-ядра AE3).
14.1.1.4.5. Windows: мост J-Link между WSL и Windows¶
WSL 2 не может напрямую видеть USB-устройство J-Link, поэтому разделение такое: Windows обслуживает зонд (туда он подключён), а VS Code + gdb работают в WSL и обращаются к нему по TCP.
В Windows установите пакет Segger J-Link и подключите J-Link к USB-порту Windows.
В Windows запустите J-Link Remote Server (он поставляется в составе пакета J-Link): запустите его с подключённым J-Link и нажмите OK. Разрешите его прохождение через брандмауэр Windows при появлении запроса. В окне отображается IP-адрес, по которому обслуживается зонд – запишите его.
В WSL соберите с
DEBUG=1и убедитесь, чтоarm-none-eabi-gdbдоступен (установитеarmToolchainPath, как описано выше).В WSL VS Code оставьте
servertype: "jlink"– GDB-сервер работает в WSL и обращается к зонду через Remote Server – и добавьтеserverpath+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" }
Установите
ipAddressв адрес, отображаемый в окне Remote Server. Это и есть весь мост.
Совет
Альтернатива мосту GDB-сервера: usbipd-win. Вместо запуска сервера в Windows вы можете подключить USB-устройство J-Link напрямую в WSL с помощью usbipd-win. Из PowerShell с правами администратора:
winget install usbipd
usbipd list
usbipd bind --busid <busid>
usbipd attach --wsl --busid <busid>
(<busid> – это идентификатор шины J-Link из usbipd list.) После этого зонд появляется внутри WSL, и вы используете обычную одномашинную конфигурацию servertype: "jlink" из раздела Настройка Cortex-Debug в VS Code без IP-адреса и без отдельного сервера в Windows. Мост GDB-сервера требует меньше настройки для эпизодического использования; usbipd-win удобнее для повседневной разработки.
Совет
Используйте "request": "attach" для отладки прошивки в то время, как она уже работает, без сброса или перепрошивки – идеально для выявления зависания в полевых условиях. Используйте "request": "launch" для сброса, прошивки ELF и запуска с начала с runToEntryPoint.
14.1.1.4.6. Отладка из командной строки с помощью gdbrunner¶
Настройка сеанса GDB для встраиваемой цели вручную – это танец из пяти шагов: запустить GDB-сервер J-Link / ST-Link в одном окне с правильными флагами устройства, порта и интерфейса; дождаться, пока он выведет Waiting for GDB connection; запустить arm-none-eabi-gdb во втором окне; ввести target remote localhost:<port>; направить gdb на ELF. Когда сеанс gdb завершается, нужно не забыть закрыть окно сервера. gdbrunner – это небольшой CLI, сворачивающий всё это в одну команду на переднем плане. Он поставляется в составе Python-окружения SDK OpenMV, поэтому устанавливать ничего не нужно; обычная точка входа – цель make debug репозитория прошивки:
make -j$(nproc) TARGET=<TARGET> DEBUG=1 debug
Это запускает gdbrunner с аргументами отладчика из конфигурации платы – именем устройства J-Link и, где это необходимо, внешним загрузчиком флеш-памяти ST-Link – с arm-none-eabi-gdb из SDK, уже находящимся в PATH. Бэкенд по умолчанию – J-Link; make DEBUGGER=STLINK debug работает с зондом ST-Link.
gdbrunner также можно вызывать напрямую (вне 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
Первый позиционный аргумент выбирает бэкенд сервера (jlink, stlink, qemu); остальные передаются этому бэкенду, со значениями по умолчанию, подходящими для камер OpenMV. gdbrunner --help выводит полный список флагов для каждого бэкенда; таблица аргументов каждого бэкенда задаётся через JSON (src/gdbrunner/backends.json), поэтому добавление нового сервера – это правка конфигурации, а не кода.
Что gdbrunner делает для работы из командной строки:
Один процесс, чистый жизненный цикл. Сервер запускается, gdb подключается, когда порт открыт, сервер корректно завершается при выходе из gdb. Никаких осиротевших процессов
JLinkGDBServer, переживающих сеанс, никаких двух терминалов для управления.Автообнаружение STM32CubeProgrammer. Бэкенд
stlinkищет инструменты STM32CubeProgrammer в обычных местах установки (~/STM32CubeProgrammer/,/opt/st/, дерево плагинов STM32CubeIDE), чтобы длинный путь--cube-progне приходилось вводить каждый раз. SDK содержит собственную копию по пути~/openmv-sdk-<version>/stcubeprog/bin– направьте--cube-progтуда, если системной установки нет.Учёт gdbinit для каждого проекта. Файл
.gdbinitв текущем каталоге загружается с-ix– переопределяя общесистемный~/.gdbinit– поэтому gdb-скрипты для конкретного проекта (pretty-printer’ы, макросы для конкретной платы, наборы точек останова) подключаются простым присутствием в рабочем каталоге.make debugзапускается из корня репозитория, поэтому.gdbinitтам применяется.Пробный прогон.
--dryrunвыводит команду сервера без её запуска, что полезно для адаптации вызова к скрипту-обёртке, копирования в конфигурацию запуска IDE или просто для проверки того, какие аргументы составляет gdbrunner.Видимый вывод сервера.
--show-outputсохраняет stdout / stderr сервера видимыми. По умолчанию они подавляются (чтобы интерфейс gdb оставался чистым); включайте этот флаг, когда некорректно ведёт себя сам сервер.Бэкенд QEMU.
qemu-system-armотлаживает сборку прошивки без подключённой платы. ЦельMPS2_AN500выбирает этот бэкенд в своей конфигурации платы, поэтомуmake TARGET=MPS2_AN500 DEBUG=1 debugсобирает для машиныmps2-an500QEMU и пошагово выполняет платформенно-независимый код – всё, что не затрагивает специфичную для камеры периферию – в полёте. (qemu-system-armустанавливается на хост, а не входит в SDK.)
Для пошагового выполнения на уровне исходного кода с маркерами точек останова на полях и просмотром регистров периферии лучше подходит описанная выше настройка Cortex-Debug в VS Code; gdbrunner – правильный инструмент для всего, что живёт в командной строке.
14.1.1.4.7. Использование отладчика¶
Когда сеанс запущен (процессор остановлен на main):
Точки останова – кликните по полю рядом со строкой на C или в Debug Console введите
break <file>:<line>/break <function>. Ядра Cortex-M имеют небольшое число аппаратных компараторов точек останова (обычно 6–8 на M7 / H7, 8 на M55). Превышение этого числа для кода во флеш-памяти молча не срабатывает – держите количество активных точек останова умеренным.Пошаговое выполнение – F10 шаг с обходом (
next), F11 шаг с заходом (step), Shift+F11 шаг с выходом (finish), F5 продолжить. Пошаговое выполнение на уровне инструкций – этоstepi/nextiв Debug Console.Переменные / наблюдение / стек вызовов – панели Variables и Call Stack показывают локальные переменные и обратную трассировку; добавляйте выражения в Watch. Наведите курсор на переменную в исходном коде, чтобы увидеть её значение. Всё, что отображается как
<optimized out>, означает, что вы не на сборке сDEBUG=1.Точки наблюдения (точки останова по данным) –
watch <expr>останавливается при записи переменной,rwatchпри чтении,awatchпри любом из них. Блок DWT Cortex-M поддерживает ~4 аппаратных точки наблюдения – незаменимо для выяснения того, кто повредил переменную.Регистры и периферия – представление Cortex Registers показывает регистры ядра; при установленном
svdFileпредставление Peripherals декодирует каждый регистр периферии и битовое поле (DMA, таймеры, интерфейс камеры / CSI, XSPI и т.д.) – самый быстрый способ понять, почему драйвер ведёт себя неправильно.Память – используйте просмотрщик памяти Cortex-Debug или
x/в gdb для прямого просмотра буферов кадров, буферов DMA и структур.printf без остановки (SWO/RTT) – для проблем, чувствительных к таймингу, Segger RTT или SWO обеспечивает
printfс почти нулевыми накладными расходами во время работы целевой системы. Соберите сDEBUG_PRINTF=1и добавьтеrttConfig(RTT) илиswoConfig(SWO, требует частоту ядра) Cortex-Debug. Это правильный инструмент, когда точка останова изменила бы тайминг, который вы пытаетесь наблюдать.Отключение – Stop в сеансе
launchостанавливает целевую систему; Disconnect в сеансеattachоставляет камеру работающей. После этого выполните перезагрузку питания камеры, чтобы вернуть её к нормальной работе.
14.1.1.4.8. Подводные камни отладки¶
Оптимизированные переменные. Всё отображается как
<optimized out>– вы собрали сDEBUG=0. Пересоберите сDEBUG=1.«GDB executable not found» –
gcc/binиз SDK не вPATH; установитеarmToolchainPath/gdbPath.«Cannot connect» / неверная карта памяти – неверное или отсутствующее имя
device; используйте точную строку из таблицы.Точки останова молча не срабатывают – слишком много аппаратных точек останова на коде, размещённом во флеш-памяти; уменьшите их количество.
Пути к исходникам не совпадают (ELF, собранный в Docker) – собирайте с целью Docker
build-firmware-dev(одинаковый абсолютный путь внутри и вне контейнера) или установитеset substitute-pathв gdb.