From 6930479fdb086126fdc290afdf29ce23b480e579 Mon Sep 17 00:00:00 2001 From: Ezra Maccabee Date: Thu, 12 Mar 2026 13:15:48 +0300 Subject: [PATCH] # 9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Переход на модульную архитектуру just скриптов (см. docs/DEV_ARCH.md) --- .env.example | 0 .gitignore | 2 + .vscode/tasks.json | 28 +- HOW_TO_FLASH.md | 22 -- Justfile | 370 ++++----------------------- README.md | 70 ++--- scripts/bootstrap.sh => bootstrap.sh | 37 ++- docs/DEV_ARCH.md | 259 ++++++++++--------- firmware/test/main.c | 2 +- {scripts => just}/build.just | 58 +++-- just/ci.just | 53 ++++ just/host.just | 338 ++++++++++++++++++++++++ 12 files changed, 673 insertions(+), 566 deletions(-) create mode 100644 .env.example delete mode 100644 HOW_TO_FLASH.md rename scripts/bootstrap.sh => bootstrap.sh (83%) rename {scripts => just}/build.just (82%) create mode 100644 just/ci.just create mode 100644 just/host.just diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore index 086d016..3f6a872 100644 --- a/.gitignore +++ b/.gitignore @@ -67,4 +67,6 @@ compile_commands.json .venv/ __pycache__/ *.pyc +.env + diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 843f596..87eb93c 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -13,9 +13,7 @@ "type": "shell", "command": "just", "args": [ - "--justfile", - "scripts/build.just", - "build-${input:project}-${input:buildType}" + "build::build-${input:project}-${input:buildType}" ], "options": { "cwd": "${workspaceFolder}", @@ -41,9 +39,7 @@ "type": "shell", "command": "just", "args": [ - "--justfile", - "scripts/build.just", - "test-host" + "build::test-host" ], "options": { "cwd": "${workspaceFolder}", @@ -61,9 +57,7 @@ "type": "shell", "command": "just", "args": [ - "--justfile", - "scripts/build.just", - "test-host-release" + "build::test-host-release" ], "options": { "cwd": "${workspaceFolder}" @@ -79,9 +73,7 @@ "type": "shell", "command": "just", "args": [ - "--justfile", - "scripts/build.just", - "hab-${input:habProject}-${input:buildType}" + "build::hab-${input:habProject}-${input:buildType}" ], "options": { "cwd": "${workspaceFolder}", @@ -99,9 +91,7 @@ "type": "shell", "command": "just", "args": [ - "--justfile", - "scripts/build.just", - "hab-all-debug" + "build::hab-all-debug" ], "options": { "cwd": "${workspaceFolder}" @@ -114,9 +104,7 @@ "type": "shell", "command": "just", "args": [ - "--justfile", - "scripts/build.just", - "hab-all-release" + "build::hab-all-release" ], "options": { "cwd": "${workspaceFolder}" @@ -132,9 +120,7 @@ "type": "shell", "command": "just", "args": [ - "--justfile", - "scripts/build.just", - "clean" + "build::clean" ], "options": { "cwd": "${workspaceFolder}", diff --git a/HOW_TO_FLASH.md b/HOW_TO_FLASH.md deleted file mode 100644 index 413b25d..0000000 --- a/HOW_TO_FLASH.md +++ /dev/null @@ -1,22 +0,0 @@ -# Прошивка платы со стороны хоста - -```bash -# Прошить во Flash -just flash firmware_test debug # разработка — итерации с отладчиком -just flash firmware_test release # проверить как будет на сервере -just flash bootloader debug -just flash bootloader release -just flash app debug -just flash app release - - -# TODO:Загрузить в RAM (без записи во Flash, плата стартует сразу) -just flash-ram firmware_test # default: debug -just flash-ram firmware_test debug -just flash-ram app release - -# TODO:Псевдонимы -just flash-test-debug # = just flash firmware_test debug -just flash-test-release # = just flash firmware_test release -just flash-production # bootloader release + app release -``` diff --git a/Justfile b/Justfile index a75c1ed..98af3f0 100644 --- a/Justfile +++ b/Justfile @@ -1,344 +1,64 @@ # ============================================================================= -# Justfile — корень репозитория -# Выполняется на ХОСТ-МАШИНЕ разработчика (вне devcontainer). -# -# Отвечает только за: -# - инициализацию окружения (setup) -# - прошивку платы через USB (flash) -# - сквозные сценарии (pipeline) -# - вспомогательные утилиты (util) -# -# Сборка, тесты и HAB-образы — в just/build.just (внутри devcontainer). +# Корневой justfile — точка входа для всех команд +# Может выполняться на хосте или внутри devcontainer # # Быстрый старт: -# ./scripts/bootstrap.sh # первый запуск после git clone -# just # показать все рецепты +# just # показать все доступные команды +# just bootstrap # первая настройка после git clone (на хосте) +# just build ... # сборка (внутри devcontainer) +# just host::flash # прошивка (на хосте) # ============================================================================= -JUST_MIN := "1.27.0" -UV_MIN := "0.4.0" -DOCKER_MIN := "24.0.0" +# === Глобальные настройки === +# Все модули наследуют эти настройки -TOOLS_DIR := "tools/host" +set shell := ["bash", "-euo", "pipefail", "-c"] +# экспортируем все just-переменные в окружение +set export +# автоматически загружать .env +set dotenv-load + +# === Общие переменные (доступны во всех модулях через export) === +BOARD := env('BOARD', 'MIMXRT1052') +BUILD_DIR := env('BUILD_DIR', 'build') +TOOLS_DIR := env('TOOLS_DIR', 'tools/host') CACHE_DIR := ".cache" -# По умолчанию — показать список рецептов -_default: - @just --list --unsorted +# === Модули === +# Каждый модуль — это namespace с изолированными рецептами +# host.just — операции на хост-машине +mod host 'just/host.just' +# build.just — сборка в DevContainer +mod build 'just/build.just' +# ci.just — CI/CD сценарии (опционально) +mod ci 'just/ci.just' -# ============================================================================= -# ГРУППА: setup — инициализация рабочего окружения -# ============================================================================= +# === Default рецепт === +# Вызывается при `just` без аргументов +# Показывает полное дерево команд из всех модулей -# [1/3] Проверить наличие и версии инструментов на хост-машине -[group('setup')] -check-deps: - #!/usr/bin/env bash - set -euo pipefail +default: + @just --list --list-submodules - BOLD="\033[1m"; GREEN="\033[0;32m"; RED="\033[0;31m"; YELLOW="\033[1;33m"; RESET="\033[0m" - ERRORS=0; WARNINGS=0 +# === Популярные алиасы (для удобства команды) === +# Сокращают длинные вызовы модулей - ok() { echo -e " ${GREEN}✅ ${1}${RESET}"; } - fail() { echo -e " ${RED}❌ ${1}${RESET}"; ERRORS=$((ERRORS+1)); } - warn() { echo -e " ${YELLOW}⚠️ ${1}${RESET}"; WARNINGS=$((WARNINGS+1)); } +# Инициализация окружения (эквивалент host::bootstrap) +init: + @just host::bootstrap - _uname="$(uname -s)" - case "${_uname}" in - Linux*) PLATFORM="linux" ;; - Darwin*) PLATFORM="macos" ;; - MINGW*|MSYS*|CYGWIN*) PLATFORM="windows" ;; - *) PLATFORM="unknown" ;; - esac +# Быстрая прошивка тестового образа в Debug +flash: + @just host::flash-test-debug - # Сравнение semver без sort -V (недоступен в Git Bash) - semver_ge() { - local a="$1" b="$2" - local a1 a2 a3 b1 b2 b3 - IFS='.' read -r a1 a2 a3 <<< "${a}" - IFS='.' read -r b1 b2 b3 <<< "${b}" - a1="${a1//[^0-9]/}"; a2="${a2//[^0-9]/}"; a3="${a3//[^0-9]/}" - b1="${b1//[^0-9]/}"; b2="${b2//[^0-9]/}"; b3="${b3//[^0-9]/}" - [[ "${a1:-0}" -gt "${b1:-0}" ]] && return 0 - [[ "${a1:-0}" -lt "${b1:-0}" ]] && return 1 - [[ "${a2:-0}" -gt "${b2:-0}" ]] && return 0 - [[ "${a2:-0}" -lt "${b2:-0}" ]] && return 1 - [[ "${a3:-0}" -ge "${b3:-0}" ]] && return 0 - return 1 - } +# Полная сборка всех проектов в Release (в контейнере) +build-all: + @just build::hab-all-release - install_hint() { - local tool=$1 - case "${PLATFORM}" in - linux) - case "$tool" in - just) echo "curl -sSf https://just.systems/install.sh | bash -s -- --to ~/.local/bin" ;; - uv) echo "curl -LsSf https://astral.sh/uv/install.sh | sh" ;; - docker) echo "https://docs.docker.com/engine/install/ubuntu/" ;; - esac ;; - macos) - case "$tool" in - just) echo "brew install just" ;; - uv) echo "brew install uv" ;; - docker) echo "brew install --cask docker" ;; - esac ;; - windows) - case "$tool" in - just) echo "winget install --id Casey.Just (в PowerShell)" ;; - uv) echo 'powershell -c "irm https://astral.sh/uv/install.ps1 | iex"' ;; - docker) echo "winget install --id Docker.DockerDesktop (в PowerShell)" ;; - esac ;; - esac - } +# CI пайплайн +run-ci: + @just ci::pipeline - check_tool() { - local name=$1 cmd=$2 min_ver=$3 - shift 3 - local ver_args=("${@:---version}") - if ! command -v "$cmd" &>/dev/null; then - fail "$name not found -- $(install_hint "$name")" - return - fi - local actual - actual=$("$cmd" "${ver_args[@]}" 2>&1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) - if [[ -z "$actual" ]]; then - warn "$name found but version could not be detected" - return - fi - if semver_ge "$actual" "$min_ver"; then - ok "$name $actual (>= $min_ver)" - else - fail "$name $actual -- need >= $min_ver -- $(install_hint "$name")" - fi - } - echo "" - echo -e "${BOLD} Platform: ${PLATFORM}${RESET}" - echo "" - echo -e "${BOLD} Checking host dependencies...${RESET}" - echo "" - check_tool "just" "just" "{{JUST_MIN}}" - check_tool "uv" "uv" "{{UV_MIN}}" - check_tool "docker" "docker" "{{DOCKER_MIN}}" version --format '{{{{.Client.Version}}}}' - if [[ "${PLATFORM}" == "linux" ]]; then - echo "" - echo -e "${BOLD} Checking udev rules (Linux only)...${RESET}" - echo "" - local rules_file="/etc/udev/rules.d/99-nxp-mimxrt.rules" - local udev_ok=true - grep -q "1fc9" "$rules_file" 2>/dev/null || { warn "udev: BootROM rule missing (VID 1fc9)"; udev_ok=false; } - grep -q "15a2" "$rules_file" 2>/dev/null || { warn "udev: Flashloader rule missing (VID 15a2)"; udev_ok=false; } - if ! groups 2>/dev/null | grep -q plugdev; then - warn "User '$(whoami)' not in 'plugdev' group" - udev_ok=false - fi - if [[ "$udev_ok" == "false" ]]; then - warn "USB flashing requires root without udev rules -- run: just setup-udev" - else - ok "udev rules (NXP USB) + plugdev group" - fi - fi - - echo "" - if [[ $ERRORS -gt 0 ]]; then - echo -e " ${RED}${BOLD}$ERRORS error(s) found. Fix them before continuing.${RESET}" - echo "" - exit 1 - elif [[ $WARNINGS -gt 0 ]]; then - echo -e " ${YELLOW}${BOLD}$WARNINGS warning(s). Some features may not work.${RESET}" - else - echo -e " ${GREEN}${BOLD}All dependencies satisfied.${RESET}" - fi - echo "" - -# [2/3] Установить udev правила для USB-прошивки (только Linux, требует sudo) -[group('setup')] -setup-udev: - #!/usr/bin/env bash - set -euo pipefail - case "$(uname -s)" in - Linux*) ;; - *) - echo " ℹ️ udev rules are Linux-only, skipping on $(uname -s)." - exit 0 - ;; - esac - echo " Installing udev rules for NXP USB devices..." - printf '%s\n%s\n%s\n%s\n' \ - '# NXP BootROM -- SDP mode (BOOT_MOD_1 = 3V3)' \ - 'SUBSYSTEM=="usb", ATTR{idVendor}=="1fc9", ATTR{idProduct}=="0130", MODE="0666", GROUP="plugdev"' \ - '# NXP Flashloader -- after jump-address' \ - 'SUBSYSTEM=="usb", ATTR{idVendor}=="15a2", ATTR{idProduct}=="0073", MODE="0666", GROUP="plugdev"' \ - | sudo tee /etc/udev/rules.d/99-nxp-mimxrt.rules > /dev/null - sudo udevadm control --reload-rules - sudo udevadm trigger - sudo usermod -a -G plugdev "$USER" - echo " ✅ udev rules installed" - echo " ⚠️ Re-login required for 'plugdev' group to take effect" - -# [3/3] Синхронизировать зависимости tools/host (spsdk и др.) -# Идемпотентен: пропускает sync если uv.lock не изменился -[group('setup')] -setup-tools: - #!/usr/bin/env bash - set -euo pipefail - LOCK="{{TOOLS_DIR}}/uv.lock" - STAMP="{{CACHE_DIR}}/uv_lock.sha256" - mkdir -p "{{CACHE_DIR}}" - current=$(sha256sum "$LOCK" 2>/dev/null | cut -d' ' -f1 || echo "missing") - previous=$(cat "$STAMP" 2>/dev/null || echo "none") - if [[ "$current" == "$previous" ]]; then - echo " ✅ tools/host deps up to date (lockfile unchanged)" - else - echo " 📦 Syncing tools/host deps..." - cd "{{TOOLS_DIR}}" && uv sync - echo "$current" > "../../$STAMP" - echo " ✅ tools/host deps installed" - fi - -# Полная инициализация — запустить один раз после git clone -[group('setup')] -bootstrap: - #!/usr/bin/env bash - set -euo pipefail - _uname="$(uname -s)" - case "${_uname}" in - Linux*) PLATFORM="linux" ;; - Darwin*) PLATFORM="macos" ;; - MINGW*|MSYS*|CYGWIN*) PLATFORM="windows" ;; - *) PLATFORM="unknown" ;; - esac - echo "" - echo " === Bootstrap: step 1/3 -- check-deps ===" - just check-deps - if [[ "${PLATFORM}" == "linux" ]]; then - echo " === Bootstrap: step 2/3 -- setup-udev ===" - just setup-udev - else - echo " ℹ️ step 2/3 -- setup-udev skipped (${PLATFORM})" - fi - echo " === Bootstrap: step 3/3 -- setup-tools ===" - just setup-tools - echo "" - echo " ✅ Bootstrap complete." - echo " Next: open project in VSCode -> 'Reopen in Container'" - echo "" - -# ============================================================================= -# ГРУППА: flash — прошивка платы через USB -# Требует: uv + spsdk (just setup-tools), плата в SDP-режиме -# HAB-образы должны быть собраны заранее (just hab-* внутри devcontainer) -# -# Матрица прошивки: -# just flash firmware_test debug -# just flash firmware_test release -# just flash bootloader debug -# just flash bootloader release -# just flash app debug -# just flash app release -# ============================================================================= - -# Прошить выбранный HAB-образ. -# Использование: just flash -# project: firmware_test | bootloader | app -# type: debug | release (default: release) -# -# Образ должен быть собран заранее в devcontainer: -# just --justfile just/build.just hab-- -[group('flash')] -flash project type="release": - #!/usr/bin/env bash - set -euo pipefail - case "{{type}}" in - debug) BUILD_TYPE="Debug" ;; - release) BUILD_TYPE="Release" ;; - *) - echo " ❌ Unknown type: {{type}}" - echo " Valid: debug, release" - exit 1 ;; - esac - cd "{{TOOLS_DIR}}" && uv run python3 flash_usb.py \ - --firmware "{{project}}" --build-type "${BUILD_TYPE}" - -# Загрузить образ в RAM без записи во Flash. -# Полезно для быстрой проверки — не изнашивает Flash, плата стартует сразу. -# Использование: just flash-ram -[group('flash')] -flash-ram project type="debug": - #!/usr/bin/env bash - set -euo pipefail - case "{{type}}" in - debug) BUILD_TYPE="Debug" ;; - release) BUILD_TYPE="Release" ;; - *) - echo " ❌ Unknown type: {{type}}" - echo " Valid: debug, release" - exit 1 ;; - esac - cd "{{TOOLS_DIR}}" && uv run python3 flash_usb.py \ - --firmware "{{project}}" --build-type "${BUILD_TYPE}" --ram-only - -# Псевдонимы для частых сценариев ──────────────────────────────────────────── - -# firmware_test Debug — самый частый при разработке -[group('flash')] -flash-test-debug: - @just flash firmware_test debug - -# firmware_test Release — как будет на производстве -[group('flash')] -flash-test-release: - @just flash firmware_test release - -# bootloader + app Release — финальная прошивка -[group('flash')] -flash-production: - @just flash bootloader release - @just flash app release - -# ============================================================================= -# ГРУППА: pipeline — производственные сценарии (для сервера) -# ============================================================================= - -# Входной контроль платы: тестовая прошивка (Release) -> HIL-тесты -[group('pipeline')] -incoming: flash-test-release - #!/usr/bin/env bash - set -euo pipefail - echo " ▶ Running HIL tests (CAN, UART, SDRAM, SPI Flash)..." - # cd tools/hil && uv run python3 run_hil.py <- раскомментить когда готово - echo " ⚠️ HIL tests not yet implemented" - -# Финальная прошивка: загрузчик + основная прошивка (Release) -[group('pipeline')] -production: flash-production - @echo " ✅ Production firmware flashed (bootloader + app)" - -# ============================================================================= -# ГРУППА: util — вспомогательные инструменты -# ============================================================================= - -# Найти подключённые NXP USB-устройства -[group('util')] -scan: - cd "{{TOOLS_DIR}}" && uv run nxpdevscan - -# Проверить связь с BootROM через SDP (плата в SDP-режиме) -[group('util')] -sdp-status: - sdphost -u 0x1FC9,0x0130 -- error-status - -# Проверить что Flashloader отвечает (после jump-address) -[group('util')] -flashloader-status: - blhost -u 0x15A2,0x0073 -- get-property 1 0 - -# Обновить spsdk до новой версии -# Использование: just upgrade-tools 3.8.0 -[group('util')] -upgrade-tools version: - cd "{{TOOLS_DIR}}" && uv add "spsdk=={{version}}" && uv sync - @echo " ✅ spsdk upgraded to {{version}}" - @echo " Run: git add tools/host/uv.lock tools/host/pyproject.toml" diff --git a/README.md b/README.md index 0524ef9..397b3e4 100644 --- a/README.md +++ b/README.md @@ -45,11 +45,13 @@ │ └── host/ # Инструменты для работы с таргетом │ ├── flash_usb.py # Прошивка через USB ROM (nxp-spsdk / blhost) │ ├── hab/ # Утилиты и гайд по HAB (Secure Boot) -│ ├── dcd/ # Device Configuration Data -│ └── HOW_TO_FLASH.md → см. также /HOW_TO_FLASH.md +│ └── dcd/ # Device Configuration Data +├── just/ # Just-модули (автоматизация) +│ ├── build.just # Сборка, тесты, HAB-образы (devcontainer) +│ ├── host.just # Прошивка, bootstrap, HIL (хост) +│ └── ci.just # CI/CD пайплайны ├── scripts/ -│ ├── bootstrap.sh # Первичная настройка окружения -│ └── build.just # Рецепты сборки (используется через Justfile) +│ └── bootstrap.sh # Первичная настройка окружения (уровень 0) ├── docs/ # Документация проекта │ ├── DEV_ARCH.md # Архитектура окружения разработки │ ├── CMAKE_HINTS.md # Шпаргалка по CMake в проекте @@ -58,7 +60,7 @@ │ └── manufacturing_user's_guide.pdf ├── CMakeLists.txt # Корневой CMake ├── CMakePresets.json # Пресеты сборки (Release/Debug/Host) -├── Justfile # Точка входа для команд сборки/тестирования/прошивки +├── Justfile # Точка входа для команд (модули: build, host, ci) └── README.md ``` @@ -88,10 +90,10 @@ Bare-metal прошивка для **входного контроля** пла Стратегия тестирования двухуровневая: -| Уровень | Расположение | Инструменты | Запуск | -|---|---|---|---| -| **Host-тесты** (unit + интеграционные) | `tests/host/` | Unity + fff | `ctest` в devcontainer | -| **Target-тесты** (аппаратные) | `tests/target/` | Unity на железе | Удалённый ПК-сервер через SSH | +| Уровень | Расположение | Инструменты | Запуск | +| -------------------------------------- | --------------- | --------------- | -------------------------------------- | +| **Host-тесты** (unit + интеграционные) | `tests/host/` | Unity + fff | `just build::test-host` в devcontainer | +| **Target-тесты** (аппаратные) | `tests/target/` | Unity на железе | Удалённый ПК-сервер через SSH | Подробнее — [tests/HostTestingGuide.md](tests/HostTestingGuide.md) и [tests/README.md](tests/README.md). @@ -99,12 +101,12 @@ Bare-metal прошивка для **входного контроля** пла ## Управление зависимостями -| Зависимость | Подход | Причина | -|---|---|---| -| NXP MCUXpresso SDK | vendored | Стабильная версия, обновлений не планируется | -| FreeRTOS, FatFS, LittleFS и др. | vendored (через SDK) | Стабильные версии | -| Unity + fff | vendored | Маленькие, стабильные | -| SEGGER RTT | vendored | Стабильный | +| Зависимость | Подход | Причина | +| ------------------------------- | -------------------- | -------------------------------------------- | +| NXP MCUXpresso SDK | vendored | Стабильная версия, обновлений не планируется | +| FreeRTOS, FatFS, LittleFS и др. | vendored (через SDK) | Стабильные версии | +| Unity + fff | vendored | Маленькие, стабильные | +| SEGGER RTT | vendored | Стабильный | **Принцип:** всё что не меняется — vendored (закоммичено в репозиторий). Это обеспечивает полностью автономную сборку после `git clone` без доступа к интернету. @@ -112,18 +114,18 @@ Bare-metal прошивка для **входного контроля** пла ## Devcontainer — состав окружения -| Инструмент | Назначение | -|---|---| -| `arm-none-eabi-gcc` | Сборка firmware для таргета | -| `arm-none-eabi-gdb` | Отладка через GDB server (удалённая) | -| `gcc` (host) | Сборка и запуск host-тестов | -| `CMake + Ninja` | Система сборки | -| `CTest` | Запуск тестов (Unity + fff) | -| `clangd` | Language server для VSCode | -| `clang-format` | Форматирование кода | -| `clang-tidy` | Статический анализ | -| `Python 3 + nxp-spsdk` | Прошивка (blhost, nxpimage), HAB, провизия | -| `just` | Запуск рецептов сборки/тестирования/прошивки | +| Инструмент | Назначение | +| ---------------------- | -------------------------------------- | +| `arm-none-eabi-gcc` | Сборка firmware для таргета | +| `arm-none-eabi-gdb` | Отладка через GDB server (удалённая) | +| `gcc` (host) | Сборка и запуск host-тестов | +| `CMake + Ninja` | Система сборки | +| `CTest` | Запуск тестов (Unity + fff) | +| `clangd` | Language server для VSCode | +| `clang-format` | Форматирование кода | +| `clang-tidy` | Статический анализ | +| `Python 3 + nxp-spsdk` | HAB-образы (nxpimage) | +| `just` | Запуск рецептов через модули `build::` | --- @@ -139,13 +141,19 @@ Bare-metal прошивка для **входного контроля** пла git clone cd tft_manufacture_test +# Инициализация хоста (один раз) +sudo chmod +x bootstrap.sh +./bootstrap.sh + # Открыть в VSCode → Reopen in Container # Затем внутри devcontainer: -just build-host # сборка host-тестов -just test # запуск host-тестов через CTest -just build-firmware # сборка firmware для таргета -just flash # прошивка через USB ROM +just build::test-host # сборка и запуск host-тестов +just build::build-firmware-test-debug # сборка firmware для таргета +just build::hab-firmware-test-debug # подготовка HAB-образа + +# На хосте (вне контейнера): +just flash # прошивка через USB ROM ``` > Подробнее о прошивке — [HOW_TO_FLASH.md](HOW_TO_FLASH.md) diff --git a/scripts/bootstrap.sh b/bootstrap.sh similarity index 83% rename from scripts/bootstrap.sh rename to bootstrap.sh index 13cf5bc..2ea08e0 100755 --- a/scripts/bootstrap.sh +++ b/bootstrap.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash # ============================================================================= -# scripts/bootstrap.sh +# bootstrap.sh # Уровень 0: устанавливает just (если отсутствует), затем передаёт управление -# just bootstrap для полной инициализации окружения. +# just host::bootstrap для полной инициализации окружения. # # Поддерживаемые платформы: # Linux — bash (native) @@ -10,7 +10,7 @@ # Windows — Git Bash (поставляется вместе с git) # # Требования: bash >= 4, curl (Linux/macOS) или winget (Windows) -# Запуск: ./scripts/bootstrap.sh +# Запуск: ./bootstrap.sh # ============================================================================= set -euo pipefail @@ -23,18 +23,17 @@ YELLOW="\033[1;33m" GREEN="\033[0;32m" RESET="\033[0m" -info() { echo -e " ${BOLD}${*}${RESET}"; } -success() { echo -e " ${GREEN}✅ ${*}${RESET}"; } -warn() { echo -e " ${YELLOW}⚠️ ${*}${RESET}"; } -error() { echo -e " ${RED}❌ ${*}${RESET}"; } +info() { echo -e " ${BOLD}${*}${RESET}"; } +success() { echo -e " ${GREEN}✅ ${*}${RESET}"; } +warn() { echo -e " ${YELLOW}⚠️ ${*}${RESET}"; } +error() { echo -e " ${RED}❌ ${*}${RESET}"; } echo "" -echo -e "${BOLD}=== TFT Firmware — Bootstrap ===${RESET}" +echo -e "${BOLD}=== Project Bootstrap ===${RESET}" echo "" # ----------------------------------------------------------------------------- # 1. Определить платформу -# uname на Git Bash: MINGW64_NT-10.0-19045, MSYS_NT-..., CYGWIN_NT-... # ----------------------------------------------------------------------------- _uname="$(uname -s)" case "${_uname}" in @@ -52,17 +51,15 @@ echo "" # ----------------------------------------------------------------------------- # 2. Сравнение semver без sort -V (недоступен в Git Bash) -# Возвращает 0 если $1 >= $2 # ----------------------------------------------------------------------------- semver_ge() { local a="$1" b="$2" local a1 a2 a3 b1 b2 b3 IFS='.' read -r a1 a2 a3 <<< "${a}" IFS='.' read -r b1 b2 b3 <<< "${b}" - # Убрать нечисловые суффиксы (1.36.0-beta -> обнулить суффикс) a1="${a1//[^0-9]/}"; a2="${a2//[^0-9]/}"; a3="${a3//[^0-9]/}" b1="${b1//[^0-9]/}"; b2="${b2//[^0-9]/}"; b3="${b3//[^0-9]/}" - + [[ "${a1:-0}" -gt "${b1:-0}" ]] && return 0 [[ "${a1:-0}" -lt "${b1:-0}" ]] && return 1 [[ "${a2:-0}" -gt "${b2:-0}" ]] && return 0 @@ -79,7 +76,7 @@ install_just_linux() { mkdir -p "${LINUX_INSTALL_DIR}" curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh \ | bash -s -- --tag "${JUST_VERSION}" --to "${LINUX_INSTALL_DIR}" - + if ! echo "${PATH}" | grep -q "${LINUX_INSTALL_DIR}"; then warn "${LINUX_INSTALL_DIR} not in PATH -- adding for this session" warn "Add to ~/.bashrc to make permanent:" @@ -98,18 +95,18 @@ install_just_macos() { } install_just_windows() { - # Предпочитаем winget -- кладёт just.exe в системный PATH автоматически if command -v winget &>/dev/null; then info "Installing just via winget..." winget install --id Casey.Just \ --accept-package-agreements --accept-source-agreements || { error "winget install failed." echo " Run manually in PowerShell: winget install --id Casey.Just" - echo " Then restart Git Bash and re-run: ./scripts/bootstrap.sh" + echo " Then restart Git Bash and re-run: ./bootstrap.sh" exit 1 } + warn "Restart Git Bash so the new PATH from winget takes effect," - warn "then re-run: ./scripts/bootstrap.sh" + warn "then re-run: ./bootstrap.sh" exit 0 else error "winget not found." @@ -143,15 +140,15 @@ if [[ "${JUST_OK}" == "false" ]]; then macos) install_just_macos ;; windows) install_just_windows ;; esac - + JUST_CURRENT="$(just --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" success "just ${JUST_CURRENT} installed" echo "" fi # ----------------------------------------------------------------------------- -# 5. Передать управление just bootstrap +# 5. Передать управление just host::bootstrap для дальнейшей настройки рабочего окружения # ----------------------------------------------------------------------------- -info "Delegating to: just bootstrap" +info "Delegating to: just host::bootstrap" echo "" -exec just bootstrap "$@" \ No newline at end of file +exec just host::bootstrap "$@" diff --git a/docs/DEV_ARCH.md b/docs/DEV_ARCH.md index 64e3396..ab273d8 100644 --- a/docs/DEV_ARCH.md +++ b/docs/DEV_ARCH.md @@ -13,10 +13,10 @@ **Devcontainer** — всё что касается кода: сборка, статический анализ, форматирование, host-тесты, подготовка HAB-образов. Разработчик проводит -здесь большую часть времени. Управляется через VSCode tasks и `scripts/build.just`. +здесь большую часть времени. Управляется через VSCode tasks и модуль `just build::`. **Хост** — всё что касается железа: прошивка платы через USB, отладка -через JLink/probe-rs. Управляется через корневой `Justfile`. +через JLink/probe-rs. Управляется через модуль `just host::`. Такое разделение решает несколько проблем: USB-устройства не требуют проброса в контейнер; оба разработчика работают в идентичных условиях; @@ -30,10 +30,11 @@ CI использует те же команды что и локальная р ПК разработчика │ ├── Хост (Linux / macOS / Windows + Git Bash) -│ ├── just ← запуск задач хостового уровня +│ ├── just ← запуск задач хостового уровня (just host::*) │ ├── docker ← управление devcontainer │ ├── git ← работа с репозиторием │ ├── uv + spsdk ← прошивка платы (flash_usb.py, sdphost, blhost) +│ │ venv: tools/host/.venv-host │ ├── JLinkGDBServer / probe-rs ← сервер отладки (USB → TCP :2331) │ └── VSCode ← IDE (Dev Containers extension) │ @@ -45,8 +46,9 @@ CI использует те же команды что и локальная р │ ├── clang-tidy-17 ← статический анализ │ ├── clang-format-17 ← форматирование кода │ ├── cmake-format ← форматирование CMakeLists -│ ├── just ← запуск задач внутри контейнера +│ ├── just ← запуск задач внутри контейнера (just build::*) │ ├── uv + spsdk ← сборка HAB-образов (только nxpimage) +│ │ venv: tools/host/.venv-container │ └── Unity + fff ← фреймворки host-тестов │ └── Плата TFT (IMXRT1052) — на столе у разработчика @@ -59,18 +61,18 @@ CI использует те же команды что и локальная р ## 3. Что устанавливается и где -| Инструмент | Хост | Devcontainer | Сервер | -|---|---|---|---| -| `just` | ✅ | ✅ Dockerfile | ✅ | -| `docker` | ✅ | — | — | -| `git` | ✅ | ✅ | ✅ | -| `uv` | ✅ | ✅ Dockerfile | ✅ | -| `spsdk` | ✅ uv sync | ✅ uv sync | ✅ uv sync | -| ARM GCC toolchain | — | ✅ | — | -| `cmake` / `ninja` | — | ✅ | — | -| `clang` / `clangd` | — | ✅ | — | -| Unity / fff | — | ✅ | — | -| JLink / probe-rs | ✅ | — | — | +| Инструмент | Хост | Devcontainer | Сервер | +| ------------------ | --------- | ------------ | --------- | +| `just` | ✅ | ✅ Dockerfile | ✅ | +| `docker` | ✅ | — | — | +| `git` | ✅ | ✅ | ✅ | +| `uv` | ✅ | ✅ Dockerfile | ✅ | +| `spsdk` | ✅ uv sync | ✅ uv sync | ✅ uv sync | +| ARM GCC toolchain | — | ✅ | — | +| `cmake` / `ninja` | — | ✅ | — | +| `clang` / `clangd` | — | ✅ | — | +| Unity / fff | — | ✅ | — | +| JLink / probe-rs | ✅ | — | — | `spsdk` присутствует везде, но с разными ролями: @@ -81,16 +83,21 @@ CI использует те же команды что и локальная р Версия `spsdk` зафиксирована в `tools/host/uv.lock` — все три места используют одну и ту же версию. + + --- ## 4. Структура репозитория (automation-часть) ```bash / -├── Justfile ← хост: прошивка, setup, pipeline +├── Justfile ← корневой оркестратор; модули: build, host, ci +├── just/ +│ ├── build.just ← devcontainer: сборка, тесты, HAB +│ ├── host.just ← хост: прошивка, bootstrap, HIL +│ └── ci.just ← CI/CD пайплайны ├── scripts/ -│ └── bootstrap.sh ← уровень 0: just → just bootstrap -│ └── build.just ← devcontainer: сборка, тесты, HAB +│ └── bootstrap.sh ← уровень 0: just → just host::bootstrap ├── .devcontainer/ │ ├── Dockerfile │ └── devcontainer.json @@ -110,7 +117,7 @@ CI использует те же команды что и локальная р │ ├── pyproject.toml │ └── uv.lock ├── .vscode/ -│ └── tasks.json ← UI для build.just (внутри devcontainer) +│ └── tasks.json ← UI для just build::* (внутри devcontainer) ├── CMakePresets.json ├── cmake/ ├── sdk/ @@ -119,7 +126,7 @@ CI использует те же команды что и локальная р ├── firmware/ │ ├── test/ │ ├── bootloader/ -│ └── app/ +│ └── tft_app/ └── tests/ ← host-тесты (Unity + fff) ``` @@ -129,17 +136,17 @@ CI использует те же команды что и локальная р ### 5.1 Предварительные требования -| Платформа | Что нужно до bootstrap | -|---|---| -| Linux | `docker`, `git`, `curl` | -| macOS | Docker Desktop, `git` (Xcode CLT) | -| Windows | Docker Desktop, Git for Windows → **использовать Git Bash** | +| Платформа | Что нужно до bootstrap | +| --------- | ----------------------------------------------------------- | +| Linux | `docker`, `git`, `curl` | +| macOS | Docker Desktop, `git` (Xcode CLT) | +| Windows | Docker Desktop, Git for Windows → **использовать Git Bash** | ### 5.2 Единственная команда для нового разработчика ```bash git clone && cd -./scripts/bootstrap.sh +./bootstrap.sh ``` ### 5.3 Что делает bootstrap @@ -151,15 +158,15 @@ bootstrap.sh (уровень 0) │ uname: MINGW64_NT-... → windows, Linux → linux, Darwin → macos │ ├── проверить just (semver без sort -V — работает в Git Bash) -│ < 1.27.0 или отсутствует: +│ < 1.36.0 или отсутствует: │ Linux/macOS → curl | bash → ~/.local/bin/just │ Windows → winget install --id Casey.Just │ (перезапустить Git Bash после установки) │ -└── exec just bootstrap +└── exec just host::bootstrap │ ├── [1/3] check-deps - │ just >= 1.27.0 · uv >= 0.4.0 · docker >= 24.0.0 + │ just >= 1.36.0 · uv >= 0.4.0 · docker >= 24.0.0 │ платформо-зависимые подсказки при ошибках │ ├── [2/3] setup-udev (только Linux) @@ -170,7 +177,7 @@ bootstrap.sh (уровень 0) │ требует re-login · на macOS/Windows пропускается │ └── [3/3] setup-tools - uv sync в tools/host/ + uv sync в tools/host/ (venv: .venv-host на хосте) SHA-256 uv.lock кэшируется в .cache/ повторный вызов мгновенный если lockfile не изменился ``` @@ -184,7 +191,7 @@ bootstrap.sh (уровень 0) `postCreateCommand` выполняется автоматически при поднятии контейнера: ```bash -cd tools/host && uv sync && +cd tools/host && uv sync && # venv: .venv-container cd ../.. && cmake --preset host-debug && cmake --preset Debug @@ -199,21 +206,21 @@ cmake --preset Debug ### 6.1 Три подпроекта -| Прошивка | Boot-стратегия | DCD | Назначение | -|---|---|---|---| -| `firmware_test` | XIP из Flash | ✅ | Входной контроль, тестирование периферии | -| `bootloader` | Копирование в ITCM | ❌ | Загрузчик, не использует SDRAM | -| `app` | XIP + буферы в SDRAM | ✅ | Основное приложение (FreeRTOS, LCDIF) | +| Прошивка | Boot-стратегия | DCD | Назначение | +| --------------- | -------------------- | ---- | ---------------------------------------- | +| `firmware_test` | XIP из Flash | ✅ | Входной контроль, тестирование периферии | +| `bootloader` | Копирование в ITCM | ❌ | Загрузчик, не использует SDRAM | +| `tft_app` | XIP + буферы в SDRAM | ✅ | Основное приложение (FreeRTOS, LCDIF) | ### 6.2 Матрица сборки Каждый проект собирается в двух режимах: -| | Debug | Release | -|---|---|---| -| `firmware_test` | разработка, отладка | HAB для сервера | -| `bootloader` | отладка загрузчика | финальная прошивка | -| `app` | отладка приложения | финальная прошивка | +| | Debug | Release | +| --------------- | ------------------- | ------------------ | +| `firmware_test` | разработка, отладка | HAB для сервера | +| `bootloader` | отладка загрузчика | финальная прошивка | +| `tft_app` | отладка приложения | финальная прошивка | ### 6.3 CMake пресеты @@ -235,7 +242,7 @@ buildPresets (host): ```bash 0x60000000 FCB — Flash Config Block 512 байт (пишет Flashloader) 0x60001000 IVT + BDT ← начало HAB-образа -0x60001040 DCD — инициализация SDRAM ~1088 байт (firmware_test, app) +0x60001040 DCD — инициализация SDRAM ~1088 байт (firmware_test, tft_app) 0x60003000 Код прошивки (.text, .data…) ``` @@ -245,17 +252,17 @@ buildPresets (host): ### 7.1 Карта задач по контекстам -| Задача | Где | -|---|---| -| Написание кода, clangd, форматирование | devcontainer | -| Статический анализ (clang-tidy) | devcontainer | -| Host-тесты (Unity + fff) | devcontainer | -| Сборка ARM firmware (ELF) | devcontainer | -| Подготовка HAB-образов (nxpimage) | devcontainer | -| Прошивка платы через USB | **хост** | -| Отладка — GDB-сервер (JLink/probe-rs) | **хост** | -| Отладка — GDB-клиент | devcontainer → хост по TCP | -| Target-тесты (управление стендом) | **хост** | +| Задача | Где | +| -------------------------------------- | -------------------------- | +| Написание кода, clangd, форматирование | devcontainer | +| Статический анализ (clang-tidy) | devcontainer | +| Host-тесты (Unity + fff) | devcontainer | +| Сборка ARM firmware (ELF) | devcontainer | +| Подготовка HAB-образов (nxpimage) | devcontainer | +| Прошивка платы через USB | **хост** | +| Отладка — GDB-сервер (JLink/probe-rs) | **хост** | +| Отладка — GDB-клиент | devcontainer → хост по TCP | +| Target-тесты (управление стендом) | **хост** | ### 7.2 Типичная сессия разработки @@ -265,7 +272,7 @@ buildPresets (host): ├── писать код │ ├── Ctrl+Shift+P → "Run Task" → 🧪 Host Tests (Debug) -│ или в терминале: just --justfile just/build.just test-host +│ или в терминале: just build::test-host │ ├── Ctrl+Shift+P → "Run Task" → 🔨 Build → firmware-test · debug │ → build/Debug/firmware/test/firmware_test.elf @@ -277,22 +284,22 @@ buildPresets (host): │ ├── just flash firmware_test debug ← прошить отладочный образ │ или -├── just flash-ram firmware_test debug ← загрузить в RAM (быстро, без износа Flash) +├── just host::flash-ram firmware_test debug ← загрузить в RAM (быстро, без износа Flash) │ └── F5 в VSCode → отладка через JLink ``` ### 7.3 VSCode Tasks (внутри devcontainer) -| Таск | Input 1 | Input 2 | Команда | -|---|---|---|---| -| 🔨 Build | project | debug/release | `just build--` | -| 🧪 Host Tests (Debug) | — | — | `just test-host` | -| 🧪 Host Tests (Release) | — | — | `just test-host-release` | -| 📦 HAB Image | project | debug/release | `just hab--` | -| 📦 HAB All (Debug) | — | — | `just hab-all-debug` | -| 📦 HAB All (Release) | — | — | `just hab-all-release` | -| 🗑️ Clean | — | — | `just clean` | +| Таск | Input 1 | Input 2 | Команда | +| ---------------------- | ------- | ------------- | ------------------------------------ | +| 🔨 Build | project | debug/release | `just build::build--` | +| 🧪 Host Tests (Debug) | — | — | `just build::test-host` | +| 🧪 Host Tests (Release) | — | — | `just build::test-host-release` | +| 📦 HAB Image | project | debug/release | `just build::hab--` | +| 📦 HAB All (Debug) | — | — | `just build::hab-all-debug` | +| 📦 HAB All (Release) | — | — | `just build::hab-all-release` | +| 🗑️ Clean | — | — | `just build::clean` | Таски «Build» и «HAB Image» запрашивают два input последовательно: сначала проект (`firmware-test / bootloader / app / all`), @@ -309,7 +316,7 @@ buildPresets (host): 2. Reset 3. Подключить USB к хосту → плата определяется как VID:PID = 1FC9:0130 -4. Выполнить нужный just flash-* рецепт +4. Выполнить нужный just host::flash-* рецепт 5. После прошивки: BOOT_MOD_1 → GND, Reset → плата стартует из Flash ``` @@ -318,23 +325,24 @@ buildPresets (host): ```bash # Основной рецепт (project × type) -just flash firmware_test debug # разработка — итерации с отладчиком -just flash firmware_test release # проверить как будет на сервере -just flash bootloader debug -just flash bootloader release -just flash app debug -just flash app release +just host::flash firmware_test debug # разработка — итерации с отладчиком +just host::flash firmware_test release # проверить как будет на сервере +just host::flash bootloader debug +just host::flash bootloader release +just host::flash tft_app debug +just host::flash tft_app release -# TODO: Загрузка в RAM — без записи во Flash, мгновенный старт -# Удобно для частых итераций: не изнашивает Flash, не нужен BOOT_MOD переключатель -just flash-ram firmware_test # default: debug -just flash-ram firmware_test debug -just flash-ram app release +# Загрузка в RAM — без записи во Flash, мгновенный старт +# Удобно для частых итераций: не изнашивает Flash +just host::flash-ram firmware_test # default: debug +just host::flash-ram firmware_test debug +just host::flash-ram tft_app release -# TODO: Быстрые псевдонимы -just flash-test-debug # = just flash firmware_test debug -just flash-test-release # = just flash firmware_test release -just flash-production # bootloader release + app release +# Быстрые псевдонимы (из корневого Justfile) +just flash # = just host::flash-test-debug +just host::flash-test-debug +just host::flash-test-release +just host::flash-production # bootloader release + tft_app release ``` ### 8.3 Что происходит внутри flash_usb.py @@ -403,7 +411,7 @@ Devcontainer Фреймворк: Unity + fff Пресеты: host-debug / host-release Компилятор: системный clang-17 (не ARM GCC) -Запуск: just --justfile just/build.just test-host +Запуск: just build::test-host Результат: JUnit XML → VSCode CTest Lab + GitLab CI ``` @@ -439,17 +447,17 @@ Devcontainer feature-ветка │ ├── код в devcontainer - │ 🧪 Host Tests ← зелёные? - │ 🔨 Build firmware-test debug ← компилируется? + │ just build::test-host ← зелёные? + │ just build::build-firmware-test-debug ← компилируется? │ ├── проверка на железе - │ 📦 HAB Image → firmware-test · debug - │ just flash-test-debug ← прошить - │ target-тесты со стендом ← периферия работает? + │ just build::hab-firmware-test-debug + │ just flash ← прошить (алиас flash-test-debug) + │ target-тесты со стендом ← периферия работает? │ ├── подготовка к MR - │ 📦 HAB All (Release) ← финальные образы - │ just flash firmware_test release ← убедиться что release работает + │ just build::hab-all-release ← финальные образы + │ just host::flash firmware_test release ← убедиться что release работает │ └── Merge Request → GitLab [CI pipeline — отдельная тема] @@ -457,29 +465,29 @@ feature-ветка │ ▼ Производственный сервер - just incoming → firmware_test release → HIL - just production → bootloader + app release + just host::incoming → firmware_test release → HIL + just host::production → bootloader + tft_app release ``` --- ## 12. Обновление зависимостей -### spsdk (на хосте разработчика) +### spsdk ```bash -just upgrade-tools 3.8.0 +just host::upgrade-tools 3.8.0 git add tools/host/uv.lock tools/host/pyproject.toml git commit -m "chore: upgrade spsdk to 3.8.0" ``` После этого у всех разработчиков и в контейнере обновится автоматически -при следующем `just setup-tools` / `uv sync`. +при следующем `just host::setup-tools` / `uv sync`. ### just в Dockerfile ```dockerfile -ARG JUST_VERSION=1.40.0 # .devcontainer/Dockerfile — единственное место +ARG JUST_VERSION=1.36.0 # .devcontainer/Dockerfile — единственное место ``` ### ARM toolchain @@ -494,7 +502,7 @@ ARG TOOLCHAIN_VERSION=14.2.rel1 # .devcontainer/Dockerfile ### После git pull если изменился uv.lock ```bash -just setup-tools # автоматически обнаружит изменение и выполнит uv sync +just host::setup-tools # автоматически обнаружит изменение и выполнит uv sync ``` --- @@ -509,12 +517,12 @@ just setup-tools # автоматически обнаружит измене — только теггированные релизы, прошедшие CI Сценарий входного контроля новой платы: - 1. just incoming + 1. just host::incoming └── flash firmware_test release → HIL-тесты периферии 2. Тесты пройдены: - just production + just host::production ├── flash bootloader release - └── flash app release + └── flash tft_app release Установлено: just · uv + spsdk · git НЕ установлено: docker · cmake · компилятор · ARM toolchain @@ -526,41 +534,42 @@ just setup-tools # автоматически обнаружит измене ## Приложение А: минимальные версии -| Инструмент | Версия | Причина | -|---|---|---| -| `just` | 1.27.0 | поддержка `[group()]` | -| `uv` | 0.4.0 | стабильный lockfile формат | -| `docker` | 24.0.0 | Compose v2, `--build-arg` | -| `spsdk` | 3.7.x | совместимость с HAB yaml-форматом | -| ARM GCC | 13.3.rel1 | C11, LTO, текущий SDK | -| clang/clangd | 17 | поддержка `If:` в `.clangd` | +| Инструмент | Версия | Причина | +| ------------ | --------- | ------------------------------------------------------------ | +| `just` | 1.36.0 | поддержка `mod` с кастомным путём (`mod host 'just/host.just'`) | +| `uv` | 0.4.0 | стабильный lockfile формат | +| `docker` | 24.0.0 | Compose v2, `--build-arg` | +| `spsdk` | 3.7.x | совместимость с HAB yaml-форматом | +| ARM GCC | 13.3.rel1 | C11, LTO, текущий SDK | +| clang/clangd | 17 | поддержка `If:` в `.clangd` | ## Приложение Б: быстрая шпаргалка ```bash # ── Первый запуск ────────────────────────────────────────────── -./scripts/bootstrap.sh # инициализация хоста +./bootstrap.sh # инициализация хоста # VSCode → Reopen in Container # ── Внутри devcontainer (терминал VSCode) ────────────────────── -just --justfile just/build.just test-host -just --justfile just/build.just build-firmware-test-debug -just --justfile just/build.just build-app-release -just --justfile just/build.just hab-firmware-test-debug -just --justfile just/build.just hab-all-release -just --justfile just/build.just clean +just build::test-host +just build::build-firmware-test-debug +just build::build-tft-app-release +just build::hab-firmware-test-debug +just build::hab-all-release +just build::clean # ── Хостовый терминал — прошивка ─────────────────────────────── -just flash firmware_test debug # во Flash -just flash firmware_test release -just flash bootloader release -just flash app release -just flash-ram firmware_test # в RAM (быстро, без износа Flash) -just flash-production # bootloader + app release +just flash # алиас: firmware_test debug +just host::flash firmware_test debug # во Flash +just host::flash firmware_test release +just host::flash bootloader release +just host::flash tft_app release +just host::flash-ram firmware_test # в RAM (быстро, без износа Flash) +just host::flash-production # bootloader + tft_app release # ── Хостовый терминал — обслуживание ─────────────────────────── -just scan # найти NXP USB-устройства -just sdp-status # проверить BootROM -just setup-tools # обновить spsdk после git pull -just check-deps # проверить версии инструментов -``` +just host::scan # найти NXP USB-устройства +just host::sdp-status # проверить BootROM +just host::setup-tools # обновить spsdk после git pull +just host::check-deps # проверить версии инструментов +``` \ No newline at end of file diff --git a/firmware/test/main.c b/firmware/test/main.c index 4d2ff81..c1b852c 100644 --- a/firmware/test/main.c +++ b/firmware/test/main.c @@ -30,7 +30,7 @@ int main(void) { BOARD_Init(); GPIO_PinWrite(BOARD_INITPINS_UserLed1_PORT, BOARD_INITPINS_UserLed1_PIN, 0); - //GPIO_PinWrite(BOARD_INITPINS_UserLed2_PORT, BOARD_INITPINS_UserLed2_PIN, 0); + GPIO_PinWrite(BOARD_INITPINS_UserLed2_PORT, BOARD_INITPINS_UserLed2_PIN, 0); while (1) { } diff --git a/scripts/build.just b/just/build.just similarity index 82% rename from scripts/build.just rename to just/build.just index 6a982cf..65bf7e7 100644 --- a/scripts/build.just +++ b/just/build.just @@ -5,10 +5,11 @@ # Рабочая директория — корень репозитория (не scripts/). # ============================================================================= -set working-directory := ".." +# === Импорт общих переменных === +TOOLS_DIR := env('TOOLS_DIR', justfile_directory() / 'tools/host') +BUILD_DIR := env('BUILD_DIR', justfile_directory() / 'build') -TOOLS_DIR := "tools/host" -BUILD_DIR := "build" +set working-directory := '..' _default: @just --list --justfile scripts/build.just --unsorted @@ -22,7 +23,7 @@ _default: # app × debug / release # all × debug / release (все проекты сразу, для CI) # ============================================================================= - +# Приватные рецепты для configure (не показываются в --list) [private] _configure-debug: cmake --preset Debug @@ -68,7 +69,7 @@ build-all-release: _configure-release cmake --build --preset all-release # ── утилиты ─────────────────────────────────────────────────────────────────── -[group('build')] +[group('build'), confirm("Delete build/ directory?")] clean: rm -rf build @@ -112,7 +113,7 @@ test-host-release: _configure-host-release # ============================================================================= # ── firmware_test ───────────────────────────────────────────────────────────── -[group('flash')] +[group('hab_image_gen')] hab-firmware-test-debug: build-firmware-test-debug #!/usr/bin/env bash set -euo pipefail @@ -122,7 +123,7 @@ hab-firmware-test-debug: build-firmware-test-debug -o "../../../{{BUILD_DIR}}/Debug/firmware_test_hab.bin" echo " ✅ firmware_test_hab.bin (Debug)" -[group('flash')] +[group('hab_image_gen')] hab-firmware-test-release: build-firmware-test-release #!/usr/bin/env bash set -euo pipefail @@ -133,7 +134,7 @@ hab-firmware-test-release: build-firmware-test-release echo " ✅ firmware_test_hab.bin (Release)" # ── bootloader ─────────────────────────────────────────────────────────────── -[group('flash')] +[group('hab_image_gen')] hab-bootloader-debug: build-bootloader-debug #!/usr/bin/env bash set -euo pipefail @@ -143,7 +144,7 @@ hab-bootloader-debug: build-bootloader-debug -o "../../../{{BUILD_DIR}}/Debug/bootloader_hab.bin" echo " ✅ bootloader_hab.bin (Debug)" -[group('flash')] +[group('hab_image_gen')] hab-bootloader-release: build-bootloader-release #!/usr/bin/env bash set -euo pipefail @@ -154,7 +155,7 @@ hab-bootloader-release: build-bootloader-release echo " ✅ bootloader_hab.bin (Release)" # ── app ─────────────────────────────────────────────────────────────────────── -[group('flash')] +[group('hab_image_gen')] hab-app-debug: build-app-debug #!/usr/bin/env bash set -euo pipefail @@ -164,7 +165,7 @@ hab-app-debug: build-app-debug -o "../../../{{BUILD_DIR}}/Debug/app_hab.bin" echo " ✅ app_hab.bin (Debug)" -[group('flash')] +[group('hab_image_gen')] hab-app-release: build-app-release #!/usr/bin/env bash set -euo pipefail @@ -175,27 +176,28 @@ hab-app-release: build-app-release echo " ✅ app_hab.bin (Release)" # ── все образы сразу ───────────────────────────────────────────────────────── -[group('flash')] +[group('hab_image_gen')] hab-all-debug: hab-firmware-test-debug hab-bootloader-debug hab-app-debug @echo " ✅ All HAB images (Debug) ready" -[group('flash')] +[group('hab_image_gen')] hab-all-release: hab-firmware-test-release hab-bootloader-release hab-app-release @echo " ✅ All HAB images (Release) ready" # ── верификация образа (для отладки) ───────────────────────────────────────── # Использование: just --justfile just/build.just hab-verify firmware_test debug -[group('flash')] +[group('hab_image_gen')] hab-verify project="firmware_test" type="release": #!/usr/bin/env bash set -euo pipefail + ROOT="$(pwd)" case "{{project}}-{{type}}" in - firmware_test-debug) BIN="{{BUILD_DIR}}/Debug/firmware_test_hab.bin" ;; - firmware_test-release) BIN="{{BUILD_DIR}}/Release/firmware_test_hab.bin" ;; - bootloader-debug) BIN="{{BUILD_DIR}}/Debug/bootloader_hab.bin" ;; - bootloader-release) BIN="{{BUILD_DIR}}/Release/bootloader_hab.bin" ;; - app-debug) BIN="{{BUILD_DIR}}/Debug/app_hab.bin" ;; - app-release) BIN="{{BUILD_DIR}}/Release/app_hab.bin" ;; + firmware_test-debug) BIN="${ROOT}/{{BUILD_DIR}}/Debug/firmware_test_hab.bin" ;; + firmware_test-release) BIN="${ROOT}/{{BUILD_DIR}}/Release/firmware_test_hab.bin" ;; + bootloader-debug) BIN="${ROOT}/{{BUILD_DIR}}/Debug/bootloader_hab.bin" ;; + bootloader-release) BIN="${ROOT}/{{BUILD_DIR}}/Release/bootloader_hab.bin" ;; + app-debug) BIN="${ROOT}/{{BUILD_DIR}}/Debug/app_hab.bin" ;; + app-release) BIN="${ROOT}/{{BUILD_DIR}}/Release/app_hab.bin" ;; *) echo " ❌ Unknown: {{project}}-{{type}}" echo " Projects: firmware_test, bootloader, app" @@ -203,6 +205,20 @@ hab-verify project="firmware_test" type="release": exit 1 ;; esac OUT="/tmp/hab_parse_{{project}}_{{type}}.yaml" - uv run nxpimage hab parse -b "${BIN}" -o "${OUT}" + cd "{{TOOLS_DIR}}" && uv run nxpimage hab parse -b "${BIN}" -o "${OUT}" echo " ✅ ${OUT}" grep -E "(entry|csf|tag)" "${OUT}" || true + +# ============================================================================= +# ГРУППА: quality — линтеры, форматтеры +# ============================================================================= +# TODO: проверять bsp,firmware, modules +[group('quality')] +lint: + echo "TODO!" +# clang-tidy -p {{BUILD_DIR}}/Debug src/**/*.c + +[group('quality')] +format: + echo "TODO!" +# clang-format -i src/**/*.{c,h} diff --git a/just/ci.just b/just/ci.just new file mode 100644 index 0000000..2dd7190 --- /dev/null +++ b/just/ci.just @@ -0,0 +1,53 @@ +# ============================================================================= +# ci.just — CI/CD пайплайны +# Выполняется в CI-окружении (GitHub Actions, GitLab CI, Jenkins) +# +# Предполагается что CI работает внутри devcontainer. +# ============================================================================= + +# === Импорт общих переменных === +BUILD_DIR := env('BUILD_DIR', justfile_directory() / 'build') +set working-directory := '..' +# ============================================================================= +# ГРУППА: ci — полные пайплайны +# ============================================================================= + +# Полный CI pipeline: сборка + тесты + линтеры +[group('ci')] +pipeline: build test lint + @echo " ✅ CI pipeline complete" + +# Сборка всех проектов в Release +[group('ci')] +build: + just build::build-all-release + +# Unit-тесты +[group('ci')] +test: + just build::test-host-release + +# TODO: Линтеры + статический анализ +[group('ci')] +lint: + echo "TODO" +#just build::lint +#just build::check + +# Генерация HAB-образов для релиза +[group('ci')] +release: build + just build::hab-all-release + @echo " ✅ Release artifacts ready in {{BUILD_DIR}}/Release/" + +# Генерация coverage отчёта (если gcov настроен) +[group('ci'), private] +_coverage: + #!/usr/bin/env bash + set -euo pipefail + if command -v gcovr &>/dev/null; then + echo " 📊 Generating coverage report..." + gcovr --xml -o {{BUILD_DIR}}/coverage.xml + else + echo " ⚠️ gcovr not found, skipping coverage" + fi diff --git a/just/host.just b/just/host.just new file mode 100644 index 0000000..82ddef3 --- /dev/null +++ b/just/host.just @@ -0,0 +1,338 @@ +# ============================================================================= +# host.just — операции на хост-машине (вне devcontainer) +# +# Ответственность: +# - bootstrap окружения (just, uv, docker, udev) +# - USB-прошивка через spsdk +# - HIL-тесты на реальном железе +# - производственные сценарии (incoming, production) +# +# Все рецепты должны работать БЕЗ devcontainer. +# ============================================================================= + +# === Импорт общих переменных из корневого justfile === +# Благодаря `set export` в корне, эти переменные доступны +TOOLS_DIR := env('TOOLS_DIR', justfile_directory() / 'tools/host') +CACHE_DIR := env('CACHE_DIR', justfile_directory() / '.cache') + +# === Минимальные версии зависимостей === +JUST_MIN := "1.36.0" +UV_MIN := "0.4.0" +DOCKER_MIN := "24.0.0" +set working-directory := '..' +# ============================================================================= +# ГРУППА: setup — инициализация рабочего окружения +# ============================================================================= + +# [1/3] Проверить наличие и версии инструментов на хост-машине +[group('setup')] +check-deps: + #!/usr/bin/env bash + set -euo pipefail + + BOLD="\033[1m"; GREEN="\033[0;32m"; RED="\033[0;31m"; YELLOW="\033[1;33m"; RESET="\033[0m" + ERRORS=0; WARNINGS=0 + + ok() { echo -e " ${GREEN}✅ ${1}${RESET}"; } + fail() { echo -e " ${RED}❌ ${1}${RESET}"; ERRORS=$((ERRORS+1)); } + warn() { echo -e " ${YELLOW}⚠️ ${1}${RESET}"; WARNINGS=$((WARNINGS+1)); } + + _uname="$(uname -s)" + case "${_uname}" in + Linux*) PLATFORM="linux" ;; + Darwin*) PLATFORM="macos" ;; + MINGW*|MSYS*|CYGWIN*) PLATFORM="windows" ;; + *) PLATFORM="unknown" ;; + esac + + # Сравнение semver без sort -V (недоступен в Git Bash) + semver_ge() { + local a="$1" b="$2" + local a1 a2 a3 b1 b2 b3 + IFS='.' read -r a1 a2 a3 <<< "${a}" + IFS='.' read -r b1 b2 b3 <<< "${b}" + a1="${a1//[^0-9]/}"; a2="${a2//[^0-9]/}"; a3="${a3//[^0-9]/}" + b1="${b1//[^0-9]/}"; b2="${b2//[^0-9]/}"; b3="${b3//[^0-9]/}" + [[ "${a1:-0}" -gt "${b1:-0}" ]] && return 0 + [[ "${a1:-0}" -lt "${b1:-0}" ]] && return 1 + [[ "${a2:-0}" -gt "${b2:-0}" ]] && return 0 + [[ "${a2:-0}" -lt "${b2:-0}" ]] && return 1 + [[ "${a3:-0}" -ge "${b3:-0}" ]] && return 0 + return 1 + } + + install_hint() { + local tool=$1 + case "${PLATFORM}" in + linux) + case "$tool" in + just) echo "curl -sSf https://just.systems/install.sh | bash -s -- --to ~/.local/bin" ;; + uv) echo "curl -LsSf https://astral.sh/uv/install.sh | sh" ;; + docker) echo "https://docs.docker.com/engine/install/ubuntu/" ;; + esac ;; + macos) + case "$tool" in + just) echo "brew install just" ;; + uv) echo "brew install uv" ;; + docker) echo "brew install --cask docker" ;; + esac ;; + windows) + case "$tool" in + just) echo "winget install --id Casey.Just (в PowerShell)" ;; + uv) echo 'powershell -c "irm https://astral.sh/uv/install.ps1 | iex"' ;; + docker) echo "winget install --id Docker.DockerDesktop (в PowerShell)" ;; + esac ;; + esac + } + + check_tool() { + local name=$1 cmd=$2 min_ver=$3 + shift 3 + local ver_args=("${@:---version}") + if ! command -v "$cmd" &>/dev/null; then + fail "$name not found -- $(install_hint "$name")" + return + fi + local actual + actual=$("$cmd" "${ver_args[@]}" 2>&1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) + if [[ -z "$actual" ]]; then + warn "$name found but version could not be detected" + return + fi + if semver_ge "$actual" "$min_ver"; then + ok "$name $actual (>= $min_ver)" + else + fail "$name $actual -- need >= $min_ver -- $(install_hint "$name")" + fi + } + + echo "" + echo -e "${BOLD} Platform: ${PLATFORM}${RESET}" + echo "" + echo -e "${BOLD} Checking host dependencies...${RESET}" + echo "" + + check_tool "just" "just" "{{JUST_MIN}}" + check_tool "uv" "uv" "{{UV_MIN}}" + check_tool "docker" "docker" "{{DOCKER_MIN}}" version --format '{{{{.Client.Version}}}}' + + if [[ "${PLATFORM}" == "linux" ]]; then + echo "" + echo -e "${BOLD} Checking udev rules (Linux only)...${RESET}" + echo "" + rules_file="/etc/udev/rules.d/99-nxp-mimxrt.rules" + udev_ok=true + grep -q "1fc9" "$rules_file" 2>/dev/null || { warn "udev: BootROM rule missing (VID 1fc9)"; udev_ok=false; } + grep -q "15a2" "$rules_file" 2>/dev/null || { warn "udev: Flashloader rule missing (VID 15a2)"; udev_ok=false; } + if ! groups 2>/dev/null | grep -q plugdev; then + warn "User '$(whoami)' not in 'plugdev' group" + udev_ok=false + fi + if [[ "$udev_ok" == "false" ]]; then + warn "USB flashing requires root without udev rules -- run: just setup-udev" + else + ok "udev rules (NXP USB) + plugdev group" + fi + fi + + echo "" + if [[ $ERRORS -gt 0 ]]; then + echo -e " ${RED}${BOLD}$ERRORS error(s) found. Fix them before continuing.${RESET}" + echo "" + exit 1 + elif [[ $WARNINGS -gt 0 ]]; then + echo -e " ${YELLOW}${BOLD}$WARNINGS warning(s). Some features may not work.${RESET}" + else + echo -e " ${GREEN}${BOLD}All dependencies satisfied.${RESET}" + fi + echo "" + +# [2/3] Установить udev правила для USB-прошивки (только Linux, требует sudo) +[group('setup')] +setup-udev: + #!/usr/bin/env bash + set -euo pipefail + case "$(uname -s)" in + Linux*) ;; + *) + echo " ℹ️ udev rules are Linux-only, skipping on $(uname -s)." + exit 0 + ;; + esac + echo " Installing udev rules for NXP USB devices..." + printf '%s\n%s\n%s\n%s\n' \ + '# NXP BootROM -- SDP mode (BOOT_MOD_1 = 3V3)' \ + 'SUBSYSTEM=="usb", ATTR{idVendor}=="1fc9", ATTR{idProduct}=="0130", MODE="0666", GROUP="plugdev"' \ + '# NXP Flashloader -- after jump-address' \ + 'SUBSYSTEM=="usb", ATTR{idVendor}=="15a2", ATTR{idProduct}=="0073", MODE="0666", GROUP="plugdev"' \ + | sudo tee /etc/udev/rules.d/99-nxp-mimxrt.rules > /dev/null + sudo udevadm control --reload-rules + sudo udevadm trigger + sudo usermod -a -G plugdev "$USER" + echo " ✅ udev rules installed" + echo " ⚠️ Re-login required for 'plugdev' group to take effect" + +# [3/3] Синхронизировать зависимости tools/host (spsdk и др.) +# Идемпотентен: пропускает sync если uv.lock не изменился +[group('setup')] +setup-tools: + #!/usr/bin/env bash + set -euo pipefail + LOCK="{{TOOLS_DIR}}/uv.lock" + STAMP="{{CACHE_DIR}}/uv_lock.sha256" + mkdir -p "{{CACHE_DIR}}" + current=$(sha256sum "$LOCK" 2>/dev/null | cut -d' ' -f1 || echo "missing") + previous=$(cat "$STAMP" 2>/dev/null || echo "none") + if [[ "$current" == "$previous" ]]; then + echo " ✅ tools/host deps up to date (lockfile unchanged)" + else + echo " 📦 Syncing tools/host deps..." + cd "{{TOOLS_DIR}}" && uv sync + echo "$current" > "$STAMP" + echo " ✅ tools/host deps installed" + fi + +# Полная инициализация — запустить один раз после git clone +[group('setup')] +bootstrap: + #!/usr/bin/env bash + set -euo pipefail + _uname="$(uname -s)" + case "${_uname}" in + Linux*) PLATFORM="linux" ;; + Darwin*) PLATFORM="macos" ;; + MINGW*|MSYS*|CYGWIN*) PLATFORM="windows" ;; + *) PLATFORM="unknown" ;; + esac + echo "" + echo " === Bootstrap: step 1/3 -- check-deps ===" + just host::check-deps + if [[ "${PLATFORM}" == "linux" ]]; then + echo " === Bootstrap: step 2/3 -- setup-udev ===" + just host::setup-udev + else + echo " ℹ️ step 2/3 -- setup-udev skipped (${PLATFORM})" + fi + echo " === Bootstrap: step 3/3 -- setup-tools ===" + just host::setup-tools + echo "" + echo " ✅ Bootstrap complete." + echo " Next: open project in VSCode -> 'Reopen in Container'" + echo "" + +# ============================================================================= +# ГРУППА: flash — прошивка платы через USB +# Требует: uv + spsdk (just setup-tools), плата в SDP-режиме +# HAB-образы должны быть собраны заранее (just hab-* внутри devcontainer) +# +# Матрица прошивки: +# just flash firmware_test debug +# just flash firmware_test release +# just flash bootloader debug +# just flash bootloader release +# just flash app debug +# just flash app release +# ============================================================================= + +# Прошить выбранный HAB-образ. +# Использование: just flash +# project: firmware_test | bootloader | app +# type: debug | release (default: release) +# +# Образ должен быть собран заранее в devcontainer: +# just --justfile just/build.just hab-- +[group('flash')] +flash project type="release": + #!/usr/bin/env bash + set -euo pipefail + case "{{type}}" in + debug) BUILD_TYPE="Debug" ;; + release) BUILD_TYPE="Release" ;; + *) + echo " ❌ Unknown type: {{type}}" + echo " Valid: debug, release" + exit 1 ;; + esac + cd "{{TOOLS_DIR}}" && uv run python3 flash_usb.py \ + --firmware "{{project}}" --build-type "${BUILD_TYPE}" + +# Загрузить образ в RAM без записи во Flash. +# Полезно для быстрой проверки — не изнашивает Flash, плата стартует сразу. +# Использование: just flash-ram +[group('flash')] +flash-ram project type="debug": + #!/usr/bin/env bash + set -euo pipefail + case "{{type}}" in + debug) BUILD_TYPE="Debug" ;; + release) BUILD_TYPE="Release" ;; + *) + echo " ❌ Unknown type: {{type}}" + echo " Valid: debug, release" + exit 1 ;; + esac + cd "{{TOOLS_DIR}}" && uv run python3 flash_usb.py \ + --firmware "{{project}}" --build-type "${BUILD_TYPE}" --ram-only + +# Псевдонимы для частых сценариев ──────────────────────────────────────────── + +# firmware_test Debug — самый частый при разработке +[group('flash')] +flash-test-debug: + @just host::flash firmware_test debug + +# firmware_test Release — как будет на производстве +[group('flash')] +flash-test-release: + @just host::flash firmware_test release + +# TODO: bootloader + app Release — финальная прошивка +[group('flash'), confirm("Flash bootloader + app (Release)?")] +flash-production: + @just host::flash bootloader release + @just host::flash app release + +# ============================================================================= +# ГРУППА: pipeline — производственные сценарии (для сервера) +# ============================================================================= + +# Входной контроль платы: тестовая прошивка (Release) -> HIL-тесты +[group('pipeline')] +incoming: flash-test-release + #!/usr/bin/env bash + set -euo pipefail + echo " ▶ Running HIL tests (CAN, UART, SDRAM, SPI Flash)..." + # cd tools/hil && uv run python3 run_hil.py <- раскомментить когда готово + echo " ⚠️ HIL tests not yet implemented" + +# Финальная прошивка: загрузчик + основная прошивка (Release) +[group('pipeline')] +production: flash-production + @echo " ✅ Production firmware flashed (bootloader + app)" + +# ============================================================================= +# ГРУППА: util — вспомогательные инструменты +# ============================================================================= + +# Найти подключённые NXP USB-устройства +[group('util')] +scan: + cd "{{TOOLS_DIR}}" && uv run nxpdevscan + +# Проверить связь с BootROM через SDP (плата в SDP-режиме) +[group('util'), no-cd] +sdp-status: + cd "{{TOOLS_DIR}}" && uv run sdphost -u 0x1FC9,0x0130 -- error-status + +# Проверить что Flashloader отвечает (после jump-address) +[group('util')] +flashloader-status: + cd "{{TOOLS_DIR}}" && uv run blhost -u 0x15A2,0x0073 -- get-property 1 0 + +# Обновить spsdk до новой версии +# Использование: just upgrade-tools 3.8.0 +[group('util')] +upgrade-tools version: + cd "{{TOOLS_DIR}}" && uv add "spsdk=={{version}}" && uv sync + @echo " ✅ spsdk upgraded to {{version}}" + @echo " Run: git add tools/host/uv.lock tools/host/pyproject.toml"