Files
templates/c/protocan-boot/README.md
T
Andrey c6390ae1c9 feat(protocan-boot): Добавь порты MCU и сервисы загрузки
Обеспечь единый процесс обновления для STM32F103/F407/G474 и TMS320F2812/F28335: discovery, служебные ответы, BUSY, тайм-аут сессии и подтверждение приложения. Добавь самостоятельные проекты CCS, аппаратные хранилища и Python-клиент.

Совместимость: структуры конфигурации и порта расширены, поэтому потребители должны пересобраться и обнулить новые поля. Для C28x callbacks используют pcan_boot_byte_t; периодический pcan_boot_poll обслуживает discovery и настроенный тайм-аут.
2026-10-23 14:16:19 +03:00

218 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ProtoCAN Boot
Каноническое описание обмена загрузчика перенесено из SETCAN в
[`docs/BOOTLOADER.md`](docs/BOOTLOADER.md). Реализация и документ меняются
вместе в этом переносимом модуле.
`protocan-boot` — переносимое C99-ядро адресной прошивки приборов по classic
CAN 2.0B и 29-битному ProtoCAN ID. Оно реализует сессию обновления, два
логических слота по 512 КиБ либо single-slot обновление, последовательную запись 8-байтовых блоков,
CRC32, проверку совместимости, продолжение по номеру следующего блока и
безопасный выбор неактивного слота.
Ядро не содержит HAL или регистров Flash. Готовые аппаратные callbacks для
STM32F103C8, STM32F407VE и STM32G474VE находятся в `ports`; подключение описано ниже.
Linker script, startup, тактирование, GPIO и переход в приложение предоставляет
проект устройства. Криптография в этих single-slot портах не реализована.
Для [STM32G474VET6 из CANdle_stm32](ports/stm32g474/README.md) задаются
`slot_count = 1` и `max_image_size = 480 * 1024`. В этом режиме запись идёт в единственную
область приложения, callback `set_pending_slot` не требуется, а аппаратный
rollback недоступен. Значение `slot_count = 0` сохраняет прежний A/B-режим.
## Слои
```text
приложение / CAN RX loop
|
v
pcan_boot_process() — протокол и state machine
|
v
pcan_boot_port_t — Flash, политика подписи, commit, reboot
|
v
STM32 HAL / другой MCU / host-тест
```
## Файлы
| Файл | Назначение | Зависимости |
|---|---|---|
| `include/pcan_boot.h` | публичный API, команды и структуры | C99 stdint/stdbool |
| `src/pcan_boot.c` | ProtoCAN ID, state machine и CRC32 | только публичный заголовок |
| `tests/test_pcan_boot.c` | хостовые тесты с RAM вместо Flash | стандартная библиотека C |
| `CMakeLists.txt` | сборка библиотеки и теста | CMake 3.13+ |
Модуль не зависит от legacy-модулей `set-protocol`: разбор пяти полей 29-битного ID
сделан переносимыми сдвигами, без непереносимых C-битовых полей.
## Карта `MsgType`
| Значение | Имя | Назначение |
|---:|---|---|
| `0x9` | `BOOT_CONTROL` | команды и метаданные |
| `0xA` | `BOOT_DATA_A` | блоки слота A |
| `0xB` | `BOOT_DATA_B` | блоки слота B |
| `0xC` | `BOOT_STATUS` | ответы и прогресс |
| `0xD` | `BOOT_DISCOVERY` | идентификация |
Для `BOOT_DATA_A/B` поле `MsgBody` — номер блока `0..65535`, а payload —
8 байт образа:
```text
offset = MsgBody * 8
512 КиБ = 512 * 1024 = 524 288 байт
524 288 / 8 = 65 536 блоков на слот
```
| `MsgBody` | Номер блока | Смещение | Байты слота |
|---:|---:|---:|---:|
| `0x0000` | 0 | `0x00000` | `0x00000..0x00007` |
| `0x0001` | 1 | `0x00008` | `0x00008..0x0000F` |
| `0xFFFF` | 65 535 | `0x7FFF8` | `0x7FFF8..0x7FFFF` |
Последний блок заканчивается на смещении `0x7FFFF`. Значение `0x80000`
равно размеру слота и является первой позицией за его границей. В коде эти
границы заданы константами `PCAN_BOOT_BLOCK_COUNT=65536` и
`PCAN_BOOT_SLOT_SIZE=524288`.
Последний кадр дополняется `0xFF`; ядро передаёт во Flash только оставшиеся
байты фактического образа и включает в CRC только их.
## Контракт порта
```c
typedef struct {
bool (*send)(void *, uint32_t can_id, const uint8_t *, uint8_t dlc);
bool (*erase_slot)(void *, uint8_t slot);
bool (*write_slot)(void *, uint8_t slot, uint32_t offset,
const uint8_t *, uint8_t length);
bool (*authorize)(void *, const pcan_boot_manifest_t *);
bool (*verify_image)(void *, uint8_t slot,
const pcan_boot_manifest_t *);
bool (*set_pending_slot)(void *, uint8_t slot,
const pcan_boot_manifest_t *);
bool (*confirm_running_slot)(void *);
void (*reboot)(void *);
} pcan_boot_port_t;
```
Обязательны `send`, `erase_slot`, `write_slot`; в A/B также `set_pending_slot`.
В single-slot этот callback необязателен и может завершать активацию образа.
`authorize` проверяет политику до стирания (тип, версия, anti-rollback).
`verify_image` выполняет платформенную проверку подписанного контейнера после
CRC32. Если callbacks отсутствуют, соответствующие дополнительные проверки
пропускаются; для серийного изделия их следует реализовать.
`set_pending_slot` должен атомарно сохранить boot metadata. До подтверждения
нового приложения загрузчик сохраняет возможность отката. `confirm_running_slot`
снимает pending-флаг после самопроверки приложения.
## Быстрый старт
```c
pcan_boot_t boot;
pcan_boot_config_t cfg = {
.device_type = 3, .device_id = 5, .product_type = 0x1234,
.hardware_revision = 2, .firmware_version = 0x01020000,
.active_slot = 0, .ack_window = 16
};
pcan_boot_port_t port = {
.send = can_send, .erase_slot = flash_erase_slot,
.write_slot = flash_write_slot, .authorize = image_authorize,
.verify_image = image_verify_signature,
.set_pending_slot = boot_set_pending, .reboot = system_reboot
};
pcan_boot_init(&boot, &cfg, &port, &board);
/* Из CAN RX-задачи, не из длительного Flash-прерывания: */
pcan_boot_process(&boot, rx.ExtId, rx.Data, rx.DLC);
```
## Последовательность обновления
1. `IDENTIFY` или discovery получает тип и версии прибора.
2. `ENTER_BOOT` открывает ненулевой `SessionID`.
3. `BEGIN_IMAGE` передаёт размер и CRC32.
4. `BEGIN_COMPAT` передаёт тип изделия, диапазон HW и версию FW.
5. Ядро выбирает неактивный слот и отвечает его номером.
6. `ERASE` вызывает порт стирания.
7. `BOOT_DATA_A` либо `BOOT_DATA_B` передаются строго по порядку.
8. `VERIFY` сравнивает CRC32 и вызывает проверку образа/подписи порта.
9. `COMMIT` атомарно помечает слот как pending.
10. После reboot приложение подтверждает запуск через `CONFIRM`.
Все изменяющие Flash команды адресуются конкретному `DeviceType/DeviceID`.
Ядро не принимает broadcast-запись и игнорирует кадры с чужим адресом или
`Route=FROM_DEVICE`.
## State machine
```text
IDLE -> ENTER_BOOT -> METADATA -> BEGIN_IMAGE + BEGIN_COMPAT
-> READY_TO_ERASE -> ERASE -> RECEIVING -> VERIFY -> VERIFIED
-> COMMIT -> REBOOT -> CONFIRM
```
При ошибке Flash, CRC, совместимости или подписи состояние переходит в
`FAILED`. Новая сессия начинается командой `ENTER_BOOT`; `ABORT` очищает
текущую сессию без изменения Flash.
## Сборка тестов
```sh
cmake -S . -B build
cmake --build build
ctest --test-dir build --output-on-failure
```
## Что остаётся проекту устройства
- зарезервировать bootloader, slot A/B и metadata в linker script;
- реализовать выравнивание и размеры страниц Flash;
- не выполнять длительное стирание непосредственно в CAN IRQ;
- атомарно хранить active/pending/confirmed и счётчик попыток запуска;
- проверить вектор, границы и подпись образа;
- включить watchdog и rollback при неподтверждённом запуске;
- согласовать фильтры CAN для `MsgType=0x9..0xD`.
Ядро поддерживает платформенные порты; особенности интеграции перечислены ниже.
## F28335 / большая EEPROM
[Порт M95M02](ports/tms320f28335/README.md) подключён в `28335_periph`, ветка 167.
Использует отдельный Flash-загрузчик и два слота EEPROM по 130560 байт.
Реализованы readback/CRC, проверка SCI8, журнал метаданных, пробный запуск,
CONFIRM/откат, PING/discovery и сервисный паспорт. Первичная установка Flash-
загрузчика и выбор Flash boot описаны в README порта.
## STM32 / прошивка по ProtoCAN
| Порт | Источник профиля | Область приложения | Ёмкость |
|---|---|---|---:|
| [STM32F103C8](ports/stm32f103/README.md) | `KONOR_ds18b20/src/bootloader.c` | `0x08003000..0x08010000` | 52 КиБ |
| [STM32F407VE](ports/stm32f407/README.md) | `climate_control_f407vet6_f4/Bootloader/bootloader_f407.c` | `0x08008000..0x08040000` | 224 КиБ |
| [STM32G474VE](ports/stm32g474/README.md) | `CANdle_stm32/src/boot/g474_can_bootloader.c` | `0x08008000..0x08080000` | 480 КиБ |
Верхние границы исключены. F103/F407 предоставляют готовые CMSIS `boot_main.c`
для исходных проектов и отдельные reusable HAL-порты. G474 предоставляет
reusable HAL FDCAN/Flash-порт, CAN/UART boot main, startup и вход из приложения.
Общие компоненты и правила подключения описаны в
[`ports/stm32-common`](ports/stm32-common/README.md). Это ProtoCAN Boot v1 по
CAN, с отдельной картой памяти относительно USB SET v2-загрузчиков из `set-protocol`.
Конфигурация задаётся по именам полей и принудительно использует `slot_count=1`.
Образы пишутся в логический слот A; A/B-rollback и постоянные commit-метаданные
отсутствуют. Ограничения запуска после потери питания описаны в общей инструкции.
Проекты KONOR, climate и CANdle G474 подключены к этим исходникам через свои
submodule `templates`; в старых путях оставлены тонкие include-обёртки.
Состав сборок, разметка SRAM-запросов и результаты проверки перечислены в
[`projects/STM32.md`](projects/STM32.md).
## Самостоятельные проекты CCS
Готовые проекты **F28335_CAN_AB** (M95M02) и **F2812_CAN_NOR** (AM29LV800BT),
включая необходимые файлы поддержки TI, находятся в `projects/ccs`.
[Импорт, сборка, карты памяти и установка](projects/README.md).