# 8 - IMPORTANT

- Переход на task-runner Just (Justfile - для хоста, build.just - для контейнера)
- настройки clang-format
- небольшие изменения в структуре проекта
This commit is contained in:
Dmitry Akimov 2026-03-11 14:58:11 +03:00
parent 6eaca0c183
commit e8a9826944
37 changed files with 1957 additions and 286 deletions

55
.clang-format Normal file
View file

@ -0,0 +1,55 @@
---
Language: Cpp
BasedOnStyle: LLVM
# --- Отступы ---
IndentWidth: 4 # 4 пробела — стандарт для embedded
TabWidth: 4
UseTab: Never # Только пробелы, чтобы не было войны в git
# --- Скобки ---
BreakBeforeBraces: Allman # Открывающая скобка на новой строке
# Альтернатива: Linux (скобка после if/for, но
# Allman нагляднее при отладке железа)
# --- Длина строки ---
ColumnLimit: 100 # 80 — слишком мало для современных мониторов
# --- Выравнивание ---
AlignConsecutiveAssignments:
Enabled: true # Выравнивает = в блоках присваиваний
AcrossEmptyLines: false
AcrossComments: false
AlignConsecutiveMacros:
Enabled: true # Очень удобно для регистровых масок
AcrossEmptyLines: false
AcrossComments: false
AlignConsecutiveDeclarations:
Enabled: false # Спорно — может ломать читаемость
# --- Пробелы ---
SpaceBeforeParens: ControlStatements # if (...) но func(...)
SpaceInEmptyParentheses: false
SpacesInParentheses: false
SpaceAfterCStyleCast: true # (uint8_t) value
# --- Указатели ---
PointerAlignment: Right # uint8_t *ptr (или Left — uint8_t* ptr)
# Right — традиционный C стиль
# --- Переносы ---
AllowShortFunctionsOnASingleLine: None # Запрет коротких функций в одну строку
AllowShortIfStatementsOnASingleLine: Never # if всегда с телом на новой строке
AllowShortLoopsOnASingleLine: false
# --- Include-сортировка ---
SortIncludes: CaseSensitive
IncludeBlocks: Regroup # Группирует и сортирует #include блоки
# --- Комментарии ---
ReflowComments: false # НЕ переформатировать комментарии автоматически
# Важно для ASCII-art схем и таблиц регистров!
# --- Прочее ---
InsertTrailingCommas: None
Cpp11BracedListStyle: false

View file

@ -58,12 +58,23 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
&& update-alternatives --install /usr/bin/clang clang /usr/bin/clang-17 100 \
&& update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-17 100 \
&& update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-17 100 \
&& pip3 install --no-cache-dir spsdk cmake-format==0.6.13 \
&& pip3 install --no-cache-dir cmake-format==0.6.13 \
&& ln -s /usr/bin/clangd /usr/local/bin/clangd \
&& ln -s /usr/bin/clang /usr/local/bin/clang \
&& rm -rf /var/lib/apt/lists/*
# uv — менеджер Python-окружений для spsdk и других host-инструментов.
# spsdk НЕ ставится через pip напрямую — версия фиксируется в tools/host/uv.lock.
# uv sync в postCreateCommand подтянет нужную версию автоматически.
ARG UV_VERSION=0.5.0
RUN curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/usr/local/bin sh
ARG JUST_VERSION=1.36.0
RUN curl -sSf https://just.systems/install.sh \
| bash -s -- --tag ${JUST_VERSION} --to /usr/local/bin
ARG USER_ID=1000
ARG GROUP_ID=1000
ARG WORKSPACE_PATH="/workspace"

View file

@ -6,7 +6,7 @@
},
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind",
"workspaceFolder": "/workspace",
"postCreateCommand": "cmake --preset host-debug && cmake --preset Debug",
"postCreateCommand": "cd tools/host && uv sync && cd ../.. && cmake --preset host-debug && cmake --preset Debug",
"customizations": {
"vscode": {
"extensions": [
@ -43,7 +43,6 @@
"*.h": "c",
"*.c": "c"
},
// clangd
"clangd.path": "clangd",
"clangd.arguments": [
"--query-driver=/opt/arm-toolchain/bin/arm-none-eabi-*",
@ -52,20 +51,26 @@
"--header-insertion=iwyu",
"--log=error"
],
// Отключаем встроенный IntelliSense от ms-vscode.cpptools, чтобы не конфликтовал с clangd
"C_Cpp.intelliSenseEngine": "disabled",
"C_Cpp.formatting": "disabled",
// clang-format
"clang-format.executable": "clang-format",
"clang-format.style": "file",
"clang-format.fallbackStyle": "LLVM",
"[c]": {
"editor.defaultFormatter": "xaver.clang-format"
"editor.defaultFormatter": "xaver.clang-format",
"editor.formatOnSave": true
},
"[h]": {
"editor.defaultFormatter": "xaver.clang-format",
"editor.formatOnSave": true
},
// cmake-format
"cmakeFormat.exePath": "cmake-format",
"[cmake]": {
"editor.defaultFormatter": "cheshirekow.cmake-format"
}
}
}
}
},
"runArgs": [
"--name",
"tft-devcontainer"
]
}

1
.gitignore vendored
View file

@ -57,6 +57,7 @@ Debug/
Release/
build*
Testing/
!build.just
# Файлы настройки среды разработки
.project

310
.vscode/tasks.json vendored
View file

@ -1,213 +1,185 @@
{
"version": "2.0.0",
"tasks": [
// Очистка директории с артефактами сборки
// =============================================================
// СБОРКА
// =============================================================
{
"label": "Clean",
"group": "build",
"type": "shell",
"command": "rm -rf build*",
"windows": {
"command": "rm build* -Recurse -Force"
"label": "🔨 Build",
"group": {
"kind": "build",
"isDefault": true
},
"options": {
"statusbar": {
"color": "#ff8b8b",
"label": "$(clear-all) CleanBuildDir",
"detail": "Clean ALL"
}
}
},
// Сборка и запуск тестов на хосте в режиме Debug
{
"label": "Configure Host Debug tests",
"group": "build",
"hide": true,
"type": "shell",
"command": "cmake",
"command": "just",
"args": [
"--preset",
"host-debug"
],
"options": {
"cwd": "${workspaceFolder}"
}
},
{
"label": "Build Host Debug tests",
"group": "build",
"hide": true,
"type": "shell",
"command": "cmake",
"args": [
"--build",
"--preset",
"host-debug-build"
],
"options": {
"cwd": "${workspaceFolder}"
},
"dependsOn": "Configure Host Debug tests"
},
{
"label": "🧪 Host Build & Test All",
"type": "shell",
"command": "ctest",
"args": [
"--preset",
"host-debug-test"
],
"options": {
"cwd": "${workspaceFolder}",
"statusbar": {
"color": "#c800ff",
"label": "$(test-view-icon) Host Test Debug",
"detail": "Perform all the host tests"
}
},
"group": "test",
"dependsOrder": "sequence",
"dependsOn": [
"Build Host Debug tests"
]
},
// Сборка и запуск тестов на хосте в режиме Release
{
"label": "Configure Host Release tests",
"group": "build",
"hide": true,
"type": "shell",
"command": "cmake",
"args": [
"--preset",
"host-release"
],
"options": {
"cwd": "${workspaceFolder}"
},
},
{
"label": "Build Host Release tests",
"group": "build",
"hide": true,
"type": "shell",
"command": "cmake",
"args": [
"--build",
"--preset",
"host-release-build"
],
"dependsOn": "Configure Host Release tests"
},
{
"label": "🧪 Host Release Tests",
"type": "shell",
"command": "ctest",
"args": [
"--preset",
"host-release-test"
],
"options": {
"cwd": "${workspaceFolder}",
"statusbar": {
"color": "#c800ff",
"label": "$(test-view-icon) Host Test Release",
"detail": "Perform all the host tests"
}
},
"group": "test",
"dependsOrder": "sequence",
"dependsOn": [
"Build Host Release tests"
],
},
// Подготовка сборки в контейнере с пресетом Debug
{
"label": "Docker: Configure (Debug)",
"type": "shell",
"hide": true,
"command": "cmake",
"args": [
"--preset",
"Debug"
],
"options": {
"cwd": "${workspaceFolder}"
},
"group": "build",
"problemMatcher": []
},
// Сборка проекта в контейнере Debug
{
"label": "Docker: Build (Debug)",
"type": "shell",
"hide": false,
"command": "cmake",
"args": [
"--build",
"--preset",
"Debug"
"--justfile",
"scripts/build.just",
"build-${input:project}-${input:buildType}"
],
"options": {
"cwd": "${workspaceFolder}",
"statusbar": {
"color": "#ff8bd4",
"label": "$(debug-console-view-icon) Target: Build (Debug)",
"detail": "do Debug build"
"label": "$(debug-console-view-icon) Build",
"detail": "Build selected firmware"
}
},
"group": "build",
"dependsOn": "Docker: Configure (Debug)",
"problemMatcher": [
"$gcc"
]
},
// Подготовка сборки в контейнере с пресетом Release
// =============================================================
// HOST-ТЕСТЫ
// =============================================================
{
"label": "Docker: Configure (Release)",
"label": "🧪 Host Tests (Debug)",
"group": {
"kind": "test",
"isDefault": true
},
"type": "shell",
"hide": true,
"command": "cmake",
"command": "just",
"args": [
"--preset",
"Release"
"--justfile",
"scripts/build.just",
"test-host"
],
"options": {
"cwd": "${workspaceFolder}",
"statusbar": {
"color": "#c800ff",
"label": "$(test-view-icon) Host Tests",
"detail": "Run host unit tests"
}
},
"problemMatcher": []
},
{
"label": "🧪 Host Tests (Release)",
"group": "test",
"type": "shell",
"command": "just",
"args": [
"--justfile",
"scripts/build.just",
"test-host-release"
],
"options": {
"cwd": "${workspaceFolder}"
},
"group": "build",
"problemMatcher": []
},
// Сборка проекта в контейнере Release
// =============================================================
// HAB-ОБРАЗЫ
// =============================================================
{
"label": "Docker: Build (Release)",
"label": "📦 HAB Image",
"group": "build",
"type": "shell",
"hide": false,
"command": "cmake",
"command": "just",
"args": [
"--build",
"--preset",
"Release"
"--justfile",
"scripts/build.just",
"hab-${input:habProject}-${input:buildType}"
],
"options": {
"cwd": "${workspaceFolder}",
"statusbar": {
"color": "#03b3ff",
"label": "$(pass-filled) Target: Build (Release)",
"detail": "do Release build"
"label": "$(package) HAB",
"detail": "Build HAB image"
}
},
"group": "build",
"dependsOn": "Docker: Configure (Release)",
"problemMatcher": []
},
{
"label": "📦 HAB All (Debug)",
"group": "build",
"type": "shell",
"command": "just",
"args": [
"--justfile",
"scripts/build.just",
"hab-all-debug"
],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "📦 HAB All (Release)",
"group": "build",
"type": "shell",
"command": "just",
"args": [
"--justfile",
"scripts/build.just",
"hab-all-release"
],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
// =============================================================
// УТИЛИТЫ
// =============================================================
{
"label": "🗑️ Clean",
"group": "build",
"type": "shell",
"command": "just",
"args": [
"--justfile",
"scripts/build.just",
"clean"
],
"options": {
"cwd": "${workspaceFolder}",
"statusbar": {
"color": "#ff8b8b",
"label": "$(clear-all) Clean",
"detail": "Remove build artifacts"
}
},
"problemMatcher": []
}
],
"inputs": [
{
"id": "project",
"type": "pickString",
"description": "Project",
"options": [
"firmware-test",
"bootloader",
"app",
"all"
],
"default": "firmware-test"
},
{
"id": "buildType",
"type": "pickString",
"description": "Тип сборки",
"description": "Build type",
"options": [
"Debug",
"Release",
"debug",
"release"
],
"default": "Debug"
"default": "debug"
},
{
"id": "habProject",
"type": "pickString",
"description": "HAB project",
"options": [
"firmware-test",
"bootloader",
"app"
],
"default": "firmware-test"
}
],
]
}

View file

@ -66,17 +66,67 @@
],
"buildPresets": [
{
"name": "Debug",
"name": "all-debug",
"displayName": "All — Debug",
"configurePreset": "Debug"
},
{
"name": "Release",
"name": "all-release",
"displayName": "All — Release",
"configurePreset": "Release"
},
{
"name": "firmware-test-debug",
"displayName": "firmware_test — Debug",
"configurePreset": "Debug",
"targets": [
"firmware_test"
]
},
{
"name": "firmware-test-release",
"displayName": "firmware_test — Release",
"configurePreset": "Release",
"targets": [
"firmware_test"
]
},
{
"name": "bootloader-debug",
"displayName": "bootloader — Debug",
"configurePreset": "Debug",
"targets": [
"bootloader"
]
},
{
"name": "bootloader-release",
"displayName": "bootloader — Release",
"configurePreset": "Release",
"targets": [
"bootloader"
]
},
{
"name": "app-debug",
"displayName": "app — Debug",
"configurePreset": "Debug",
"targets": [
"app"
]
},
{
"name": "app-release",
"displayName": "app — Release",
"configurePreset": "Release",
"targets": [
"app"
]
},
{
"name": "host-debug-build",
"displayName": "Host Tests — Debug",
"configurePreset": "host-debug",
"displayName": "Build Host Debug Tests",
"targets": [
"test_host_simple",
"test_host_second"
@ -84,8 +134,8 @@
},
{
"name": "host-release-build",
"displayName": "Host Tests — Release",
"configurePreset": "host-release",
"displayName": "Build Host Release Tests",
"targets": [
"test_host_simple",
"test_host_second"

22
HOW_TO_FLASH.md Normal file
View file

@ -0,0 +1,22 @@
# Прошивка платы со стороны хоста
```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
```

344
Justfile Normal file
View file

@ -0,0 +1,344 @@
# =============================================================================
# Justfile — корень репозитория
# Выполняется на ХОСТ-МАШИНЕ разработчика (вне devcontainer).
#
# Отвечает только за:
# - инициализацию окружения (setup)
# - прошивку платы через USB (flash)
# - сквозные сценарии (pipeline)
# - вспомогательные утилиты (util)
#
# Сборка, тесты и HAB-образы — в just/build.just (внутри devcontainer).
#
# Быстрый старт:
# ./scripts/bootstrap.sh # первый запуск после git clone
# just # показать все рецепты
# =============================================================================
JUST_MIN := "1.27.0"
UV_MIN := "0.4.0"
DOCKER_MIN := "24.0.0"
TOOLS_DIR := "tools/host"
CACHE_DIR := ".cache"
# По умолчанию — показать список рецептов
_default:
@just --list --unsorted
# =============================================================================
# ГРУППА: 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 ""
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> <type>
# project: firmware_test | bootloader | app
# type: debug | release (default: release)
#
# Образ должен быть собран заранее в devcontainer:
# just --justfile just/build.just hab-<project>-<type>
[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 <project> <type>
[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"

148
README.md
View file

@ -1,8 +1,8 @@
# tft_manufacture_test
## Структура проекта
Монорепозиторий для **MIMXRT1052CVJ5B**. Содержит три независимых firmware-проекта с общей инфраструктурой сборки, тестирования и инструментарием.
Монорепозиторий для MIMXRT1052CVJ5B. Три независимых firmware-проекта с общей инфраструктурой сборки, тестирования и инструментарием.
> Архитектура рабочего окружения разработчика — [docs/DEV_ARCH.md](docs/DEV_ARCH.md)
---
@ -11,44 +11,55 @@
```bash
/
├── .devcontainer/ # VSCode Devcontainer — единое окружение для всех разработчиков
├── bsp/ # Board Support Package
│ └── generated/ # Сгенерировано NXP Config Tools (Pins + Clocks Tool)
│ ├── TFT_Board.mex # Источник истины конфигурации пинов и тактирования
│ ├── pin_mux.c/h # Сгенерировано из .mex (Pins Tool)
│ ├── clock_config.c/h # Сгенерировано из .mex (Clocks Tool)
│ ├── board.c/h # Ручная инициализация специфики платы
│ └── BOOT_FLAGS.md # Описание флагов загрузчика
├── cmake/ # Общие CMake модули и toolchain files
│ ├── linker/ # Линкер-скрипты под разные схемы размещения
│ ├── toolchain_arm.cmake # ARM cross-compilation toolchain
│ └── toolchain_host.cmake # Host GCC для unit-тестов
├── sdk/ # NXP MCUXpresso SDK — vendored, только нужные компоненты
│ ├── CMakeLists.txt # CMake-таргеты для каждого драйвера
├── sdk/ # NXP MCUXpresso SDK — vendored
│ ├── CMakeLists.txt
│ ├── CMSIS/
│ ├── devices/
│ │ └── MIMXRT1052/
│ │ ├── drivers/ # fsl_flexcan, fsl_lpuart, fsl_usdhc и др.
│ │ ├── startup/
│ │ └── utilities/
│ └── components/
├── bsp/ # Board Support Package
│ └── board/
│ ├── board.mex # Исходник конфигурации для NXP Config Tools
│ ├── pin_mux.c/h # Сгенерировано из board.mex (Pins Tool)
│ ├── clock_config.c/h # Сгенерировано из board.mex (Clocks Tool)
│ └── board.c/h # Ручная инициализация специфики платы
├── lib/ # Библиотеки и зависимости
│ ├── CMakeLists.txt # Агрегатор — подключает нужные модули через опции
│ ├── freertos/ # vendored
│ ├── fatfs/ # vendored
│ ├── mbedtls/ # vendored (если используется)
│ ├── unity/ # vendored (test framework)
│ ├── fff/ # vendored (fake functions для тестов)
│ └── hal/ # submodule — аппаратно-независимые библиотеки
│ # (второй разработчик, активно развивается)
│ ├── devices/MIMXRT1052/ # Драйверы, startup, утилиты
│ ├── components/ # fsl_button, fsl_led, serial_manager и др.
│ ├── middleware/ # FatFS, FreeRTOS, LittleFS, USB, mcuboot и др.
│ └── rtos/freertos/ # FreeRTOS (vendored через SDK)
├── lib/ # Внешние библиотеки
│ ├── Unity/ # Фреймворк для unit-тестов (vendored)
│ ├── fff/ # Fake Function Framework для моков (vendored)
│ └── SEGGER/ # SEGGER RTT — вывод логов через отладчик
├── firmware/
│ ├── test/ # [Проект 1] Тестовая прошивка — входной контроль
│ ├── test/ # [Проект 1] Тестовая прошивка — входной контроль платы
│ ├── bootloader/ # [Проект 2] Загрузчик с поддержкой A/B обновления
│ └── app/ # [Проект 3] Основная боевая прошивка (FreeRTOS)
├── tests/ # Host-тесты (unit + integration)
│ ├── host/
│ └── target/
└── tools/ # Скрипты для прошивки, провизии, HIL-тестов
├── flash_usb.py # Прошивка через USB ROM (blhost / nxp-spsdk)
├── flash_remote.sh # Прошивка на удалённый сервер через SSH
└── provision.py # Производственная провизия (SPT / nxpimage)
│ └── tft_app/ # [Проект 3] Основная боевая прошивка (FreeRTOS)
├── tests/ # Тесты (host + target)
│ ├── host/ # Unit/интеграционные тесты, запускаемые на хосте
│ ├── target/ # Тесты периферии, запускаемые на таргете
│ └── HostTestingGuide.md
├── tools/
│ └── 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
├── scripts/
│ ├── bootstrap.sh # Первичная настройка окружения
│ └── build.just # Рецепты сборки (используется через Justfile)
├── docs/ # Документация проекта
│ ├── DEV_ARCH.md # Архитектура окружения разработки
│ ├── CMAKE_HINTS.md # Шпаргалка по CMake в проекте
│ ├── schematic.pdf # Схема платы
│ ├── mcu_rm.pdf # Reference Manual IMXRT1052
│ └── manufacturing_user's_guide.pdf
├── CMakeLists.txt # Корневой CMake
├── CMakePresets.json # Пресеты сборки (Release/Debug/Host)
├── Justfile # Точка входа для команд сборки/тестирования/прошивки
└── README.md
```
---
@ -57,15 +68,32 @@
### 1. Тестовая прошивка (`firmware/test/`)
Bare-metal прошивка для входного контроля на производстве. Проверяет базовую работоспособность всех интерфейсов и периферии: CAN, UART, SDRAM, QSPI Flash, uSD, RGB-интерфейс, гальванически развязанные входы, светодиоды, кнопки. Загружается через USB ROM (Serial Download Mode). **Рекомендуется начать разработку с этого проекта** — он наиболее прост и позволяет полностью отладить окружение сборки.
Bare-metal прошивка для **входного контроля** платы. Проверяет базовую работоспособность всех интерфейсов: CAN, UART, SDRAM, QSPI Flash, uSD (SDIO), RGB-интерфейс, гальванически развязанные входы, светодиоды, кнопки, IR-приёмник, MQS.
Загружается через USB ROM (Serial Download Mode) — подробнее в [HOW_TO_FLASH.md](HOW_TO_FLASH.md).
> **Рекомендуется начать разработку с этого проекта** — он наиболее прост и позволяет полностью отладить окружение сборки и прошивки.
### 2. Загрузчик (`firmware/bootloader/`)
Отвечает за обновление боевой прошивки в полевых условиях. Поддерживает схему A/B с шифрованием (обновление через uSD). Обновление самого загрузчика — только через внешний инструмент (USB ROM + blhost), не через себя. Загружается на производстве вместе с первой версией боевой прошивки единым blob-ом.
Отвечает за обновление боевой прошивки в полевых условиях. Поддерживает схему **A/B** с обновлением через uSD. Обновление самого загрузчика — только через внешний инструмент (USB ROM + blhost), не через себя. На производстве загружается единым blob-ом вместе с первой версией боевой прошивки.
### 3. Боевая прошивка (`firmware/app/`)
### 3. Боевая прошивка (`firmware/tft_app/`)
Основная прошивка на базе FreeRTOS. Включает FatFS, бизнес-логику, модули. Обновляется через загрузчик по схеме A/B.
Основная прошивка на базе **FreeRTOS**. Включает FatFS, бизнес-логику, модули. Обновляется через загрузчик по схеме A/B.
---
## Тестирование
Стратегия тестирования двухуровневая:
| Уровень | Расположение | Инструменты | Запуск |
|---|---|---|---|
| **Host-тесты** (unit + интеграционные) | `tests/host/` | Unity + fff | `ctest` в devcontainer |
| **Target-тесты** (аппаратные) | `tests/target/` | Unity на железе | Удалённый ПК-сервер через SSH |
Подробнее — [tests/HostTestingGuide.md](tests/HostTestingGuide.md) и [tests/README.md](tests/README.md).
---
@ -73,44 +101,52 @@ Bare-metal прошивка для входного контроля на про
| Зависимость | Подход | Причина |
|---|---|---|
| NXP SDK | vendored | Стабильная версия, обновлений не планируется |
| FreeRTOS | vendored | Стабильная версия |
| FatFS, mbedTLS и др. | vendored | Стабильные версии |
| NXP MCUXpresso SDK | vendored | Стабильная версия, обновлений не планируется |
| FreeRTOS, FatFS, LittleFS и др. | vendored (через SDK) | Стабильные версии |
| Unity + fff | vendored | Маленькие, стабильные |
| lib/hal (второй разработчик) | **submodule** | Активно развивается параллельно |
| SEGGER RTT | vendored | Стабильный |
**Принцип:** всё что не меняется — vendored (скачано и закоммичено). Submodule только для активно развивающихся зависимостей. Это обеспечивает полностью автономную сборку после `git clone` без доступа к интернету.
**Принцип:** всё что не меняется — vendored (закоммичено в репозиторий). Это обеспечивает полностью автономную сборку после `git clone` без доступа к интернету.
---
## Devcontainer — состав окружения
| Инструмент | Назначение |
|---|---|
| `arm-none-eabi-gcc` | Сборка firmware для таргета |
| `arm-none-eabi-gdb` | Отладка через GDB server |
| `host-gcc` | Сборка и запуск host-тестов |
| `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), производственная провизия |
| `Python 3 + nxp-spsdk` | Прошивка (blhost, nxpimage), HAB, провизия |
| `just` | Запуск рецептов сборки/тестирования/прошивки |
---
## Конфигурация платы (NXP Config Tools)
Файл `bsp/board/board.mex` — источник истины для конфигурации пинов и тактирования. Открывается в NXP Config Tools (Pins Tool + Clocks Tool) для генерации `pin_mux.c/h` и `clock_config.c/h`. Используется **один раз** при старте проекта или при изменении аппаратной схемы. Коммитится в репозиторий вместе со сгенерированным кодом.
Файл `bsp/generated/TFT_Board.mex` — **источник истины** для конфигурации пинов и тактирования. Открывается в NXP Config Tools (Pins Tool + Clocks Tool) для регенерации `pin_mux.c/h` и `clock_config.c/h`. Используется при старте проекта или при изменении аппаратной схемы. Коммитится вместе со сгенерированным кодом.
---
## Доставка прошивки
## Быстрый старт
| Сценарий | Способ |
|---|---|
| Разработка (с ПК разработчика) | `rsync` / `scp` → SSH на ПК-сервер → OpenOCD/blhost |
| Производство | `nxp-spsdk` (blhost + nxpimage) через USB ROM |
| Готовое ПО на сервер | GitLab CI/CD → GitLab Package Registry → сервер подтягивает |
| Обновление загрузчика | USB ROM + blhost (только так, не через сам загрузчик) |
| Обновление боевой прошивки в поле | Через загрузчик, схема A/B, uSD |
```bash
git clone <repo-url>
cd tft_manufacture_test
# Открыть в VSCode → Reopen in Container
# Затем внутри devcontainer:
just build-host # сборка host-тестов
just test # запуск host-тестов через CTest
just build-firmware # сборка firmware для таргета
just flash # прошивка через USB ROM
```
> Подробнее о прошивке — [HOW_TO_FLASH.md](HOW_TO_FLASH.md)
> Подробнее об окружении разработки — [docs/DEV_ARCH.md](docs/DEV_ARCH.md)

View file

@ -6,14 +6,17 @@ endif()
# bsp_board — стартап, сгенерированные файлы Config Tools, инициализация платы
# -----------------------------------------------------------------------------
add_library(bsp_board STATIC board/board.c board/pin_mux.c board/clock_config.c
board/startup/startup_MIMXRT1052.S)
add_library(
bsp_board STATIC
generated/board.c generated/pin_mux.c generated/clock_config.c
generated/startup/startup_MIMXRT1052.S)
target_include_directories(bsp_board PUBLIC board)
# Подавляем предупреждения при компиляции собственных .c файлов библиотеки
target_compile_options(bsp_board PRIVATE -w)
# SYSTEM подавляет предупреждения для всех внешних потребителей
target_include_directories(bsp_board SYSTEM PUBLIC generated)
# Подавляем предупреждения о macro redefined
set_source_files_properties(board/board.c board/pin_mux.c
PROPERTIES COMPILE_OPTIONS "-w")
# Дефайны PUBLIC — транзитивно подтянутся во все firmware таргеты TODO: для
# каждого приложения свои дефайны
target_compile_definitions(

219
docs/CMAKE_HINTS.md Normal file
View file

@ -0,0 +1,219 @@
# PRIVATE, PUBLIC и INTERFACE в CMake
## Основная идея
Каждый таргет в CMake — это «чёрный ящик» с двумя границами:
- **внутренняя** — то, что нужно только для компиляции самого таргета
- **внешняя** — то, что таргет «экспортирует» своим потребителям
Ключевые слова `PRIVATE`, `PUBLIC` и `INTERFACE` управляют тем, в какую из этих границ попадает свойство.
``````bash
┌─────────────────────────────────────────────────┐
│ lib_a │
│ │
│ PRIVATE │ INTERFACE │
│ (только внутри) │ (только снаружи) │
│ │ │
│ PUBLIC = PRIVATE + INTERFACE │
└─────────────────────────────────────────────────┘
│ target_link_libraries(app PRIVATE lib_a)
[ app ] ← получает только INTERFACE-свойства lib_a
```
---
## Определения
| Ключевое слово | Применяется к самому таргету | Передаётся потребителям |
|----------------|:----------------------------:|:-----------------------:|
| `PRIVATE` | ✅ | ❌ |
| `PUBLIC` | ✅ | ✅ |
| `INTERFACE` | ❌ | ✅ |
---
## Разбор на примерах
### Пример 1 — `target_include_directories`
``````bash
src/
├── lib_math/
│ ├── CMakeLists.txt
│ ├── include/ ← публичные заголовки (нужны потребителям)
│ │ └── math.h
│ ├── internal/ ← внутренние заголовки (только для lib_math)
│ │ └── impl.h
│ └── math.c
└── app/
├── CMakeLists.txt
└── main.c ← #include "math.h"
```
```cmake
# lib_math/CMakeLists.txt
add_library(lib_math STATIC math.c)
target_include_directories(lib_math
PRIVATE internal/ # impl.h нужен только при компиляции math.c
PUBLIC include/ # math.h нужен и lib_math, и всем её потребителям
)
```
```cmake
# app/CMakeLists.txt
add_library(app main.c)
target_link_libraries(app PRIVATE lib_math)
# app автоматически получает include/ через PUBLIC-свойство lib_math
# app НЕ получает internal/ — оно PRIVATE
```
**Итог:** `main.c` может писать `#include "math.h"`, но не видит `impl.h`.
---
### Пример 2 — `target_compile_definitions`
``````bash
lib_json ──→ lib_http ──→ app
```
```cmake
# lib_json
add_library(lib_json STATIC json.c)
target_compile_definitions(lib_json
PRIVATE JSON_INTERNAL_DEBUG # дефайн только для json.c
PUBLIC JSON_VERSION=2 # нужен и json.c, и потребителям
)
```
```cmake
# lib_http линкуется с lib_json
add_library(lib_http STATIC http.c)
target_link_libraries(lib_http PUBLIC lib_json)
# lib_http транзитивно передаёт JSON_VERSION=2 дальше в app
```
```cmake
# app
add_executable(app main.c)
target_link_libraries(app PRIVATE lib_http)
# app видит JSON_VERSION=2 (транзитивно через lib_http → lib_json)
# app НЕ видит JSON_INTERNAL_DEBUG (PRIVATE)
```
---
### Пример 3 — `INTERFACE` (header-only библиотека)
`INTERFACE` используется тогда, когда таргет сам **не компилируется** — например, header-only библиотека или набор флагов.
```cmake
# Набор флагов для встроенных таргетов — сам не компилируется
add_library(flags_embedded INTERFACE)
target_compile_options(flags_embedded INTERFACE
-mcpu=cortex-m7
-mfpu=fpv5-d16
-mfloat-abi=hard
-mthumb
)
target_compile_definitions(flags_embedded INTERFACE
ARM_MATH_CM7
__FPU_PRESENT=1
)
```
```cmake
# Любой firmware-таргет подключает флаги одной строкой
target_link_libraries(firmware_app PRIVATE flags_embedded)
# firmware_app получает все -mcpu, -mfpu и дефайны
```
Такой паттерн часто используется для:
- HAL/SDK флагов конкретного МК
- Флагов линкера (`-T linker.ld`)
- Опций оптимизации для конкретного сценария сборки
---
### Пример 4 — транзитивность
Понять транзитивность важно: свойства распространяются по цепочке зависимостей.
```
lib_base ──→ lib_mid ──→ app
```
```cmake
add_library(lib_base STATIC base.c)
target_compile_definitions(lib_base
PUBLIC BASE_FEATURE_ENABLED # пойдёт вглубь цепочки
PRIVATE BASE_INTERNAL # остановится здесь
)
add_library(lib_mid STATIC mid.c)
target_link_libraries(lib_mid PUBLIC lib_base)
# ^^^^^^
# PUBLIC здесь означает: "lib_mid использует lib_base,
# и мои потребители тоже должны о ней знать"
add_executable(app main.c)
target_link_libraries(app PRIVATE lib_mid)
```
**Что видит `app`:**
| Дефайн | Виден в app? | Причина |
|-----------------------|:------------:|--------------------------------------------------|
| `BASE_FEATURE_ENABLED`| ✅ | PUBLIC в lib_base → PUBLIC в lib_mid → app |
| `BASE_INTERNAL` | ❌ | PRIVATE в lib_base, цепочка обрывается |
Если бы `lib_mid` слинковался с `lib_base` через `PRIVATE`:
```cmake
target_link_libraries(lib_mid PRIVATE lib_base)
# тогда app НЕ увидел бы BASE_FEATURE_ENABLED — цепочка оборвалась бы на lib_mid
```
---
## Применимость к командам
Ключевые слова работают одинаково во всех `target_*` командах:
| Команда | Типичное использование |
|--------------------------------|----------------------------------------------------------|
| `target_include_directories` | PRIVATE — внутренние папки; PUBLIC — публичные заголовки |
| `target_compile_definitions` | PRIVATE — отладочные дефайны; PUBLIC — версии API |
| `target_compile_options` | PRIVATE — флаги оптимизации; INTERFACE — флаги МК |
| `target_link_libraries` | PRIVATE — внутр. зависимость; PUBLIC — транзитивная |
| `target_link_options` | INTERFACE — скрипт линкера для всей цепочки |
| `target_sources` | Всегда PRIVATE — исходники не передаются потребителям |
---
## Правило выбора
```bash
Нужно ли это свойство самому таргету?
ДА ──→ Нужно ли оно потребителям?
│ │
│ ДА ──→ PUBLIC
│ │
НЕТ ──→ PRIVATE
НЕТ ──→ Нужно ли оно потребителям?
ДА ──→ INTERFACE
НЕТ ──→ (не добавляйте это свойство вообще)
```
---

566
docs/DEV_ARCH.md Normal file
View file

@ -0,0 +1,566 @@
# Архитектура рабочего окружения разработчика
> Проект: TFT Firmware (MIMXRT1052CVJ5B)
> Документ описывает итоговый рабочий процесс разработчиков: от разворачивания
> окружения до сборки, тестирования, отладки и прошивки платы.
> Производственный сервер описан кратко — подробно в отдельном документе.
---
## 1. Концепция
Рабочее окружение разделено на два контекста с чёткой границей:
**Devcontainer** — всё что касается кода: сборка, статический анализ,
форматирование, host-тесты, подготовка HAB-образов. Разработчик проводит
здесь большую часть времени. Управляется через VSCode tasks и `scripts/build.just`.
**Хост** — всё что касается железа: прошивка платы через USB, отладка
через JLink/probe-rs. Управляется через корневой `Justfile`.
Такое разделение решает несколько проблем: USB-устройства не требуют
проброса в контейнер; оба разработчика работают в идентичных условиях;
CI использует те же команды что и локальная разработка.
---
## 2. Компоненты окружения
```bash
ПК разработчика
├── Хост (Linux / macOS / Windows + Git Bash)
│ ├── just ← запуск задач хостового уровня
│ ├── docker ← управление devcontainer
│ ├── git ← работа с репозиторием
│ ├── uv + spsdk ← прошивка платы (flash_usb.py, sdphost, blhost)
│ ├── JLinkGDBServer / probe-rs ← сервер отладки (USB → TCP :2331)
│ └── VSCode ← IDE (Dev Containers extension)
├── Devcontainer (Docker)
│ ├── ARM GCC 13.3 ← кросс-компилятор
│ ├── cmake + ninja ← система сборки
│ ├── clang-17 ← компилятор для host-тестов
│ ├── clangd-17 ← LSP (автодополнение, диагностика)
│ ├── clang-tidy-17 ← статический анализ
│ ├── clang-format-17 ← форматирование кода
│ ├── cmake-format ← форматирование CMakeLists
│ ├── just ← запуск задач внутри контейнера
│ ├── uv + spsdk ← сборка HAB-образов (только nxpimage)
│ └── Unity + fff ← фреймворки host-тестов
└── Плата TFT (IMXRT1052) — на столе у разработчика
├── USB ──────────────▶ хост (SDP-режим, прошивка)
├── SWD ──────────────▶ JLink/probe-rs на хосте (отладка)
└── CAN / UART / IO ──▶ локальный стенд (target-тесты)
```
---
## 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 | ✅ | — | — |
`spsdk` присутствует везде, но с разными ролями:
- **devcontainer** — только `nxpimage` для сборки HAB-образов
- **хост** — полный стек: `nxpimage` + `sdphost` + `blhost` для прошивки
- **сервер** — то же что на хосте, но для производственного сценария
Версия `spsdk` зафиксирована в `tools/host/uv.lock` — все три места
используют одну и ту же версию.
---
## 4. Структура репозитория (automation-часть)
```bash
/
├── Justfile ← хост: прошивка, setup, pipeline
├── scripts/
│ └── bootstrap.sh ← уровень 0: just → just bootstrap
│ └── build.just ← devcontainer: сборка, тесты, HAB
├── .devcontainer/
│ ├── Dockerfile
│ └── devcontainer.json
├── tools/
│ └── host/
│ ├── hab/
│ │ ├── hab_firmware_test_debug.yaml
│ │ ├── hab_firmware_test_release.yaml
│ │ ├── hab_bootloader_debug.yaml
│ │ ├── hab_bootloader_release.yaml
│ │ ├── hab_app_debug.yaml
│ │ └── hab_app_release.yaml
│ ├── dcd/
│ │ ├── dcd.bin
│ │ └── ivt_flashloader.bin
│ ├── flash_usb.py
│ ├── pyproject.toml
│ └── uv.lock
├── .vscode/
│ └── tasks.json ← UI для build.just (внутри devcontainer)
├── CMakePresets.json
├── cmake/
├── sdk/
├── bsp/
├── lib/
├── firmware/
│ ├── test/
│ ├── bootloader/
│ └── app/
└── tests/ ← host-тесты (Unity + fff)
```
---
## 5. Первый запуск: разворачивание окружения
### 5.1 Предварительные требования
| Платформа | Что нужно до bootstrap |
|---|---|
| Linux | `docker`, `git`, `curl` |
| macOS | Docker Desktop, `git` (Xcode CLT) |
| Windows | Docker Desktop, Git for Windows → **использовать Git Bash** |
### 5.2 Единственная команда для нового разработчика
```bash
git clone <repo-url> && cd <repo>
./scripts/bootstrap.sh
```
### 5.3 Что делает bootstrap
```bash
bootstrap.sh (уровень 0)
├── определить платформу (Linux / macOS / Windows Git Bash)
│ uname: MINGW64_NT-... → windows, Linux → linux, Darwin → macos
├── проверить just (semver без sort -V — работает в Git Bash)
< 1.27.0 или отсутствует:
│ Linux/macOS → curl | bash → ~/.local/bin/just
│ Windows → winget install --id Casey.Just
│ (перезапустить Git Bash после установки)
└── exec just bootstrap
├── [1/3] check-deps
│ just >= 1.27.0 · uv >= 0.4.0 · docker >= 24.0.0
│ платформо-зависимые подсказки при ошибках
├── [2/3] setup-udev (только Linux)
│ /etc/udev/rules.d/99-nxp-mimxrt.rules:
│ 1FC9:0130 ← NXP BootROM (SDP-режим)
│ 15A2:0073 ← NXP Flashloader
│ usermod -a -G plugdev $USER
│ требует re-login · на macOS/Windows пропускается
└── [3/3] setup-tools
uv sync в tools/host/
SHA-256 uv.lock кэшируется в .cache/
повторный вызов мгновенный если lockfile не изменился
```
### 5.4 После bootstrap
```bash
Открыть VSCode → "Reopen in Container"
```
`postCreateCommand` выполняется автоматически при поднятии контейнера:
```bash
cd tools/host && uv sync &&
cd ../.. &&
cmake --preset host-debug &&
cmake --preset Debug
```
Прогрев CMake-кэша нужен чтобы clangd и IntelliSense заработали сразу,
без первой ручной сборки.
---
## 6. Прошивки, boot-стратегии и матрица сборки
### 6.1 Три подпроекта
| Прошивка | Boot-стратегия | DCD | Назначение |
|---|---|---|---|
| `firmware_test` | XIP из Flash | ✅ | Входной контроль, тестирование периферии |
| `bootloader` | Копирование в ITCM | ❌ | Загрузчик, не использует SDRAM |
| `app` | XIP + буферы в SDRAM | ✅ | Основное приложение (FreeRTOS, LCDIF) |
### 6.2 Матрица сборки
Каждый проект собирается в двух режимах:
| | Debug | Release |
|---|---|---|
| `firmware_test` | разработка, отладка | HAB для сервера |
| `bootloader` | отладка загрузчика | финальная прошивка |
| `app` | отладка приложения | финальная прошивка |
### 6.3 CMake пресеты
```bash
configurePresets: Debug · Release · host-debug · host-release
buildPresets (ARM):
all-debug / all-release ← все проекты (для CI)
firmware-test-debug / release
bootloader-debug / release
app-debug / release
buildPresets (host):
host-debug-build / host-release-build
```
### 6.4 Карта Flash (W25Q128, 16 MB)
```bash
0x60000000 FCB — Flash Config Block 512 байт (пишет Flashloader)
0x60001000 IVT + BDT ← начало HAB-образа
0x60001040 DCD — инициализация SDRAM ~1088 байт (firmware_test, app)
0x60003000 Код прошивки (.text, .data…)
```
---
## 7. Рабочий процесс разработчика
### 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-тесты (управление стендом) | **хост** |
### 7.2 Типичная сессия разработки
```
Открыть VSCode → работать в devcontainer весь день
├── писать код
├── Ctrl+Shift+P → "Run Task" → 🧪 Host Tests (Debug)
│ или в терминале: just --justfile just/build.just test-host
├── Ctrl+Shift+P → "Run Task" → 🔨 Build → firmware-test · debug
│ → build/Debug/firmware/test/firmware_test.elf
├── Ctrl+Shift+P → "Run Task" → 📦 HAB Image → firmware-test · debug
│ → build/Debug/firmware_test_hab.bin
│ Переключиться в хостовый терминал
├── just flash firmware_test debug ← прошить отладочный образ
│ или
├── just 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-<project>-<type>` |
| 🧪 Host Tests (Debug) | — | — | `just test-host` |
| 🧪 Host Tests (Release) | — | — | `just test-host-release` |
| 📦 HAB Image | project | debug/release | `just hab-<project>-<type>` |
| 📦 HAB All (Debug) | — | — | `just hab-all-debug` |
| 📦 HAB All (Release) | — | — | `just hab-all-release` |
| 🗑️ Clean | — | — | `just clean` |
Таски «Build» и «HAB Image» запрашивают два input последовательно:
сначала проект (`firmware-test / bootloader / app / all`),
затем тип (`debug / release`).
---
## 8. Прошивка платы (хост)
### 8.1 Перевод платы в SDP-режим
```
1. BOOT_MOD_1 → 3V3
2. Reset
3. Подключить USB к хосту
→ плата определяется как VID:PID = 1FC9:0130
4. Выполнить нужный just flash-* рецепт
5. После прошивки: BOOT_MOD_1 → GND, Reset
→ плата стартует из Flash
```
### 8.2 Команды прошивки
```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
# TODO: Загрузка в RAM — без записи во Flash, мгновенный старт
# Удобно для частых итераций: не изнашивает Flash, не нужен BOOT_MOD переключатель
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
```
### 8.3 Что происходит внутри flash_usb.py
```
Плата в SDP-режиме (1FC9:0130)
├── sdphost: загрузить ivt_flashloader.bin в RAM (0x20001C00)
└── sdphost: jump-address 0x20001C00
│ ожидание до 10с — Flashloader поднимается как 15A2:0073
Flashloader (15A2:0073)
├── fill-memory + configure-memory (0xC0000007) — инициализация FlexSPI
├── flash-erase-region 0x60000000
├── fill-memory + configure-memory (0xF000000F) — запись FCB
├── write-memory 0x60001000 ← HAB-образ
└── reset
```
---
## 9. Отладка
JLink/probe-rs работает на хосте напрямую через USB/SWD.
GDB-клиент в devcontainer подключается к серверу по TCP.
```bash
Хост
├── JLinkGDBServer -device MIMXRT1052 -if SWD -port 2331
│ USB/SWD → плата
└── host.docker.internal:2331 ← доступен из devcontainer
Devcontainer
└── arm-none-eabi-gdb / probe-rs
target remote host.docker.internal:2331
```
`.vscode/launch.json`:
```json
{
"type": "cortex-debug",
"servertype": "external",
"gdbTarget": "host.docker.internal:2331",
"executable": "${workspaceFolder}/build/Debug/firmware/test/firmware_test.elf"
}
```
`host.docker.internal` — стандартный DNS-алиас Docker для хоста.
Работает на macOS, Windows и Linux (Docker Desktop).
> USB-passthrough программатора в Docker на macOS невозможен, на других
> платформах нестабилен. TCP-мост — единственное надёжное решение для
> всех платформ.
---
## 10. Тесты
### 10.1 Host-тесты
Выполняются в devcontainer на хостовом компиляторе (x86/arm64). Железо не нужно.
```bash
Фреймворк: Unity + fff
Пресеты: host-debug / host-release
Компилятор: системный clang-17 (не ARM GCC)
Запуск: just --justfile just/build.just test-host
Результат: JUnit XML → VSCode CTest Lab + GitLab CI
```
`BUILD_TESTS_HOST=ON` отключает ARM-специфику (BSP, SDK) — компилируются
только тестируемые модули и fff-заглушки.
### 10.2 Target-тесты
Выполняются на физической плате. Прошивается `firmware_test`,
стенд подаёт сигналы и проверяет ответы.
```bash
Стенд (M5StampPLC или аналог) ←→ Плата TFT
Тестируемые подсистемы:
SDRAM 32 MB — чтение/запись паттернов
QSPI Flash — erase / write / verify
CAN — loopback + внешний фрейм от стенда
UART TTL — echo-тест
UART +24V изол. — echo-тест
Гальв. входы +24V — состояния при подаче напряжения от стенда
RTC BM8563 — установка / чтение времени
SD-карта (SDIO) — монтирование, R/W файл
MQS (аудио) — воспроизведение тестового сигнала
IR-приёмник — приём тестового кода от стенда
```
---
## 11. Жизненный цикл изменений
```bash
feature-ветка
├── код в devcontainer
│ 🧪 Host Tests ← зелёные?
│ 🔨 Build firmware-test debug ← компилируется?
├── проверка на железе
│ 📦 HAB Image → firmware-test · debug
│ just flash-test-debug ← прошить
│ target-тесты со стендом ← периферия работает?
├── подготовка к MR
│ 📦 HAB All (Release) ← финальные образы
│ just flash firmware_test release ← убедиться что release работает
└── Merge Request → GitLab
[CI pipeline — отдельная тема]
host-тесты, сборка, публикация артефактов
Производственный сервер
just incoming → firmware_test release → HIL
just production → bootloader + app release
```
---
## 12. Обновление зависимостей
### spsdk (на хосте разработчика)
```bash
just 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 в Dockerfile
```dockerfile
ARG JUST_VERSION=1.40.0 # .devcontainer/Dockerfile — единственное место
```
### ARM toolchain
```dockerfile
ARG TOOLCHAIN_VERSION=14.2.rel1 # .devcontainer/Dockerfile
```
Пересборка инвалидирует только Stage 1 (toolchain). Stage 2
(clang, cmake и др.) берётся из кэша — пересборка быстрая.
### После git pull если изменился uv.lock
```bash
just setup-tools # автоматически обнаружит изменение и выполнит uv sync
```
---
## 13. Производственный сервер (к сведению)
Сервер работает **только с готовыми проверенными артефактами**.
Никакой сборки, никакого компилятора.
```bash
Источник: GitLab Releases или FTP
— только теггированные релизы, прошедшие CI
Сценарий входного контроля новой платы:
1. just incoming
└── flash firmware_test release → HIL-тесты периферии
2. Тесты пройдены:
just production
├── flash bootloader release
└── flash app release
Установлено: just · uv + spsdk · git
НЕ установлено: docker · cmake · компилятор · ARM toolchain
```
Детальный `server.just` и пайплайн сервера — отдельная задача.
---
## Приложение А: минимальные версии
| Инструмент | Версия | Причина |
|---|---|---|
| `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` |
## Приложение Б: быстрая шпаргалка
```bash
# ── Первый запуск ──────────────────────────────────────────────
./scripts/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 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 scan # найти NXP USB-устройства
just sdp-status # проверить BootROM
just setup-tools # обновить spsdk после git pull
just check-deps # проверить версии инструментов
```

View file

@ -1,30 +0,0 @@
# Программные компоненты ThirdParty
## Библиотеки сторонних разработчиков, загруженные в виде субмодулей
* [SEGGER_RTT](https://www.segger.com/products/debug-probes/j-link/technology/about-real-time-transfer/)
* [Unity](https://www.throwtheswitch.org/unity)
* [FFF](https://github.com/meekrosoft/fff)
## Работа с Git-Submodules
```bash
# 1. Добавление субмодулей (например SEGGER_RTT)
git submodule add https://github.com/SEGGERMicro/RTT ThirdParty/RTT
# 2. Синхронизация состояния субмодулей, загруженных локально с remote
git submodule update --init --recursive
# Полная переинициализация субмодулей (На примере SEGGER_RTT)
# 1. Удалить submodule полностью
git submodule deinit -f ThirdParty/RTT
rm -rf .git/modules/ThirdParty/RTT
git rm -rf ThirdParty/RTT
# 2. Удалить .gitmodules запись
vim .gitmodules # удалить секцию [submodule "ThirdParty/RTT"]
# 3. Добавить заново
git submodule add https://github.com/SEGGERMicro/RTT ThirdParty/RTT
git submodule update --init --recursive
```

View file

@ -3,8 +3,8 @@
add_executable(firmware_test)
target_sources(firmware_test PRIVATE main.c
${PROJECT_SOURCE_DIR}/bsp/board/syscalls.c)
target_sources(firmware_test
PRIVATE main.c ${PROJECT_SOURCE_DIR}/bsp/generated/syscalls.c)
# -----------------------------------------------------------------------------
# Зависимости — только то что нужно для входного контроля bsp_board транзитивно
@ -36,6 +36,7 @@ target_link_options(
-Wl,-Map=${CMAKE_BINARY_DIR}/firmware_test.map
-T${PROJECT_SOURCE_DIR}/cmake/linker/MIMXRT1052xxxxx_flexspi_nor.ld)
# TODO: дефайны BOOT
# -----------------------------------------------------------------------------
# Post-build: генерация .bin для прошивки через blhost
# -----------------------------------------------------------------------------

View file

@ -7,7 +7,6 @@
*/
#include "board.h"
#include "fsl_gpio.h"
#include "pin_mux.h"
@ -27,12 +26,12 @@
* Code
******************************************************************************/
int main(void) {
const uint32_t USER_DELAY_US = 1000000;
BOARD_Init();
// GPIO_PinWrite(BOARD_INITPINS_UserLed1_PORT, BOARD_INITPINS_UserLed1_PIN,
// 0);
GPIO_PinWrite(BOARD_INITPINS_UserLed2_PORT, BOARD_INITPINS_UserLed2_PIN, 0);
while (1) {
}
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);
while (1)
{
}
}

157
scripts/bootstrap.sh Executable file
View file

@ -0,0 +1,157 @@
#!/usr/bin/env bash
# =============================================================================
# scripts/bootstrap.sh
# Уровень 0: устанавливает just (если отсутствует), затем передаёт управление
# just bootstrap для полной инициализации окружения.
#
# Поддерживаемые платформы:
# Linux — bash (native)
# macOS — bash (native)
# Windows — Git Bash (поставляется вместе с git)
#
# Требования: bash >= 4, curl (Linux/macOS) или winget (Windows)
# Запуск: ./scripts/bootstrap.sh
# =============================================================================
set -euo pipefail
JUST_VERSION="1.36.0"
LINUX_INSTALL_DIR="${HOME}/.local/bin"
BOLD="\033[1m"
RED="\033[0;31m"
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}"; }
echo ""
echo -e "${BOLD}=== TFT Firmware — Bootstrap ===${RESET}"
echo ""
# -----------------------------------------------------------------------------
# 1. Определить платформу
# uname на Git Bash: MINGW64_NT-10.0-19045, MSYS_NT-..., CYGWIN_NT-...
# -----------------------------------------------------------------------------
_uname="$(uname -s)"
case "${_uname}" in
Linux*) PLATFORM="linux" ;;
Darwin*) PLATFORM="macos" ;;
MINGW*|MSYS*|CYGWIN*) PLATFORM="windows" ;;
*)
error "Unsupported platform: ${_uname}"
exit 1
;;
esac
info "Platform detected: ${PLATFORM}"
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
[[ "${a2:-0}" -lt "${b2:-0}" ]] && return 1
[[ "${a3:-0}" -ge "${b3:-0}" ]] && return 0
return 1
}
# -----------------------------------------------------------------------------
# 3. Платформо-зависимая установка just
# -----------------------------------------------------------------------------
install_just_linux() {
info "Installing just ${JUST_VERSION} -> ${LINUX_INSTALL_DIR}"
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:"
warn " export PATH=\"${LINUX_INSTALL_DIR}:\$PATH\""
export PATH="${LINUX_INSTALL_DIR}:${PATH}"
fi
}
install_just_macos() {
if command -v brew &>/dev/null; then
info "Installing just via Homebrew..."
brew install just
else
install_just_linux
fi
}
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"
exit 1
}
warn "Restart Git Bash so the new PATH from winget takes effect,"
warn "then re-run: ./scripts/bootstrap.sh"
exit 0
else
error "winget not found."
echo ""
echo " Options to install just on Windows:"
echo " 1. Install 'App Installer' from Microsoft Store (brings winget)"
echo " 2. Download just.exe manually: https://github.com/casey/just/releases"
echo " Place it in a directory that is in your Git Bash PATH"
exit 1
fi
}
# -----------------------------------------------------------------------------
# 4. Проверить / установить just
# -----------------------------------------------------------------------------
JUST_OK=false
if command -v just &>/dev/null; then
JUST_CURRENT="$(just --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')"
if semver_ge "${JUST_CURRENT}" "${JUST_VERSION}"; then
success "just ${JUST_CURRENT} (>= ${JUST_VERSION} required)"
JUST_OK=true
else
warn "just ${JUST_CURRENT} is outdated (need >= ${JUST_VERSION}), reinstalling..."
fi
fi
if [[ "${JUST_OK}" == "false" ]]; then
case "${PLATFORM}" in
linux) install_just_linux ;;
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
# -----------------------------------------------------------------------------
info "Delegating to: just bootstrap"
echo ""
exec just bootstrap "$@"

208
scripts/build.just Normal file
View file

@ -0,0 +1,208 @@
# =============================================================================
# scripts/build.just — сборка, тесты, HAB-образы
# Выполняется ТОЛЬКО внутри devcontainer (из терминала VSCode или tasks).
#
# Рабочая директория — корень репозитория (не scripts/).
# =============================================================================
set working-directory := ".."
TOOLS_DIR := "tools/host"
BUILD_DIR := "build"
_default:
@just --list --justfile scripts/build.just --unsorted
# =============================================================================
# ГРУППА: build
#
# Матрица сборки:
# firmware-test × debug / release
# bootloader × debug / release
# app × debug / release
# all × debug / release (все проекты сразу, для CI)
# =============================================================================
[private]
_configure-debug:
cmake --preset Debug
[private]
_configure-release:
cmake --preset Release
# ── firmware_test ─────────────────────────────────────────────────────────────
[group('build')]
build-firmware-test-debug: _configure-debug
cmake --build --preset firmware-test-debug
[group('build')]
build-firmware-test-release: _configure-release
cmake --build --preset firmware-test-release
# ── bootloader ───────────────────────────────────────────────────────────────
[group('build')]
build-bootloader-debug: _configure-debug
cmake --build --preset bootloader-debug
[group('build')]
build-bootloader-release: _configure-release
cmake --build --preset bootloader-release
# ── app ───────────────────────────────────────────────────────────────────────
[group('build')]
build-app-debug: _configure-debug
cmake --build --preset app-debug
[group('build')]
build-app-release: _configure-release
cmake --build --preset app-release
# ── все проекты сразу ─────────────────────────────────────────────────────────
[group('build')]
build-all-debug: _configure-debug
cmake --build --preset all-debug
[group('build')]
build-all-release: _configure-release
cmake --build --preset all-release
# ── утилиты ───────────────────────────────────────────────────────────────────
[group('build')]
clean:
rm -rf build
# =============================================================================
# ГРУППА: test
# =============================================================================
[private]
_configure-host-debug:
cmake --preset host-debug
[private]
_configure-host-release:
cmake --preset host-release
[group('test')]
test-host: _configure-host-debug
cmake --build --preset host-debug-build
ctest --preset host-debug-test
[group('test')]
test-host-release: _configure-host-release
cmake --build --preset host-release-build
ctest --preset host-release-test
# =============================================================================
# ГРУППА: flash — подготовка HAB-образов
#
# Матрица HAB:
# firmware-test × debug / release
# bootloader × debug / release
# app × debug / release
# all × debug / release
#
# Debug HAB — для прошивки своей платы и отладки.
# Release HAB — для производственного сервера.
#
# HAB yaml конфиги:
# tools/host/hab/hab_<project>_debug.yaml — путь к Debug ELF
# tools/host/hab/hab_<project>_release.yaml — путь к Release ELF
# =============================================================================
# ── firmware_test ─────────────────────────────────────────────────────────────
[group('flash')]
hab-firmware-test-debug: build-firmware-test-debug
#!/usr/bin/env bash
set -euo pipefail
cd "{{TOOLS_DIR}}/hab"
uv run nxpimage hab export --force \
-c hab_firmware_test_debug.yaml \
-o "../../../{{BUILD_DIR}}/Debug/firmware_test_hab.bin"
echo " ✅ firmware_test_hab.bin (Debug)"
[group('flash')]
hab-firmware-test-release: build-firmware-test-release
#!/usr/bin/env bash
set -euo pipefail
cd "{{TOOLS_DIR}}/hab"
uv run nxpimage hab export --force \
-c hab_firmware_test_release.yaml \
-o "../../../{{BUILD_DIR}}/Release/firmware_test_hab.bin"
echo " ✅ firmware_test_hab.bin (Release)"
# ── bootloader ───────────────────────────────────────────────────────────────
[group('flash')]
hab-bootloader-debug: build-bootloader-debug
#!/usr/bin/env bash
set -euo pipefail
cd "{{TOOLS_DIR}}/hab"
uv run nxpimage hab export --force \
-c hab_bootloader_debug.yaml \
-o "../../../{{BUILD_DIR}}/Debug/bootloader_hab.bin"
echo " ✅ bootloader_hab.bin (Debug)"
[group('flash')]
hab-bootloader-release: build-bootloader-release
#!/usr/bin/env bash
set -euo pipefail
cd "{{TOOLS_DIR}}/hab"
uv run nxpimage hab export --force \
-c hab_bootloader_release.yaml \
-o "../../../{{BUILD_DIR}}/Release/bootloader_hab.bin"
echo " ✅ bootloader_hab.bin (Release)"
# ── app ───────────────────────────────────────────────────────────────────────
[group('flash')]
hab-app-debug: build-app-debug
#!/usr/bin/env bash
set -euo pipefail
cd "{{TOOLS_DIR}}/hab"
uv run nxpimage hab export --force \
-c hab_app_debug.yaml \
-o "../../../{{BUILD_DIR}}/Debug/app_hab.bin"
echo " ✅ app_hab.bin (Debug)"
[group('flash')]
hab-app-release: build-app-release
#!/usr/bin/env bash
set -euo pipefail
cd "{{TOOLS_DIR}}/hab"
uv run nxpimage hab export --force \
-c hab_app_release.yaml \
-o "../../../{{BUILD_DIR}}/Release/app_hab.bin"
echo " ✅ app_hab.bin (Release)"
# ── все образы сразу ─────────────────────────────────────────────────────────
[group('flash')]
hab-all-debug: hab-firmware-test-debug hab-bootloader-debug hab-app-debug
@echo " ✅ All HAB images (Debug) ready"
[group('flash')]
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')]
hab-verify project="firmware_test" type="release":
#!/usr/bin/env bash
set -euo pipefail
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" ;;
*)
echo " ❌ Unknown: {{project}}-{{type}}"
echo " Projects: firmware_test, bootloader, app"
echo " Types: debug, release"
exit 1 ;;
esac
OUT="/tmp/hab_parse_{{project}}_{{type}}.yaml"
uv run nxpimage hab parse -b "${BIN}" -o "${OUT}"
echo " ✅ ${OUT}"
grep -E "(entry|csf|tag)" "${OUT}" || true

View file

@ -0,0 +1,17 @@
# =============================================================================
# HAB Container — firmware/app (боевая прошивка)
# Расположение: tools/host/hab/hab_app.yaml
#
# =============================================================================
options:
flags: 0x00
startAddress: 0x60000000
ivtOffset: 0x1000
initialLoadSize: 0x2000
family: mimxrt1050
DCDFilePath: ../dcd/dcd.bin # обязателен — heap FreeRTOS и LCDIF в SDRAM
inputImageFile: "../../../build/Debug/app.bin"
sections: []

View file

@ -0,0 +1,17 @@
# =============================================================================
# HAB Container — firmware/bootloader
# Расположение: tools/host/hab/hab_bootloader.yaml
#
# =============================================================================
options:
flags: 0x00
startAddress: 0x60000000
ivtOffset: 0x1000
initialLoadSize: 0x2000
family: mimxrt1050
# DCD намеренно отсутствует — загрузчик не использует SDRAM
inputImageFile: "../../../build/Debug/bootloader.bin"
sections: []

View file

@ -0,0 +1,18 @@
# =============================================================================
# HAB Container — firmware/test (входной контроль производства)
# Расположение: tools/host/hab/hab_firmware_test.yaml
#
# =============================================================================
options:
flags: 0x00 # unsigned — без подписи
startAddress: 0x60000000 # FlexSPI NOR base (XIP)
ivtOffset: 0x1000 # IVT offset for FlexSPI NOR boot
initialLoadSize: 0x2000 # размер области IVT+BDT+DCD до кода
family: mimxrt1050
DCDFilePath: ../dcd/dcd.bin # инициализация SDRAM
# .bin или .srec — оба принимаются
inputImageFile: "../../../build/Release/firmware_test.bin"
sections: []