18 KiB
Версия работающей прошивки и SHA-256 файла: TMS2812, TMS28335, STM32
1. Какие задачи решает механизм
Механизм продолжает контракт Climate: устройство сообщает SemVer, дату/время
сборки и Git Build ID в 12 регистрах. Компьютер после сборки вычисляет SHA-256
готового файла и сохраняет паспорт *.manifest.json и файл *.sha256.
Это три разных идентификатора:
| Идентификатор | Что определяет | Где хранится |
|---|---|---|
major.minor.patch |
Назначенный разработчиком номер версии | В образе и паспорте |
| Git Build ID | Исходный коммит; + отмечает локальные изменения |
В образе и паспорте |
| SHA-256 | Точные байты конкретного BIN/HEX, включая заголовки | Рядом с образом |
SHA Git-коммита и SHA-256 файла прошивки не взаимозаменяемы. Один коммит может давать разные бинарные файлы при смене компилятора, опций, времени сборки или зависимостей. Полные коммиты приложения и templates записываются в паспорт.
Контроллер не вычисляет SHA своей памяти. Ответ версии подтверждает, какие метаданные содержит работающий образ; SHA файла проверяет файл на компьютере. Для проверки фактической записи нужна отдельная штатная проверка программатора или чтение памяти назад. SHA рядом с файлом не является цифровой подписью.
2. Слои и файлы
firmware_identity.json проекта + Git приложения + Git templates
|
tools/identity.py prepare (до компиляции)
|
generated/firmware_identity.h + .json
|
src/firmware_info_static.c (общий C-код)
|
12 неизменяемых слов -> порт транспорта -> клиент
линковка -> преобразование BIN -> проверка размера -> identity.py seal
|
BIN + BIN.manifest.json + BIN.sha256
| Файл | Задача и зависимости |
|---|---|
include/firmware_info_static.h |
Контракт компактного порта и упаковка слов, только stdint.h |
src/firmware_info_static.c |
Константная таблица и чтение одного слова; нужен generated header |
include/firmware_info.h, src/firmware_info.c |
Существующий динамический API Climate, валидация и сериализация |
src/firmware_info_port.c |
Динамический адаптер конфигурации; поддерживает новый UTC stamp |
ports/tms320f2812, ports/tms320f28335, ports/stm32 |
Примеры конфигурации для нового генератора |
tools/identity.py |
Git, JSON, SHA-256 файла; Python 3.10+ и Git, без pip-зависимостей |
tests/test_static.c |
Совпадение компактного и динамического контрактов, чтение RS/CAN, запрет записи |
tests/test_identity.py |
Генерация, dirty-зависимость, отсутствие Git, порча файла |
Ядро не знает HAL, CMSIS, XINTF, EEPROM, UART, CAN-регистры и адрес устройства.
firmware_info_static_read(unsigned index) принимает индекс 0..11, возвращает
uint16_t; вне диапазона возвращает 0. Проверять диапазон запроса и запрещать
запись обязан транспорт. Нулевую версию контракта клиент показывает как
«версия неизвестна», а не подставляет версию из выбранного файла.
3. Конфигурация проекта и генерация
В репозитории приложения создайте firmware_identity.json:
{
"product": "MyController",
"target": "TMS320F2812",
"version": [1, 0, 0]
}
product и target нужны для паспорта файла. В контракт v1 они не входят:
тип подключённого устройства по-прежнему определяет его собственный протокол.
Не используйте совпадение версии как разрешение залить образ другого изделия.
До каждой компиляции выполните (пути приведены для checkout в lib/templates):
python lib/templates/c/firmware-info/tools/identity.py prepare `
--repository . --templates lib/templates `
--config firmware_identity.json --output build/generated
Добавьте build/generated и lib/templates/c/firmware-info/include в include
paths. Скомпилируйте один экземпляр
lib/templates/c/firmware-info/src/firmware_info_static.c. Если проект включает
этот файл через агрегирующий .c, отдельно добавлять его в сборку нельзя.
Генератор намеренно переписывает header при каждом запуске: зависимый объект
должен пересобраться. Отсутствие header — ошибка компиляции, а не LOCALDEV.
Нужна корректная обработка зависимостей сборщиком; после изменения настроек IDE
выполните полную пересборку. Не редактируйте исходники во время сборки.
Для разных конфигураций и параллельных сборок используйте разные generated
каталоги. Не запускайте две сборки с одним output одновременно.
Время в новом пути — UTC, снято перед компиляцией. Если задан
SOURCE_DATE_EPOCH, используется этот Unix timestamp; это помогает повторяемым
сборкам, но не делает весь toolchain автоматически воспроизводимым.
Значения Build ID
| Значение | Смысл |
|---|---|
a1b2c3d4 |
Приложение и templates доступны в Git и оба дерева чистые |
a1b2c3d+ |
Изменено приложение или templates, включая неотслеживаемые файлы |
NOGIT000 |
Не удалось определить хотя бы один из двух Git-репозиториев |
LOCALDEV |
Только старый динамический путь без generated header |
Полные commit SHA находятся в source.commit и templates.commit паспорта.
+ не идентифицирует содержимое локальных правок. Точный файл определяет его
SHA-256; для воспроизводимого выпуска нужны сохранённые исходники и настройки.
Добавляйте generated/build каталоги в .gitignore, иначе они будут честно
делать следующую сборку dirty. Намеренно не игнорируйте новые исходные файлы.
4. Формат: одинаковые 12 слов на всех МК
| Индекс | Значение |
|---|---|
| 0 | Версия контракта: 1 |
| 1 | Major, 0..255 |
| 2 | Minor, 0..255 |
| 3 | Patch, 0..999 |
| 4 | Год, 2000..2199 |
| 5 | Месяц в старшем октете, день в младшем |
| 6 | Час в старшем октете, минута в младшем |
| 7 | Секунда |
| 8 | ASCII Build ID: символы 0 и 1, первый в старшем октете |
| 9 | Символы 2 и 3 |
| 10 | Символы 4 и 5 |
| 11 | Символы 6 и 7 |
Например, abcdef0+ даёт 0x6162, 0x6364, 0x6566, 0x302B в словах 8..11.
Modbus передаёт каждое слово старшим октетом вперёд. Старый
firmware_info_to_le_bytes() передаёт слова little-endian для соответствующих
byte-протоколов; эти два представления нельзя путать.
На TMS320F2812/F28335 CHAR_BIT == 16, sizeof(uint16_t) == 1, тип uint8_t
отсутствует. В динамическом API используется firmware_info_octet, основанный
на uint_least8_t: на STM32 это прежние 8 бит, на C28x — контейнер 16 бит.
Размеры byte-API означают число элементов-октетов, не физический sizeof
переданного массива. На проводе всё равно 24 восьмибитных октета.
5. Порты и включение в транспорт
TMS320F28335 / ПМ35
В 167 уже подключено через Source/Internal/shared_ump.c. Макрос
UMP_FIRMWARE_INFO_READ связывает общий порт UMP с
firmware_info_static_read. Диапазон 0x1200..0x120B обслуживается до
обращения к регистратору. Окно параметров 0..127 и лог не расширяются.
RS: функция 03, начальный адрес 0x1200 (4608), количество 12.
Разрешены частичные чтения, не выходящие за конец окна. Функция 06 возвращает
исключение 02, не изменяя ни версию, ни настройки регистратора.
Служебный CAN ПМ35: тот же UMP-запрос функции 03 на 0x1200, количество 12,
ненулевой token. Ответ — шесть фрагментов по два слова с тем же token.
Частичные CAN-чтения этого окна отвергаются. Используйте существующие UMP
сборщики/парсеры templates, не создавайте второй кодек в GUI.
TMS320F2812 / ПМ67
Добавьте тот же firmware_info_static.c и include paths. Компактный порт не
требует FPU и не использует регистры F28335. Конфигурация-пример лежит в
ports/tms320f2812/firmware_info_config.template.h.
Выберите свободное окно в карте конкретного ПМ67. В обработчике чтения
отдавайте firmware_info_static_read(address - base) после проверки диапазона.
Не переносите автоматически CAN ID и UMP ПМ35: у ПМ67 отдельный протокол.
Это поставляемый переносимый порт, а не заявление, что конкретная прошивка
ПМ67 уже изменена или испытана на устройстве.
STM32 / Modbus или SET
Компактный порт одинаков для F1/F4/G4. Для HAL он не требует ни инициализации, ни callback. Пример адаптации чтения после проверки диапазона транспортом:
#include "firmware_info_static.h"
/* FW_BASE выбирает проект; соответствие адресов проверяет Modbus/SET handler. */
uint16_t app_firmware_word(uint16_t address)
{
unsigned index = (unsigned)(address - FW_BASE);
return firmware_info_static_read(index);
}
Для STM32 UMP-периферии можно использовать уже существующий
set-protocol/ports/stm32f407-periph и подставить этот reader в ump_backend.
Для обычного Modbus используйте собственное свободное input/holding read-only
окно, сохраняя 12 слов контракта. В Climate адрес был 1960; это не адрес
167. Если удобнее заполнить RAM-регистры при старте, оставьте динамический API:
firmware_info_port_describe() и firmware_info_to_registers().
6. SHA-256 и паспорт после сборки
Сначала завершите линковку и преобразование в файл, который реально отправляется загрузчику. Затем проверьте формат и допустимый размер. Только после успеха:
python lib/templates/c/firmware-info/tools/identity.py seal `
--image build/controller.bin --metadata build/generated/firmware_identity.json
Результат:
controller.bin
controller.bin.manifest.json
controller.bin.sha256
Паспорт содержит product, target, version, build_id, built_at_utc, полные commit
SHA и dirty-состояния двух репозиториев, имя файла, размер и SHA-256. Передавайте
эти три файла вместе. seal не извлекает версию из произвольного бинарника:
metadata должен быть из той же сборки. Нельзя перепривязать старый BIN к
новому pre-build JSON. Проверка версии на работающей плате замыкает этот цикл.
Проверка точного файла:
python lib/templates/c/firmware-info/tools/identity.py verify `
--image build/controller.bin --manifest build/controller.bin.manifest.json
При изменении даже одного байта команда завершится ненулевым кодом. SHA считается для всего файла: для SCI8 — вместе с boot table, для HEX — для текстового HEX, а не для нормализованного содержимого Flash. Выбирайте BIN для однозначного сравнения с бинарным файлом программатора.
7. Выпуск и проверка на устройстве
- Назначьте SemVer в конфигурации приложения.
- Сохраните изменения templates отдельным коммитом и закрепите именно эту зависимость в потребителе. Затем сохраните исходники приложения.
- Убедитесь, что оба дерева чистые; соберите в отдельном output.
- Проверьте ограничения образа, наличие паспорта и результат
verify. - Загрузите именно этот файл штатным загрузчиком и перезапустите устройство.
- Прочитайте версию с устройства заново, отключив кэш клиента.
- Сравните все 12 слов с SemVer, UTC stamp и Build ID паспорта.
- Сохраните BIN, паспорт, настройки компилятора и результаты проверки вместе.
Изменение SemVer не должно менять contract_version: последняя меняется только
при несовместимом изменении формата. При несовпадении даты/Build ID сначала
проверьте выбранный BIN, конфигурацию IDE, generated include path и полную
пересборку, затем адрес и кэш клиента.
8. Автоматические проверки
cmake -S c/firmware-info -B build/firmware-info
cmake --build build/firmware-info
ctest --test-dir build/firmware-info --output-on-failure
python -m unittest discover -s c/firmware-info/tests -p test_identity.py
Дополнительно компилируйте core/static через TI C2000 (-v28 -ml) и ARM
toolchain. Host-тест не заменяет проверку C28x ABI и фактического обмена с платой.
Для 167 есть отдельный tests/firmware_identity/port_test.c: он проверяет
реальную привязку reader, RTU CRC, CAN prepare и сохранение окна регистратора.
9. Совместимость с существующими проектами
Старый make_build_id.ps1, конфигурации STM32F1/F4/G4 и динамический API остаются.
Новый генератор не подключается к другим проектам сам: добавьте pre/post-build
и generated include path по этой инструкции. На STM32 размеры публичных полей
и сигнатура byte-буфера остаются прежними благодаря uint_least8_t.
Изменения сделаны в checkout templates, который уже использует 167:
newProject/SETGUI/third_party/templates. Другие checkout/submodule не следует
обновлять копированием отдельных .c: перенесите проверенный коммит templates
и обновите ссылку зависимости. ПМ67 и ПМ35 сохраняют разные транспортные порты.