Update README.md

This commit is contained in:
Dmitry Akimov 2026-07-16 16:29:59 +03:00
parent 8cb1cb143c
commit 12bb949834

View file

@ -1,71 +1,45 @@
# bootloader # bootloader
> Загрузчик MIMXRT1052 — A/Б обновление `tft_app` через microSD (MCUboot, Загрузчик MIMXRT1052: выбирает и запускает приложение `tft_app` из одного из двух слотов
> Direct-XIP). Сам bootloader обновляется только через USB ROM (blhost) или (MCUboot, Direct-XIP), обновляет его с microSD, восстанавливает плату при зависании образа. Сам
> SWD — не в поле. Единственный канал диагностики: USB CDC ACM (JSON-lines, загрузчик прошивается только по USB ROM (blhost) или SWD — в поле не обновляется. Канал диагностики —
> тот же стиль протокола, что у `firmware_test`). USB CDC ACM (JSON-строки).
>
> Версия: `0.1.0` | Статус: Фаза 2 (bootutil / MCUboot Direct-XIP) завершена — см. [PLAN.md](PLAN.md)
--- **Принцип работы** (карта памяти, выбор образа, обновление, версии, даунгрейд, recovery) —
[BOOT_FLOW.md](BOOT_FLOW.md).
## Содержание
- [bootloader](#bootloader)
- [Содержание](#содержание)
- [Быстрый старт](#быстрый-старт)
- [1. Сборка](#1-сборка)
- [2. Прошивка](#2-прошивка)
- [3. Подключение](#3-подключение)
- [4. Отладка](#4-отладка)
- [Архитектура](#архитектура)
- [Карта Flash](#карта-flash)
- [Протокол](#протокол)
- [Roadmap](#roadmap)
- [Версионирование](#версионирование)
--- ---
## Быстрый старт ## Быстрый старт
### 1. Сборка ### Сборка
```bash ```bash
just build::build-bootloader-debug just build::build-bootloader-debug # bootloader.elf/.bin
just build::hab-bootloader-debug just build::hab-bootloader-debug # HAB-контейнер bootloader_hab.bin
``` ```
### 2. Прошивка ### Прошивка
Для итеративной разработки — SWD (не требует смены boot-режима платы): SWD (для итеративной разработки, не требует смены boot-режима платы):
```bash ```bash
just host::flash-swd-bootloader-debug just host::flash-swd-bootloader-debug
# обязателен power cycle платы после прошивки # после прошивки обязателен power cycle платы
``` ```
Через USB ROM (SDP, плата в режиме Serial Downloader): USB ROM (SDP, плата в режиме Serial Downloader):
```bash ```bash
just host::flash bootloader debug just host::flash bootloader debug
``` ```
### 3. Подключение ### Подключение
CDC поднимается, только если в обоих слотах (A/Б) нет валидного образа — иначе
bootloader сразу выбирает слот и прыгает в `tft_app` (Direct-XIP), не поднимая
USB вообще. Для проверки диагностического режима слоты должны быть пустыми
или содержать только невалидные образы.
USB CDC ACM (тот же порядок, что у `firmware_test`):
```bash ```bash
# macOS screen /dev/cu.usbmodemXXXX # macOS; порт свой на каждое подключение
screen /dev/cu.usbmodemXXXX
``` ```
Проверка связи:
```json ```json
→ {"type":"cmd","cmd":"ping"} → {"type":"cmd","cmd":"ping"}
← {"type":"pong"} ← {"type":"pong"}
@ -74,125 +48,103 @@ screen /dev/cu.usbmodemXXXX
← {"type":"version_response","fw":"0.1.0"} ← {"type":"version_response","fw":"0.1.0"}
``` ```
### 4. Отладка USB поднимается на каждой загрузке до обращения к SD, поэтому статусы видны, даже если подключиться
заранее.
VSCode → `🐛 Debug: bootloader` (`.vscode/launch.json`) — пересобирает через ### Отладка
`build:bootloader-debug`, подключается к GDB-серверу (`just host::debug-server`
должен быть запущен), останавливается на `main`. VSCode → `🐛 Debug: bootloader` — пересобирает, подключается к GDB-серверу
(`just host::debug-server` должен быть запущен), останавливается на `main`. Под отладчиком аппаратный
watchdog приостановлен, пошаговая отладка сбросами не сбивается.
--- ---
## Архитектура ## Архитектура
Bring-up идентичен `firmware_test` (`firmware/test/src/main.c`), но вместо Загрузчик работает **без SDRAM** (SEMC поднимает само приложение в своём раннем startup) и без
тестового раннера — сразу попытка выбрать и запустить `tft_app` из одного из дисплея/RTOS: инициализация, доступ к QSPI-flash, чтение FatFS с SD, проверка и выбор образа, прыжок.
двух слотов (MCUboot Direct-XIP, `bootutil`), и только если валидного образа Линкер жёстко ограничивает код бюджетом области загрузчика (256 КБ) с `ASSERT` на границу Slot A —
нет ни в одном слоте — диагностический CLI: превышение становится ошибкой сборки, а не тихим заездом в чужую область.
```bash ```text
firmware/bootloader/ firmware/bootloader/
├── CMakeLists.txt ├── src/
├── mcuboot_port/ — интеграция bootutil (MCUboot) с bsp_qspi_flash │ ├── main.c — точка входа: инициализация → одна попытка загрузки
│ ├── flash_map_backend.c — flash_area_* поверх bsp_qspi_flash (2 слота) │ │ (SD-скан + recovery-гейт + прыжок) → цикл ожидания
│ ├── keys.c — публичный ключ ECDSA-P256 для imgtool-подписи │ ├── boot_select.* — выбор валидного слота и прыжок в выбранный образ
│ ├── bootutil_sources.cmake — общий список исходников bootutil+TinyCrypt+ │ ├── slot_version.* — read-only проверка и чтение версии слота (без побочных
│ │ ASN.1 (используется и host-тестами) │ │ эффектов на flash)
│ └── mcuboot_config/, sysflash/, flash_map_backend/, keys/ — конфиг-заголовки │ ├── update_policy.* — чистая логика «ставить/пропустить» + целевой слот
│ ├── recovery.* — чистая логика решения recovery (порог / фолбэк / режим)
│ ├── sd_update.* — оркестрация: смонтировать SD, найти TFT_APP.BIN,
│ │ установить в целевой слот с потоковой verify-записью
│ ├── cli.* — построчный IO + диспетчеризация команд
│ ├── protocol.* — сериализация исходящих событий
│ └── version.h.in — шаблон версии (CMake → generated/version.h)
└── src/ ├── mcuboot_port/ — интеграция bootutil (MCUboot) поверх bsp_qspi_flash:
├── main.c — board_hw_init → led/tick/qspi init → │ flash_area_* на 2 слота, конфиг, публичный ключ ECDSA-P256
│ boot_select_and_jump() (при успехе не ├── fatfs/ — read-only FatFS для чтения TFT_APP.BIN с карты
│ возвращается) → иначе usb_cdc init → ожидание └── test_stub/ — самостоятельный подписанный образ-заглушка вместо
│ CDC (LED_HEARTBEAT мигает) → LED_APP on → tft_app для аппаратной проверки загрузчика
│ главный цикл (poll + cli_process)
├── boot_select.c/.h — обёртка над bootutil: `boot_go()` (выбор
│ валидного слота по подписи/версии) + прыжок в
│ выбранный образ (`jump_to_image`)
├── version.h.in — шаблон версии (CMake → generated/version.h)
├── cli.h / cli.c — IO-слой: буферизация строк, парсинг "type"/"cmd"
│ (урезанное подмножество firmware_test/src/cli.c)
└── protocol.h / .c — сериализация исходящих событий → cli_send()
``` ```
Bootloader **без SDRAM** — DCD не используется (`bsp_boot_xip_no_dcd` вместо `update_policy` и `recovery` — чистые функции без доступа к железу, целиком покрыты host-тестами.
`bsp_boot_xip`). `tft_app` инициализирует SEMC сама в своём раннем startup `boot_select`, `sd_update`, `flash_map_backend` — тонкий аппаратный слой поверх них.
(см. [BOOTLOADER_FLASH_MAP.md](../../docs/mimxrt1052/BOOTLOADER_FLASH_MAP.md)).
Линкер: `cmake/linker/MIMXRT1052xxxxx_bootloader_flexspi_nor.ld``m_text`
жёстко ограничен 247 КБ, с `ASSERT` на границу Slot A.
Верификация образов — подпись ECDSA-P256 (bootutil/TinyCrypt), два независимых
слота (A/Б) по 2 МБ каждый, без swap/scratch (Direct-XIP). Аппаратно
верифицировано все 5 сценариев чек-листа (единственный валидный слот, выбор
более новой версии, откат при повреждении, оба слота пусты, revert
неподтверждённого образа)
---
## Карта Flash
Полная карта, обоснование размеров и принцип "один bootloader на любую
ёмкость чипа" — в
[docs/mimxrt1052/BOOTLOADER_FLASH_MAP.md](../../docs/mimxrt1052/BOOTLOADER_FLASH_MAP.md).
| Область | Смещение | Размер |
| --------------------------- | ------------ | ------------------- |
| Bootloader | `0x60000000` | 256 КБ |
| Slot A (tft_app) | `0x60040000` | 2 МБ |
| Slot Б (tft_app) | `0x60240000` | 2 МБ |
| ФС ассетов (спрайты/музыка) | `0x60440000` | остальное (рантайм) |
--- ---
## Протокол ## Протокол
Транспорт и фреймирование — как у `firmware_test` USB CDC ACM, JSON-строки, максимум 128 байт на строку.
([firmware/test/README.md](../test/README.md#протокол-v2)): USB CDC ACM,
JSON-lines, максимум 128 байт на строку.
**Реализовано (Фаза 1):** **Команды хоста:**
| Команда хоста | Ответ | | Команда | Ответ |
| ------------------------------------ | ------------------------------------------ | | ------------------------------------ | ------------------------------------------------------------------------------------- |
| `{"type":"cmd","cmd":"ping"}` | `{"type":"pong"}` | | `{"type":"cmd","cmd":"ping"}` | `{"type":"pong"}` |
| `{"type":"cmd","cmd":"get_version"}` | `{"type":"version_response","fw":"X.Y.Z"}` | | `{"type":"cmd","cmd":"get_version"}` | `{"type":"version_response","fw":"X.Y.Z"}` |
| `{"type":"cmd","cmd":"wdog"}` | `{"type":"wdog","armed":…,"timeout_s":…,"recovered":…,"reset_count":…,"threshold":…}` |
Ошибки: `{"ok":false,"error":"PARSE_ERR"}` / `"UNKNOWN_CMD"` / `"LINE_TOO_LONG"`. **Исходящие статусы** `{"type":"status","state":"…"}`:
Без `session_start` — в отличие от `firmware_test`, bootloader не шлёт | Состояние | Когда |
приветствие автоматически; живость проверяется явным `ping` (тот же паттерн, | ---------------- | ------------------------------- |
что использует HIL-фикстура `firmware_cdc` для `firmware_test`). | `waiting_for_sd` | нет валидного слота, ждём карту |
| `installing` | идёт запись образа в слот |
| `update_skipped` | кандидат отклонён по версии |
| `recovery_mode` | плата в режиме восстановления |
**Появится в следующих фазах** (см. [PLAN.md](PLAN.md)): `status`-события **Ошибки** `{"ok":false,"error":"…"}`: `SD_CANDIDATE_INVALID`, `SD_INSTALL_WRITE_FAILED`,
(`waiting_for_sd`, `installing`, `smoke_pass`/`smoke_fail`, `booting`) — `SD_INSTALL_REJECTED`, `SD_DOWNGRADE_ERASE_FAILED`, `PARSE_ERR`, `UNKNOWN_CMD`, `LINE_TOO_LONG`.
Фазы 3-4.
Событие `wdog` эмитится также автоматически один раз на старте, если предыдущий сброс был по
watchdog (`recovered:true`).
--- ---
## Roadmap ## Тесты
Полный план по фазам с целями и критериями верификации — [PLAN.md](PLAN.md). **Host** — вся логика без железа (выбор слота, сравнение версий, политика установки, решение
recovery, протокол, CLI) на in-memory flash-фейках:
| Фаза | Что делает | ```bash
| ---- | ---------------------------------------------------------------- | just build::test-host
| 0 ✅ | Карта Flash, регистрация в сборке | ```
| 1 ✅ | Скелет: bring-up, USB CDC, ping/get_version, LED heartbeat |
| 2 ✅ | bootutil (MCUboot Direct-XIP) — выбор слота, верификация подписи | **Аппаратный стенд** — сборка и подпись образов-заглушек, замещающих `tft_app` при ручной проверке
| 3 | Установка образа с microSD, состояние "нет валидного слота" | на плате (здоровые образы + варианты с зависанием на разных стадиях для проверки восстановления):
| 4 | SDRAM/W25Q smoke-test, словарь LED-паттернов |
| 5 | HAB Release, интеграция в service-tui | ```bash
just build::build-mcuboot-stub
```
Чек-листы ручной проверки лежат рядом со стендом в `test_stub/`.
--- ---
## Версионирование ## Версионирование
Версия задаётся `project(bootloader VERSION X.Y.Z)` в `CMakeLists.txt`, Версия задаётся `project(bootloader VERSION X.Y.Z)` в `CMakeLists.txt` и прокидывается через
прокидывается через `configure_file(src/version.h.in → generated/version.h)` `configure_file(src/version.h.in → generated/version.h)` в строку, которую возвращает
в `BOOTLOADER_VERSION_STR` — тот же механизм, что у `firmware_test` `get_version`. `version.h` генерируется, вручную не редактируется.
(см. [firmware/test/README.md#версионирование](../test/README.md#версионирование)).
`version.h` генерируется, не редактируется вручную.