28 KiB
Архитектура рабочего окружения разработчика
Проект: TFT Firmware (MIMXRT1052CVJ5B) Документ описывает рабочий процесс разработчиков: разворачивание окружения, сборка, тестирование (host + HIL), отладка и прошивка платы.
1. Концепция
Рабочее окружение разделено на два контекста с чёткой границей:
Devcontainer — всё что касается кода: сборка, статический анализ,
форматирование, host-тесты, сборка HIL target-прошивок, подготовка HAB-образов.
Управляется через VSCode tasks и модуль just build::.
Хост — всё что касается железа: прошивка платы через USB или SWD,
HIL-тесты через pyOCD + pytest, GDB-сервер для отладки.
Управляется через модуль just host::.
2. Компоненты окружения
Физические связи
graph LR
Host["Хост"]
subgraph Board["Плата TFT (MIMXRT1052)"]
USB_SDP["USB"]
MCULink["MCU-Link"]
end
subgraph M5["HIL стенд (M5StampPLC)"]
M5_USB["USB"]
RLY["RLY1–4"]
end
USB_SDP -->|"SDP — прошивка через ROM"| Host
MCULink -->|"SWD — GDB-сервер, прошивка Flash, загрузка HIL ELF"| Host
MCULink -->|"VCOM — UART CLI (pytest ↔ HIL firmware)"| Host
M5_USB -->|"JSON-lines CLI"| Host
RLY -->|"VIN · RS_RX · EXT_IN1 · EXT_IN2"| Board
Состав инструментов
graph TB
subgraph Host["Хост (Linux / macOS / Windows + Git Bash)"]
H1["just host::*\nзапуск задач хостового уровня"]
H2["docker\nуправление devcontainer"]
H3["uv + spsdk — tools/host/\nsdphost · blhost · nxpimage"]
H4["uv + pyocd + pyserial + pytest — tools/hil/\nGDB-сервер · HIL-тесты"]
H5["mpremote\nдеплой агента на M5StampPLC"]
H6["VSCode (Dev Containers extension)"]
end
subgraph DC["Devcontainer (Docker)"]
D1["arm-none-eabi-gcc\nкросс-компилятор firmware + HIL"]
D2["cmake + ninja\nсистема сборки"]
D3["clang-17 · clangd-17\nclang-tidy · clang-format"]
D4["uv + spsdk — tools/host/\nтолько nxpimage (HAB-образы)"]
D5["Unity + fff\nфреймворки host-тестов"]
D6["just build::*\nзапуск задач внутри контейнера"]
end
3. Что устанавливается и где
| Инструмент | Хост | Devcontainer |
|---|---|---|
just |
✅ | ✅ Dockerfile |
docker |
✅ | — |
uv |
✅ | ✅ Dockerfile |
spsdk (sdphost, blhost, nxpimage) |
✅ tools/host/ |
✅ tools/host/ |
pyocd + pyserial + pytest |
✅ tools/hil/ |
— |
mpremote |
✅ tools/hil/ |
— |
| ARM GCC toolchain | — | ✅ |
cmake / ninja |
— | ✅ |
clang / clangd / clang-tidy |
— | ✅ |
| Unity / fff | — | ✅ vendored |
spsdk и pyocd — отдельные uv-проекты с разными ролями:
tools/host/—spsdk:nxpimage+sdphost+blhost— прошивка через USB ROM и HAB-образыtools/hil/—pyocd+pyserial+pytest+mpremote— GDB-сервер, HIL-тесты, деплой M5 агента
3.1 Конфигурация проекта — .env
.env в корне репозитория — единый источник конфигурации для всего стека.
.env.example — шаблон без значений, коммитится в репозиторий.
# Hardware
BOARD=MIMXRT1052
# USB VID:PID (NXP ROM + Flashloader)
BOOTROM_VID=1fc9
BOOTROM_PID=0130
FLASHLOADER_VID=15a2
FLASHLOADER_PID=0073
# Debug / SWD
GDB_PORT=3333
GDB_EXECUTABLE=arm-none-eabi-gdb
PYOCD_TARGET=mimxrt1050_quadspi
PYOCD_FREQUENCY=4000000
FCB_PATH=tools/host/dcd/w25q128_fdcb.bin
# HIL — аппаратный стенд
HIL_PYOCD_FREQUENCY=1000000
HIL_BUILD_DIR=build/target-debug
HIL_TARGET_POWER_SETTLE_S=1.5 # задержка после включения питания (POR + стабилизация)
# VCOM на программаторе NXP MCU-Link
HIL_VCOM_PORT=/dev/cu.usbmodemXXXX # macOS: cu.usbmodem*, Linux: ttyACM*
HIL_VCOM_BAUD=115200
HIL_READY_TIMEOUT=5.0
# M5Stack StamPLC (промежуточная платформа для HIL)
HIL_M5_PORT=/dev/cu.usbmodemYYYY # M5StampPLC USB CDC
HIL_M5_BAUD=115200
HIL_M5_TIMEOUT=3.0
# VCOM на плате таргета (USB CDC, появляется после загрузки ELF)
HIL_USB_CDC_PORT=/dev/cu.usbmodemZZZZ
HIL_USB_CDC_BAUD=115200
HIL_USB_CDC_TIMEOUT=5.0
Как значения попадают в инструменты:
flowchart LR
ENV[".env"]
subgraph Just["just (dotenv-load + export)"]
JR["just-рецепты\n{{BOOTROM_VID}}\n{{HIL_VCOM_PORT}}\n{{GDB_PORT}}"]
UV["uv run python\n(наследует os.environ)"]
end
subgraph Python["Python-скрипты"]
FU["flash_usb.py\nos.environ[BOOTROM_VID]"]
FS["flash_swd.py\nos.environ[PYOCD_TARGET]"]
EC["env_config.py\nos.environ[HIL_VCOM_PORT]"]
end
VS[".vscode/launch.json\n${env:GDB_PORT}"]
ENV --> Just
JR --> Python
UV --> FU
UV --> FS
UV --> EC
ENV --> VS
4. Структура automation-части репозитория
/
├── justfile ← корневой оркестратор; модули: build, host, ci
├── bootstrap.sh ← уровень 0: устанавливает just + uv → just host::bootstrap
├── pyocd.yaml ← конфигурация pyOCD (HIL RAM-режим)
├── pyocd_debug.yaml ← конфигурация pyOCD (GDB-сервер отладки)
├── .env / .env.example
│
├── just/
│ ├── build.just ← devcontainer: сборка firmware, host-тесты,
│ │ HAB-образы, HIL target-прошивки
│ ├── host.just ← хост: прошивка (USB + SWD), HIL-тесты,
│ │ GDB-сервер, M5StampPLC, bootstrap
│ └── ci.just
│
├── .devcontainer/
│ ├── Dockerfile
│ └── devcontainer.json
│
├── .vscode/
│ ├── launch.json ← cortex-debug конфигурации (firmware_test, bootloader, tft_app)
│ └── tasks.json ← UI для just build::* (внутри devcontainer)
│
├── tools/
│ ├── host/ ← spsdk-окружение (прошивка через USB ROM)
│ │ ├── flash_usb.py ← USB SDP: sdphost + blhost
│ │ ├── flash_swd.py ← SWD: FCB + HAB → pyOCD Flash
│ │ ├── hab/ ← HAB yaml-конфиги (nxpimage)
│ │ ├── dcd/ ← w25q128_fdcb.bin, w25q512_fdcb.bin,
│ │ │ dcd.bin, ivt_flashloader.bin
│ │ └── uv.lock
│ │
│ ├── production/ ← service-tui: TUI сервисного инженера (Textual)
│ │ прошивка/диагностика готовых плат, см.
│ │ tools/service_tui/README.md + DEV_ARCH.md
│ │
│ └── hil/ ← HIL pytest-окружение
│ ├── conftest.py ← фикстуры: m5, loaded_<n>, uart_<n>
│ ├── pyocd_utils.py ← FLEXRAM init, ELF loader, run_from_vectors
│ ├── env_config.py ← конфигурация из os.environ / .env
│ ├── load_and_run.py ← CLI-утилита загрузки ELF в RAM вручную
│ ├── 01_test_uart.py ← HIL тест bsp_uart_host (без M5)
│ ├── 02_test_opto.py ← HIL тест bsp_opto (через M5StampPLC)
│ ├── 03_test_can.py ← HIL тест bsp_can
│ ├── 04_test_button.py ← HIL тест bsp_button (интерактивный, оператор)
│ ├── 05_test_usb_cdc.py ← HIL тест USB CDC (bsp_usb_cdc, UART CLI)
│ ├── 06_test_firmware_opto.py ← HIL тест opto через firmware_test CDC
│ ├── 06_test_firmware_can.py ← HIL тест CAN через firmware_test CDC
│ ├── m5/
│ └── uv.lock
│
├── CMakePresets.json ← Debug · Release · host-debug · target-debug
│
├── tests/
│ ├── host/ ← host unit-тесты (Unity + fff)
│ │ ├── mocks/ ← stub-хедеры NXP SDK для компиляции на хосте
│ │ ├── button/
│ │ ├── can/
│ │ ├── cli/
│ │ ├── led/
│ │ ├── log/
│ │ ├── opto/
│ │ ├── prio_queue/
│ │ ├── protocol/
│ │ ├── ring_buffer/
│ │ ├── runner/
│ │ ├── timeout/
│ │ └── uart_host/
│ └── target/ ← HIL target-прошивки (RAM, pyOCD)
│ ├── host_uart/ ← CLI для тестирования bsp_uart_host
│ ├── hil_button/ ← CLI для тестирования bsp_button
│ ├── hil_can/ ← CLI для тестирования bsp_can
│ ├── hil_opto/ ← CLI для тестирования bsp_opto
│ └── hil_usb_cdc/ ← CLI для тестирования USB CDC
│
└── docs/
├── DEV_ARCH.md ← этот документ
├── HOW_TO_FLASH.md ← прошивка (USB SDP + SWD)
├── HOW_TO_DEBUG.md ← отладка (GDB + RTT + FreeRTOS)
├── hardware/ ← схемы, datasheet платы
├── mimxrt1052/ ← MCU: BOOT_FLAGS, reference manual
└── testing/
├── hil/
│ ├── HIL_HOW_TO.md ← как проводить HIL-тесты
│ ├── HIL_BENCH.md ← стенд: оборудование, подключение
│ └── HIL_CREATE_TEST.md ← как добавить новый HIL-тест
└── host/
└── HOST_CREATE_TEST.md ← как добавить host unit-тест
5. Первый запуск: разворачивание окружения
5.1 Предварительные требования
| Платформа | Что нужно до bootstrap |
|---|---|
| Linux | docker, git, curl |
| macOS | Docker Desktop, git (Xcode CLT) |
| Windows | Docker Desktop, Git for Windows → Git Bash |
5.2 Единственная команда
git clone <repo-url> && cd <repo>
./bootstrap.sh
5.3 Что делает bootstrap
flowchart TD
A["bootstrap.sh\n(уровень 0)"]
A --> B["определить платформу\nLinux / macOS / Windows Git Bash"]
B --> C["проверить/установить\nuv >= 0.4.0"]
C --> D["проверить/установить\njust >= 1.36.0\n(через uv tool)"]
D --> E["exec just host::bootstrap"]
E --> F["[1/3] check-deps\njust · uv · docker"]
E --> G["[2/3] setup-udev\n(только Linux)"]
E --> H["[3/3] setup-tools\nuv sync → tools/host/"]
G --> G1["1FC9:0130 — BootROM SDP"]
G --> G2["15A2:0073 — Flashloader"]
G --> G3["dialout — /dev/ttyACM*"]
H --> H1["SHA-256 uv.lock кешируется\nповторный вызов мгновенный"]
5.4 После bootstrap
# Открыть VSCode → "Reopen in Container"
# postCreateCommand выполняется автоматически:
# uv sync (tools/host) + cmake --preset host-debug + cmake --preset Debug
# Настроить .env (один раз):
cp .env.example .env
# Заполнить HIL_VCOM_PORT и HIL_M5_PORT под свои порты
# Задеплоить агент на M5StampPLC (один раз, и каждый раз после изменений в agent.py):
just host::m5-deploy
6. Прошивки, boot-стратегии и матрица сборки
6.1 Четыре типа сборки
| Пресет | Toolchain | Назначение | Линкер-скрипт |
|---|---|---|---|
Debug / Release |
ARM GCC | firmware_test, bootloader, tft_app | flexspi_nor.ld |
host-debug / host-release |
clang (хост) | Unity + fff тесты | — |
target-debug |
ARM GCC | HIL target-прошивки | ram.ld |
6.2 CMake пресеты
configurePresets:
Debug · Release ← firmware (XIP из Flash)
host-debug · host-release ← host unit-тесты
target-debug ← HIL target-прошивки (RAM)
buildPresets (ARM firmware):
firmware-test-debug / release
bootloader-debug / release
app-debug / release
all-debug / all-release
buildPresets (host-тесты):
host-debug-build: test_bsp_led, test_log, test_bsp_opto, test_bsp_button,
test_bsp_can, test_cli, test_protocol, test_firmware_runner,
test_prio_queue, uart_host_mock_example, test_ring_buffer,
test_timeout_pattern
host-release-build: то же
buildPresets (HIL):
target-debug-build: test_host_uart, test_hil_button, test_hil_can,
test_hil_usb_cdc, test_hil_opto
Источник истины по списку целей — CMakePresets.json.
6.3 Boot-стратегии
| Прошивка | Стратегия | Инструмент загрузки |
|---|---|---|
firmware_test |
XIP из Flash (flexspi_nor.ld) |
SPSDK → Flash |
bootloader |
Копирование в ITCM | SPSDK → Flash |
tft_app |
XIP + буферы в SDRAM | SPSDK → Flash |
HIL target (tests/target/) |
Исполнение из ITCM/DTCM (ram.ld) |
pyOCD → RAM |
HIL boot-стратегия: pyOCD настраивает FLEXRAM (128 KB ITCM + 128 KB DTCM + 256 KB OCRAM), записывает PT_LOAD сегменты ELF по физическим адресам, устанавливает SP/PC из таблицы векторов и запускает выполнение. Flash не используется — прошивка исчезает при отключении питания.
7. Рабочий процесс разработчика
7.1 Карта задач по контекстам
| Задача | Где |
|---|---|
| Написание кода, clangd, форматирование | devcontainer |
| Статический анализ (clang-tidy) | devcontainer |
| Host unit-тесты (Unity + fff) | devcontainer |
| Сборка ARM firmware (ELF) | devcontainer |
| Сборка HIL target-прошивок | devcontainer |
| Подготовка HAB-образов (nxpimage) | devcontainer |
| Прошивка платы через USB ROM | хост |
| Прошивка платы через SWD | хост |
| HIL-тесты (pyOCD + pytest + M5) | хост |
| Отладка — GDB-сервер (pyOCD) | хост |
| Отладка — GDB-клиент (cortex-debug) | devcontainer → хост по TCP |
7.2 Типичная сессия разработки
# ── Devcontainer (терминал VSCode) ──────────────────────────────────────────
just build::test-host # host unit-тесты — зелёные?
just build::build-firmware-test-debug # ELF собирается?
just build::hab-firmware-test-debug # HAB-образ для прошивки
# ── Хостовый терминал — прошивка ─────────────────────────────────────────────
just host::flash-test-debug # прошить через USB SDP
# или
just host::flash-swd-test-debug # прошить через SWD (power cycle после)
# ── Хостовый терминал — HIL ──────────────────────────────────────────────────
just build::build-hil # (devcontainer) собрать HIL ELF
just host::hil-run # загрузить ELF → запустить все тесты
just host::hil-uart # только UART тесты
just host::hil-opto # только opto тесты (нужен стенд M5)
# ── Отладка ───────────────────────────────────────────────────────────────────
just host::debug-server # запустить pyOCD GDB-сервер на хосте
# F5 в VSCode → 🐛 Debug: firmware_test
7.3 VSCode Tasks (внутри devcontainer)
| Таск | Команда |
|---|---|
| 🔨 Build | just build::build-<project>-<type> |
| 🧪 Host Tests (Debug) | just build::test-host |
| 🧪 Host Tests (Release) | just build::test-host-release |
| 🎯 Build HIL Target Tests | just build::build-hil |
| 📦 HAB Image | just build::hab-<project>-<type> |
| 📦 HAB All (Debug/Release) | just build::hab-all-debug/release |
| 🗑️ Clean | just build::clean |
8. Прошивка платы (хост)
Подробно — docs/HOW_TO_FLASH.md. Краткая сводка:
USB SDP (Serial Download Protocol)
Требует перевода платы в SDP-режим (BOOT_MOD_1 → 3V3 → Reset).
just host::flash-test-debug # firmware_test Debug → Flash
just host::flash-test-release # firmware_test Release → Flash
just host::flash-production # bootloader + tft_app Release (с подтверждением)
SWD (через MCU-Link, без смены режима загрузки)
just host::flash-swd-test-debug
just host::flash-swd-test-release
# После любого flash-swd — обязательный power cycle платы
9. HIL-тесты (хост)
HIL-тесты проверяют периферию на реальном железе. Два типа:
Базовые (без стенда) — 01_test_uart.py
Только MCU-Link: SWD загружает ELF в RAM, VCOM обеспечивает UART CLI.
sequenceDiagram
participant PT as pytest
participant ML as MCU-Link VCOM
participant RT as RT1052
PT->>ML: uart_cmd("PING")\n(pyserial)
ML->>RT: LPUART1
RT-->>ML: "PONG"
ML-->>PT: "PONG"
С M5StampPLC — 02_test_opto.py и другие
M5StampPLC управляет входными сигналами таргета через реле. pytest оркестрирует оба канала одновременно.
sequenceDiagram
participant PT as pytest
participant M5 as M5StampPLC
participant ML as MCU-Link VCOM
participant RT as RT1052
PT->>M5: m5.opto_set(1, True)\n(JSON-lines)
M5->>RT: RLY3 → EXT_IN1
PT->>ML: uart_cmd("OPTO_READ 1")\n(pyserial)
ML->>RT: LPUART1
RT-->>ML: "ACTIVE"
ML-->>PT: "ACTIVE"
Через firmware_test CDC — 06_test_firmware_*.py
Новый класс HIL-тестов: firmware_test прошит во Flash (не загружается pyOCD),
общение идёт по USB CDC ACM через FirmwareCdcClient. M5StampPLC управляет
сигналами, TUI/pytest оркестрирует confirm_request автоматически.
sequenceDiagram
participant PT as pytest
participant M5 as M5StampPLC
participant FW as firmware_test (CDC)
PT->>FW: run_selected(["opto"])
FW-->>PT: test_begin
FW-->>PT: confirm_request("opto_in1_active")
PT->>M5: relay_set(RLY3, ON)
PT->>FW: confirm("opto_in1_active", true)
FW-->>PT: test_result(pass/fail)
FW-->>PT: summary
Фикстура firmware_cdc не ждёт session_start (одноразовое событие, может быть
пропущено при подключении) — проверяет живость через ping → pong.
Перед каждой тест-сессией фикстура m5 автоматически включает питание таргета (RLY1), ждёт стабилизации, затем loaded_<n> загружает ELF через pyOCD.
Команды
just build::build-hil # (devcontainer) собрать HIL ELF
just host::hil-run # прогнать все HIL тесты
just host::hil-uart # только 01_test_uart.py
just host::hil-opto # только 02_test_opto.py
Подробно — docs/testing/hil/HIL_HOW_TO.md. Добавление нового теста — docs/testing/hil/HIL_CREATE_TEST.md. Стенд и подключение — docs/testing/hil/HIL_BENCH.md.
10. Тестирование
10.1 Host unit-тесты
Фреймворк: Unity + fff
Пресеты: host-debug / host-release
Компилятор: clang-17 (не ARM GCC)
Запуск: just build::test-host (внутри devcontainer)
BSP-модули тестируются через fff-фейки и stub-хедеры в tests/host/mocks/.
BUILD_TESTS_HOST=ON отключает ARM-специфику и SDK-заголовки.
Покрытие: bsp_led, bsp_opto, bsp_button, bsp_can, bsp_uart_host,
cli, protocol, firmware_runner, prio_queue, ring_buffer, log,
timeout-паттерн.
Гайд — testing/host/HOST_CREATE_TEST.md.
10.2 HIL target-тесты
Два класса HIL-тестов с разными транспортами:
Класс A — ELF-in-RAM (UART CLI): pyOCD загружает прошивку в RAM, общение через LPUART1 (MCU-Link VCOM). Тесты BSP-уровня, независимы от firmware_test.
Инструменты: pyOCD (SWD) + pyserial (UART) + pytest + M5StampPLC (реле)
Пресет: target-debug → ram.ld → ITCM/DTCM
Запуск: just host::hil-run
Тесты: 01_test_uart.py (PING/ECHO/BUF_SIZE), 02_test_opto.py
(оптовходы IN1/IN2/RS), 03_test_can.py (CAN-шина), 04_test_button.py
(кнопки), 05_test_usb_cdc.py (USB CDC ACM).
Класс B — firmware_test во Flash (CDC): firmware_test прошит штатно,
общение через USB CDC ACM (FirmwareCdcClient). M5StampPLC оркестрирует
confirm_request автоматически без участия оператора.
Инструменты: pyserial (CDC) + pytest + M5StampPLC (реле)
firmware_test: прошит в Flash через USB SDP заранее
Запуск: just host::hil-firmware-opto / hil-firmware-can
Тесты: 06_test_firmware_opto.py (opto IN1/IN2/RS, 6 шагов),
06_test_firmware_can.py (CAN RX + TX).
11. Отладка
Подробно — docs/HOW_TO_DEBUG.md. Краткая схема:
flowchart LR
subgraph DC["Devcontainer"]
CD["cortex-debug\n(VSCode F5)"]
GDB["arm-none-eabi-gdb"]
CD --> GDB
end
subgraph Host["Хост"]
DS["just host::debug-server"]
PO["pyocd gdbserver :3333"]
DS --> PO
end
ML["MCU-Link\n(USB/SWD)"]
Board["Плата TFT"]
GDB -->|"TCP host.docker.internal:3333"| PO
PO --> ML
ML -->|"SWD"| Board
Три конфигурации в .vscode/launch.json:
🐛 Debug: firmware_test— bare-metal, входной контроль🐛 Debug: bootloader— bare-metal, A/B обновление🐛 Debug: tft_app (FreeRTOS)— FreeRTOS task view
RTT-логи доступны в Debug-сборках (SEGGER_RTT_ENABLED=ON); подробности — в
docs/HOW_TO_DEBUG.md.
12. Жизненный цикл изменений
flowchart TD
FB["feature-ветка"]
subgraph DC["Devcontainer"]
T1["just build::test-host\nhost-тесты зелёные?"]
T2["just build::build-firmware-test-debug"]
T3["just build::build-hil\nHIL-прошивки собираются?"]
end
subgraph HostW["Хост"]
T4["just host::flash-test-debug\nпрошить, проверить на железе"]
T5["just host::hil-run\nHIL зелёные?"]
end
subgraph MR["Подготовка к MR"]
T6["just build::hab-all-release"]
T7["just host::flash firmware_test release"]
end
CI["Merge Request → CI\nhost-тесты · сборка · HIL\nпубликация артефактов"]
subgraph Prod["Производственный сервер"]
P1["just host::incoming\nfirmware_test release → HIL"]
P2["just host::production\nbootloader + tft_app release"]
end
FB --> DC
T1 --> T2 --> T3
DC --> HostW
T4 --> T5
HostW --> MR
T6 --> T7
MR --> CI
CI --> Prod
13. Производственный сервер
Сервер работает только с готовыми проверенными артефактами. Никакой сборки.
Сценарий входного контроля:
just host::incoming
└── flash firmware_test release → HIL-тесты
Финальная прошивка:
just host::production
├── flash bootloader release
└── flash tft_app release
Установлено: just · uv + spsdk · uv + pyocd/pytest · git
НЕ установлено: docker · cmake · компилятор · ARM toolchain