lift_indicator_suite/firmware/bootloader
2026-07-20 15:52:22 +03:00
..
fatfs # bootloader: Phase 3 - checked and done 580e5f5 2026-07-10 15:02:18 +03:00
mcuboot_port # bootloader: Phase 4 - done 2026-07-20 11:40:36 +03:00
src # bootloader: Phase 4 - done 2026-07-20 11:40:36 +03:00
test_stub # bootloader, service-tui: ready for release 2026-07-20 15:52:22 +03:00
CMakeLists.txt # bootloader, service-tui: ready for release 2026-07-20 15:52:22 +03:00
README.md # bootloader: Phase 4 - done 2026-07-20 11:40:36 +03:00

bootloader

Загрузчик MIMXRT1052: выбирает и запускает приложение tft_app из одного из двух слотов (MCUboot, Direct-XIP), обновляет его с microSD, восстанавливает плату при зависании образа. Сам загрузчик прошивается только по USB ROM (blhost) или SWD — в поле не обновляется. Канал диагностики — USB CDC ACM (JSON-строки).

Принцип работы (карта памяти, выбор образа, обновление, версии, даунгрейд, recovery) — BOOT_FLOW.md. LED-индикация — LED_PATTERNS.md.


Быстрый старт

Сборка

just build::build-bootloader-debug    # bootloader.elf/.bin
just build::hab-bootloader-debug       # HAB-контейнер bootloader_hab.bin

Прошивка

SWD (для итеративной разработки, не требует смены boot-режима платы):

just host::flash-swd-bootloader-debug
# после прошивки обязателен power cycle платы

USB ROM (SDP, плата в режиме Serial Downloader):

just host::flash bootloader debug

Подключение

screen /dev/cu.usbmodemXXXX      # macOS; порт свой на каждое подключение
 {"type":"cmd","cmd":"ping"}
 {"type":"pong"}

 {"type":"cmd","cmd":"get_version"}
 {"type":"version_response","fw":"0.1.0"}

USB поднимается на каждой загрузке до обращения к SD, поэтому статусы видны, даже если подключиться заранее.

Отладка

VSCode → 🐛 Debug: bootloader — пересобирает, подключается к GDB-серверу (just host::debug-server должен быть запущен), останавливается на main. Под отладчиком аппаратный watchdog приостановлен, пошаговая отладка сбросами не сбивается.


Архитектура

Загрузчик не зависит от SDRAM для своей работы (XIP только из W25Q, без DCD) и без дисплея/RTOS: инициализация, доступ к QSPI-flash, чтение FatFS с SD, проверка и выбор образа, прыжок. SEMC/SDRAM трогаются только диагностически (bsp_sdram_configure(), boot-time smoke-test) — реально их поднимает для себя уже само приложение в своём раннем startup. Линкер жёстко ограничивает код бюджетом области загрузчика (256 КБ) с ASSERT на границу Slot A — превышение становится ошибкой сборки, а не тихим заездом в чужую область.

firmware/bootloader/
├── src/
│   ├── main.c            — точка входа: инициализация → одна попытка загрузки
│   │                       (SD-скан + recovery-гейт + прыжок) → цикл ожидания
│   ├── boot_select.*     — выбор валидного слота и прыжок в выбранный образ
│   ├── slot_version.*    — read-only проверка и чтение версии слота (без побочных
│   │                       эффектов на flash)
│   ├── update_policy.*   — чистая логика «ставить/пропустить» + целевой слот
│   ├── recovery.*        — чистая логика решения recovery (порог / фолбэк / режим)
│   ├── sd_update.*       — оркестрация: смонтировать SD, найти TFT_APP.BIN,
│   │                       установить в целевой слот с потоковой verify-записью
│   ├── cli.*             — построчный IO + диспетчеризация команд
│   ├── protocol.*        — сериализация исходящих событий
│   ├── led_status.*      — словарь LED-паттернов (см. ../../docs/bootloader/LED_PATTERNS.md)
│   ├── dev_sdram_test.*  — [DEV-ONLY, Debug] глубокий тест SDRAM по команде "sdram_test"
│   └── version.h.in      — шаблон версии (CMake → generated/version.h)
│
├── mcuboot_port/         — интеграция bootutil (MCUboot) поверх bsp_qspi_flash:
│                           flash_area_* на 2 слота, конфиг, публичный ключ ECDSA-P256
├── fatfs/                — read-only FatFS для чтения TFT_APP.BIN с карты
└── test_stub/            — самостоятельный подписанный образ-заглушка вместо
                            tft_app для аппаратной проверки загрузчика

update_policy и recovery — чистые функции без доступа к железу, целиком покрыты host-тестами. boot_select, sd_update, flash_map_backend — тонкий аппаратный слой поверх них.


Протокол

USB CDC ACM, JSON-строки. Входящая строка — до CLI_LINE_BUF_SIZE (128) байт, исходящее сообщение — до PROTO_BUF_SIZE (192) байт (несимметрично: qspi_info/sdram_test длиннее старых сообщений).

Команды хоста:

Команда Ответ
{"type":"cmd","cmd":"ping"} {"type":"pong"}
{"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":…}
{"type":"cmd","cmd":"smoke_status"} {"type":"status","state":"smoke_pass"} / "smoke_fail" — результат boot-time smoke-теста SDRAM/SEMC (переспрос, см. ниже)
{"type":"cmd","cmd":"qspi_info"} {"type":"qspi_info","chip":"W25Q128","mfr":"0xEF","cap_byte":"0x18","size_mb":16,"pass":true} (переспрос, см. ниже)
{"type":"cmd","cmd":"sdram_test"}
[DEV-ONLY, Debug-сборка]
серия из 6 {"type":"sdram_test","phase":"…","pass":…,"duration_ms":…,"fail_addr":"…","expected":"…","got":"…"} (configure/address_bus/data_bus/sequential/retention/summary) — блокирует главный цикл на ~4 с. Нет в Release/HAB (BOOTLOADER_DEV_DIAGNOSTICS)

smoke_status/qspi_info ничего не отвечают, если соответствующий boot-time чек ещё не отработал — в штатной последовательности main.c такого не бывает.

Исходящие статусы {"type":"status","state":"…"}:

Состояние Когда
waiting_for_sd нет валидного слота, ждём карту
installing идёт запись образа в слот
update_skipped кандидат отклонён по версии
recovery_mode плата в режиме восстановления
smoke_pass boot-time smoke-тест SDRAM/SEMC прошёл
smoke_fail boot-time smoke-тест SDRAM/SEMC провалился

Ошибки {"ok":false,"error":"…"}: SD_CANDIDATE_INVALID, SD_INSTALL_WRITE_FAILED, SD_INSTALL_REJECTED, SD_DOWNGRADE_ERASE_FAILED, PARSE_ERR, UNKNOWN_CMD, LINE_TOO_LONG.

Автоматически на старте, без команды: wdog — если предыдущий сброс был по watchdog (recovered:true); qspi_info и smoke_pass/smoke_fail — сразу после соответствующей проверки. Все три — best-effort: хост почти никогда не успевает открыть порт к этому моменту (USB enumeration), поэтому у qspi_info/smoke_status (но не у одноразового boot-time wdog) есть команда-переспрос в таблице выше.

LED-индикация, соответствующая этим состояниям, — LED_PATTERNS.md.


Тесты

Host — вся логика без железа (выбор слота, сравнение версий, политика установки, решение recovery, протокол, CLI) на in-memory flash-фейках:

just build::test-host

Аппаратный стенд — сборка и подпись образов-заглушек, замещающих tft_app при ручной проверке на плате (здоровые образы + варианты с зависанием на разных стадиях для проверки восстановления):

just build::build-mcuboot-stub

Чек-листы ручной проверки лежат рядом со стендом в test_stub/.


Версионирование

Версия задаётся project(bootloader VERSION X.Y.Z) в CMakeLists.txt и прокидывается через configure_file(src/version.h.in → generated/version.h) в строку, которую возвращает get_version. version.h генерируется, вручную не редактируется.