diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..9ec0358 --- /dev/null +++ b/.clang-format @@ -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 \ No newline at end of file diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index fff7dbc..e569fd9 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -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" diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bd73793..f96f1d4 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -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" + ] } \ No newline at end of file diff --git a/.gitignore b/.gitignore index e2e914e..086d016 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,7 @@ Debug/ Release/ build* Testing/ +!build.just # Файлы настройки среды разработки .project diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 79cf828..843f596 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -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" } - ], + ] } \ No newline at end of file diff --git a/CMakePresets.json b/CMakePresets.json index 45b3ea5..95c7ec0 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -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" diff --git a/HOW_TO_FLASH.md b/HOW_TO_FLASH.md new file mode 100644 index 0000000..413b25d --- /dev/null +++ b/HOW_TO_FLASH.md @@ -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 +``` diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..a75c1ed --- /dev/null +++ b/Justfile @@ -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: 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 f1c393e..0524ef9 100644 --- a/README.md +++ b/README.md @@ -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 | \ No newline at end of file +```bash +git clone +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) diff --git a/docs/BOOT_FLAGS.md b/bsp/BOOT_FLAGS.md similarity index 100% rename from docs/BOOT_FLAGS.md rename to bsp/BOOT_FLAGS.md diff --git a/bsp/CMakeLists.txt b/bsp/CMakeLists.txt index d8bc15f..419acb9 100644 --- a/bsp/CMakeLists.txt +++ b/bsp/CMakeLists.txt @@ -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( diff --git a/bsp/board/.clang-tidy b/bsp/generated/.clang-tidy similarity index 100% rename from bsp/board/.clang-tidy rename to bsp/generated/.clang-tidy diff --git a/docs/CONFIG_UTILS.md b/bsp/generated/README.md similarity index 100% rename from docs/CONFIG_UTILS.md rename to bsp/generated/README.md diff --git a/bsp/board/TFT_Board.mex b/bsp/generated/TFT_Board.mex similarity index 100% rename from bsp/board/TFT_Board.mex rename to bsp/generated/TFT_Board.mex diff --git a/bsp/board/board.c b/bsp/generated/board.c similarity index 100% rename from bsp/board/board.c rename to bsp/generated/board.c diff --git a/bsp/board/board.h b/bsp/generated/board.h similarity index 100% rename from bsp/board/board.h rename to bsp/generated/board.h diff --git a/bsp/board/clock_config.c b/bsp/generated/clock_config.c similarity index 100% rename from bsp/board/clock_config.c rename to bsp/generated/clock_config.c diff --git a/bsp/board/clock_config.h b/bsp/generated/clock_config.h similarity index 100% rename from bsp/board/clock_config.h rename to bsp/generated/clock_config.h diff --git a/bsp/board/pin_mux.c b/bsp/generated/pin_mux.c similarity index 100% rename from bsp/board/pin_mux.c rename to bsp/generated/pin_mux.c diff --git a/bsp/board/pin_mux.h b/bsp/generated/pin_mux.h similarity index 100% rename from bsp/board/pin_mux.h rename to bsp/generated/pin_mux.h diff --git a/bsp/board/MIMXRT1052.xml b/bsp/generated/startup/MIMXRT1052.xml similarity index 100% rename from bsp/board/MIMXRT1052.xml rename to bsp/generated/startup/MIMXRT1052.xml diff --git a/bsp/board/startup/startup_MIMXRT1052.S b/bsp/generated/startup/startup_MIMXRT1052.S similarity index 100% rename from bsp/board/startup/startup_MIMXRT1052.S rename to bsp/generated/startup/startup_MIMXRT1052.S diff --git a/bsp/board/syscalls.c b/bsp/generated/syscalls.c similarity index 100% rename from bsp/board/syscalls.c rename to bsp/generated/syscalls.c diff --git a/docs/CMAKE_HINTS.md b/docs/CMAKE_HINTS.md new file mode 100644 index 0000000..a0bb217 --- /dev/null +++ b/docs/CMAKE_HINTS.md @@ -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 + │ + НЕТ ──→ (не добавляйте это свойство вообще) +``` + +--- diff --git a/docs/DEV_ARCH.md b/docs/DEV_ARCH.md new file mode 100644 index 0000000..64e3396 --- /dev/null +++ b/docs/DEV_ARCH.md @@ -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 && cd +./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--` | +| 🧪 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` | + +Таски «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 # проверить версии инструментов +``` diff --git a/docs/SDK_DRIVERS.md b/docs/SDK_DRIVERS.md deleted file mode 100644 index 629d4b3..0000000 --- a/docs/SDK_DRIVERS.md +++ /dev/null @@ -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 - -``` diff --git a/firmware/test/CMakeLists.txt b/firmware/test/CMakeLists.txt index 757555f..d4d9fc8 100644 --- a/firmware/test/CMakeLists.txt +++ b/firmware/test/CMakeLists.txt @@ -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 # ----------------------------------------------------------------------------- diff --git a/firmware/test/main.c b/firmware/test/main.c index b82b53e..4d2ff81 100644 --- a/firmware/test/main.c +++ b/firmware/test/main.c @@ -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) + { + } } diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh new file mode 100755 index 0000000..13cf5bc --- /dev/null +++ b/scripts/bootstrap.sh @@ -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 "$@" \ No newline at end of file diff --git a/scripts/build.just b/scripts/build.just new file mode 100644 index 0000000..6a982cf --- /dev/null +++ b/scripts/build.just @@ -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__debug.yaml — путь к Debug ELF +# tools/host/hab/hab__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 diff --git a/docs/HAB_GUIDE.md b/tools/host/HAB_GUIDE.md similarity index 100% rename from docs/HAB_GUIDE.md rename to tools/host/HAB_GUIDE.md diff --git a/tools/host/hab/hab_app_debug.yaml b/tools/host/hab/hab_app_debug.yaml new file mode 100644 index 0000000..e231eac --- /dev/null +++ b/tools/host/hab/hab_app_debug.yaml @@ -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: [] \ No newline at end of file diff --git a/tools/host/hab/hab_app.yaml b/tools/host/hab/hab_app_release.yaml similarity index 100% rename from tools/host/hab/hab_app.yaml rename to tools/host/hab/hab_app_release.yaml diff --git a/tools/host/hab/hab_bootloader_debug.yaml b/tools/host/hab/hab_bootloader_debug.yaml new file mode 100644 index 0000000..8c76565 --- /dev/null +++ b/tools/host/hab/hab_bootloader_debug.yaml @@ -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: [] \ No newline at end of file diff --git a/tools/host/hab/hab_bootloader.yaml b/tools/host/hab/hab_bootloader_release.yaml similarity index 100% rename from tools/host/hab/hab_bootloader.yaml rename to tools/host/hab/hab_bootloader_release.yaml diff --git a/tools/host/hab/hab_firmware_test.yaml b/tools/host/hab/hab_firmware_test_debug.yaml similarity index 100% rename from tools/host/hab/hab_firmware_test.yaml rename to tools/host/hab/hab_firmware_test_debug.yaml diff --git a/tools/host/hab/hab_firmware_test_release.yaml b/tools/host/hab/hab_firmware_test_release.yaml new file mode 100644 index 0000000..99313dd --- /dev/null +++ b/tools/host/hab/hab_firmware_test_release.yaml @@ -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: [] \ No newline at end of file