lift_indicator_suite/docs/HOW_TO_DEBUG.md

9.2 KiB
Raw Permalink Blame History

Отладка прошивок через SWD + GDB

Обзор архитектуры

Отладка построена на пробросе GDB-сервера с хоста в devcontainer по TCP. Это позволяет держать весь инструментарий сборки и языковой сервер внутри контейнера, не проводя USB-пробник внутрь Docker.

flowchart LR
    subgraph Host["Хост (macOS / Linux)"]
        DS["just host::debug-server\npyocd gdbserver :3333"]
        ML["MCU-Link (CMSIS-DAP)"]
        DS --> ML
    end

    subgraph DC["Devcontainer"]
        CD["cortex-debug\n(VSCode F5)"]
        GDB["arm-none-eabi-gdb\nсимволы из .elf"]
        CD --> GDB
    end

    Board["MIMXRT1052\nFlash / SDRAM\nSEGGER RTT буфер"]

    GDB -->|"TCP host.docker.internal:3333"| DS
    ML -->|"SWD"| Board

Ключевой принцип: pyocd gdbserver слушает на 0.0.0.0:3333. Из контейнера GDB подключается через host.docker.internal:3333 — специальный DNS-алиас Docker, резолвится в IP хост-машины.


Компоненты

На хосте

Компонент Роль Источник
pyocd GDB-сервер + flash-программатор tools/hil/uv.lock
MCU-Link CMSIS-DAP v2 пробник USB к плате
just host::debug-server Запуск GDB-сервера just/host.just
just host::flash-swd-* Прошивка через SWD just/host.just
tools/host/flash_swd.py Сборка FCB+HAB образа и запись tools/host/
tools/host/dcd/w25q128_fdcb.bin FCB для W25Q128 (Quad SPI) NXP SecureProvisioningTool

В devcontainer

Компонент Роль
arm-none-eabi-gdb GDB клиент, подключается к серверу на хосте
cortex-debug (VSCode extension) UI для GDB: брейкпоинты, стек, регистры
.vscode/launch.json Конфигурации запуска отладки
.vscode/tasks.json preLaunchTask — пересборка ELF перед стартом
build/Debug/*.elf Символы для GDB (DWARF debug info)
bsp/generated/startup/MIMXRT1052.xml SVD — описание регистров периферии

Конфигурация

Параметры отладки задаются в .env:

GDB_PORT=3333
PYOCD_TARGET=mimxrt1050_quadspi
PYOCD_FREQUENCY=4000000
FCB_PATH=tools/host/dcd/w25q128_fdcb.bin

Поддерживаемые прошивки

Конфигурация VSCode ELF Особенности
🐛 Debug: firmware_test build/Debug/firmware_test.elf Bare-metal, входной контроль
🐛 Debug: bootloader build/Debug/bootloader.elf Bare-metal, A/B обновление
🐛 Debug: tft_app (FreeRTOS) build/Debug/app.elf FreeRTOS, task view

Все три — XIP-прошивки, исполняются из QuadSPI NOR Flash (0x60000000).


Режимы запуска отладки

Режим А — прошивка уже в Flash

# 1. Хост — запустить GDB-сервер (оставить в отдельном терминале)
just host::debug-server

# 2. DevContainer — VSCode
#    Run & Debug (Ctrl+Shift+D) → выбрать конфигурацию → F5

GDB сбрасывает MCU, загружает символы из ELF и останавливается на входе в main. Flash не перезаписывается.

Режим Б — прошить через SWD, затем отладить

# 1. DevContainer
just build::hab-firmware-test-debug

# 2. Хост
just host::flash-swd-test-debug

# 3. ⚡ Power cycle платы (обязательно)

# 4. Хост
just host::debug-server

# 5. DevContainer — VSCode → 🐛 Debug: firmware_test → F5

Режим В — прошить через USB SDP, затем отладить

# 1. DevContainer
just build::build-firmware-test-debug

# 2. Хост — перевести плату в SDP-режим, затем:
just host::flash-test-debug

# 3. Хост
just host::debug-server

# 4. DevContainer — VSCode → 🐛 Debug: firmware_test → F5

Почему flash через SWD требует FCB

При USB SDP ROM-загрузчик инициализирует FlexSPI по DCD из HAB-образа — FCB не нужен. При SWD flash-алгоритм pyOCD пишет в NOR Flash напрямую. При cold-start Boot ROM сначала читает FCB по адресу 0x60000000, конфигурирует FlexSPI, и только потом ищет IVT. Без FCB бутлоадер не стартует.

flash_swd.py решает это, собирая образ перед записью:

0x60000000  w25q128_fdcb.bin  (512 байт)  — FCB
0x60000200  0xFF × 3584 байт             — padding
0x60001000  firmware_test_hab.bin         — IVT + DCD + код

Весь диапазон 0x600000000x6000FFFF — один 64KB сектор: стирается и записывается за одну транзакцию.


RTT-логи

SEGGER RTT включён только в Debug-сборках (SEGGER_RTT_ENABLED=ON). После старта отладки вкладка TERMINAL → RTT принимает вывод канала 0. cortex-debug находит адрес буфера по символу _SEGGER_RTT из ELF.

#include "SEGGER_RTT.h"
SEGGER_RTT_printf(0, "value = %d\n", value);

FreeRTOS task view

Конфигурация 🐛 Debug: tft_app (FreeRTOS) включает "rtos": "FreeRTOS" — cortex-debug разбирает структуры планировщика и показывает вкладку RTOS с таблицей задач: имя, состояние, использование стека, приоритет.


Просмотр регистров периферии

Вкладка Peripherals показывает все блоки MIMXRT1052 по SVD-файлу bsp/generated/startup/MIMXRT1052.xml. Значения обновляются при каждой паузе.


Ограничения

MCU-Link монопольный ресурс. debug-server и flash-swd не могут работать одновременно. Перед flash-swd остановите сервер (Ctrl+C).

HIL-тесты vs отладка. pyOCD также используется для HIL. Перед just host::hil-run остановите GDB-сервер.

Power cycle после flash-swd обязателен. VECTRESET не реинициализирует FlexSPI — только полное отключение питания гарантирует корректный cold-start.

Только Debug-сборки. Release компилируется с -O2 без DWARF-символов.


Быстрый старт (первый запуск)

# 1. Убедиться что в .devcontainer/devcontainer.json есть (для Linux):
#    "runArgs": ["--add-host=host.docker.internal:host-gateway"]

# 2. Залить прошивку
just host::flash-test-debug

# 3. Хост — запустить GDB-сервер
just host::debug-server

# 4. DevContainer — VSCode
#    Ctrl+Shift+D → 🐛 Debug: firmware_test → F5

Дерево файлов отладки

.
├── .env                                  # GDB_PORT, PYOCD_TARGET, PYOCD_FREQUENCY, FCB_PATH
├── .vscode/
│   ├── launch.json                       # cortex-debug конфигурации (3 проекта)
│   └── tasks.json                        # preLaunchTask: build:*-debug
├── bsp/generated/startup/
│   └── MIMXRT1052.xml                    # SVD — регистры периферии
├── just/
│   └── host.just                         # debug-server, flash-swd-*
└── tools/
    ├── hil/                              # uv-проект с pyocd
    └── host/
        ├── flash_swd.py                  # FCB + HAB → Flash через pyOCD
        └── dcd/
            └── w25q128_fdcb.bin          # FCB для W25Q128 Quad SPI