14.1.1.4. Налагодження мікропрограми

Налагодження на реальному обладнанні означає зупинку процесора, встановлення точок зупину у C-коді, покрокове виконання та перевірку змінних, пам’яті, регістрів і периферійних пристроїв – безпосередньо з VS Code. Для цього потрібні три речі: збірка для налагодження, SWD-пробник (Segger J-Link) та розширення Cortex-Debug, яке керує arm-none-eabi-gdb через J-Link GDB-сервер.

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.3. Налаштування Cortex-Debug у VS Code

Створіть .vscode/launch.json у репозиторії. Найпростіший випадок – VS Code, J-Link і збірка розташовані на одній машині Linux/macOS – використовує servertype: "jlink", і тоді Cortex-Debug сам запускає J-Link GDB-сервер:

{
  "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 для ядра AE3 HP).

14.1.1.4.6. Налагодження з командного рядка за допомогою gdbrunner

Ручне налаштування GDB-сесії для вбудованої цілі – це п’ять кроків: запустити J-Link/ST-Link GDB-сервер в одному вікні з правильними параметрами пристрою, порту та інтерфейсу; дочекатися повідомлення 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 і, де потрібно, завантажувачем зовнішньої flash-пам’яті 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 збирає для машини QEMU mps2-an500 і дозволяє покроково виконувати платформонезалежний код – все, що не торкається специфічних периферійних пристроїв камери – без реального обладнання. (qemu-system-arm встановлюється на хості, не входить до SDK.)

Для покрокового виконання з точками зупину та переглядом регістрів периферії налаштування Cortex-Debug у VS Code описане вище є кращим інструментом; gdbrunner підходить для всього, що виконується з командного рядка.

14.1.1.4.7. Використання відладчика

Після запуску сесії (процесор зупинено на main):

  • Точки зупину – клацніть поле ліворуч від рядка C-коду або у консолі налагодження введіть break <file>:<line> / break <function>. Ядра Cortex-M мають обмежену кількість апаратних компараторів точок зупину (зазвичай 6–8 на M7/H7, 8 на M55). Перевищення їх кількості на коді у flash мовчки ігнорується – тримайте кількість активних точок зупину помірною.

  • Покрокове виконанняF10 крок через (next), F11 крок у (step), Shift+F11 крок назовні (finish), F5 продовжити. Покрокове виконання на рівні інструкцій – stepi / nexti у консолі налагодження.

  • Змінні / watch / стек викликів – панелі Variables і Call Stack показують локальні змінні та стек; додавайте вирази до Watch. Наведіть курсор на змінну у коді, щоб побачити її значення. Якщо відображається <optimized out>, значить збірка виконана без DEBUG=1.

  • Точки спостереження (data breakpoints)watch <expr> зупиняє при записі змінної, rwatch – при читанні, awatch – при будь-якому доступі. Блок DWT Cortex-M підтримує ~4 апаратні точки спостереження – незамінні для визначення того, хто пошкодив змінну.

  • Регістри та периферія – вигляд Cortex Registers показує регістри ядра; якщо встановлено svdFile, вигляд Peripherals декодує кожен регістр і бітове поле периферії (DMA, таймери, інтерфейс камери/CSI, XSPI тощо) – найшвидший спосіб зрозуміти, чому драйвер не працює.

  • Пам’ять – використовуйте переглядач пам’яті Cortex-Debug або команду gdb x/, щоб безпосередньо оглядати кадрові буфери, 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; використовуйте точний рядок із таблиці.

  • Точки зупину мовчки не спрацьовують – занадто багато апаратних точок зупину на коді у flash; зменшіть їх кількість.

  • Шляхи до джерел не збігаються (ELF, зібраний у Docker) – зберіть із ціллю Docker build-firmware-dev (той самий абсолютний шлях всередині та поза контейнером) або встановіть у gdb set substitute-path.