# Версия работающей прошивки и 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. Слои и файлы ```text 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`: ```json { "product": "MyController", "target": "TMS320F2812", "version": [1, 0, 0] } ``` `product` и `target` нужны для паспорта файла. В контракт v1 они не входят: тип подключённого устройства по-прежнему определяет его собственный протокол. Не используйте совпадение версии как разрешение залить образ другого изделия. До каждой компиляции выполните (пути приведены для checkout в `lib/templates`): ```powershell 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. Пример адаптации чтения **после проверки диапазона транспортом**: ```c #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 и паспорт после сборки Сначала завершите линковку и преобразование в файл, который реально отправляется загрузчику. Затем проверьте формат и допустимый размер. Только после успеха: ```powershell python lib/templates/c/firmware-info/tools/identity.py seal ` --image build/controller.bin --metadata build/generated/firmware_identity.json ``` Результат: ```text 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. Проверка версии на работающей плате замыкает этот цикл. Проверка точного файла: ```powershell 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. Выпуск и проверка на устройстве 1. Назначьте SemVer в конфигурации приложения. 2. Сохраните изменения templates отдельным коммитом и закрепите именно эту зависимость в потребителе. Затем сохраните исходники приложения. 3. Убедитесь, что оба дерева чистые; соберите в отдельном output. 4. Проверьте ограничения образа, наличие паспорта и результат `verify`. 5. Загрузите именно этот файл штатным загрузчиком и перезапустите устройство. 6. Прочитайте версию с устройства заново, отключив кэш клиента. 7. Сравните все 12 слов с SemVer, UTC stamp и Build ID паспорта. 8. Сохраните BIN, паспорт, настройки компилятора и результаты проверки вместе. Изменение SemVer не должно менять `contract_version`: последняя меняется только при несовместимом изменении формата. При несовпадении даты/Build ID сначала проверьте выбранный BIN, конфигурацию IDE, generated include path и полную пересборку, затем адрес и кэш клиента. ## 8. Автоматические проверки ```powershell 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 сохраняют разные транспортные порты.