From cf65439400450a1345217485628b6d0fe468a7f4 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 17 Oct 2026 14:43:11 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D0=B2=D1=8B=D0=BF=D1=83=D1=81=D0=BA=20=D0=B8=20=D1=8D?= =?UTF-8?q?=D0=BA=D1=81=D0=BF=D0=BE=D1=80=D1=82=20Flash-=D0=BE=D0=B1=D1=80?= =?UTF-8?q?=D0=B0=D0=B7=D0=BE=D0=B2=20C2000?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- c/firmware-info/tests/test_c2000_postbuild.py | 87 ++++++++++ c/firmware-info/tools/c2000_postbuild.py | 40 ++++- tools/firmware-build/README.md | 158 ++++++++++++++++-- 3 files changed, 269 insertions(+), 16 deletions(-) create mode 100644 c/firmware-info/tests/test_c2000_postbuild.py diff --git a/c/firmware-info/tests/test_c2000_postbuild.py b/c/firmware-info/tests/test_c2000_postbuild.py new file mode 100644 index 0000000..9a2809f --- /dev/null +++ b/c/firmware-info/tests/test_c2000_postbuild.py @@ -0,0 +1,87 @@ +"""Проверяет совместимость postbuild, экспорт двух образов и отказ от устаревшего Flash OUT.""" # Фиксирует контракт общего выпуска без зависимости от установленного TI-компилятора. +import json # Формирует паспорт текущей сборки для проверки архива и контрольных сумм. +from pathlib import Path # Создаёт независимые пути проекта, компилятора и результатов в каждом тесте. +import subprocess # Использует штатное исключение ненулевого кода проектного сборщика. +import sys # Подключает общий модуль из каталога tools текущей копии templates. +import tempfile # Изолирует проверочные файлы от реальных прошивок и каталогов GUI. +import unittest # Организует проверки публичного контракта postbuild стандартной библиотекой Python. +from unittest.mock import patch # Подменяет только внешние инструменты, оставляя настоящее архивирование и SHA-256. + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'tools')) # Находит проверяемую реализацию относительно теста без путей конкретной рабочей станции. +import c2000_postbuild as c2000 # Вызывает тот же postbuild, который запускается штатным postcompile.bat. +from identity import verify # Проверяет байты скопированных образов по настоящему manifest. + + +class PostbuildTests(unittest.TestCase): # Проверяет общую цепочку от готового OUT до экспортированной папки выпуска. + def setUp(self): # Для каждой проверки создаёт отдельный проект с типичной структурой CCS. + temporary = tempfile.TemporaryDirectory() # Все файлы сценария принадлежат временному каталогу теста. + self.addCleanup(temporary.cleanup) # Освобождает только каталог данной проверки при любом результате. + self.root = Path(temporary.name) # Корень не зависит от реальной прошивки и установленного templates. + self.build = self.root / 'project/Debug' # Проверяет соглашение о расположении сборщика рядом с конфигурациями CCS. + self.build.mkdir(parents=True) # Подготавливает рабочий каталог внешних инструментов. + self.compiler = self.root / 'compiler' # Позволяет проверить передачу компилятора текущего проекта вместо фиксированного пути. + (self.compiler / 'bin').mkdir(parents=True) # Воспроизводит структуру установленного C2000 compiler. + (self.compiler / 'bin/hex2000.exe').write_bytes(b'fake tool') # Проверка наличия инструмента реальна, а выполнение будет подменено. + self.source = self.root / 'Bin/Firmware.out' # Основной OUT размещён вне Debug, как в рабочем проекте пользователя. + self.source.parent.mkdir() # Создаёт отдельный каталог исходного COFF и итогового Flash COFF. + self.source.write_bytes(b'RAM image') # Даёт узнаваемые байты для проверки, что основной образ не подменён Flash-вариантом. + self.metadata = self.build / 'metadata.json' # Один паспорт используется обоими вариантами текущего выпуска. + self.metadata.write_text(json.dumps({'build_id': 'NOGIT000', 'built_at_utc': '2026-10-14T13:00:00+00:00', 'source': {'commit': None}, 'version': [1, 2, 3]}), encoding='utf-8') # Фиксированное время проверяет выбор папки по prebuild, а не времени файлов. + self.output = self.build / 'firmware' # Локальный выпуск получает оба образа и их сопроводительные файлы. + self.export = self.root / 'GUI' # Экспорт изолирован от настоящего GUI и сетевых ресурсов. + + def fake_tool(self, command, **kwargs): # Имитирует границу внешних инструментов, оставляя реальную логику упаковки и проверки. + if '--postbuild' in command: # Вложенная Flash-сборка обязана использовать режим без повторного Make. + self.assertEqual(Path(command[command.index('--compiler') + 1]), self.compiler.resolve()) # Проверяет выбор текущей версии TI compiler. + self.assertEqual(Path(command[command.index('--build-dir') + 1]), self.build.resolve()) # Проверяет использование текущих объектов и generated-паспорта. + image = Path(command[command.index('--output') + 1]) # Результат должен создаваться по переданному временному пути. + self.assertNotEqual(image.parent, self.source.parent) # Запрещает выдавать существующий OUT из Bin за результат текущего вызова. + image.write_bytes(b'current Flash image') # Создаёт узнаваемый новый COFF для сравнения всех копий. + image.with_suffix('.map').write_text('codestart 0033fff6', encoding='utf-8') # Воспроизводит обязательный второй результат сборщика. + else: # Обычная SCI8-конверсия должна продолжать использовать основной RAM OUT. + expected = 'Firmware_flash.out' if '_flash_rs.bin' in command[command.index('-o') + 1] else self.source.name # Только отдельная RS-конверсия должна брать Flash COFF. + self.assertEqual(Path(command[1]).name, expected) # Проверяет, что EEPROM BIN и Flash RS BIN создаются из разных исходных образов. + Path(command[command.index('-o') + 1]).write_bytes(b'EEPROM image') # Выпускает HEX/BIN с отличимыми байтами основного варианта. + Path(command[command.index('-map') + 1]).write_text('RAM map', encoding='utf-8') # Сохраняет карты, необходимые настоящему архиватору. + return subprocess.CompletedProcess(command, 0) # Подтверждает только имитируемый инструмент, не подменяя проверки выпуска. + + def run_postbuild(self): # Вызывает публичную функцию с теми же путями, что передаёт BAT из CCS. + return c2000.postbuild(self.compiler, self.build, str(self.source.with_suffix('')), self.metadata, self.output, self.export) # Включает настоящий экспорт и проверку manifest. + + def test_project_without_flash_builder_keeps_eeprom_workflow(self): # Старые проекты должны работать без новых настроек или Flash-файлов. + with patch.object(c2000.subprocess, 'run', side_effect=self.fake_tool) as calls: # Подменяет только TI Hex Utility. + result = self.run_postbuild() # Выполняет полный прежний выпуск EEPROM. + self.assertEqual(calls.call_count, 2) # Для HEX и BIN выполнены два вызова; дополнительного сборщика не было. + self.assertEqual(self.source.read_bytes(), b'RAM image') # Основной OUT не изменён упаковкой. + self.assertFalse(self.source.with_name('Firmware_flash.out').exists()) # Отсутствие builder не создаёт фиктивный Flash-образ. + self.assertEqual(verify(result, Path(str(result) + '.manifest.json'))['size_bytes'], len(b'EEPROM image')) # Проверяет настоящее содержимое EEPROM BIN и его паспорт. + + def test_flash_and_eeprom_share_current_export_with_separate_checksums(self): # Новый выпуск должен содержать оба варианта под своими именами. + (self.build.parent / 'build_flash.py').write_text('# Сборщик проекта подключён явно.\n', encoding='utf-8') # Наличие сценария включает дополнительный шаг postbuild. + with patch.object(c2000.subprocess, 'run', side_effect=self.fake_tool) as calls: # Flash и hex2000 имитируются на границе процессов. + self.run_postbuild() # Архиватор и экспорт работают без подмены. + self.assertEqual(calls.call_count, 4) # Один Flash-вызов, две EEPROM-конверсии и отдельный RS BIN подтверждают отсутствие рекурсивного Make. + folder = self.export / 'NOGIT000_2026-10-14_16-00-00_UTC+3' # Имя следует времени prebuild с переводом в московский UTC+3. + for directory in (self.source.parent, self.output, folder): # Проверяет копию для CCS, плоский выпуск и архив для GUI. + self.assertEqual((directory / 'Firmware_flash.out').read_bytes(), b'current Flash image') # Каждая копия содержит новый Flash COFF. + self.assertTrue((directory / 'Firmware_flash.map').is_file()) # Карта Flash не теряется при сохранении или экспорте. + for name in ('Firmware_flash.out', 'Firmware_NOGIT000.bin', 'Firmware_flash_rs.bin'): # Контрольные суммы каждого варианта должны проверяться независимо. + verify(folder / name, folder / (name + '.manifest.json')) # Использует реальные SHA-256 экспортированных байтов. + self.assertEqual((folder / self.source.name).read_bytes(), b'RAM image') # Архив сохраняет исходный RAM OUT вместе с новым Flash OUT. + + def test_failed_or_incomplete_builder_cannot_export_stale_flash(self): # Ошибка или отсутствие карты должны остановить выпуск до экспорта старого COFF. + (self.build.parent / 'build_flash.py').write_text('# Подключён неисправный сборщик для проверки отказа.\n', encoding='utf-8') # Включает путь Flash-проверки. + stale = self.source.with_name('Firmware_flash.out') # В Bin уже есть образ от предыдущей успешной сборки. + stale.write_bytes(b'old Flash image') # Отличимые байты выявят ошибочную повторную публикацию. + for failure in (subprocess.CalledProcessError(1, 'builder'), None): # Проверяет ненулевой код и ложный успех без обязательных результатов. + with self.subTest(failure=failure): # Даёт отдельную диагностику двум независимым отказам. + with patch.object(c2000.subprocess, 'run', side_effect=failure) as calls: # Сборщик либо падает, либо не создаёт файлов. + with self.assertRaises((subprocess.CalledProcessError, ValueError)): # Оба отказа должны выйти наружу как ошибка postbuild. + self.run_postbuild() # Не разрешает продолжение EEPROM-конверсии после сбоя Flash. + self.assertEqual(calls.call_count, 1) # После отказа Flash ни hex2000, ни новый сборщик не запускаются. + self.assertEqual(stale.read_bytes(), b'old Flash image') # Предыдущий образ сохраняется, но не считается новым результатом. + self.assertFalse(self.export.exists()) # Неполная сборка не создаёт выпуск для GUI. + + +if __name__ == '__main__': # Позволяет запускать проверки отдельно или через unittest discover. + unittest.main() # Возвращает ненулевой код при нарушении контракта упаковки или экспорта. diff --git a/c/firmware-info/tools/c2000_postbuild.py b/c/firmware-info/tools/c2000_postbuild.py index 8bd0c26..7fe3900 100644 --- a/c/firmware-info/tools/c2000_postbuild.py +++ b/c/firmware-info/tools/c2000_postbuild.py @@ -10,11 +10,25 @@ import json from pathlib import Path import re import subprocess +import sys # Запускает проектный сборщик тем же Python, которым выполняется общий postbuild. import tempfile from identity import archive_build, seal, verify +def build_flash_variant(compiler: Path, build_dir: Path, source: Path, stage: Path) -> tuple: # Возвращает файлы дополнительного Flash-образа только для проектов с явным сборщиком. + builder = build_dir.resolve().parent / "build_flash.py" # Соглашение CCS: Debug/Release расположены рядом с проектным сценарием сборки Flash. + if not builder.is_file(): # Отсутствие сценария оставляет прежний выпуск EEPROM без дополнительных требований к проекту. + return () # Пустой список не добавляет Flash-файлы в архив обычного проекта. + image = stage / (source.stem + "_flash.out") # Отдельное имя и временный каталог исключают подмену RAM-образа и использование устаревшего Flash-файла. + subprocess.run([sys.executable, str(builder), "--postbuild", "--compiler", str(compiler.resolve()), "--build-dir", str(build_dir.resolve()), "--output", str(image)], cwd=build_dir, check=True) # Режим postbuild использует уже собранные объекты и запрещает повторный запуск Make. + files = (image, image.with_suffix(".map")) # Карта линковки сопровождает COFF и позволяет проверить адрес аппаратной точки входа. + for file in files: # Оба результата должны быть выпущены текущим вызовом сборщика. + if not file.is_file() or not file.stat().st_size: # Успешный код процесса без непустого результата не считается успешной сборкой. + raise ValueError(f"Flash builder produced no file: {file}") # Ошибка останавливает postbuild до архивирования или экспорта неполного выпуска. + return files # Передаёт проверенные файлы вызывающему шагу для подписи и сохранения. + + def postbuild(compiler: Path, build_dir: Path, artifact: str, metadata: Path, output: Path, export: Path = None) -> Path: # CCS may include the configured output directory in this macro. @@ -47,6 +61,7 @@ def postbuild(compiler: Path, build_dir: Path, artifact: str, stage = Path(temporary) hex_image = stage / (artifact + ".hex") bin_image = stage / (artifact + "_" + build_id + ".bin") + flash_files = build_flash_variant(compiler, build_dir, source, stage) # Сначала собирает дополнительный Flash-вариант; его ошибка не допускает выпуск нового BIN. for image, mode in ((hex_image, "-i"), (bin_image, "-b")): subprocess.run([str(tool), str(source), "-boot", "-sci8", "-map", str(image.with_suffix(image.suffix + ".map")), @@ -55,13 +70,32 @@ def postbuild(compiler: Path, build_dir: Path, artifact: str, raise ValueError(f"hex2000 produced no image: {image}") seal(bin_image, metadata) verify(bin_image, bin_image.with_suffix(bin_image.suffix + ".manifest.json")) + if flash_files: # Проекты со сборщиком получают независимую проверку точных байтов Flash-образа. + seal(flash_files[0], metadata) # Использует тот же паспорт prebuild для второго варианта этой сборки. + verify(flash_files[0], flash_files[0].with_suffix(".out.manifest.json")) # Проверяет размер и SHA-256 перед сохранением результатов. + serial_image = stage / (artifact + "_flash_rs.bin") # Отдельный SCI8 BIN содержит Flash-адреса и предназначен только для новой команды записи по RS. + serial_map = serial_image.with_suffix('.bin.map') # Карта конверсии позволяет отличить Flash-адреса от EEPROM/RAM BIN. + subprocess.run([str(tool), str(flash_files[0]), '-boot', '-sci8', '-map', str(serial_map), '-o', str(serial_image), '-b'], check=True) # Конвертирует именно Flash COFF с загрузочными адресами секций. + if not serial_image.is_file() or not serial_image.stat().st_size: # Пустая конверсия не должна попасть в выпуск либо каталог GUI. + raise ValueError(f'hex2000 produced no Flash RS image: {serial_image}') # Передаёт ошибку конверсии общему postcompile. + seal(serial_image, metadata) # Привязывает RS BIN к той же версии, ревизии и времени текущего prebuild. + verify(serial_image, serial_image.with_suffix('.bin.manifest.json')) # Проверяет точные байты перед копированием в Bin и архив. + flash_files += (serial_image, serial_map, serial_image.with_suffix('.bin.manifest.json'), serial_image.with_suffix('.bin.sha256')) # Включает транспортный BIN, карту и контрольные файлы в общий выпуск Flash. + for file in flash_files: # Сохраняет COFF и карту также рядом с обычным OUT для выбора в CCS. + destination = source.parent / file.name # Каталог исходного OUT задаёт место назначения без путей конкретного изделия в templates. + destination.write_bytes(file.read_bytes()) # Копирует только результаты успешного текущего вызова, сохраняя исходный RAM-образ. for item in stage.iterdir(): item.replace(output / item.name) - result = output / bin_image.name - archive_build(result, metadata, output, ( + result = output / bin_image.name # BIN остаётся основным образом EEPROM и возвращаемым результатом общего postbuild. + flash_archive = tuple(output / file.name for file in flash_files) # После переноса из временного каталога берёт актуальные Flash-файлы из firmware. + if flash_files: # Добавляет сопроводительные файлы только при выполненной сборке Flash. + flash_archive += (output / (flash_files[0].name + ".manifest.json"), output / (flash_files[0].name + ".sha256")) # Архив и GUI получают отдельный паспорт и контрольную сумму Flash COFF. + print(f"[firmware-build] Flash OUT: {source.parent / flash_files[0].name}") # Показывает пользователю CCS конкретный файл для прошивки внутренней Flash через JTAG. + archive_build(result, metadata, output, ( # Сохраняет файлы текущей сборки в архиве и передаёт весь архив в каталог GUI. + source, # Копирует исходный COFF-файл OUT с символами отладки под его именем для загрузки через JTAG из GUI. output / hex_image.name, output / (hex_image.name + ".map"), - output / (bin_image.name + ".map")), export=export) + output / (bin_image.name + ".map"), *flash_archive), export=export) # Экспортирует EEPROM и Flash в одной папке текущего выпуска. print(f"[firmware-build] BIN: {result}") print(f"[firmware-build] Manifest: {result}.manifest.json") print(f"[firmware-build] SHA-256: {result}.sha256") diff --git a/tools/firmware-build/README.md b/tools/firmware-build/README.md index 3ead431..0453a24 100644 --- a/tools/firmware-build/README.md +++ b/tools/firmware-build/README.md @@ -1,5 +1,93 @@ # Подключение precompile и postcompile из templates +## Пути на этом компьютере + +| Назначение | Полный путь | +|---|---| +| Корень общей библиотеки templates | `K:\git_project\newProject\templates` | +| Сценарии CCS | `K:\git_project\newProject\templates\tools\firmware-build\ccs` | +| Сценарии Keil | `K:\git_project\newProject\templates\tools\firmware-build\keil` | +| Проект Бальзам-166 / TMS320F28335 | `K:\git_project\28335_periph\UKSSTMS320F28335` | +| Каталог сборок Бальзам-166 в SETGUI | `K:\git_project\newProject\SETGUI\..periph_28335\firmware\periph28335\balzam166` | + +Все команды ниже используют эту копию templates. При переносе библиотеки +замените её корень в командах и в include paths компилятора. +`${PROJECT_LOC}`, `${BuildDirectory}`, `${CG_TOOL_ROOT}` и +`${BuildArtifactFileBaseName}` раскрывает CCS; `$P` и `#H` раскрывает Keil. +Для ручного запуска замените макросы полными путями. + +В пути SETGUI `..periph_28335` — существующее имя папки с двумя точками +в начале. Переход к родителю записывается отдельным компонентом `..\`. + +## Текущий проект 28335_periph: вызов через firmware_build.bat + +Для проекта `K:\git_project\28335_periph\UKSSTMS320F28335` используйте +локальную обвязку. Она вызывает общие BAT из templates, увеличивает версию +в проектном JSON и перед публикацией дополнительно проверяет встроенный +паспорт SCI8 и предел EEPROM. + +В CCS → **Project Properties → Build → Steps** задайте для Debug и Release +следующие команды, каждую одной строкой. + +**Pre-build:** + +```bat +cmd /c call "${PROJECT_LOC}/firmware_build.bat" prepare --build-dir "${BuildDirectory}" +``` + +**Post-build:** + +```bat +cmd /c call "${PROJECT_LOC}/firmware_build.bat" build --compiler "${CG_TOOL_ROOT}" --build-dir "${BuildDirectory}" --artifact "${BuildArtifactFileBaseName}" +``` + +Настройки находятся в +`K:\git_project\28335_periph\UKSSTMS320F28335\firmware_identity.json`: + +| Поле | Рабочее значение и смысл | +|---|---| +| `templates` | `../../newProject/templates`; разрешается относительно папки JSON в `K:\git_project\newProject\templates`. Абсолютный эквивалент: `K:/git_project/newProject/templates`. | +| `copy_to` | `../Bin`; копирует проверенные BIN, manifest, SHA-256, HEX и карту в `K:\git_project\28335_periph\Bin`. | +| `increment_version` | `true`; каждый pre-build резервирует следующий номер в поле `version`. | +| `max_image_bytes` | `32508`; предельный размер SCI8 BIN в байтах для этой платы. | + +В JSON используйте `/` либо удваивайте обратные слеши. +`copy_to` принимает также абсолютный путь, например +`K:/git_project/newProject/SETGUI/..periph_28335/firmware/periph28335/balzam166`. +Эта настройка копирует отдельные файлы комплекта. Архивные папки остаются +в `${BuildDirectory}/firmware`. + +`firmware_build.bat build` принимает именованные параметры. Дописывание +каталога после `--artifact` вызывает `unrecognized arguments`; для этой +обвязки каталог копирования задаётся полем `copy_to`. + +Include paths компилятора для текущего проекта: + +| Запись в CCS | Реальный каталог для Debug | +|---|---| +| `${SRC_ROOT}/../newProject/templates/c/firmware-info/include` | `K:\git_project\newProject\templates\c\firmware-info\include` | +| `${SRC_ROOT}/../newProject/templates/c/set-protocol` | `K:\git_project\newProject\templates\c\set-protocol` | +| `${BuildDirectory}/generated` | `K:\git_project\28335_periph\UKSSTMS320F28335\Debug\generated` | + +`${SRC_ROOT}` в этом проекте равен `K:\git_project\28335_periph`. +При переносе templates обновите и JSON, и include paths в CCS. +Подробная инструкция проекта: +[README Бальзам-166](../../../../28335_periph/UKSSTMS320F28335/README.md). + +Готовый BIN ищите по строке `[firmware-build] Verified BIN: ...`. +Постоянный каталог Debug: +`K:\git_project\28335_periph\UKSSTMS320F28335\Debug\firmware`. +Папки `.identity-release-...` временные и удаляются после проверки. + +## Прямое подключение общих BAT к новому CCS-проекту + +Следующие разделы описывают прямые вызовы `ccs/precompile.bat` и +`ccs/postcompile.bat`. В этом варианте дополнительный последний аргумент +postcompile поддерживается и экспортирует целиком папку текущей сборки. +Общие BAT берут версию из JSON и не увеличивают её автоматически. +Выберите одну схему подключения: её pre-build и post-build должны +использовать один набор метаданных. + ## Новый CCS-проект C2000: локальные версия и SCI8 BIN Используйте обновлённую папку templates целиком. Режим `--identity` добавлен для нового проекта: Windows, Python 3.8+ в PATH и TI C2000 compiler с hex2000.exe. Git в PATH нужен для хеша коммита. Без Git либо без commit в проекте или templates используется `NOGIT000`; сборка и локальная конвертация разрешены. Это явно видно в manifest. SETGUI и доступ к серверу не требуются. @@ -20,20 +108,37 @@ ### 2. Добавьте команды в CCS -Project Properties → Build → Steps, для нужной конфигурации. Пример предполагает templates в `C:\Projects\templates`. Замените только этот путь своим; макросы CCS оставьте как есть. +Project Properties → Build → Steps, для нужной конфигурации. Пример предполагает templates в `K:\git_project\newProject\templates`. Замените только этот путь своим; макросы CCS оставьте как есть. Pre-build steps → Command, одной строкой: ```bat -cmd /c call "C:\Projects\templates\tools\firmware-build\ccs\precompile.bat" --identity "${PROJECT_LOC}" "${PROJECT_LOC}\firmware_identity.json" "${BuildDirectory}\generated" +cmd /c call "K:\git_project\newProject\templates\tools\firmware-build\ccs\precompile.bat" --identity "${PROJECT_LOC}" "${PROJECT_LOC}\firmware_identity.json" "${BuildDirectory}\generated" ``` Post-build steps → Command, одной строкой: ```bat -cmd /c call "C:\Projects\templates\tools\firmware-build\ccs\postcompile.bat" --identity "${CG_TOOL_ROOT}" "${BuildDirectory}" "${BuildArtifactFileBaseName}" "${BuildDirectory}\generated\firmware_identity.json" "${BuildDirectory}\firmware" +cmd /c call "K:\git_project\newProject\templates\tools\firmware-build\ccs\postcompile.bat" --identity "${CG_TOOL_ROOT}" "${BuildDirectory}" "${BuildArtifactFileBaseName}" "${BuildDirectory}\generated\firmware_identity.json" "${BuildDirectory}\firmware" ``` +Позиционные аргументы общего `ccs/postcompile.bat` после `--identity`: + +| Позиция | Значение | Источник в CCS | +|---|---|---| +| 1 | Корень TI C2000 compiler, содержащий `bin/hex2000.exe` | `${CG_TOOL_ROOT}` | +| 2 | Каталог текущей конфигурации сборки | `${BuildDirectory}` | +| 3 | Имя или путь OUT без расширения `.out` | `${BuildArtifactFileBaseName}` | +| 4 | Метаданные, созданные pre-build | `${BuildDirectory}\generated\firmware_identity.json` | +| 5 | Постоянный каталог результатов | `${BuildDirectory}\firmware` | +| 6, необязательно | Каталог экспорта архивной папки текущей сборки | Полный путь назначения | + +Здесь templates определяется расположением вызываемого общего BAT. +Для прямой схемы поле `templates` в JSON не заменяет путь в команде CCS. +Добавьте `${BuildDirectory}/generated` в include paths; при подключении +общего порта firmware-info добавьте +`K:/git_project/newProject/templates/c/firmware-info/include`. + Нужны оба шага. Precompile создаёт версию, UTC-время и Git-метаданные до компиляции. Postcompile преобразует OUT в SCI8 HEX/BIN, создаёт manifest и SHA-256 для точного BIN. Он не повторяет запрос Git после компиляции. В этом режиме `firmware-release.cmd` не нужен. `BuildArtifactFileBaseName` передаётся без изменения и без расширения `.out`: допускается простое имя, абсолютный путь или путь относительно BuildDirectory. Postcompile находит OUT по этому пути, а для имён BIN/HEX использует только имя файла. Поэтому отдельная выходная папка проекта (например, `../bin`) не требует правки общей команды. Метаданные и результаты остаются в указанных каталогах generated и firmware. @@ -62,23 +167,23 @@ Debug/ firmware_identity.json ``` -Postcompile автоматически создаёт папку `HASH_YYYY-MM-DD_HH-MM-SS_UTC+3` для каждой сборки. HASH — первые 8 символов commit прошивки из pre-build JSON; при незакоммиченных изменениях добавляется `+`. Дата и время берутся из `built_at_utc` и переводятся в UTC+3 для имени папки. В JSON и заголовке сохраняется UTC. Если commit исходников неизвестен, используется build ID или NOGIT000. Например: `f5c3654e+_2026-10-08_15-30-00_UTC+3`. Сохраняются точные байты образа, manifest, SHA-256 и firmware_identity.json; в CCS также HEX и map. Повтор postcompile с теми же метаданными обновляет ту же папку. Файлы непосредственно в firmware сохраняются, поэтому существующие команды копирования продолжают работать. +Postcompile автоматически создаёт папку `HASH_YYYY-MM-DD_HH-MM-SS_UTC+3` для каждой сборки. HASH — первые 8 символов commit прошивки из pre-build JSON; при незакоммиченных изменениях добавляется `+`. Дата и время берутся из `built_at_utc` и переводятся в UTC+3 для имени папки. В JSON и заголовке сохраняется UTC. Если commit исходников неизвестен, используется build ID или NOGIT000. Например: `f5c3654e+_2026-10-08_15-30-00_UTC+3`. Сохраняются точные байты образа, manifest, SHA-256 и firmware_identity.json; в CCS также исходный OUT, HEX и map. Повтор postcompile с теми же метаданными обновляет ту же папку. Файлы непосредственно в firmware сохраняются, поэтому существующие команды копирования продолжают работать. Добавьте generated и firmware в игнорируемые Git-каталоги. Обычно вся папка Debug уже игнорируется. Полный Rebuild обязателен для выпуска. BAT сами по себе не подтверждают, что OUT собран именно с этим заголовком. -### 4. Копирование папки текущей сборки в SETGUI +### 4. Прямой postcompile: копирование папки текущей сборки в SETGUI -Передайте каталог назначения последним аргументом postcompile. Скрипт скопирует целиком только папку HASH_DATE, созданную текущим запуском: BIN, HEX, map, manifest, SHA-256 и метаданные. Он использует папку из pre-build JSON, не выбирает её по времени изменения файлов и не копирует остальные архивы или отдельные файлы из корня firmware. +При прямом вызове общего ccs/postcompile.bat передайте каталог назначения последним аргументом после output-dir. Это правило относится к общему BAT; проектный firmware_build.bat использует copy_to из JSON. Скрипт скопирует целиком только папку HASH_DATE, созданную текущим запуском: исходный OUT (для CCS), BIN, HEX, map, manifest, SHA-256 и метаданные. OUT сохраняется с исходным именем для выбора и загрузки через JTAG в GUI. Скрипт использует папку из pre-build JSON, не выбирает её по времени изменения файлов и не копирует остальные архивы или отдельные файлы из корня firmware. -Полная команда Post-build для templates в `K:\git_project\newProject\newTEMPL\templates`, одной строкой: +Полная команда Post-build для templates в `K:\git_project\newProject\templates`, одной строкой: ```bat -cmd /c call "K:\git_project\newProject\newTEMPL\templates\tools\firmware-build\ccs\postcompile.bat" --identity "${CG_TOOL_ROOT}" "${BuildDirectory}" "${BuildArtifactFileBaseName}" "${BuildDirectory}\generated\firmware_identity.json" "${BuildDirectory}\firmware" "K:\git_project\newProject\SETGUI\..periph_28335\firmware" +cmd /c call "K:\git_project\newProject\templates\tools\firmware-build\ccs\postcompile.bat" --identity "${CG_TOOL_ROOT}" "${BuildDirectory}" "${BuildArtifactFileBaseName}" "${BuildDirectory}\generated\firmware_identity.json" "${BuildDirectory}\firmware" "K:\git_project\newProject\SETGUI\..periph_28335\firmware\periph28335\balzam166" ``` Старые суффиксы `&& copy ...` и `&& xcopy ...` удалите. Папка назначения создаётся автоматически. В ней появится, например: ```text -K:\git_project\newProject\SETGUI\..periph_28335\firmware\ +K:\git_project\newProject\SETGUI\..periph_28335\firmware\periph28335\balzam166\ f5c3654e+_2026-10-08_15-30-00_UTC+3\ MyFirmware.hex MyFirmware_BUILDID.bin @@ -89,6 +194,33 @@ K:\git_project\newProject\SETGUI\..periph_28335\firmware\ Копирование выполняется после успешного архивирования; SHA-256 копии проверяется. Console покажет `[firmware-build] Exported folder: ...`. Повтор для той же папки обновляет её файлы; другие папки назначения сохраняются. Без последнего аргумента экспорт отключён. Это локальное копирование без сетевой публикации. +### Дополнительный Flash OUT из postbuild CCS + +Если рядом с CCS-проектом, на уровень выше BuildDirectory, есть `build_flash.py`, +общий postbuild автоматически вызывает его с `--postbuild`, `--compiler`, +`--build-dir` и `--output`. Команда postcompile в настройках CCS остаётся прежней. +Сценарий проекта должен использовать уже готовые объекты, не запускать Make +и создать непустые `<имя>_flash.out` и `<имя>_flash.map` по переданному пути. +Адреса секций, отдельный linker-файл и необходимые определения препроцессора +задаёт проектный сборщик, а не общий templates. + +Flash OUT и карта сохраняются рядом с обычным OUT. Копии, отдельный manifest +и SHA-256 попадают в firmware и ту же папку HASH_DATE, что и EEPROM BIN, +а затем копируются в GUI при настроенном export. Обычный BIN по-прежнему +создаётся из основного OUT; Flash OUT не преобразуется в EEPROM SCI8 BIN. +Ошибка сборщика или отсутствие любого результата завершает postcompile +с ненулевым кодом до архивирования и экспорта. Существующий Flash OUT +не считается результатом нового вызова: сборщик работает во временном каталоге. +Без `build_flash.py` дополнительные действия отключены. + +Дополнительно hex2000 преобразует Flash OUT в отдельный `<имя>_flash_rs.bin` +с адресной таблицей SCI8, картой `.bin.map`, manifest и SHA-256. +Эти файлы сохраняются рядом с OUT, в firmware и в экспортируемой папке +HASH_DATE. Flash RS BIN содержит адреса внутренней Flash и предназначен +для совместимого проектного CMD_TFLASH; его нельзя выбирать как обычный +EEPROM BIN. Проект сам должен реализовать проверку адресов и алгоритм +программирования из RAM. Общий postbuild только формирует транспортный файл. + ### Версия внутри устройства Это подготовка метаданных сборки и файла. Чтобы версия присутствовала в программе и ответах устройства, подключите заголовок `firmware_identity.h` к модулю версии и обработчику запроса. Include path: `${BuildDirectory}/generated`. Для C2000 с 16-битным char нужна адаптация байтового контракта; обычный 8-битный порт нельзя считать готовым. Смотрите [PORTING.md](../../c/firmware-info/PORTING.md). @@ -121,24 +253,24 @@ project/ ### 2. Настройте Before/After Build -В Options for Target → Output включите Create HEX File. Затем откройте User и включите выполнение команд Before Build/Rebuild и After Build/Rebuild. Для обеих команд задайте Stop on Exit Code `>=1`, чтобы ошибка скрипта останавливала сборку. Пример предполагает templates в `C:\Projects\templates`; замените этот путь своим. +В Options for Target → Output включите Create HEX File. Затем откройте User и включите выполнение команд Before Build/Rebuild и After Build/Rebuild. Для обеих команд задайте Stop on Exit Code `>=1`, чтобы ошибка скрипта останавливала сборку. Пример предполагает templates в `K:\git_project\newProject\templates`; замените этот путь своим. Before Build/Rebuild, одной строкой: ```bat -cmd /c call "C:\Projects\templates\tools\firmware-build\keil\precompile.bat" --identity "$P.." "$P..\firmware_identity.json" "$PGenerated" +cmd /c call "K:\git_project\newProject\templates\tools\firmware-build\keil\precompile.bat" --identity "$P.." "$P..\firmware_identity.json" "$PGenerated" ``` After Build/Rebuild, одной строкой: ```bat -cmd /c call "C:\Projects\templates\tools\firmware-build\keil\postcompile.bat" --identity "#H" "$PGenerated\firmware_identity.json" +cmd /c call "K:\git_project\newProject\templates\tools\firmware-build\keil\postcompile.bat" --identity "#H" "$PGenerated\firmware_identity.json" ``` Для копирования только папки текущей сборки добавьте каталог назначения последним аргументом: ```bat -cmd /c call "C:\Projects\templates\tools\firmware-build\keil\postcompile.bat" --identity "#H" "$PGenerated\firmware_identity.json" "K:\git_project\newProject\SETGUI\..periph_28335\firmware" +cmd /c call "K:\git_project\newProject\templates\tools\firmware-build\keil\postcompile.bat" --identity "#H" "$PGenerated\firmware_identity.json" "K:\git_project\newProject\SETGUI\..periph_28335\firmware\periph28335\balzam166" ``` Будет скопирована целиком новая папка HASH_DATE с HEX/BIN, manifest, SHA-256 и метаданными. Старые команды copy/xcopy в этой строке не нужны. В Console появится `[firmware-build] Exported folder`.