From 310a8d6bc9f060b149cc1b08540d9dc769cd72bf Mon Sep 17 00:00:00 2001 From: Dmitry Akimov Date: Thu, 12 Mar 2026 13:39:28 +0300 Subject: [PATCH] =?UTF-8?q?=20=D0=9D=D0=B0=D1=87=D0=B0=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D1=8B=D0=B9=20=D1=8D=D1=82=D0=B0=D0=BF=20=D0=BF=D0=BE=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B7=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B5=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=87=D0=B5=D0=B3=D0=BE=20=D0=BE=D0=BA=D1=80?= =?UTF-8?q?=D1=83=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B7=D0=B0=D0=BA=D0=BE?= =?UTF-8?q?=D0=BD=D1=87=D0=B5=D0=BD=20-=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=BD=D0=B0=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BE?= =?UTF-8?q?=D1=81=D0=BF=D0=BE=D1=81=D0=BE=D0=B1=D0=BD=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D1=8C=20=D0=BE=D0=BA=D1=80=D1=83=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=20(host=20+=20=D0=B2=20=D0=BA=D0=BE=D0=BD=D1=82=D0=B5=D0=B9?= =?UTF-8?q?=D0=BD=D0=B5=D1=80=D0=B5)=20=D0=BD=D0=B0=20=D0=BD=D0=B5=D1=81?= =?UTF-8?q?=D0=BA=D0=BE=D0=BB=D1=8C=D0=BA=D0=B8=D1=85=20=D1=85=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D1=85=20(Win,=20Mac)=20-=20=D0=BF=D0=BE=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BD=D0=B5=20=D0=B7=D0=B0=D1=82=D1=80=D0=BE=D0=BD?= =?UTF-8?q?=D1=83=D1=82=D1=8B=20=D0=BC=D0=BE=D0=BC=D0=B5=D0=BD=D1=82=D1=8B?= =?UTF-8?q?=20=D1=81=20ci,=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 29 +++++++++++ .gitattributes | 2 + .gitignore | 3 +- HOW_TO_FLASH.md | 104 ++++++++++++++++++++++++++++++++++++++ Justfile | 6 +-- README.md | 23 ++++++--- docs/DEV_ARCH.md | 84 +++++++++++++++++++++++++----- firmware/test/main.c | 2 +- just/build.just | 31 +++++++----- just/host.just | 17 ++++--- tools/host/flash_usb.py | 76 ++++++++++++++++------------ tools/host/pyproject.toml | 1 + 12 files changed, 302 insertions(+), 76 deletions(-) create mode 100644 HOW_TO_FLASH.md diff --git a/.env.example b/.env.example index e69de29..42f5b33 100644 --- a/.env.example +++ b/.env.example @@ -0,0 +1,29 @@ +# ============================================================================= +# .env — единый источник конфигурации проекта +# Читается: just (set dotenv-load) → экспортируется в окружение (set export) +# → наследуется uv run python3 автоматически +# ============================================================================= + +# --- Hardware --- +BOARD=MIMXRT1052 + +# --- USB VID:PID (NXP) --- +# Используются: just host.just (check-deps, udev) + flash_usb.py +BOOTROM_VID=1fc9 +BOOTROM_PID=0130 +FLASHLOADER_VID=15a2 +FLASHLOADER_PID=0073 + +# --- Paths --- +# BUILD_DIR и TOOLS_DIR задаются абсолютно в корневом justfile +# через justfile_directory(), здесь можно переопределить если нужно +# BUILD_DIR=/custom/path/build + +# --- Build --- +CMAKE_GENERATOR=Ninja + +# --- Debug / SWD --- +OPENOCD_INTERFACE=cmsis-dap.cfg +TARGET_CFG=target/imxrt.cfg +GDB_PORT=3333 +GDB_EXECUTABLE=gdb-multiarch \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index dfe0770..903c009 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,4 @@ # Auto detect text files and perform LF normalization * text=auto +.env text eol=lf +.env.example text eol=lf diff --git a/.gitignore b/.gitignore index 3f6a872..2679cdf 100644 --- a/.gitignore +++ b/.gitignore @@ -68,5 +68,6 @@ compile_commands.json __pycache__/ *.pyc .env - +tools/host/.venv-host/ +tools/host/.venv-host-win/ diff --git a/HOW_TO_FLASH.md b/HOW_TO_FLASH.md new file mode 100644 index 0000000..f73f209 --- /dev/null +++ b/HOW_TO_FLASH.md @@ -0,0 +1,104 @@ +# HOW TO FLASH + +Прошивка выполняется **на хосте** (вне devcontainer) через USB ROM (Serial Download Protocol). + +--- + +## 1. Перевод платы в SDP-режим + +```bash +1. BOOT_MOD_1 → 3V3 +2. Reset +3. Подключить USB к хосту + → плата определяется как VID:PID 1FC9:0130 +``` + +После прошивки — вернуть в нормальный режим: + +```bash +BOOT_MOD_1 → GND → Reset +``` + +--- + +## 2. Подготовить HAB-образ (внутри devcontainer) + +HAB-образ собирается из ELF-файла командой `nxpimage`. Выполнять в терминале VSCode: + +```bash +just build::hab-firmware-test-debug # → build/Debug/firmware_test_hab.bin +just build::hab-firmware-test-release # → build/Release/firmware_test_hab.bin +just build::hab-bootloader-release # → build/Release/bootloader_hab.bin +just build::hab-app-release # → build/Release/app_hab.bin +just build::hab-all-release # все три Release за один раз +``` + +--- + +## 3. Прошивка (хостовый терминал) + +### Запись во Flash + +```bash +# Основной рецепт: just host::flash +just host::flash firmware_test debug # разработка, итерации с отладчиком +just host::flash firmware_test release # проверить как будет на сервере +just host::flash bootloader release +just host::flash app release +``` + +### Загрузка в RAM (без записи во Flash) + +Быстро, не изнашивает Flash. Плата стартует сразу после загрузки. + +```bash +just host::flash-ram firmware_test # default: debug +just host::flash-ram firmware_test debug +just host::flash-ram firmware_test release +``` + +### Быстрые алиасы + +```bash +just flash # = just host::flash firmware_test debug +just host::flash-test-debug # то же +just host::flash-test-release +just host::flash-production # bootloader release + app release (с подтверждением) +``` + +--- + +## 4. Диагностика + +```bash +just host::scan # найти подключённые NXP USB-устройства +just host::sdp-status # проверить связь с BootROM (плата в SDP-режиме) +just host::flashloader-status # проверить Flashloader (после jump-address) +just host::check-deps # проверить версии just / uv / docker +``` + +--- + +## 5. Что происходит при прошивке + +```bash +Плата в SDP-режиме (1FC9:0130) + │ + ├── sdphost: загрузить ivt_flashloader.bin в RAM (0x20001C00) + └── sdphost: jump-address → Flashloader поднимается как 15A2:0073 + │ + ├── configure-memory (0xC0000007) — инициализация FlexSPI NOR + ├── flash-erase-region 0x60000000 + ├── configure-memory (0xF000000F) — запись FCB в 0x60000000 + ├── write-memory 0x60001000 ← HAB-образ + └── reset +``` + +--- + +## 6. Производственный сценарий (сервер) + +```bash +just host::incoming # firmware_test release → HIL-тесты периферии +just host::production # bootloader release + app release +``` diff --git a/Justfile b/Justfile index 98af3f0..b69de58 100644 --- a/Justfile +++ b/Justfile @@ -20,9 +20,9 @@ set dotenv-load # === Общие переменные (доступны во всех модулях через export) === BOARD := env('BOARD', 'MIMXRT1052') -BUILD_DIR := env('BUILD_DIR', 'build') -TOOLS_DIR := env('TOOLS_DIR', 'tools/host') -CACHE_DIR := ".cache" +BUILD_DIR := env('BUILD_DIR', justfile_directory() / 'build') +TOOLS_DIR := env('TOOLS_DIR', justfile_directory() / 'tools/host') +CACHE_DIR := justfile_directory() / '.cache' # === Модули === # Каждый модуль — это namespace с изолированными рецептами diff --git a/README.md b/README.md index 397b3e4..9b4bd44 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,11 @@ ```bash / ├── .devcontainer/ # VSCode Devcontainer — единое окружение для всех разработчиков +│ ├── Dockerfile +│ └── devcontainer.json +├── .vscode/ +│ ├── launch.json +│ └── tasks.json # UI для just build::* (внутри devcontainer) ├── bsp/ # Board Support Package │ └── generated/ # Сгенерировано NXP Config Tools (Pins + Clocks Tool) │ ├── TFT_Board.mex # Источник истины конфигурации пинов и тактирования @@ -40,27 +45,31 @@ ├── tests/ # Тесты (host + target) │ ├── host/ # Unit/интеграционные тесты, запускаемые на хосте │ ├── target/ # Тесты периферии, запускаемые на таргете -│ └── HostTestingGuide.md +│ ├── HostTestingGuide.md +│ └── README.md ├── tools/ │ └── host/ # Инструменты для работы с таргетом │ ├── flash_usb.py # Прошивка через USB ROM (nxp-spsdk / blhost) │ ├── hab/ # Утилиты и гайд по HAB (Secure Boot) -│ └── dcd/ # Device Configuration Data +│ ├── dcd/ # Device Configuration Data +│ ├── pyproject.toml +│ └── uv.lock ├── just/ # Just-модули (автоматизация) │ ├── build.just # Сборка, тесты, HAB-образы (devcontainer) │ ├── host.just # Прошивка, bootstrap, HIL (хост) │ └── ci.just # CI/CD пайплайны -├── scripts/ -│ └── bootstrap.sh # Первичная настройка окружения (уровень 0) ├── docs/ # Документация проекта │ ├── DEV_ARCH.md # Архитектура окружения разработки │ ├── CMAKE_HINTS.md # Шпаргалка по CMake в проекте │ ├── schematic.pdf # Схема платы │ ├── mcu_rm.pdf # Reference Manual IMXRT1052 │ └── manufacturing_user's_guide.pdf +├── .env # Конфигурация проекта (VID:PID, пути, GDB и др.) +├── .env.example # Шаблон .env для новых разработчиков +├── bootstrap.sh # Первичная настройка окружения (уровень 0) ├── CMakeLists.txt # Корневой CMake ├── CMakePresets.json # Пресеты сборки (Release/Debug/Host) -├── Justfile # Точка входа для команд (модули: build, host, ci) +├── justfile # Точка входа для команд (модули: build, host, ci) └── README.md ``` @@ -142,7 +151,7 @@ git clone cd tft_manufacture_test # Инициализация хоста (один раз) -sudo chmod +x bootstrap.sh +# Устанавливает just и uv, затем настраивает окружение ./bootstrap.sh # Открыть в VSCode → Reopen in Container @@ -158,3 +167,5 @@ just flash # прошивка через USB ROM > Подробнее о прошивке — [HOW_TO_FLASH.md](HOW_TO_FLASH.md) > Подробнее об окружении разработки — [docs/DEV_ARCH.md](docs/DEV_ARCH.md) +> +> \ No newline at end of file diff --git a/docs/DEV_ARCH.md b/docs/DEV_ARCH.md index 8cddf52..4836e41 100644 --- a/docs/DEV_ARCH.md +++ b/docs/DEV_ARCH.md @@ -34,7 +34,8 @@ CI использует те же команды что и локальная р │ ├── docker ← управление devcontainer │ ├── git ← работа с репозиторием │ ├── uv + spsdk ← прошивка платы (flash_usb.py, sdphost, blhost) -│ │ venv: tools/host/.venv-host +│ │ venv: tools/host/.venv-host (Linux/macOS) +│ │ tools/host/.venv-host-win (Windows) │ ├── JLinkGDBServer / probe-rs ← сервер отладки (USB → TCP :2331) │ └── VSCode ← IDE (Dev Containers extension) │ @@ -48,7 +49,7 @@ CI использует те же команды что и локальная р │ ├── cmake-format ← форматирование CMakeLists │ ├── just ← запуск задач внутри контейнера (just build::*) │ ├── uv + spsdk ← сборка HAB-образов (только nxpimage) -│ │ venv: tools/host/.venv-container +│ │ venv: tools/host/.venv │ └── Unity + fff ← фреймворки host-тестов │ └── Плата TFT (IMXRT1052) — на столе у разработчика @@ -83,7 +84,45 @@ CI использует те же команды что и локальная р Версия `spsdk` зафиксирована в `tools/host/uv.lock` — все три места используют одну и ту же версию. +--- +## 3.1 Конфигурация проекта — `.env` + +`.env` в корне репозитория — единый источник конфигурации для всего стека. + +```ini +# USB VID:PID (NXP) +BOOTROM_VID=1fc9 # используется: host.just (udev-правила) + flash_usb.py +BOOTROM_PID=0130 +FLASHLOADER_VID=15a2 +FLASHLOADER_PID=0073 + +# GDB / отладка +GDB_PORT=3333 +GDB_EXECUTABLE=gdb-multiarch +OPENOCD_INTERFACE=cmsis-dap.cfg +TARGET_CFG=target/imxrt.cfg +``` + +**Как значения попадают в инструменты:** + +```bash +.env + │ + ├─▶ just (set dotenv-load + set export) + │ └─▶ just-рецепты видят переменные напрямую: {{BOOTROM_VID}} + │ └─▶ uv run flash_usb.py ← наследует окружение автоматически + │ └─▶ os.environ.get("BOOTROM_VID") ← без python-dotenv + │ + └─▶ .vscode/launch.json ← через ${env:GDB_PORT} если нужно +``` + +Аппаратные константы (`FLASH_BASE`, `HAB_OFFSET`, `FLEXSPI_OPTION_VALUE` и т.д.) +намеренно оставлены в коде `flash_usb.py` — это не конфигурация, а часть +протокола прошивки IMXRT1052, менять их незачем. + +`.env.example` — шаблон без значений, коммитится в репозиторий. +`.env` — реальные значения, в `.gitignore` (если содержит секреты), иначе тоже коммитится. --- @@ -91,16 +130,19 @@ CI использует те же команды что и локальная р ```bash / -├── Justfile ← корневой оркестратор; модули: build, host, ci +├── justfile ← корневой оркестратор; модули: build, host, ci +├── bootstrap.sh ← уровень 0: устанавливает just+uv → just host::bootstrap +├── .env ← конфигурация проекта (VID:PID, пути, GDB) +├── .env.example ← шаблон для новых разработчиков ├── just/ │ ├── build.just ← devcontainer: сборка, тесты, HAB │ ├── host.just ← хост: прошивка, bootstrap, HIL │ └── ci.just ← CI/CD пайплайны -├── scripts/ -│ └── bootstrap.sh ← уровень 0: just → just host::bootstrap ├── .devcontainer/ │ ├── Dockerfile │ └── devcontainer.json +├── .vscode/ +│ └── tasks.json ← UI для just build::* (внутри devcontainer) ├── tools/ │ └── host/ │ ├── hab/ @@ -113,11 +155,9 @@ CI использует те же команды что и локальная р │ ├── dcd/ │ │ ├── dcd.bin │ │ └── ivt_flashloader.bin -│ ├── flash_usb.py +│ ├── flash_usb.py ← читает конфиг из env (VID:PID, BUILD_DIR) │ ├── pyproject.toml │ └── uv.lock -├── .vscode/ -│ └── tasks.json ← UI для just build::* (внутри devcontainer) ├── CMakePresets.json ├── cmake/ ├── sdk/ @@ -157,12 +197,19 @@ bootstrap.sh (уровень 0) ├── определить платформу (Linux / macOS / Windows Git Bash) │ uname: MINGW64_NT-... → windows, Linux → linux, Darwin → macos │ -├── проверить just (semver без sort -V — работает в Git Bash) +├── проверить/установить just (semver без sort -V — работает в Git Bash) │ < 1.36.0 или отсутствует: │ Linux/macOS → curl | bash → ~/.local/bin/just +│ macOS → brew install just (если есть Homebrew) │ Windows → winget install --id Casey.Just │ (перезапустить Git Bash после установки) │ +├── проверить/установить uv +│ < 0.4.0 или отсутствует: +│ Linux/macOS → curl -LsSf https://astral.sh/uv/install.sh | sh → ~/.local/bin/uv +│ macOS → brew install uv (если есть Homebrew) +│ Windows → powershell.exe irm https://astral.sh/uv/install.ps1 | iex +│ └── exec just host::bootstrap │ ├── [1/3] check-deps @@ -173,11 +220,15 @@ bootstrap.sh (уровень 0) │ /etc/udev/rules.d/99-nxp-mimxrt.rules: │ 1FC9:0130 ← NXP BootROM (SDP-режим) │ 15A2:0073 ← NXP Flashloader + │ VID:PID берутся из .env (BOOTROM_VID/PID, FLASHLOADER_VID/PID) │ usermod -a -G plugdev $USER │ требует re-login · на macOS/Windows пропускается │ └── [3/3] setup-tools - uv sync в tools/host/ (venv: .venv-host на хосте) + uv sync в tools/host/ + venv: .venv-host (Linux/macOS) · .venv-host-win (Windows) + разделение нужно: devcontainer создаёт .venv с Linux-симлинками, + Windows не может их удалить без прав администратора SHA-256 uv.lock кэшируется в .cache/ повторный вызов мгновенный если lockfile не изменился ``` @@ -191,7 +242,7 @@ bootstrap.sh (уровень 0) `postCreateCommand` выполняется автоматически при поднятии контейнера: ```bash -cd tools/host && uv sync && # venv: .venv-container +cd tools/host && uv sync && # venv: tools/host/.venv (Linux, внутри контейнера) cd ../.. && cmake --preset host-debug && cmake --preset Debug @@ -266,7 +317,7 @@ buildPresets (host): ### 7.2 Типичная сессия разработки -``` +```bash Открыть VSCode → работать в devcontainer весь день │ ├── писать код @@ -311,7 +362,7 @@ buildPresets (host): ### 8.1 Перевод платы в SDP-режим -``` +```bash 1. BOOT_MOD_1 → 3V3 2. Reset 3. Подключить USB к хосту @@ -347,7 +398,12 @@ just host::flash-production # bootloader release + tft_app release ### 8.3 Что происходит внутри flash_usb.py -``` +`flash_usb.py` читает конфигурацию из окружения (just экспортирует `.env`): + +- `BOOTROM_VID/PID`, `FLASHLOADER_VID/PID` → USB-адреса устройств +- `BUILD_DIR` → абсолютный путь к артефактам сборки + +```bash Плата в SDP-режиме (1FC9:0130) │ ├── sdphost: загрузить ivt_flashloader.bin в RAM (0x20001C00) diff --git a/firmware/test/main.c b/firmware/test/main.c index c1b852c..5366beb 100644 --- a/firmware/test/main.c +++ b/firmware/test/main.c @@ -30,7 +30,7 @@ int main(void) { BOARD_Init(); GPIO_PinWrite(BOARD_INITPINS_UserLed1_PORT, BOARD_INITPINS_UserLed1_PIN, 0); - GPIO_PinWrite(BOARD_INITPINS_UserLed2_PORT, BOARD_INITPINS_UserLed2_PIN, 0); + // GPIO_PinWrite(BOARD_INITPINS_UserLed2_PORT, BOARD_INITPINS_UserLed2_PIN, 0); while (1) { } diff --git a/just/build.just b/just/build.just index 65bf7e7..bd7b4af 100644 --- a/just/build.just +++ b/just/build.just @@ -117,20 +117,22 @@ test-host-release: _configure-host-release hab-firmware-test-debug: build-firmware-test-debug #!/usr/bin/env bash set -euo pipefail + mkdir -p "{{BUILD_DIR}}/Debug" cd "{{TOOLS_DIR}}/hab" uv run nxpimage hab export --force \ -c hab_firmware_test_debug.yaml \ - -o "../../../{{BUILD_DIR}}/Debug/firmware_test_hab.bin" + -o "{{BUILD_DIR}}/Debug/firmware_test_hab.bin" echo " ✅ firmware_test_hab.bin (Debug)" [group('hab_image_gen')] hab-firmware-test-release: build-firmware-test-release #!/usr/bin/env bash set -euo pipefail + mkdir -p "{{BUILD_DIR}}/Release" cd "{{TOOLS_DIR}}/hab" uv run nxpimage hab export --force \ -c hab_firmware_test_release.yaml \ - -o "../../../{{BUILD_DIR}}/Release/firmware_test_hab.bin" + -o "{{BUILD_DIR}}/Release/firmware_test_hab.bin" echo " ✅ firmware_test_hab.bin (Release)" # ── bootloader ─────────────────────────────────────────────────────────────── @@ -138,20 +140,22 @@ hab-firmware-test-release: build-firmware-test-release hab-bootloader-debug: build-bootloader-debug #!/usr/bin/env bash set -euo pipefail + mkdir -p "{{BUILD_DIR}}/Debug" cd "{{TOOLS_DIR}}/hab" uv run nxpimage hab export --force \ -c hab_bootloader_debug.yaml \ - -o "../../../{{BUILD_DIR}}/Debug/bootloader_hab.bin" + -o "{{BUILD_DIR}}/Debug/bootloader_hab.bin" echo " ✅ bootloader_hab.bin (Debug)" [group('hab_image_gen')] hab-bootloader-release: build-bootloader-release #!/usr/bin/env bash set -euo pipefail + mkdir -p "{{BUILD_DIR}}/Release" cd "{{TOOLS_DIR}}/hab" uv run nxpimage hab export --force \ -c hab_bootloader_release.yaml \ - -o "../../../{{BUILD_DIR}}/Release/bootloader_hab.bin" + -o "{{BUILD_DIR}}/Release/bootloader_hab.bin" echo " ✅ bootloader_hab.bin (Release)" # ── app ─────────────────────────────────────────────────────────────────────── @@ -159,20 +163,22 @@ hab-bootloader-release: build-bootloader-release hab-app-debug: build-app-debug #!/usr/bin/env bash set -euo pipefail + mkdir -p "{{BUILD_DIR}}/Debug" cd "{{TOOLS_DIR}}/hab" uv run nxpimage hab export --force \ -c hab_app_debug.yaml \ - -o "../../../{{BUILD_DIR}}/Debug/app_hab.bin" + -o "{{BUILD_DIR}}/Debug/app_hab.bin" echo " ✅ app_hab.bin (Debug)" [group('hab_image_gen')] hab-app-release: build-app-release #!/usr/bin/env bash set -euo pipefail + mkdir -p "{{BUILD_DIR}}/Release" cd "{{TOOLS_DIR}}/hab" uv run nxpimage hab export --force \ -c hab_app_release.yaml \ - -o "../../../{{BUILD_DIR}}/Release/app_hab.bin" + -o "{{BUILD_DIR}}/Release/app_hab.bin" echo " ✅ app_hab.bin (Release)" # ── все образы сразу ───────────────────────────────────────────────────────── @@ -190,14 +196,13 @@ hab-all-release: hab-firmware-test-release hab-bootloader-release hab-app-releas hab-verify project="firmware_test" type="release": #!/usr/bin/env bash set -euo pipefail - ROOT="$(pwd)" case "{{project}}-{{type}}" in - firmware_test-debug) BIN="${ROOT}/{{BUILD_DIR}}/Debug/firmware_test_hab.bin" ;; - firmware_test-release) BIN="${ROOT}/{{BUILD_DIR}}/Release/firmware_test_hab.bin" ;; - bootloader-debug) BIN="${ROOT}/{{BUILD_DIR}}/Debug/bootloader_hab.bin" ;; - bootloader-release) BIN="${ROOT}/{{BUILD_DIR}}/Release/bootloader_hab.bin" ;; - app-debug) BIN="${ROOT}/{{BUILD_DIR}}/Debug/app_hab.bin" ;; - app-release) BIN="${ROOT}/{{BUILD_DIR}}/Release/app_hab.bin" ;; + 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" diff --git a/just/host.just b/just/host.just index 82ddef3..bb8f994 100644 --- a/just/host.just +++ b/just/host.just @@ -15,6 +15,9 @@ TOOLS_DIR := env('TOOLS_DIR', justfile_directory() / 'tools/host') CACHE_DIR := env('CACHE_DIR', justfile_directory() / '.cache') +_uname := `uname -s` +_venv := if _uname =~ "MINGW|MSYS|CYGWIN" { ".venv-host-win" } else { ".venv-host" } + # === Минимальные версии зависимостей === JUST_MIN := "1.36.0" UV_MIN := "0.4.0" @@ -114,7 +117,7 @@ check-deps: check_tool "just" "just" "{{JUST_MIN}}" check_tool "uv" "uv" "{{UV_MIN}}" - check_tool "docker" "docker" "{{DOCKER_MIN}}" version --format '{{{{.Client.Version}}}}' + check_tool "docker" "docker" "{{DOCKER_MIN}}" if [[ "${PLATFORM}" == "linux" ]]; then echo "" @@ -253,7 +256,7 @@ flash project type="release": echo " Valid: debug, release" exit 1 ;; esac - cd "{{TOOLS_DIR}}" && uv run python3 flash_usb.py \ + cd "{{TOOLS_DIR}}" && UV_PROJECT_ENVIRONMENT={{_venv}} uv run python flash_usb.py \ --firmware "{{project}}" --build-type "${BUILD_TYPE}" # Загрузить образ в RAM без записи во Flash. @@ -271,7 +274,7 @@ flash-ram project type="debug": echo " Valid: debug, release" exit 1 ;; esac - cd "{{TOOLS_DIR}}" && uv run python3 flash_usb.py \ + cd "{{TOOLS_DIR}}" && UV_PROJECT_ENVIRONMENT={{_venv}} uv run python flash_usb.py \ --firmware "{{project}}" --build-type "${BUILD_TYPE}" --ram-only # Псевдонимы для частых сценариев ──────────────────────────────────────────── @@ -302,7 +305,7 @@ 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 <- раскомментить когда готово + # cd tools/hil && UV_PROJECT_ENVIRONMENT={{_venv}} uv run python run_hil.py <- раскомментить когда готово echo " ⚠️ HIL tests not yet implemented" # Финальная прошивка: загрузчик + основная прошивка (Release) @@ -317,17 +320,17 @@ production: flash-production # Найти подключённые NXP USB-устройства [group('util')] scan: - cd "{{TOOLS_DIR}}" && uv run nxpdevscan + cd "{{TOOLS_DIR}}" && UV_PROJECT_ENVIRONMENT={{_venv}} uv run nxpdevscan # Проверить связь с BootROM через SDP (плата в SDP-режиме) [group('util'), no-cd] sdp-status: - cd "{{TOOLS_DIR}}" && uv run sdphost -u 0x1FC9,0x0130 -- error-status + cd "{{TOOLS_DIR}}" && UV_PROJECT_ENVIRONMENT={{_venv}} uv run sdphost -u 0x1FC9,0x0130 -- error-status # Проверить что Flashloader отвечает (после jump-address) [group('util')] flashloader-status: - cd "{{TOOLS_DIR}}" && uv run blhost -u 0x15A2,0x0073 -- get-property 1 0 + cd "{{TOOLS_DIR}}" && UV_PROJECT_ENVIRONMENT={{_venv}} uv run blhost -u 0x15A2,0x0073 -- get-property 1 0 # Обновить spsdk до новой версии # Использование: just upgrade-tools 3.8.0 diff --git a/tools/host/flash_usb.py b/tools/host/flash_usb.py index 8564e95..c0c4fc5 100644 --- a/tools/host/flash_usb.py +++ b/tools/host/flash_usb.py @@ -17,35 +17,56 @@ flash_usb.py — прошивка MIMXRT1052 через USB (BootROM SDP → Fla → blhost стирает нужный регион Flash → blhost пишет HAB образ начиная с 0x60002000 → blhost reset + +Конфигурация: + Переменные окружения (задаются в .env, экспортируются через just): + BOOTROM_VID — VID BootROM SDP (default: 1fc9) + BOOTROM_PID — PID BootROM SDP (default: 0130) + FLASHLOADER_VID — VID Flashloader (default: 15a2) + FLASHLOADER_PID — PID Flashloader (default: 0073) + BUILD_DIR — путь к директории сборки (default: /build) """ import argparse +import os import subprocess import sys import time from pathlib import Path -# ─── Константы ──────────────────────────────────────────────────────────────── -SCRIPT_DIR = Path(__file__).parent.resolve() -REPO_ROOT = SCRIPT_DIR.parent.parent -BUILD_DIR = REPO_ROOT / "build" -FLASHLOADER = SCRIPT_DIR / "dcd" / "ivt_flashloader.bin" +# ─── Пути ───────────────────────────────────────────────────────────────────── +SCRIPT_DIR = Path(__file__).parent.resolve() +REPO_ROOT = SCRIPT_DIR.parent.parent +FLASHLOADER = SCRIPT_DIR / "dcd" / "ivt_flashloader.bin" -# USB VID:PID -SDP_USB = "0x1FC9,0x0130" # BootROM Serial Download Protocol -BLHOST_USB = "0x15A2,0x0073" # Flashloader запущен +# BUILD_DIR: берём из окружения (just экспортирует из .env как абсолютный путь), +# fallback — рассчитываем от расположения скрипта +BUILD_DIR = Path(os.environ.get("BUILD_DIR", str(REPO_ROOT / "build"))) +# ─── USB VID:PID — из окружения (.env → just set export → uv run) ───────────── +def _usb(vid_key: str, vid_default: str, pid_key: str, pid_default: str) -> str: + + vid = os.environ.get(vid_key, vid_default).strip().upper().lstrip("0X") + pid = os.environ.get(pid_key, pid_default).strip().upper().lstrip("0X") + return f"0x{vid},0x{pid}" + +SDP_USB = _usb("BOOTROM_VID", "1fc9", "BOOTROM_PID", "0130") # BootROM SDP +BLHOST_USB = _usb("FLASHLOADER_VID","15a2", "FLASHLOADER_PID","0073") # Flashloader + +# ─── Аппаратные константы (часть логики прошивки, не конфигурация) ──────────── # FlexSPI NOR config option word: 0xC0000007 # bits[31:28]=0xC — tag (QuadSPI NOR) # bits[3:0]=0x7 — option size FLEXSPI_OPTION_ADDR = "0x2000" FLEXSPI_OPTION_VALUE = "0xC0000007" -FLEXSPI_MEMORY_ID = "9" # FlexSPI NOR memory interface ID +FLEXSPI_MEMORY_ID = "9" # FlexSPI NOR memory interface ID + +# Option word для записи FCB: tag=0xF → Write FCB command +FLEXSPI_FCB_VALUE = "0xF000000F" # Flash layout -FLASH_BASE = 0x60000000 -HAB_OFFSET = 0x1000 # IVT offset: HAB binary starts with IVT at byte 0 - # write address = FLASH_BASE + ivtOffset = 0x60001000 +FLASH_BASE = 0x60000000 +HAB_OFFSET = 0x1000 # IVT offset: write address = FLASH_BASE + HAB_OFFSET def run(cmd: list[str], check: bool = True) -> subprocess.CompletedProcess: @@ -64,7 +85,7 @@ def step(msg: str) -> None: def wait_for_flashloader(timeout: int = 10) -> bool: - """Ждём пока Flashloader поднимется (VID:PID 15A2:0073)""" + """Ждём пока Flashloader поднимется""" print(f"\n Ожидание Flashloader (до {timeout}с)...", end="", flush=True) for i in range(timeout): time.sleep(1) @@ -82,7 +103,6 @@ def wait_for_flashloader(timeout: int = 10) -> bool: def load_flashloader() -> None: """Загружает Flashloader через SDP если ещё не запущен""" - # Проверяем — вдруг уже запущен result = subprocess.run( ["blhost", "-u", BLHOST_USB, "-j", "--", "get-property", "1", "0"], capture_output=True @@ -108,11 +128,6 @@ def load_flashloader() -> None: sys.exit(1) -# Option word для записи FCB в Flash[0x60000000] -# 0xF000000F: tag=0xF → Write FCB command (Flashloader пишет готовый FCB в начало Flash) -FLEXSPI_FCB_VALUE = "0xF000000F" - - def configure_flexspi() -> None: """Инициализирует FlexSPI NOR контроллер через Flashloader""" step("Конфигурация FlexSPI NOR (инициализация контроллера)") @@ -124,7 +139,7 @@ def configure_flexspi() -> None: def write_fcb() -> None: """Записывает Flash Configuration Block в 0x60000000 - + Отдельный шаг после erase! Flashloader генерирует FCB из параметров FlexSPI и пишет его по адресу 0x60000000. Без FCB BootROM не знает как читать Flash. Option word 0xF000000F: tag=0xF → Write FCB command. @@ -144,7 +159,6 @@ def flash(hab_bin: Path, ram_only: bool = False) -> None: if ram_only: step(f"Загрузка в RAM (без записи во Flash): {hab_bin.name}") - # RAM-only: грузим в RAM прямо через SDP без flashloader run(["sdphost", "-u", SDP_USB, "-j", "--", "write-file", f"0x{FLASH_BASE + HAB_OFFSET:08X}", str(hab_bin)]) run(["sdphost", "-u", SDP_USB, "-j", "--", @@ -152,10 +166,9 @@ def flash(hab_bin: Path, ram_only: bool = False) -> None: return load_flashloader() - configure_flexspi() # 0xC0000007 — инициализация FlexSPI контроллера + configure_flexspi() write_addr = f"0x{FLASH_BASE + HAB_OFFSET:08X}" - # Размер стирания: HAB_OFFSET (0x1000) + размер образа, выровнено вверх до 4KB erase_size = ((HAB_OFFSET + hab_bin.stat().st_size + 0xFFF) // 0x1000) * 0x1000 step(f"Прошивка Flash: {hab_bin.name}") @@ -164,14 +177,12 @@ def flash(hab_bin: Path, ram_only: bool = False) -> None: print(f" Адрес: {write_addr}") print(f" Стирание: 0x{FLASH_BASE:08X} .. +{erase_size} байт") - # memoryId=0 — mapped (XIP) адресное пространство run(["blhost", "-u", BLHOST_USB, "--", "flash-erase-region", f"0x{FLASH_BASE:08X}", str(erase_size), "0"]) - write_fcb() # 0xF000000F — запись FCB в 0x60000000 (после erase!) + write_fcb() - # memoryId=0 — mapped (XIP) адресное пространство run(["blhost", "-u", BLHOST_USB, "--", "write-memory", write_addr, str(hab_bin), "0"]) @@ -181,6 +192,9 @@ def flash(hab_bin: Path, ram_only: bool = False) -> None: def main() -> None: + print(f"DEBUG SDP_USB = {repr(SDP_USB)}") + print(f"DEBUG BLHOST_USB = {repr(BLHOST_USB)}") + print(f"DEBUG BUILD_DIR = {repr(BUILD_DIR)}") parser = argparse.ArgumentParser(description="Прошивка MIMXRT1052 через USB") parser.add_argument("--firmware", required=True, choices=["firmware_test", "bootloader", "app"], @@ -192,18 +206,18 @@ def main() -> None: help="Загрузить в RAM без записи во Flash") args = parser.parse_args() - # HAB образ собирается командой: nxpimage hab export -c hab_.yaml - # Выходной файл: build//_hab.bin hab_bin = BUILD_DIR / args.build_type / f"{args.firmware}_hab.bin" print(f"\n{'═'*60}") print(f" MIMXRT1052 Flash Tool") - print(f" Прошивка: {args.firmware} [{args.build_type}]") - print(f" Образ: {hab_bin}") + print(f" Прошивка: {args.firmware} [{args.build_type}]") + print(f" Образ: {hab_bin}") + print(f" SDP USB: {SDP_USB}") + print(f" BL USB: {BLHOST_USB}") print(f"{'═'*60}") flash(hab_bin, ram_only=args.ram_only) if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/tools/host/pyproject.toml b/tools/host/pyproject.toml index 59e2831..6d74ff2 100644 --- a/tools/host/pyproject.toml +++ b/tools/host/pyproject.toml @@ -7,3 +7,4 @@ requires-python = ">=3.10" dependencies = [ "spsdk==3.7.0", ] +