# 6
Кастомный скрипт в tools/host для прошивки bare-metal платы
This commit is contained in:
parent
853eda2d08
commit
3940947963
19 changed files with 640 additions and 952 deletions
|
|
@ -16,8 +16,9 @@ set_source_files_properties(board/board.c board/pin_mux.c
|
|||
PROPERTIES COMPILE_OPTIONS "-w")
|
||||
# Дефайны PUBLIC — транзитивно подтянутся во все firmware таргеты TODO: для
|
||||
# каждого приложения свои дефайны
|
||||
target_compile_definitions(bsp_board PUBLIC CPU_MIMXRT1052CVJ5B
|
||||
SKIP_SYSCLK_INIT)
|
||||
target_compile_definitions(
|
||||
bsp_board PUBLIC CPU_MIMXRT1052CVJ5B SKIP_SYSCLK_INIT XIP_EXTERNAL_FLASH=1
|
||||
XIP_BOOT_HEADER_ENABLE=1 XIP_BOOT_HEADER_DCD_ENABLE=1)
|
||||
|
||||
# Сценарий A: XIP — код исполняется прямо из Flash
|
||||
add_library(bsp_boot_xip INTERFACE)
|
||||
|
|
|
|||
223
docs/HAB_GUIDE.md
Normal file
223
docs/HAB_GUIDE.md
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
# High Assurance Boot (HAB) — Принцип работы и применение на MIMXRT1052
|
||||
|
||||
## 1. Что такое HAB
|
||||
|
||||
**High Assurance Boot (HAB)** — механизм безопасной загрузки, встроенный в BootROM микроконтроллеров семейства NXP i.MX/RT. Его цель — криптографически гарантировать, что на чипе запустится только авторизованный код.
|
||||
|
||||
HAB работает на уровне кремния: BootROM прошит на заводе и не может быть изменён. Вся цепочка доверия строится от него.
|
||||
|
||||
---
|
||||
|
||||
## 2. Угрозы без HAB
|
||||
|
||||
Без механизма защищённой загрузки любой, имеющий физический доступ к устройству, может:
|
||||
|
||||
- подключить программатор и залить произвольную прошивку
|
||||
- загрузить вредоносный код через USB в режиме Serial Download Protocol (SDP)
|
||||
- модифицировать прошивку во Flash между производством и эксплуатацией
|
||||
|
||||
HAB устраняет эти угрозы на аппаратном уровне.
|
||||
|
||||
---
|
||||
|
||||
## 3. Структура HAB-образа
|
||||
|
||||
HAB-образ — это не просто бинарник прошивки, а обёртка с метаданными вокруг него. BootрOM ищет обязательные структуры по фиксированным смещениям.
|
||||
|
||||
### Карта памяти Flash (FlexSPI NOR, XIP)
|
||||
|
||||
```bash
|
||||
0x60000000 ┌──────────────────────────────────┐
|
||||
│ FCB — Flash Configuration Block │ 512 байт
|
||||
│ (конфигурация FlexSPI контроллера│
|
||||
│ записывается Flashloader'ом) │
|
||||
0x60001000 ├──────────────────────────────────┤
|
||||
│ IVT — Image Vector Table │ 32 байта
|
||||
0x60001020 ├──────────────────────────────────┤
|
||||
│ BDT — Boot Data Table │ 12 байт
|
||||
0x60001030 ├──────────────────────────────────┤
|
||||
│ DCD — Device Config Data │ опционально
|
||||
│ (инициализация SDRAM и др.) │
|
||||
├──────────────────────────────────┤
|
||||
0x60003000 │ Код прошивки (.text, .data, ...) │
|
||||
│ │
|
||||
├──────────────────────────────────┤
|
||||
│ CSF — Command Sequence File │ только в signed-образе
|
||||
│ (подпись, ключи, хэши) │
|
||||
└──────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.1 IVT — Image Vector Table
|
||||
|
||||
Главная точка входа для BootROM. Структура фиксирована, 32 байта, тег `0xD1`:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint32_t header; // тег 0xD1, длина, версия
|
||||
uint32_t entry; // адрес Reset_Handler прошивки
|
||||
uint32_t reserved1;
|
||||
uint32_t dcd; // адрес DCD (0 если не нужен)
|
||||
uint32_t boot_data; // адрес BDT
|
||||
uint32_t self; // адрес самого IVT
|
||||
uint32_t csf; // адрес CSF (0 если unsigned)
|
||||
uint32_t reserved2;
|
||||
} ivt_t;
|
||||
```
|
||||
|
||||
BootROM находит IVT по фиксированному смещению от начала Flash (`ivtOffset = 0x1000` для FlexSPI NOR), читает его и получает адреса всех остальных структур.
|
||||
|
||||
### 3.2 BDT — Boot Data Table
|
||||
|
||||
Сообщает BootROM, где начинается образ и каков его полный размер:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint32_t start; // 0x60000000 — начало образа во Flash
|
||||
uint32_t length; // полный размер в байтах
|
||||
uint32_t plugin; // 0 для обычного образа
|
||||
} boot_data_t;
|
||||
```
|
||||
|
||||
### 3.3 DCD — Device Configuration Data
|
||||
|
||||
Опциональная секция с командами инициализации периферии, которые BootROM выполняет **до** передачи управления прошивке. Типичное применение — инициализация SDRAM (SEMC), чтобы к моменту старта первой инструкции прошивки память уже была доступна.
|
||||
|
||||
DCD содержит последовательность команд двух типов:
|
||||
|
||||
| Команда | Тег | Назначение |
|
||||
|---|---|---|
|
||||
| `Write Data` | `0xCC` | Записать значение по адресу |
|
||||
| `Check Bits Set` | `0xCF` | Ждать пока бит станет `1` (poll) |
|
||||
|
||||
Формат бинарника: `Tag(1) | Length(2 BE) | Parameter(1) | данные`.
|
||||
|
||||
### 3.4 CSF — Command Sequence File
|
||||
|
||||
Криптографическая секция. Присутствует только в подписанных образах. Содержит:
|
||||
|
||||
- таблицу публичных ключей (SRK table)
|
||||
- хэши защищённых регионов образа (IVT, BDT, код)
|
||||
- подпись RSA 2048/4096 или ECDSA
|
||||
|
||||
---
|
||||
|
||||
## 4. Режимы HAB
|
||||
|
||||
### 4.1 По типу образа
|
||||
|
||||
| Режим | `flags` | CSF | Применение |
|
||||
|---|---|---|---|
|
||||
| Unsigned | `0x00` | отсутствует | разработка, отладка |
|
||||
| Signed | `0x08` | RSA/ECDSA подпись | производство |
|
||||
| Signed + Encrypted | `0x0C` | подпись + шифрование кода | максимальная защита |
|
||||
|
||||
### 4.2 По состоянию чипа (OTP fuse)
|
||||
|
||||
Независимо от типа образа, чип может находиться в одном из двух состояний:
|
||||
|
||||
**HAB Open** (fuse `SEC_CONFIG = 0`, заводское состояние):
|
||||
BootROM проверяет подпись, но **игнорирует ошибки** — запуск происходит в любом случае. Это позволяет работать с unsigned образами на этапе разработки.
|
||||
|
||||
**HAB Closed** (fuse `SEC_CONFIG = 1`):
|
||||
При ошибке проверки подписи BootROM **отказывает в запуске** безвозвратно. Запись fuse необратима — чип нельзя вернуть в Open состояние.
|
||||
|
||||
---
|
||||
|
||||
## 5. Цепочка доверия (Trust Chain)
|
||||
|
||||
```bash
|
||||
Производитель устройства
|
||||
│
|
||||
├── Генерирует пару ключей RSA 4096
|
||||
│ private.pem → хранится в защищённом месте (HSM / vault)
|
||||
│ public.pem → включается в образ (SRK table)
|
||||
│
|
||||
├── SHA256(public.pem) записывается в OTP fuses (SRK_HASH)
|
||||
│ ↑ необратимо, выполняется один раз при производстве
|
||||
│
|
||||
└── При сборке каждого релиза:
|
||||
nxpimage hab export (flags=0x08)
|
||||
→ хэшируются IVT + BDT + код
|
||||
→ подписывается private.pem
|
||||
→ CSF встраивается в образ
|
||||
|
||||
При загрузке на чипе:
|
||||
BootROM
|
||||
→ читает IVT → находит CSF
|
||||
→ извлекает public key из CSF
|
||||
→ SHA256(public key) == SRK_HASH из fuses? → ДА
|
||||
→ проверяет подпись хэшей регионов → ОК
|
||||
→ прыгает на entry point
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Почему Unsigned-образ требует HAB-контейнер
|
||||
|
||||
Распространённое заблуждение: "раз образ без подписи, зачем вообще HAB?". Ответ прост:
|
||||
|
||||
**BootROM всегда ищет IVT по фиксированному смещению.** Это не опциональная безопасность — это формат загрузки. Без IVT и BDT BootROM не знает:
|
||||
|
||||
- куда прыгать (entry point)
|
||||
- где находится DCD для инициализации SDRAM
|
||||
- каков размер образа
|
||||
|
||||
`nxpimage hab export` для unsigned образа выполняет именно эту работу — добавляет IVT, BDT, встраивает DCD — без какой-либо криптографии. CSF при этом равен нулю.
|
||||
|
||||
---
|
||||
|
||||
## 7. Процесс прошивки MIMXRT1052 (USB, без JTAG)
|
||||
|
||||
BootROM в режиме SDP умеет только писать в RAM и прыгать. Для записи во Flash необходим **Flashloader** — специальная программа от NXP.
|
||||
|
||||
```
|
||||
Плата в SDP режиме (BOOT_MOD_1 = 3V3)
|
||||
│
|
||||
│ sdphost -u 0x1FC9,0x0130
|
||||
├─ write-file 0x20001C00 ivt_flashloader.bin
|
||||
└─ jump-address 0x20001C00
|
||||
│
|
||||
│ (ожидание ~3с инициализации)
|
||||
│
|
||||
Flashloader запущен
|
||||
│ blhost -u 0x15A2,0x0073
|
||||
├─ fill-memory 0x2000 4 0xC0000007 ← option word для FlexSPI NOR
|
||||
├─ configure-memory 9 0x2000 ← Flashloader пишет FCB в Flash
|
||||
├─ flash-erase-region 0x60000000 …
|
||||
├─ write-memory 0x60002000 firmware_hab.bin ← HAB образ
|
||||
└─ reset
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Жизненный цикл для проекта TFT
|
||||
|
||||
| Стадия | Режим HAB | Подпись | Fuse |
|
||||
|---|---|---|---|
|
||||
| Разработка | Open | Unsigned (`0x00`) | не трогаем |
|
||||
| Входной контроль | Open | Unsigned (`0x00`) | не трогаем |
|
||||
| Предсерийные образцы | Open | Signed (`0x08`) | не трогаем |
|
||||
| Серийное производство | **Closed** | Signed (`0x08`) | `SEC_CONFIG = 1` |
|
||||
|
||||
> ⚠️ Запись `SEC_CONFIG = 1` необратима. Перед закрытием HAB необходимо убедиться, что подписанный образ успешно проходит верификацию на реальном железе.
|
||||
|
||||
---
|
||||
|
||||
## 9. Инструменты
|
||||
|
||||
| Инструмент | Назначение |
|
||||
|---|---|
|
||||
| `nxpimage hab export` | Сборка HAB-образа (IVT + BDT + DCD + опционально CSF) |
|
||||
| `nxpimage hab parse` | Разбор готового образа для проверки |
|
||||
| `sdphost` | Связь с BootROM в SDP режиме (запись в RAM, прыжок) |
|
||||
| `blhost` | Связь с Flashloader (запись во Flash, конфигурация памяти) |
|
||||
| `nxpdevscan` | Обнаружение подключённых NXP устройств |
|
||||
|
||||
---
|
||||
|
||||
## 10. Литература
|
||||
|
||||
- **NXP AN12263** — "HAB Code Signing for i.MX RT" — пошаговая инструкция по подписи образов
|
||||
- **MIMXRT1052 Reference Manual** — Chapter 9: System Boot (структуры IVT, BDT, DCD, CSF)
|
||||
- [**SPSDK Documentation**](https://spsdk.readthedocs.io) — документация по nxpimage, blhost, sdphost
|
||||
- **NXP Application Note AN4581** — "Secure Boot on i.MX50, i.MX53, and i.MX 6 Series"
|
||||
|
|
@ -13,7 +13,6 @@ target_sources(firmware_test PRIVATE main.c
|
|||
target_link_libraries(
|
||||
firmware_test
|
||||
PRIVATE bsp_board
|
||||
bsp_boot_xip
|
||||
# SDK драйверы периферии — добавляй по мере написания тестов
|
||||
sdk_lpuart # UART тест
|
||||
sdk_flexcan # CAN тест
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
#include "fsl_gpio.h"
|
||||
#include "pin_mux.h"
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Definitions
|
||||
******************************************************************************/
|
||||
|
|
|
|||
253
tools/README.md
253
tools/README.md
|
|
@ -1,164 +1,257 @@
|
|||
# Host Tools — окружение для прошивки
|
||||
# tools/host — Окружение прошивки MIMXRT1052CVJ5B
|
||||
|
||||
Изолированное Python-окружение на базе [uv](https://docs.astral.sh/uv/).
|
||||
Устанавливается на хост-машине разработчика — **не внутри devcontainer**.
|
||||
Изолированное Python-окружение на базе [uv](https://docs.astral.sh/uv/) для сборки
|
||||
HAB-образов и прошивки платы через USB. Запускается на хост-машине разработчика —
|
||||
**не внутри devcontainer**.
|
||||
|
||||
---
|
||||
|
||||
## Структура
|
||||
|
||||
```bash
|
||||
tools/host/
|
||||
├── dcd/
|
||||
│ ├── dcd_mt48lc16m16a2.cfg ← источник истины для DCD (коммитить)
|
||||
│ ├── dcd_compiler.py ← скрипт: .cfg → .bin
|
||||
│ └── dcd.bin ← артефакт (пересобирается из .cfg)
|
||||
│
|
||||
├── fdcb/
|
||||
│ ├── fcb_w25q64_quad.yaml ← конфиг FCB: W25Q64, Quad I/O, 133 MHz
|
||||
│ └── fcb.bin ← артефакт (пересобирается из .yaml)
|
||||
│
|
||||
│ ├── dcd.bin ← DCD бинарник (инициализация SDRAM, коммитить как есть)
|
||||
│ └── ivt_flashloader.bin ← NXP Flashloader (коммитить)
|
||||
├── hab/
|
||||
│ ├── hab_firmware_test.yaml ← HAB: Debug + DCD (входной контроль)
|
||||
│ ├── hab_bootloader.yaml ← HAB: Release, без DCD
|
||||
│ └── hab_app.yaml ← HAB: Release + DCD (боевая прошивка)
|
||||
│
|
||||
├── image/
|
||||
│ ├── bootimg_firmware_test.yaml
|
||||
│ ├── bootimg_bootloader.yaml
|
||||
│ └── bootimg_app.yaml
|
||||
│
|
||||
├── pyproject.toml ← зависимости (spsdk, версия зафиксирована)
|
||||
├── flash_usb.py ← скрипт прошивки через USB
|
||||
├── pyproject.toml ← зависимости (spsdk==3.7.x)
|
||||
├── uv.lock ← lockfile (коммитить)
|
||||
├── .python-version
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Установка uv (один раз на машину)
|
||||
## Предварительные требования
|
||||
|
||||
### 1. uv — один раз на машину
|
||||
|
||||
```bash
|
||||
# Linux/macOS
|
||||
# macOS / Linux
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
|
||||
# Windows
|
||||
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
|
||||
```
|
||||
|
||||
## Первый запуск (один раз на репозиторий)
|
||||
### 2. Зависимости проекта — один раз на репозиторий
|
||||
|
||||
```bash
|
||||
cd tools/host
|
||||
uv sync
|
||||
```
|
||||
|
||||
### udev правило для USB SDP — только Linux, один раз на машину
|
||||
### 3. udev правила — только Linux, один раз на машину
|
||||
|
||||
Без этого правила `blhost` требует `sudo`:
|
||||
Без этого для работы с USB нужен `sudo`:
|
||||
|
||||
```bash
|
||||
echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1fc9", ATTR{idProduct}=="0135", MODE="0666", GROUP="plugdev"' \
|
||||
| sudo tee /etc/udev/rules.d/99-nxp-sdp.rules
|
||||
sudo tee /etc/udev/rules.d/99-nxp-mimxrt.rules << 'EOF'
|
||||
# NXP BootROM — SDP режим (BOOT_MOD_1 = 3V3)
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="1fc9", ATTR{idProduct}=="0130", MODE="0666", GROUP="plugdev"
|
||||
# NXP Flashloader — после jump-address
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="15a2", ATTR{idProduct}=="0073", MODE="0666", GROUP="plugdev"
|
||||
EOF
|
||||
|
||||
sudo udevadm control --reload-rules && sudo udevadm trigger
|
||||
sudo usermod -a -G plugdev $USER
|
||||
# Перелогиниться после usermod!
|
||||
# После usermod — перелогиниться!
|
||||
```
|
||||
|
||||
## Пайплайн сборки загрузочного образа
|
||||
---
|
||||
|
||||
### Шаг 1 — DCD (пересобирать при изменении dcd_mt48lc16m16a2.cfg)
|
||||
## Карта Flash (W25Q64FVSSIG, 8 MB)
|
||||
|
||||
```bash
|
||||
cd tools/host/dcd
|
||||
uv run python3 dcd_compiler.py dcd_mt48lc16m16a2.cfg dcd.bin
|
||||
0x60000000 ┌─────────────────────────────┐
|
||||
│ FCB — Flash Config Block │ 512 байт
|
||||
│ (пишет Flashloader, │
|
||||
│ не входит в HAB образ) │
|
||||
0x60001000 ├─────────────────────────────┤
|
||||
│ IVT — Image Vector Table │ ← начало HAB образа
|
||||
│ BDT — Boot Data Table │
|
||||
0x60001040 ├─────────────────────────────┤
|
||||
│ DCD — SDRAM init │ ~1088 байт (для fw_test и app)
|
||||
0x60003000 ├─────────────────────────────┤
|
||||
│ Код прошивки │
|
||||
│ (.text, .data, ...) │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
### Шаг 2 — FCB (пересобирать при изменении fcb_w25q64_quad.yaml)
|
||||
---
|
||||
|
||||
## Пайплайн: сборка HAB образа
|
||||
|
||||
```bash
|
||||
cd tools/host/fdcb
|
||||
uv run nxpimage bootable-image fcb export -c fcb_w25q64_quad.yaml -o fcb.bin
|
||||
```
|
||||
cd tools/host/hab
|
||||
|
||||
### Шаг 3 — Загрузочный образ
|
||||
|
||||
```bash
|
||||
cd tools/host/image
|
||||
|
||||
# firmware_test (Debug, входной контроль)
|
||||
uv run nxpimage bootable-image export -c bootimg_firmware_test.yaml
|
||||
# firmware_test (Debug) — входной контроль платы
|
||||
uv run nxpimage hab export --force \
|
||||
-c hab_firmware_test.yaml \
|
||||
-o ../../../../build/Debug/firmware_test_hab.bin
|
||||
|
||||
# bootloader (Release)
|
||||
uv run nxpimage bootable-image export -c bootimg_bootloader.yaml
|
||||
uv run nxpimage hab export --force \
|
||||
-c hab_bootloader.yaml \
|
||||
-o ../../../../build/Release/bootloader_hab.bin
|
||||
|
||||
# app (Release, боевая прошивка)
|
||||
uv run nxpimage bootable-image export -c bootimg_app.yaml
|
||||
# app (Release) — боевая прошивка
|
||||
uv run nxpimage hab export --force \
|
||||
-c hab_app.yaml \
|
||||
-o ../../../../build/Release/app_hab.bin
|
||||
```
|
||||
|
||||
Артефакты попадают в `build/Debug/` и `build/Release/` соответственно.
|
||||
|
||||
### Проверка образа
|
||||
### Проверка образа после сборки
|
||||
|
||||
```bash
|
||||
cd tools/host/image
|
||||
uv run nxpimage bootable-image parse \
|
||||
-b ../../../../build/Debug/firmware_test_bootable.bin \
|
||||
-f mimxrt1050
|
||||
uv run nxpimage hab parse -b ../../../../build/Debug/firmware_test_hab.bin
|
||||
```
|
||||
|
||||
В выводе должны присутствовать секции `FCB`, `IVT`, `BDT`, `DCD`, `App`.
|
||||
Ожидаемый результат: IVT с корректным `entry`, DCD с тегом `0xD2`,
|
||||
`csf = 0x00000000` (unsigned).
|
||||
|
||||
## Прошивка через USB (BootROM SDP режим)
|
||||
---
|
||||
|
||||
Перевести плату в SDP режим: `BOOT_MOD_1 = 1` (подтянуть к 3V3), затем подать питание или нажать Reset.
|
||||
## Пайплайн: прошивка через USB
|
||||
|
||||
### Подготовка платы
|
||||
|
||||
Перевести плату в режим SDP (Serial Download Protocol):
|
||||
|
||||
1. Подтянуть `BOOT_MOD_1` к `3V3`
|
||||
2. Reset
|
||||
3. Подключить USB к ПК
|
||||
|
||||
В этом режиме плата определяется как `VID:PID = 1FC9:0130`.
|
||||
|
||||
### Запуск скрипта
|
||||
|
||||
```bash
|
||||
cd tools/host
|
||||
|
||||
# Проверить что устройство определилось (VID:PID 1FC9:0135)
|
||||
export VID_PID=0x1fc9:0x0130
|
||||
uv run nxpdevscan
|
||||
# firmware_test
|
||||
uv run python3 flash_usb.py --firmware firmware_test --build-type Debug
|
||||
|
||||
# Версия BootROM на таргете
|
||||
uv run blhost -u $VID_PID get-property 1
|
||||
# Доступные BootROM области памяти
|
||||
uv run blhost -u $VID_PID list-memory
|
||||
# app
|
||||
uv run python3 flash_usb.py --firmware app --build-type Release
|
||||
|
||||
# Прошить firmware_test
|
||||
uv run blhost -u $VID_PID flash-erase-all
|
||||
uv run blhost -u $VID_PID write-memory 0x60000000 \
|
||||
../../build/Debug/firmware_test_bootable.bin
|
||||
uv run blhost -u $VID_PID reset
|
||||
|
||||
# Загрузить в RAM без записи во Flash (для быстрой отладки)
|
||||
uv run blhost -u $VID_PID load-image ../../build/Debug/firmware_test_bootable.bin
|
||||
# bootloader
|
||||
uv run python3 flash_usb.py --firmware bootloader --build-type Release
|
||||
```
|
||||
|
||||
После прошивки вернуть `BOOT_MOD_1 = 0` (подтянуть к GND) для нормального старта из Flash.
|
||||
После прошивки: вернуть `BOOT_MOD_1` к `GND`, Reset — плата стартует из Flash.
|
||||
|
||||
## Удалённая прошивка (сервер)
|
||||
### Последовательность команд внутри скрипта
|
||||
|
||||
```bash
|
||||
# Из devcontainer или локально — копируем образ на сервер и прошиваем
|
||||
./tools/host/flash_remote.sh firmware_test Debug
|
||||
Плата в SDP режиме (1FC9:0130)
|
||||
│
|
||||
├─ sdphost write-file 0x20001C00 ivt_flashloader.bin
|
||||
└─ sdphost jump-address 0x20001C00
|
||||
│
|
||||
│ (ожидание до 10с пока Flashloader поднимется на 15A2:0073)
|
||||
│
|
||||
Flashloader (15A2:0073)
|
||||
│
|
||||
│ Шаг 1 — инициализация FlexSPI NOR контроллера
|
||||
├─ blhost fill-memory 0x2000 4 0xC0000007 word
|
||||
├─ blhost configure-memory 9 0x2000
|
||||
│
|
||||
│ Шаг 2 — стирание Flash
|
||||
├─ blhost flash-erase-region 0x60000000 <size> 0 (memoryId=0, XIP)
|
||||
│
|
||||
│ Шаг 3 — запись FCB в 0x60000000
|
||||
│ (ПОСЛЕ стирания! Flashloader генерирует FCB из параметров шага 1)
|
||||
├─ blhost fill-memory 0x2000 4 0xF000000F word
|
||||
├─ blhost configure-memory 9 0x2000
|
||||
│
|
||||
│ Шаг 4 — запись HAB образа начиная с 0x60001000
|
||||
├─ blhost write-memory 0x60001000 *_hab.bin 0 (memoryId=0, XIP)
|
||||
└─ blhost reset
|
||||
```
|
||||
|
||||
> **Почему FCB не в образе?**
|
||||
> Flashloader генерирует FCB автоматически из параметров FlexSPI (option word
|
||||
> `0xC0000007`). Запись FCB через `0xF000000F` — это отдельная команда,
|
||||
> выполняемая строго после `flash-erase-region`, иначе FCB будет затёрт следующим
|
||||
> стиранием.
|
||||
|
||||
---
|
||||
|
||||
## DCD — инициализация SDRAM
|
||||
|
||||
`dcd/dcd.bin` — бинарный блоб команд, который BootROM выполняет до передачи
|
||||
управления прошивке. Инициализирует PLL, CCM clock gates, SEMC контроллер
|
||||
и микросхему SDRAM (MT48LCxxM4).
|
||||
|
||||
Файл хранится в репозитории в бинарном виде и **не требует пересборки** — он
|
||||
меняется только при изменении схемотехники.
|
||||
|
||||
```bash
|
||||
Цель: MT48LC16M16A2P-6A, 32 MB, шина 16 бит, CS0
|
||||
SEMC BR0: base=0x80000000, size=32MB, VLD=1
|
||||
SEMC BR1–BR3: VLD=0 (один чип, один CS)
|
||||
```
|
||||
|
||||
Использование DCD по прошивкам:
|
||||
|
||||
| Прошивка | DCD | Причина |
|
||||
|---|---|---|
|
||||
| `firmware_test` | ✓ | тесты работают с SDRAM |
|
||||
| `app` | ✓ | FreeRTOS heap и буферы LCDIF размещены в SDRAM |
|
||||
| `bootloader` | ✗ | загрузчик не использует SDRAM, инициализация в app |
|
||||
|
||||
---
|
||||
|
||||
## Flashloader
|
||||
|
||||
`dcd/ivt_flashloader.bin` — NXP-программа, загружаемая в RAM через SDP.
|
||||
Предоставляет `blhost` доступ к Flash, которого нет через BootROM напрямую.
|
||||
|
||||
```bash
|
||||
Entry point: 0x20002401
|
||||
Загрузка по: 0x20001C00
|
||||
VID:PID после старта: 15A2:0073
|
||||
Источник: MCUXpresso Secure Provisioning Tool 25.12
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Диагностика
|
||||
|
||||
```bash
|
||||
# Список поддерживаемых семейств (проверка установки)
|
||||
uv run nxpimage bootable-image get-families
|
||||
# Найти подключённые NXP устройства
|
||||
uv run nxpdevscan
|
||||
|
||||
# Список доступных шаблонов для mimxrt1050
|
||||
uv run nxpimage bootable-image list-templates -f mimxrt1050
|
||||
# Проверить связь с BootROM через SDP
|
||||
sdphost -u 0x1FC9,0x0130 -- error-status
|
||||
|
||||
# Версия SPSDK
|
||||
# Проверить что Flashloader отвечает (после jump-address)
|
||||
blhost -u 0x15A2,0x0073 -- get-property 1 0
|
||||
|
||||
# Версии инструментов
|
||||
uv run nxpimage --version
|
||||
uv run blhost --version
|
||||
```
|
||||
|
||||
### Типичные ошибки
|
||||
|
||||
| Симптом | Причина | Решение |
|
||||
|---|---|---|
|
||||
| `USB HID device not found: 1FC9:0130` | Плата не в SDP режиме | Проверить `BOOT_MOD_1` → 3V3 и Reset |
|
||||
| Flashloader timeout после jump | `ivt_flashloader.bin` повреждён или не тот | Взять из SPT 25.12 |
|
||||
| Плата не стартует после прошивки | `BOOT_MOD_1` не переключён обратно | Вернуть `BOOT_MOD_1` → GND, Reset |
|
||||
| Плата зависает сразу после старта | DCD завис (неверный `dcd.bin`) | Использовать только верифицированный `dcd.bin` |
|
||||
| Плата стартует, периферия не работает | CCM clock gates в DCD отключают нужные клоки | Проверить `dcd.bin` — только верифицированный вариант |
|
||||
|
||||
---
|
||||
|
||||
## Обновление зависимостей
|
||||
|
||||
```bash
|
||||
cd tools/host
|
||||
uv add "spsdk==X.Y.Z"
|
||||
uv sync
|
||||
git add uv.lock
|
||||
git add uv.lock pyproject.toml
|
||||
```
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,216 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
dcd_cfg_to_bin.py — конвертация DCD .cfg (NXP Config Tools) в .bin (BootROM формат)
|
||||
|
||||
Формат DCD бинарника (IMX RT RM, Chapter 9.7):
|
||||
[Header]
|
||||
0xD2 — DCD tag
|
||||
length[1:0] — общая длина в байтах (big-endian, 2 байта)
|
||||
0x41 — version
|
||||
|
||||
[Commands] — последовательно:
|
||||
Write Data (0xCC):
|
||||
0xCC tag, length BE16, parameter (ширина: 4=word)
|
||||
[addr BE32, value BE32] × N — группируются подряд идущие записи
|
||||
|
||||
Check Bits Set (0xCF) — wait_until (expr & mask) != 0:
|
||||
0xCF tag, 0x000C length, parameter (4=word)
|
||||
addr BE32, mask BE32, count BE32 (0 = бесконечно)
|
||||
|
||||
Использование:
|
||||
python3 dcd_cfg_to_bin.py dcd.cfg dcd.bin
|
||||
python3 dcd_cfg_to_bin.py dcd.cfg # выведет dcd.bin рядом с cfg
|
||||
"""
|
||||
|
||||
import re
|
||||
import struct
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Union
|
||||
|
||||
# ─── Константы DCD ────────────────────────────────────────────────────────────
|
||||
DCD_TAG = 0xD2
|
||||
DCD_VERSION = 0x41
|
||||
CMD_WRITE = 0xCC
|
||||
CMD_CHECK_SET = 0xCF # wait_until (reg & mask) != 0
|
||||
CMD_CHECK_CLEAR = 0xD0 # wait_until (reg & mask) == 0
|
||||
WIDTH_WORD = 0x04 # 32-bit
|
||||
|
||||
|
||||
# ─── Типы команд ──────────────────────────────────────────────────────────────
|
||||
@dataclass
|
||||
class WriteEntry:
|
||||
addr: int
|
||||
value: int
|
||||
|
||||
|
||||
@dataclass
|
||||
class WriteCmd:
|
||||
entries: list[WriteEntry] = field(default_factory=list)
|
||||
|
||||
def to_bytes(self) -> bytes:
|
||||
length = 4 + len(self.entries) * 8
|
||||
# NXP RM format: Tag(1) | Length(2 BE) | Parameter(1)
|
||||
hdr = struct.pack(">BHB", CMD_WRITE, length, WIDTH_WORD)
|
||||
data = b"".join(
|
||||
struct.pack(">II", e.addr, e.value) for e in self.entries
|
||||
)
|
||||
return hdr + data
|
||||
|
||||
|
||||
@dataclass
|
||||
class CheckCmd:
|
||||
addr: int
|
||||
mask: int
|
||||
check_set: bool = True # True = wait != 0, False = wait == 0
|
||||
|
||||
def to_bytes(self) -> bytes:
|
||||
tag = CMD_CHECK_SET if self.check_set else CMD_CHECK_CLEAR
|
||||
# NXP RM format: Tag(1) | Length(2 BE=12) | Parameter(1) | Addr(4) | Mask(4)
|
||||
# Parameter: bits[2:0]=width(4=32bit), bit[3]=check_set → 0x1C / check_clear → 0x14
|
||||
# No count field — poll indefinitely is the default (length=12, not 16)
|
||||
param = 0x1C if self.check_set else 0x14
|
||||
length = 12 # 4 header + 4 addr + 4 mask
|
||||
return struct.pack(">BHBII", tag, length, param, self.addr, self.mask)
|
||||
|
||||
|
||||
# ─── Парсер .cfg ─────────────────────────────────────────────────────────────
|
||||
def parse_hex(s: str) -> int:
|
||||
return int(s, 16) if s.startswith("0x") or s.startswith("0X") else int(s)
|
||||
|
||||
|
||||
def parse_cfg(text: str) -> list[Union[WriteCmd, CheckCmd]]:
|
||||
"""
|
||||
Парсит .cfg и возвращает список команд DCD.
|
||||
Соседние write-записи группируются в одну WriteCmd.
|
||||
"""
|
||||
# Убираем блочные комментарии /* ... */
|
||||
text = re.sub(r"/\*.*?\*/", "", text, flags=re.DOTALL)
|
||||
# Убираем строчные комментарии // ...
|
||||
text = re.sub(r"//[^\n]*", "", text)
|
||||
|
||||
commands: list[Union[WriteCmd, CheckCmd]] = []
|
||||
current_write: WriteCmd | None = None
|
||||
|
||||
# Паттерн: *(uint32_t*)0xADDR = 0xVALUE;
|
||||
re_write = re.compile(
|
||||
r"\*\s*\(\s*uint32_t\s*\*\s*\)\s*(0x[0-9A-Fa-f]+)\s*=\s*(0x[0-9A-Fa-f]+)\s*;"
|
||||
)
|
||||
|
||||
# Паттерн: (*(uint32_t*)0xADDR & 0xMASK) != 0;
|
||||
re_check_set = re.compile(
|
||||
r"\(\s*\*\s*\(\s*uint32_t\s*\*\s*\)\s*(0x[0-9A-Fa-f]+)\s*&\s*(0x[0-9A-Fa-f]+)\s*\)\s*!=\s*0\s*;"
|
||||
)
|
||||
|
||||
# Паттерн: (*(uint32_t*)0xADDR & 0xMASK) == 0;
|
||||
re_check_clear = re.compile(
|
||||
r"\(\s*\*\s*\(\s*uint32_t\s*\*\s*\)\s*(0x[0-9A-Fa-f]+)\s*&\s*(0x[0-9A-Fa-f]+)\s*\)\s*==\s*0\s*;"
|
||||
)
|
||||
|
||||
for line in text.splitlines():
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
m = re_write.search(line)
|
||||
if m:
|
||||
addr = parse_hex(m.group(1))
|
||||
value = parse_hex(m.group(2))
|
||||
if current_write is None:
|
||||
current_write = WriteCmd()
|
||||
commands.append(current_write)
|
||||
current_write.entries.append(WriteEntry(addr, value))
|
||||
continue
|
||||
|
||||
m = re_check_set.search(line)
|
||||
if m:
|
||||
if current_write is not None:
|
||||
current_write = None # разрываем группу write
|
||||
addr = parse_hex(m.group(1))
|
||||
mask = parse_hex(m.group(2))
|
||||
commands.append(CheckCmd(addr, mask, check_set=True))
|
||||
continue
|
||||
|
||||
m = re_check_clear.search(line)
|
||||
if m:
|
||||
if current_write is not None:
|
||||
current_write = None
|
||||
addr = parse_hex(m.group(1))
|
||||
mask = parse_hex(m.group(2))
|
||||
commands.append(CheckCmd(addr, mask, check_set=False))
|
||||
continue
|
||||
|
||||
# Не write и не check — разрываем текущую write-группу
|
||||
# (на случай если между записями есть другой синтаксис)
|
||||
if current_write is not None:
|
||||
current_write = None
|
||||
|
||||
return commands
|
||||
|
||||
|
||||
# ─── Сборка бинарника ─────────────────────────────────────────────────────────
|
||||
def build_dcd_binary(commands: list[Union[WriteCmd, CheckCmd]]) -> bytes:
|
||||
body = b"".join(cmd.to_bytes() for cmd in commands)
|
||||
total_length = 4 + len(body) # 4 байта header
|
||||
# NXP RM format: Tag(1) | Length(2 BE) | Version(1)
|
||||
header = struct.pack(">BHB", DCD_TAG, total_length, DCD_VERSION)
|
||||
return header + body
|
||||
|
||||
|
||||
# ─── Отчёт ────────────────────────────────────────────────────────────────────
|
||||
def print_report(commands: list[Union[WriteCmd, CheckCmd]], out_size: int) -> None:
|
||||
writes = sum(1 for c in commands if isinstance(c, WriteCmd))
|
||||
checks = sum(1 for c in commands if isinstance(c, CheckCmd))
|
||||
entries = sum(len(c.entries) for c in commands if isinstance(c, WriteCmd))
|
||||
|
||||
print(f"\n{'─'*52}")
|
||||
print(f" DCD конвертация завершена")
|
||||
print(f"{'─'*52}")
|
||||
print(f" Write команд (групп) : {writes}")
|
||||
print(f" Write записей всего : {entries}")
|
||||
print(f" Check команд : {checks}")
|
||||
print(f" Размер бинарника : {out_size} байт (0x{out_size:04X})")
|
||||
print(f"{'─'*52}")
|
||||
|
||||
print("\n Команды:")
|
||||
for i, cmd in enumerate(commands):
|
||||
if isinstance(cmd, WriteCmd):
|
||||
print(f" [{i:02d}] WRITE × {len(cmd.entries):3d} entries"
|
||||
f" (0x{cmd.entries[0].addr:08X} … 0x{cmd.entries[-1].addr:08X})")
|
||||
else:
|
||||
kind = "CHECK_SET (!= 0)" if cmd.check_set else "CHECK_CLR (== 0)"
|
||||
print(f" [{i:02d}] {kind} addr=0x{cmd.addr:08X} mask=0x{cmd.mask:08X}")
|
||||
print()
|
||||
|
||||
|
||||
# ─── main ─────────────────────────────────────────────────────────────────────
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print(__doc__)
|
||||
sys.exit(1)
|
||||
|
||||
cfg_path = Path(sys.argv[1])
|
||||
if not cfg_path.exists():
|
||||
print(f"[ERROR] Файл не найден: {cfg_path}")
|
||||
sys.exit(1)
|
||||
|
||||
out_path = Path(sys.argv[2]) if len(sys.argv) >= 3 else cfg_path.with_suffix(".bin")
|
||||
|
||||
text = cfg_path.read_text(encoding="utf-8", errors="replace")
|
||||
commands = parse_cfg(text)
|
||||
|
||||
if not commands:
|
||||
print("[ERROR] Ни одной команды не найдено. Проверь формат .cfg файла.")
|
||||
sys.exit(1)
|
||||
|
||||
binary = build_dcd_binary(commands)
|
||||
out_path.write_bytes(binary)
|
||||
|
||||
print(f"[OK] Входной файл : {cfg_path}")
|
||||
print(f"[OK] Выходной файл: {out_path}")
|
||||
print_report(commands, len(binary))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -1,232 +0,0 @@
|
|||
[DCD Descriptor]
|
||||
|
||||
// =============================================================================
|
||||
// Clock Init
|
||||
// MT48LC16M16A2 SDRAM on MIMXRT1052CVJ5B
|
||||
//
|
||||
// Включаем только необходимые тактовые домены (fix 6):
|
||||
// CCM_CCGR0: биты [1:0]=11 (aips_tz1), биты [3:2]=11 (aips_tz2) — доступ к периферии
|
||||
// CCM_CCGR2: биты [29:28]=11 (iomuxc) — конфигурация пинов
|
||||
// CCM_CCGR3: биты [5:4]=11 (semc) — контроллер SDRAM
|
||||
// CCM_CCGR4: биты [3:2]=11 (iomuxc_gpr) — GPR регистры
|
||||
// CCM_CCGR5: биты [1:0]=11 (rom) — BootROM
|
||||
//
|
||||
// ВАЖНО: если после изменения плата не стартует — проверь по RM Table 18-5
|
||||
// (CCM Clock Gating Register) и вернись к 0xFFFFFFFF для отладки.
|
||||
// =============================================================================
|
||||
|
||||
*(uint32_t*)0x400FC068 = 0x0000000F; // CCM_CCGR0: aips_tz1 + aips_tz2
|
||||
*(uint32_t*)0x400FC06C = 0x00000000; // CCM_CCGR1: не нужен для DCD
|
||||
*(uint32_t*)0x400FC070 = 0x30000000; // CCM_CCGR2: iomuxc (CG14, bits[29:28])
|
||||
*(uint32_t*)0x400FC074 = 0x00000030; // CCM_CCGR3: semc (CG2, bits[5:4])
|
||||
*(uint32_t*)0x400FC078 = 0x0000000C; // CCM_CCGR4: iomuxc_gpr (CG1, bits[3:2])
|
||||
*(uint32_t*)0x400FC07C = 0x00000003; // CCM_CCGR5: rom (CG0, bits[1:0])
|
||||
*(uint32_t*)0x400FC080 = 0x00000000; // CCM_CCGR6: не нужен для DCD
|
||||
|
||||
// PLL_SYS: 528 MHz (24 MHz * 22), DIV_SELECT=1 → Fvco=528MHz
|
||||
*(uint32_t*)0x400D8030 = 0x00002001;
|
||||
|
||||
// Ждём lock PLL_SYS (bit 31 = LOCK) перед переключением источника тактирования (fix 3)
|
||||
(*(uint32_t*)0x400D8030 & 0x80000000) != 0;
|
||||
|
||||
// PFD2 = 528 * 18 / 0x23(35) = ~271 MHz... нет.
|
||||
// PFD528[PFD2_FRAC] = 0x23 = 35 → Fout = 528 * 18 / 35 = ~271?
|
||||
// Фактически 0x00230000 → PFD2_FRAC = 0x23 = 35 → F = 528*18/35 ≈ 271 МГц
|
||||
// Но для SEMC @ 133 МГц нужен делитель CCM_CBCDR[SEMC_PODF].
|
||||
// Проверь соответствие с clock_config.c!
|
||||
*(uint32_t*)0x400D8100 = 0x00230000; // CCM_ANALOG_PFD_528: PFD2 = 133 MHz
|
||||
|
||||
// CCM_CBCDR: SEMC_CLK_SEL=1 (PLL2_PFD2), SEMC_ALT_CLK_SEL=0, SEMC_PODF=1 (делитель /2)
|
||||
// → SEMC clock = 133 / 2 = 66.5 МГц
|
||||
// Если нужно 133 МГц — SEMC_PODF=0 (делитель /1), но убедись что SDRAM выдержит
|
||||
*(uint32_t*)0x400FC014 = 0x00010D40; // CCM_CBCDR
|
||||
|
||||
// =============================================================================
|
||||
// SDRAM Init — Configure IOMUXC for SEMC pins
|
||||
// MT48LC16M16A2: 16Mx16bit, 4 Banks, LVTTL
|
||||
// =============================================================================
|
||||
|
||||
// IOMUXC — MUX mode: все пины SEMC в режиме ALT0 (SEMC функция)
|
||||
*(uint32_t*)0x401F8014 = 0x00000000;
|
||||
*(uint32_t*)0x401F8018 = 0x00000000;
|
||||
*(uint32_t*)0x401F801C = 0x00000000;
|
||||
*(uint32_t*)0x401F8020 = 0x00000000;
|
||||
*(uint32_t*)0x401F8024 = 0x00000000;
|
||||
*(uint32_t*)0x401F8028 = 0x00000000;
|
||||
*(uint32_t*)0x401F802C = 0x00000000;
|
||||
*(uint32_t*)0x401F8030 = 0x00000000;
|
||||
*(uint32_t*)0x401F8034 = 0x00000000;
|
||||
*(uint32_t*)0x401F8038 = 0x00000000;
|
||||
*(uint32_t*)0x401F803C = 0x00000000;
|
||||
*(uint32_t*)0x401F8040 = 0x00000000;
|
||||
*(uint32_t*)0x401F8044 = 0x00000000;
|
||||
*(uint32_t*)0x401F8048 = 0x00000000;
|
||||
*(uint32_t*)0x401F804C = 0x00000000;
|
||||
*(uint32_t*)0x401F8050 = 0x00000000;
|
||||
*(uint32_t*)0x401F8054 = 0x00000000;
|
||||
*(uint32_t*)0x401F8058 = 0x00000000;
|
||||
*(uint32_t*)0x401F805C = 0x00000000;
|
||||
*(uint32_t*)0x401F8060 = 0x00000000;
|
||||
*(uint32_t*)0x401F8064 = 0x00000000;
|
||||
*(uint32_t*)0x401F8068 = 0x00000000;
|
||||
*(uint32_t*)0x401F806C = 0x00000000;
|
||||
*(uint32_t*)0x401F8070 = 0x00000000;
|
||||
*(uint32_t*)0x401F8074 = 0x00000000;
|
||||
*(uint32_t*)0x401F8078 = 0x00000000;
|
||||
*(uint32_t*)0x401F807C = 0x00000000;
|
||||
*(uint32_t*)0x401F8080 = 0x00000000;
|
||||
*(uint32_t*)0x401F8084 = 0x00000000;
|
||||
*(uint32_t*)0x401F8088 = 0x00000000;
|
||||
*(uint32_t*)0x401F808C = 0x00000000;
|
||||
*(uint32_t*)0x401F8090 = 0x00000000;
|
||||
*(uint32_t*)0x401F8094 = 0x00000000;
|
||||
*(uint32_t*)0x401F8098 = 0x00000000;
|
||||
*(uint32_t*)0x401F809C = 0x00000000;
|
||||
*(uint32_t*)0x401F80A0 = 0x00000000;
|
||||
*(uint32_t*)0x401F80A4 = 0x00000000;
|
||||
*(uint32_t*)0x401F80A8 = 0x00000000;
|
||||
*(uint32_t*)0x401F80AC = 0x00000000;
|
||||
*(uint32_t*)0x401F80B0 = 0x00000010; // SEMC_DATA15: SION=1 (software input on)
|
||||
*(uint32_t*)0x401F80B4 = 0x00000000;
|
||||
*(uint32_t*)0x401F80B8 = 0x00000000;
|
||||
|
||||
// IOMUXC — PAD ctrl: 0x000110F9
|
||||
// SRE=1 (Fast Slew Rate)
|
||||
// DSE=7 (Drive Strength: R0/7 — максимальный)
|
||||
// SPEED=3 (200 MHz)
|
||||
// ODE=0 (Open Drain disabled)
|
||||
// PKE=1 (Pull/Keeper enabled)
|
||||
// PUE=1 (Pull — не Keeper)
|
||||
// PUS=0 (100K pull-down)
|
||||
// HYS=0 (Hysteresis disabled)
|
||||
*(uint32_t*)0x401F8204 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8208 = 0x000110F9;
|
||||
*(uint32_t*)0x401F820C = 0x000110F9;
|
||||
*(uint32_t*)0x401F8210 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8214 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8218 = 0x000110F9;
|
||||
*(uint32_t*)0x401F821C = 0x000110F9;
|
||||
*(uint32_t*)0x401F8220 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8224 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8228 = 0x000110F9;
|
||||
*(uint32_t*)0x401F822C = 0x000110F9;
|
||||
*(uint32_t*)0x401F8230 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8234 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8238 = 0x000110F9;
|
||||
*(uint32_t*)0x401F823C = 0x000110F9;
|
||||
*(uint32_t*)0x401F8240 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8244 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8248 = 0x000110F9;
|
||||
*(uint32_t*)0x401F824C = 0x000110F9;
|
||||
*(uint32_t*)0x401F8250 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8254 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8258 = 0x000110F9;
|
||||
*(uint32_t*)0x401F825C = 0x000110F9;
|
||||
*(uint32_t*)0x401F8260 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8264 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8268 = 0x000110F9;
|
||||
*(uint32_t*)0x401F826C = 0x000110F9;
|
||||
*(uint32_t*)0x401F8270 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8274 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8278 = 0x000110F9;
|
||||
*(uint32_t*)0x401F827C = 0x000110F9;
|
||||
*(uint32_t*)0x401F8280 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8284 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8288 = 0x000110F9;
|
||||
*(uint32_t*)0x401F828C = 0x000110F9;
|
||||
*(uint32_t*)0x401F8290 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8294 = 0x000110F9;
|
||||
*(uint32_t*)0x401F8298 = 0x000110F9;
|
||||
*(uint32_t*)0x401F829C = 0x000110F9;
|
||||
*(uint32_t*)0x401F82A0 = 0x000110F9;
|
||||
*(uint32_t*)0x401F82A4 = 0x000110F9;
|
||||
*(uint32_t*)0x401F82A8 = 0x000110F9;
|
||||
|
||||
// =============================================================================
|
||||
// SEMC Controller — конфигурация для MT48LC16M16A2
|
||||
// Базовый адрес SDRAM: 0x80000000
|
||||
// Размер: 32 MB (MT48LC16M16A2: 16M × 16bit = 32 MB)
|
||||
// =============================================================================
|
||||
|
||||
// SEMC_MCR: MDIS=0, BTO=0x1F (timeout), DQSMD=1 (DQS internal loop)
|
||||
*(uint32_t*)0x402F0000 = 0x10000004;
|
||||
|
||||
// SEMC_BMCR0 & BMCR1: burst весовые коэффициенты
|
||||
// 0x00000081: WQOS=1, RQOS=0, AGE=0, BSTLEN=0x10 (16 burst)
|
||||
*(uint32_t*)0x402F0008 = 0x00000081; // SEMC_BMCR0
|
||||
*(uint32_t*)0x402F000C = 0x00000081; // SEMC_BMCR1
|
||||
|
||||
// SEMC_BR0..BR7 — Base Register для каждого region
|
||||
// BR0: SDRAM @ 0x80000000, размер 32MB (MR=0x1B → 32MB), VLD=1
|
||||
*(uint32_t*)0x402F0010 = 0x8000001B; // BR0: SDRAM CS0, 0x80000000, 32MB
|
||||
*(uint32_t*)0x402F0014 = 0x82000000; // BR1: SDRAM CS1 (если есть)
|
||||
*(uint32_t*)0x402F0018 = 0x84000000; // BR2
|
||||
*(uint32_t*)0x402F001C = 0x86000000; // BR3
|
||||
*(uint32_t*)0x402F0020 = 0x90000021; // BR4: NAND
|
||||
*(uint32_t*)0x402F0024 = 0xA0000019; // BR5: NOR/SRAM
|
||||
*(uint32_t*)0x402F0028 = 0xA8000017; // BR6
|
||||
*(uint32_t*)0x402F002C = 0xA900001B; // BR7
|
||||
*(uint32_t*)0x402F0030 = 0x00000021; // BR8
|
||||
|
||||
// SEMC_INTEN: прерывания отключены
|
||||
*(uint32_t*)0x402F0004 = 0x000079A8;
|
||||
|
||||
// SEMC_SDRAMCR0: тайминги MT48LC16M16A2 @ 133MHz
|
||||
// PS=1 (16-bit data bus), BL=3 (Burst Length 8), COL=2 (9-bit col addr),
|
||||
// CL=3 (CAS Latency 3)
|
||||
*(uint32_t*)0x402F0040 = 0x00000F31;
|
||||
|
||||
// SEMC_SDRAMCR1
|
||||
// PRE2ACT=2, ACT2RW=2, RFRC=9 (tRFC), WRC=2, CKEOFF=2, ACT2PRE=6
|
||||
*(uint32_t*)0x402F0044 = 0x00652922;
|
||||
|
||||
// SEMC_SDRAMCR2: Refresh rate
|
||||
// MT48LC16M16A2: 4096 refresh cycles / 64ms → ~15.6 мкс @ 133MHz
|
||||
// SRRC=0x02 (самообновление), REF2REF=0x02, ACT2ACT=0x01
|
||||
*(uint32_t*)0x402F0048 = 0x00020201;
|
||||
|
||||
// SEMC_SDRAMCR3
|
||||
// REBL=1, PRESCALE=0x21, RT=0x3D, UT=0x0F
|
||||
*(uint32_t*)0x402F004C = 0x08193D0F;
|
||||
|
||||
// SEMC_IOCR
|
||||
*(uint32_t*)0x402F0080 = 0x00000021;
|
||||
*(uint32_t*)0x402F0084 = 0x00888888;
|
||||
|
||||
// SEMC_IPTXDAT / IPRXDAT placeholder
|
||||
*(uint32_t*)0x402F0094 = 0x00000002;
|
||||
*(uint32_t*)0x402F0098 = 0x00000000;
|
||||
|
||||
// =============================================================================
|
||||
// SDRAM Init Sequence (JEDEC: Precharge All → Auto-Refresh × 2 → Mode Register Set)
|
||||
// =============================================================================
|
||||
|
||||
// SEMC_IPCR0: IP command target адрес = начало SDRAM
|
||||
*(uint32_t*)0x402F0090 = 0x80000000;
|
||||
|
||||
// IPCMD: Precharge All (команда 0xF)
|
||||
*(uint32_t*)0x402F009C = 0xA55A000F;
|
||||
// Ждём завершения: SEMC_INTR[IPCMDDONE] (bit 0)
|
||||
(*(uint32_t*)0x402F003C & 0x01) != 0;
|
||||
|
||||
// IPCMD: Auto-Refresh #1 (команда 0xC)
|
||||
*(uint32_t*)0x402F0090 = 0x80000000;
|
||||
*(uint32_t*)0x402F009C = 0xA55A000C;
|
||||
(*(uint32_t*)0x402F003C & 0x01) != 0;
|
||||
|
||||
// IPCMD: Auto-Refresh #2
|
||||
*(uint32_t*)0x402F0090 = 0x80000000;
|
||||
*(uint32_t*)0x402F009C = 0xA55A000C;
|
||||
(*(uint32_t*)0x402F003C & 0x01) != 0;
|
||||
|
||||
// Mode Register Set:
|
||||
// MRS = 0x33: Burst Length=8 (BL=3), Burst Type=Sequential, CAS Latency=3
|
||||
*(uint32_t*)0x402F00A0 = 0x00000033;
|
||||
*(uint32_t*)0x402F0090 = 0x80000000;
|
||||
// IPCMD: Mode Register Set (команда 0xA)
|
||||
*(uint32_t*)0x402F009C = 0xA55A000A;
|
||||
(*(uint32_t*)0x402F003C & 0x01) != 0;
|
||||
|
||||
// SEMC_SDRAMCR3: финальное значение Refresh Rate после инит-последовательности
|
||||
// Переключаемся с медленного (init) refresh на рабочий
|
||||
*(uint32_t*)0x402F004C = 0x50210A09;
|
||||
BIN
tools/host/dcd/ivt_flashloader.bin
Normal file
BIN
tools/host/dcd/ivt_flashloader.bin
Normal file
Binary file not shown.
Binary file not shown.
|
|
@ -1,152 +0,0 @@
|
|||
# =============================================================================
|
||||
# FCB — Flash Configuration Block
|
||||
# Чип: Winbond W25Q128FVSSIG (64 Mbit = 8 MB), Quad SPI
|
||||
# Режим: Fast Read Quad I/O (0xEB), 133 MHz, 4-line
|
||||
# Цель: MIMXRT1052CVJ5B (family: mimxrt1050 в SPSDK 3.7)
|
||||
#
|
||||
# LUT-последовательности:
|
||||
# Seq 0: Fast Read Quad I/O (0xEB) — основное XIP-чтение
|
||||
# Seq 1: Read Status Reg 1 (0x05) — опрос WIP бита
|
||||
# Seq 2: Write Enable (0x06) — перед любой записью/стиранием
|
||||
# Seq 3: Erase Sector 4KB (0x20) — стирание сектора
|
||||
# Seq 4: Write SR2 (0x31) — установка QE бита (вызов при старте)
|
||||
# Seq 5: Page Program Quad (0x32) — запись страницы в Quad режиме
|
||||
# Seq 11: Chip Erase (0x60) — полное стирание чипа
|
||||
# Seq 12: Read Status Reg 1 (0x05) — дублирует seq1, для blhost
|
||||
# =============================================================================
|
||||
|
||||
family: mimxrt1050
|
||||
revision: latest
|
||||
type: flexspi_nor
|
||||
|
||||
fcb_settings:
|
||||
tag: '0x42464346' # 'FCFB' — magic, фиксировано
|
||||
version: '0x56010400' # Version 1.4 (актуально для SPSDK 3.7)
|
||||
|
||||
readSampleClkSrc: '0x01' # loopback from DQS pad
|
||||
csHoldTime: '0x03'
|
||||
csSetupTime: '0x03'
|
||||
columnAddressWidth: '0x03'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# QE bit enable — W25Q128 Status Register 2, bit 1
|
||||
# BootROM выполнит Write Enable (seq 2) + Write SR2 (seq 4) при старте.
|
||||
# deviceModeArg = 0x02 → SR2 = 0b00000010 (QE=1)
|
||||
# waitTimeCfgCommands = 30 × 100us = 3ms (достаточно для записи SR)
|
||||
# ---------------------------------------------------------------------------
|
||||
deviceModeCfgEnable: '0x01'
|
||||
deviceModeType: '0x01'
|
||||
waitTimeCfgCommands: '0x001E'
|
||||
deviceModeSeq:
|
||||
seqNum: 1
|
||||
seqId: 4 # → Seq 4: Write SR2 (0x31)
|
||||
deviceModeArg: '0x00000002' # SR2 = 0x02 → QE=1
|
||||
|
||||
deviceType: '0x01' # Serial NOR
|
||||
sflashPadType: '0x04' # Quad — 4 линии данных (IO0..IO3)
|
||||
serialClkFreq: '0x07' # 133 MHz
|
||||
sflashA1Size: '0x01000000' # 16 MB — W25Q128FVSSIG (128 Mbit / 8)
|
||||
|
||||
pageSize: '0x00000100' # 256 байт — стандарт для W25Q128
|
||||
sectorSize: '0x00001000' # 4 KB — минимальная единица стирания
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# LUT — Look-Up Table
|
||||
# Каждая последовательность: 8 инструкций (instr0..instr7)
|
||||
# Неиспользуемые инструкции = STOP (значение по умолчанию, можно не писать)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# ── Seq 0: Fast Read Quad I/O (0xEB) ───────────────────────────────────────
|
||||
# CMD(×1) 0xEB → RADDR(×4) 24bit → DUMMY(×4) 6cycles → READ(×4)
|
||||
lookupTable_seq0_instr0:
|
||||
OPERAND: 0xEB
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: CMD_SDR
|
||||
lookupTable_seq0_instr1:
|
||||
OPERAND: 0x18 # 24-bit address
|
||||
NUM_PADS: 4PAD
|
||||
OPERATION_CODE: RADDR_SDR
|
||||
lookupTable_seq0_instr2:
|
||||
OPERAND: 0x06 # 6 dummy cycles @ 133MHz (4 mode + 2 dummy)
|
||||
NUM_PADS: 4PAD
|
||||
OPERATION_CODE: DUMMY_SDR
|
||||
lookupTable_seq0_instr3:
|
||||
OPERAND: 0x04
|
||||
NUM_PADS: 4PAD
|
||||
OPERATION_CODE: READ_SDR
|
||||
|
||||
# ── Seq 1: Read Status Register 1 (0x05) ───────────────────────────────────
|
||||
# CMD(×1) 0x05 → READ(×1) 1byte
|
||||
lookupTable_seq1_instr0:
|
||||
OPERAND: 0x05
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: CMD_SDR
|
||||
lookupTable_seq1_instr1:
|
||||
OPERAND: 0x04
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: READ_SDR
|
||||
|
||||
# ── Seq 2: Write Enable (0x06) ──────────────────────────────────────────────
|
||||
# CMD(×1) 0x06
|
||||
lookupTable_seq2_instr0:
|
||||
OPERAND: 0x06
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: CMD_SDR
|
||||
|
||||
# ── Seq 3: Erase Sector 4KB (0x20) ─────────────────────────────────────────
|
||||
# CMD(×1) 0x20 → RADDR(×4) 24bit
|
||||
lookupTable_seq3_instr0:
|
||||
OPERAND: 0x20
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: CMD_SDR
|
||||
lookupTable_seq3_instr1:
|
||||
OPERAND: 0x18
|
||||
NUM_PADS: 4PAD
|
||||
OPERATION_CODE: RADDR_SDR
|
||||
|
||||
# ── Seq 4: Write Status Register 2 (0x31) — установка QE бита ──────────────
|
||||
# CMD(×1) 0x31 → WRITE(×1) 1byte
|
||||
# Вызывается через deviceModeSeq при инициализации BootROM.
|
||||
# deviceModeArg (0x02) записывается как аргумент.
|
||||
lookupTable_seq4_instr0:
|
||||
OPERAND: 0x31
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: CMD_SDR
|
||||
lookupTable_seq4_instr1:
|
||||
OPERAND: 0x01 # записать 1 байт
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: WRITE_SDR
|
||||
|
||||
# ── Seq 5: Page Program Quad Input (0x32) ──────────────────────────────────
|
||||
# CMD(×1) 0x32 → RADDR(×1) 24bit → WRITE(×4)
|
||||
lookupTable_seq5_instr0:
|
||||
OPERAND: 0x32
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: CMD_SDR
|
||||
lookupTable_seq5_instr1:
|
||||
OPERAND: 0x18
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: RADDR_SDR
|
||||
lookupTable_seq5_instr2:
|
||||
OPERAND: 0x04
|
||||
NUM_PADS: 4PAD
|
||||
OPERATION_CODE: WRITE_SDR
|
||||
|
||||
# Seq 6..10: не используются (STOP по умолчанию)
|
||||
|
||||
# ── Seq 11: Chip Erase (0x60) ───────────────────────────────────────────────
|
||||
# CMD(×1) 0x60
|
||||
lookupTable_seq11_instr0:
|
||||
OPERAND: 0x60
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: CMD_SDR
|
||||
|
||||
# ── Seq 12: Read Status Register 1 (дубль для blhost) ──────────────────────
|
||||
lookupTable_seq12_instr0:
|
||||
OPERAND: 0x05
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: CMD_SDR
|
||||
lookupTable_seq12_instr1:
|
||||
OPERAND: 0x04
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: READ_SDR
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
# =============================================================================
|
||||
# FCB — Flash Configuration Block
|
||||
# Чип: Winbond W25Q64FVSSIG (64 Mbit = 8 MB), Quad SPI
|
||||
# Режим: Fast Read Quad I/O (0xEB), 133 MHz, 4-line
|
||||
# Цель: MIMXRT1052CVJ5B (family: mimxrt1050 в SPSDK 3.7)
|
||||
#
|
||||
# LUT-последовательности:
|
||||
# Seq 0: Fast Read Quad I/O (0xEB) — основное XIP-чтение
|
||||
# Seq 1: Read Status Reg 1 (0x05) — опрос WIP бита
|
||||
# Seq 2: Write Enable (0x06) — перед любой записью/стиранием
|
||||
# Seq 3: Erase Sector 4KB (0x20) — стирание сектора
|
||||
# Seq 4: Write SR2 (0x31) — установка QE бита (вызов при старте)
|
||||
# Seq 5: Page Program Quad (0x32) — запись страницы в Quad режиме
|
||||
# Seq 11: Chip Erase (0x60) — полное стирание чипа
|
||||
# Seq 12: Read Status Reg 1 (0x05) — дублирует seq1, для blhost
|
||||
# =============================================================================
|
||||
|
||||
family: mimxrt1050
|
||||
revision: latest
|
||||
type: flexspi_nor
|
||||
|
||||
fcb_settings:
|
||||
tag: '0x42464346' # 'FCFB' — magic, фиксировано
|
||||
version: '0x56010400' # Version 1.4 (актуально для SPSDK 3.7)
|
||||
|
||||
readSampleClkSrc: '0x01' # loopback from DQS pad
|
||||
csHoldTime: '0x03'
|
||||
csSetupTime: '0x03'
|
||||
columnAddressWidth: '0x03'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# QE bit enable — W25Q64 Status Register 2, bit 1
|
||||
# BootROM выполнит Write Enable (seq 2) + Write SR2 (seq 4) при старте.
|
||||
# deviceModeArg = 0x02 → SR2 = 0b00000010 (QE=1)
|
||||
# waitTimeCfgCommands = 30 × 100us = 3ms (достаточно для записи SR)
|
||||
# ---------------------------------------------------------------------------
|
||||
deviceModeCfgEnable: '0x01'
|
||||
deviceModeType: '0x01'
|
||||
waitTimeCfgCommands: '0x001E'
|
||||
deviceModeSeq:
|
||||
seqNum: 1
|
||||
seqId: 4 # → Seq 4: Write SR2 (0x31)
|
||||
deviceModeArg: '0x00000002' # SR2 = 0x02 → QE=1
|
||||
|
||||
deviceType: '0x01' # Serial NOR
|
||||
sflashPadType: '0x04' # Quad — 4 линии данных (IO0..IO3)
|
||||
serialClkFreq: '0x07' # 133 MHz
|
||||
sflashA1Size: '0x00800000' # 8 MB — W25Q64FVSSIG (64 Mbit / 8)
|
||||
|
||||
pageSize: '0x00000100' # 256 байт — стандарт для W25Q64
|
||||
sectorSize: '0x00001000' # 4 KB — минимальная единица стирания
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# LUT — Look-Up Table
|
||||
# Каждая последовательность: 8 инструкций (instr0..instr7)
|
||||
# Неиспользуемые инструкции = STOP (значение по умолчанию, можно не писать)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# ── Seq 0: Fast Read Quad I/O (0xEB) ───────────────────────────────────────
|
||||
# CMD(×1) 0xEB → RADDR(×4) 24bit → DUMMY(×4) 6cycles → READ(×4)
|
||||
lookupTable_seq0_instr0:
|
||||
OPERAND: 0xEB
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: CMD_SDR
|
||||
lookupTable_seq0_instr1:
|
||||
OPERAND: 0x18 # 24-bit address
|
||||
NUM_PADS: 4PAD
|
||||
OPERATION_CODE: RADDR_SDR
|
||||
lookupTable_seq0_instr2:
|
||||
OPERAND: 0x06 # 6 dummy cycles @ 133MHz (4 mode + 2 dummy)
|
||||
NUM_PADS: 4PAD
|
||||
OPERATION_CODE: DUMMY_SDR
|
||||
lookupTable_seq0_instr3:
|
||||
OPERAND: 0x04
|
||||
NUM_PADS: 4PAD
|
||||
OPERATION_CODE: READ_SDR
|
||||
|
||||
# ── Seq 1: Read Status Register 1 (0x05) ───────────────────────────────────
|
||||
# CMD(×1) 0x05 → READ(×1) 1byte
|
||||
lookupTable_seq1_instr0:
|
||||
OPERAND: 0x05
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: CMD_SDR
|
||||
lookupTable_seq1_instr1:
|
||||
OPERAND: 0x04
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: READ_SDR
|
||||
|
||||
# ── Seq 2: Write Enable (0x06) ──────────────────────────────────────────────
|
||||
# CMD(×1) 0x06
|
||||
lookupTable_seq2_instr0:
|
||||
OPERAND: 0x06
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: CMD_SDR
|
||||
|
||||
# ── Seq 3: Erase Sector 4KB (0x20) ─────────────────────────────────────────
|
||||
# CMD(×1) 0x20 → RADDR(×4) 24bit
|
||||
lookupTable_seq3_instr0:
|
||||
OPERAND: 0x20
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: CMD_SDR
|
||||
lookupTable_seq3_instr1:
|
||||
OPERAND: 0x18
|
||||
NUM_PADS: 4PAD
|
||||
OPERATION_CODE: RADDR_SDR
|
||||
|
||||
# ── Seq 4: Write Status Register 2 (0x31) — установка QE бита ──────────────
|
||||
# CMD(×1) 0x31 → WRITE(×1) 1byte
|
||||
# Вызывается через deviceModeSeq при инициализации BootROM.
|
||||
# deviceModeArg (0x02) записывается как аргумент.
|
||||
lookupTable_seq4_instr0:
|
||||
OPERAND: 0x31
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: CMD_SDR
|
||||
lookupTable_seq4_instr1:
|
||||
OPERAND: 0x01 # записать 1 байт
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: WRITE_SDR
|
||||
|
||||
# ── Seq 5: Page Program Quad Input (0x32) ──────────────────────────────────
|
||||
# CMD(×1) 0x32 → RADDR(×1) 24bit → WRITE(×4)
|
||||
lookupTable_seq5_instr0:
|
||||
OPERAND: 0x32
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: CMD_SDR
|
||||
lookupTable_seq5_instr1:
|
||||
OPERAND: 0x18
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: RADDR_SDR
|
||||
lookupTable_seq5_instr2:
|
||||
OPERAND: 0x04
|
||||
NUM_PADS: 4PAD
|
||||
OPERATION_CODE: WRITE_SDR
|
||||
|
||||
# Seq 6..10: не используются (STOP по умолчанию)
|
||||
|
||||
# ── Seq 11: Chip Erase (0x60) ───────────────────────────────────────────────
|
||||
# CMD(×1) 0x60
|
||||
lookupTable_seq11_instr0:
|
||||
OPERAND: 0x60
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: CMD_SDR
|
||||
|
||||
# ── Seq 12: Read Status Register 1 (дубль для blhost) ──────────────────────
|
||||
lookupTable_seq12_instr0:
|
||||
OPERAND: 0x05
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: CMD_SDR
|
||||
lookupTable_seq12_instr1:
|
||||
OPERAND: 0x04
|
||||
NUM_PADS: 1PAD
|
||||
OPERATION_CODE: READ_SDR
|
||||
209
tools/host/flash_usb.py
Normal file
209
tools/host/flash_usb.py
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
flash_usb.py — прошивка MIMXRT1052 через USB (BootROM SDP → Flashloader → blhost)
|
||||
|
||||
Использование:
|
||||
python3 flash_usb.py --firmware firmware_test --build-type Debug
|
||||
python3 flash_usb.py --firmware app --build-type Release
|
||||
python3 flash_usb.py --firmware bootloader --build-type Release
|
||||
python3 flash_usb.py --firmware firmware_test --build-type Debug --ram-only
|
||||
|
||||
Шаги:
|
||||
1. Устройство в SDP режиме (VID:PID 1FC9:0130)
|
||||
→ sdphost загружает ivt_flashloader.bin в RAM
|
||||
→ sdphost прыгает на flashloader
|
||||
2. Flashloader запущен (VID:PID 15A2:0073)
|
||||
→ blhost конфигурирует FlexSPI NOR (пишет FCB)
|
||||
→ blhost стирает нужный регион Flash
|
||||
→ blhost пишет HAB образ начиная с 0x60002000
|
||||
→ blhost reset
|
||||
"""
|
||||
|
||||
import argparse
|
||||
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"
|
||||
|
||||
# USB VID:PID
|
||||
SDP_USB = "0x1FC9,0x0130" # BootROM Serial Download Protocol
|
||||
BLHOST_USB = "0x15A2,0x0073" # 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
|
||||
|
||||
# Flash layout
|
||||
FLASH_BASE = 0x60000000
|
||||
HAB_OFFSET = 0x1000 # IVT offset: HAB binary starts with IVT at byte 0
|
||||
# write address = FLASH_BASE + ivtOffset = 0x60001000
|
||||
|
||||
|
||||
def run(cmd: list[str], check: bool = True) -> subprocess.CompletedProcess:
|
||||
print(f" $ {' '.join(cmd)}")
|
||||
result = subprocess.run(cmd, capture_output=False)
|
||||
if check and result.returncode != 0:
|
||||
print(f"\n[ERROR] Команда завершилась с кодом {result.returncode}")
|
||||
sys.exit(1)
|
||||
return result
|
||||
|
||||
|
||||
def step(msg: str) -> None:
|
||||
print(f"\n{'─'*60}")
|
||||
print(f" {msg}")
|
||||
print(f"{'─'*60}")
|
||||
|
||||
|
||||
def wait_for_flashloader(timeout: int = 10) -> bool:
|
||||
"""Ждём пока Flashloader поднимется (VID:PID 15A2:0073)"""
|
||||
print(f"\n Ожидание Flashloader (до {timeout}с)...", end="", flush=True)
|
||||
for i in range(timeout):
|
||||
time.sleep(1)
|
||||
result = subprocess.run(
|
||||
["blhost", "-u", BLHOST_USB, "-j", "--", "get-property", "1", "0"],
|
||||
capture_output=True
|
||||
)
|
||||
if result.returncode == 0:
|
||||
print(f" OK ({i+1}с)")
|
||||
return True
|
||||
print(".", end="", flush=True)
|
||||
print(" TIMEOUT")
|
||||
return False
|
||||
|
||||
|
||||
def load_flashloader() -> None:
|
||||
"""Загружает Flashloader через SDP если ещё не запущен"""
|
||||
# Проверяем — вдруг уже запущен
|
||||
result = subprocess.run(
|
||||
["blhost", "-u", BLHOST_USB, "-j", "--", "get-property", "1", "0"],
|
||||
capture_output=True
|
||||
)
|
||||
if result.returncode == 0:
|
||||
print(" Flashloader уже запущен — пропускаем загрузку")
|
||||
return
|
||||
|
||||
if not FLASHLOADER.exists():
|
||||
print(f"[ERROR] Не найден: {FLASHLOADER}")
|
||||
print(" Скачай ivt_flashloader.bin из MCUXpresso Secure Provisioning Tool")
|
||||
print(" и положи в tools/host/dcd/ivt_flashloader.bin")
|
||||
sys.exit(1)
|
||||
|
||||
step("Загрузка Flashloader через SDP (1FC9:0130)")
|
||||
run(["sdphost", "-u", SDP_USB, "-j", "--",
|
||||
"write-file", "0x20001C00", str(FLASHLOADER)])
|
||||
run(["sdphost", "-u", SDP_USB, "-j", "--",
|
||||
"jump-address", "0x20001C00"])
|
||||
|
||||
if not wait_for_flashloader():
|
||||
print("[ERROR] Flashloader не ответил. Проверь BOOT_MOD пины и подключение.")
|
||||
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 (инициализация контроллера)")
|
||||
run(["blhost", "-u", BLHOST_USB, "--",
|
||||
"fill-memory", FLEXSPI_OPTION_ADDR, "4", FLEXSPI_OPTION_VALUE, "word"])
|
||||
run(["blhost", "-u", BLHOST_USB, "--",
|
||||
"configure-memory", FLEXSPI_MEMORY_ID, FLEXSPI_OPTION_ADDR])
|
||||
|
||||
|
||||
def write_fcb() -> None:
|
||||
"""Записывает Flash Configuration Block в 0x60000000
|
||||
|
||||
Отдельный шаг после erase! Flashloader генерирует FCB из параметров FlexSPI
|
||||
и пишет его по адресу 0x60000000. Без FCB BootROM не знает как читать Flash.
|
||||
Option word 0xF000000F: tag=0xF → Write FCB command.
|
||||
"""
|
||||
step("Запись FCB в Flash[0x60000000]")
|
||||
run(["blhost", "-u", BLHOST_USB, "--",
|
||||
"fill-memory", FLEXSPI_OPTION_ADDR, "4", FLEXSPI_FCB_VALUE, "word"])
|
||||
run(["blhost", "-u", BLHOST_USB, "--",
|
||||
"configure-memory", FLEXSPI_MEMORY_ID, FLEXSPI_OPTION_ADDR])
|
||||
|
||||
|
||||
def flash(hab_bin: Path, ram_only: bool = False) -> None:
|
||||
if not hab_bin.exists():
|
||||
print(f"[ERROR] Файл не найден: {hab_bin}")
|
||||
print(" Сначала собери образ: uv run nxpimage hab export -c ...")
|
||||
sys.exit(1)
|
||||
|
||||
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", "--",
|
||||
"jump-address", f"0x{FLASH_BASE + HAB_OFFSET:08X}"])
|
||||
return
|
||||
|
||||
load_flashloader()
|
||||
configure_flexspi() # 0xC0000007 — инициализация 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}")
|
||||
print(f" Образ: {hab_bin}")
|
||||
print(f" Размер: {hab_bin.stat().st_size} байт")
|
||||
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!)
|
||||
|
||||
# memoryId=0 — mapped (XIP) адресное пространство
|
||||
run(["blhost", "-u", BLHOST_USB, "--",
|
||||
"write-memory", write_addr, str(hab_bin), "0"])
|
||||
|
||||
step("Reset")
|
||||
run(["blhost", "-u", BLHOST_USB, "--", "reset"])
|
||||
print("\n ✓ Прошивка завершена успешно")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Прошивка MIMXRT1052 через USB")
|
||||
parser.add_argument("--firmware", required=True,
|
||||
choices=["firmware_test", "bootloader", "app"],
|
||||
help="Имя прошивки")
|
||||
parser.add_argument("--build-type", required=True,
|
||||
choices=["Debug", "Release"],
|
||||
help="Тип сборки")
|
||||
parser.add_argument("--ram-only", action="store_true",
|
||||
help="Загрузить в RAM без записи во Flash")
|
||||
args = parser.parse_args()
|
||||
|
||||
# HAB образ собирается командой: nxpimage hab export -c hab_<firmware>.yaml
|
||||
# Выходной файл: build/<BuildType>/<firmware>_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"{'═'*60}")
|
||||
|
||||
flash(hab_bin, ram_only=args.ram_only)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -1,18 +1,20 @@
|
|||
# =============================================================================
|
||||
# HAB Container — firmware/app (боевая прошивка)
|
||||
# Расположение: tools/host/hab/hab_app.yaml
|
||||
#
|
||||
# flags: 0x0 — образ НЕ подписан
|
||||
# DCD: обязателен — линкер размещает FreeRTOS heap и буферы LCDIF
|
||||
# в SDRAM (0x80000000), которая должна быть инициализирована
|
||||
# до первой инструкции прошивки.
|
||||
# Сборка:
|
||||
# cd tools/host/hab
|
||||
# uv run nxpimage hab export --force -c hab_app.yaml \
|
||||
# -o ../../../build/Release/app_hab.bin
|
||||
# =============================================================================
|
||||
|
||||
options:
|
||||
flags: 0x0
|
||||
flags: 0x00
|
||||
startAddress: 0x60000000
|
||||
ivtOffset: 0x1000
|
||||
initialLoadSize: 0x2000
|
||||
family: mimxrt1050
|
||||
bootDevice: flexspi_nor
|
||||
startAddress: 0x60000000 # FlexSPI NOR base address (XIP)
|
||||
DCDFilePath: ../dcd/dcd.bin
|
||||
DCDFilePath: ../dcd/dcd.bin # обязателен — heap FreeRTOS и LCDIF в SDRAM
|
||||
|
||||
inputImageFile: "../../../build/Release/app.bin"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,21 @@
|
|||
# =============================================================================
|
||||
# HAB Container — firmware/bootloader
|
||||
# Расположение: tools/host/hab/hab_bootloader.yaml
|
||||
#
|
||||
# flags: 0x0 — образ НЕ подписан
|
||||
# DCD: отсутствует намеренно — загрузчик не использует SDRAM,
|
||||
# минимальная зависимость от железа = максимальная надёжность.
|
||||
# SDRAM инициализируется позже, уже внутри app через её DCD.
|
||||
# Сборка:
|
||||
# cd tools/host/hab
|
||||
# uv run nxpimage hab export --force -c hab_bootloader.yaml \
|
||||
# -o ../../../build/Release/bootloader_hab.bin
|
||||
# =============================================================================
|
||||
|
||||
options:
|
||||
flags: 0x0
|
||||
flags: 0x00
|
||||
startAddress: 0x60000000
|
||||
ivtOffset: 0x1000
|
||||
initialLoadSize: 0x2000
|
||||
family: mimxrt1050
|
||||
bootDevice: flexspi_nor
|
||||
startAddress: 0x60000000 # FlexSPI NOR base address (XIP)
|
||||
|
||||
# DCD намеренно отсутствует — загрузчик не использует SDRAM
|
||||
|
||||
inputImageFile: "../../../build/Release/bootloader.bin"
|
||||
|
||||
sections: []
|
||||
|
|
@ -1,23 +1,22 @@
|
|||
# =============================================================================
|
||||
# HAB Container — firmware/test (входной контроль производства)
|
||||
# Расположение: tools/host/hab/hab_firmware_test.yaml
|
||||
#
|
||||
# flags: 0x0 — образ НЕ подписан (HAB в open/unsigned режиме)
|
||||
# DCD: присутствует — инициализация SDRAM обязательна для тестов
|
||||
#
|
||||
# Важно: sections пустой — для unsigned образа CSF не нужен.
|
||||
# BootROM примет образ без проверки подписи пока HAB не залочен (fuses).
|
||||
# Сборка:
|
||||
# cd tools/host/hab
|
||||
# uv run nxpimage hab export --force -c hab_firmware_test.yaml \
|
||||
# -o ../../../build/Debug/firmware_test.bin
|
||||
# =============================================================================
|
||||
|
||||
options:
|
||||
flags: 0x0 # unsigned — без подписи
|
||||
flags: 0x00 # unsigned — без подписи
|
||||
startAddress: 0x60000000 # FlexSPI NOR base (XIP)
|
||||
ivtOffset: 0x1000 # IVT offset for FlexSPI NOR boot
|
||||
initialLoadSize: 0x2000 # размер области IVT+BDT+DCD до кода
|
||||
family: mimxrt1050
|
||||
bootDevice: flexspi_nor # ivtOffset=0x1000, initialLoadSize=0x2000 — автоматически
|
||||
startAddress: 0x60000000 # FlexSPI NOR base address (XIP)
|
||||
|
||||
# DCD — инициализация SDRAM до передачи управления прошивке.
|
||||
# Сгенерирован из dcd.cfg скриптом tools/dcd_cfg_to_bin.py
|
||||
DCDFilePath: ../dcd/dcd.bin
|
||||
DCDFilePath: ../dcd/dcd.bin # инициализация SDRAM
|
||||
|
||||
# .bin или .srec — оба принимаются
|
||||
inputImageFile: "../../../build/Debug/firmware_test.bin"
|
||||
|
||||
sections: []
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
# =============================================================================
|
||||
# Bootable Image — firmware/app (боевая прошивка)
|
||||
# Расположение: tools/host/image/bootimg_app.yaml
|
||||
#
|
||||
# Пайплайн сборки:
|
||||
#
|
||||
# 1. FCB (один раз, из tools/host/fdcb/):
|
||||
# nxpimage bootable-image fcb export -c fcb_w25q64_quad.yaml -o fcb.bin
|
||||
#
|
||||
# 2. Образ (из tools/host/image/):
|
||||
# nxpimage bootable-image export -c bootimg_app.yaml
|
||||
#
|
||||
# 3. Проверка:
|
||||
# nxpimage bootable-image parse -b ../../../../build/Release/app_bootable.bin -f mimxrt1050
|
||||
# =============================================================================
|
||||
|
||||
family: mimxrt1050
|
||||
revision: latest
|
||||
memory_type: flexspi_nor
|
||||
|
||||
output: "../../../build/Release/app_bootable.bin"
|
||||
output_format: bin
|
||||
|
||||
fcb: ../fdcb/fcb.bin
|
||||
|
||||
hab_container: ../hab/hab_app.yaml
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
# =============================================================================
|
||||
# Bootable Image — firmware/bootloader
|
||||
# Расположение: tools/host/image/bootimg_bootloader.yaml
|
||||
#
|
||||
# Пайплайн сборки:
|
||||
#
|
||||
# 1. FCB (один раз, из tools/host/fdcb/):
|
||||
# nxpimage bootable-image fcb export -c fcb_w25q64_quad.yaml -o fcb.bin
|
||||
#
|
||||
# 2. Образ (из tools/host/image/):
|
||||
# nxpimage bootable-image export -c bootimg_bootloader.yaml
|
||||
#
|
||||
# 3. Проверка:
|
||||
# nxpimage bootable-image parse -b ../../../../build/Release/bootloader_bootable.bin -f mimxrt1050
|
||||
# =============================================================================
|
||||
|
||||
family: mimxrt1050
|
||||
revision: latest
|
||||
memory_type: flexspi_nor
|
||||
|
||||
output: "../../../build/Release/bootloader_bootable.bin"
|
||||
output_format: bin
|
||||
|
||||
fcb: ../fdcb/fcb.bin
|
||||
|
||||
hab_container: ../hab/hab_bootloader.yaml
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
# =============================================================================
|
||||
# Bootable Image — firmware/test (входной контроль производства)
|
||||
# Расположение: tools/host/image/bootimg_firmware_test.yaml
|
||||
#
|
||||
# Пайплайн сборки:
|
||||
#
|
||||
# 1. FCB (один раз, из tools/host/fdcb/):
|
||||
# nxpimage bootable-image fcb export -c fcb_w25q64_quad.yaml -o fcb.bin
|
||||
#
|
||||
# 2. Образ (из tools/host/image/):
|
||||
# nxpimage bootable-image export -c bootimg_firmware_test.yaml
|
||||
#
|
||||
# 3. Проверка:
|
||||
# nxpimage bootable-image parse -b ../../../../build/Debug/firmware_test_bootable.bin -f mimxrt1050
|
||||
#
|
||||
# Структура образа во Flash (0x60000000):
|
||||
# 0x000 FCB (512 байт)
|
||||
# 0x400 зарезервировано
|
||||
# 0x1000 IVT ]
|
||||
# 0x1020 BDT ] HAB container
|
||||
# 0x1030 DCD ]
|
||||
# 0x3000 код ]
|
||||
# =============================================================================
|
||||
|
||||
family: mimxrt1050
|
||||
revision: latest
|
||||
memory_type: flexspi_nor
|
||||
|
||||
output: "../../../build/Debug/firmware_test_bootable.bin"
|
||||
output_format: bin
|
||||
|
||||
# FCB — собран из tools/host/fdcb/fcb_w25q64_quad.yaml
|
||||
fcb: ../fdcb/fcb.bin
|
||||
|
||||
# HAB container — YAML напрямую, export соберёт его сам
|
||||
hab_container: ../hab/hab_firmware_test.yaml
|
||||
Loading…
Reference in a new issue