# 9
- работает загрузка подготовленного HAB бинарника по SWD в QSPI - после этого можно запускать отладку в VSCode devcontainer - От SeggerRTT отказался, логи будут в UART
This commit is contained in:
parent
ec2c02b957
commit
4570b27e71
17 changed files with 1321 additions and 832 deletions
13
.env.example
13
.env.example
|
|
@ -26,12 +26,19 @@ CMAKE_GENERATOR=Ninja
|
||||||
OPENOCD_INTERFACE=cmsis-dap.cfg
|
OPENOCD_INTERFACE=cmsis-dap.cfg
|
||||||
TARGET_CFG=target/imxrt.cfg
|
TARGET_CFG=target/imxrt.cfg
|
||||||
GDB_PORT=3333
|
GDB_PORT=3333
|
||||||
GDB_EXECUTABLE=gdb-multiarch
|
GDB_EXECUTABLE=arm-none-eabi-gdb
|
||||||
|
|
||||||
|
|
||||||
|
# pyOCD — таргет и частота для gdbserver и flash_swd.py
|
||||||
|
PYOCD_TARGET=mimxrt1050_quadspi
|
||||||
|
PYOCD_FREQUENCY=4000000
|
||||||
|
|
||||||
|
# FCB-бинарник для flash_swd.py (Flash Configuration Block, W25Q128 Quad SPI)
|
||||||
|
FCB_PATH=tools/host/dcd/w25q128_fdcb.bin
|
||||||
|
|
||||||
# --- HIL (аппаратный стенд) ---
|
# --- HIL (аппаратный стенд) ---
|
||||||
# VCOM-порт MCU-Link (Linux: /dev/ttyACM0, macOS: /dev/cu.usbmodem*)
|
|
||||||
HIL_VCOM_PORT=/dev/tty.usbmodemGUXFBWDJBWTGQ3
|
HIL_VCOM_PORT=/dev/tty.usbmodemGUXFBWDJBWTGQ3
|
||||||
HIL_VCOM_BAUD=115200 # default: 115200
|
HIL_VCOM_BAUD=115200 # default: 115200
|
||||||
HIL_READY_TIMEOUT=5.0 # default: 5.0 сек
|
HIL_READY_TIMEOUT=5.0 # default: 5.0 сек
|
||||||
HIL_PYOCD_FREQUENCY=1000000 # default: 1 МГц
|
HIL_PYOCD_FREQUENCY=1000000 # default: 1 МГц
|
||||||
№HIL_BUILD_DIR= # default: build/target-debug
|
HIL_BUILD_DIR=build/target-debug # default:
|
||||||
|
|
|
||||||
44
.vscode/launch.json
vendored
44
.vscode/launch.json
vendored
|
|
@ -15,21 +15,11 @@
|
||||||
"device": "MIMXRT1052CVJ5B",
|
"device": "MIMXRT1052CVJ5B",
|
||||||
"svdFile": "${workspaceFolder}/bsp/generated/startup/MIMXRT1052.xml",
|
"svdFile": "${workspaceFolder}/bsp/generated/startup/MIMXRT1052.xml",
|
||||||
"interface": "swd",
|
"interface": "swd",
|
||||||
"showDevDebugOutput": "raw",
|
|
||||||
"loadFiles": [],
|
"loadFiles": [],
|
||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"preLaunchTask": "build:firmware-test-debug",
|
"preLaunchTask": "build:firmware-test-debug",
|
||||||
"rttConfig": {
|
"rttConfig": {
|
||||||
"enabled": true,
|
"enabled": false
|
||||||
"address": "auto",
|
|
||||||
"clearSearch": false,
|
|
||||||
"decoders": [
|
|
||||||
{
|
|
||||||
"port": 0,
|
|
||||||
"type": "console",
|
|
||||||
"label": "RTT"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// =============================================================
|
// =============================================================
|
||||||
|
|
@ -42,25 +32,13 @@
|
||||||
"servertype": "external",
|
"servertype": "external",
|
||||||
"gdbTarget": "host.docker.internal:3333",
|
"gdbTarget": "host.docker.internal:3333",
|
||||||
"gdbPath": "arm-none-eabi-gdb",
|
"gdbPath": "arm-none-eabi-gdb",
|
||||||
"executable": "${workspaceFolder}/build/Debug/firmware/bootloader/bootloader.elf",
|
"executable": "${workspaceFolder}/build/Debug/bootloader.elf",
|
||||||
"device": "MIMXRT1052CVJ5B",
|
"device": "MIMXRT1052CVJ5B",
|
||||||
"svdFile": "${workspaceFolder}/bsp/generated/startup/MIMXRT1052.xml",
|
"svdFile": "${workspaceFolder}/bsp/generated/startup/MIMXRT1052.xml",
|
||||||
"interface": "swd",
|
"interface": "swd",
|
||||||
"loadFiles": [],
|
"loadFiles": [],
|
||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"preLaunchTask": "build:bootloader-debug",
|
"preLaunchTask": "build-and-rtt:firmware-test-debug",
|
||||||
"rttConfig": {
|
|
||||||
"enabled": true,
|
|
||||||
"address": "auto",
|
|
||||||
"clearSearch": false,
|
|
||||||
"decoders": [
|
|
||||||
{
|
|
||||||
"port": 0,
|
|
||||||
"type": "console",
|
|
||||||
"label": "RTT"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
// =============================================================
|
// =============================================================
|
||||||
// firmware/tft_app — FreeRTOS task view
|
// firmware/tft_app — FreeRTOS task view
|
||||||
|
|
@ -72,26 +50,14 @@
|
||||||
"servertype": "external",
|
"servertype": "external",
|
||||||
"gdbTarget": "host.docker.internal:3333",
|
"gdbTarget": "host.docker.internal:3333",
|
||||||
"gdbPath": "arm-none-eabi-gdb",
|
"gdbPath": "arm-none-eabi-gdb",
|
||||||
"executable": "${workspaceFolder}/build/Debug/firmware/tft_app/app.elf",
|
"executable": "${workspaceFolder}/build/Debug/app.elf",
|
||||||
"device": "MIMXRT1052CVJ5B",
|
"device": "MIMXRT1052CVJ5B",
|
||||||
"svdFile": "${workspaceFolder}/bsp/generated/startup/MIMXRT1052.xml",
|
"svdFile": "${workspaceFolder}/bsp/generated/startup/MIMXRT1052.xml",
|
||||||
"interface": "swd",
|
"interface": "swd",
|
||||||
"loadFiles": [],
|
"loadFiles": [],
|
||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"rtos": "FreeRTOS",
|
"rtos": "FreeRTOS",
|
||||||
"preLaunchTask": "build:app-debug",
|
"preLaunchTask": "build:firmware-test-debug",
|
||||||
"rttConfig": {
|
|
||||||
"enabled": true,
|
|
||||||
"address": "auto",
|
|
||||||
"clearSearch": false,
|
|
||||||
"decoders": [
|
|
||||||
{
|
|
||||||
"port": 0,
|
|
||||||
"type": "console",
|
|
||||||
"label": "RTT"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
11
.vscode/tasks.json
vendored
11
.vscode/tasks.json
vendored
|
|
@ -169,9 +169,6 @@
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
},
|
},
|
||||||
"problemMatcher": [
|
|
||||||
"$gcc"
|
|
||||||
],
|
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"reveal": "silent",
|
"reveal": "silent",
|
||||||
"panel": "shared"
|
"panel": "shared"
|
||||||
|
|
@ -188,9 +185,6 @@
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
},
|
},
|
||||||
"problemMatcher": [
|
|
||||||
"$gcc"
|
|
||||||
],
|
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"reveal": "silent",
|
"reveal": "silent",
|
||||||
"panel": "shared"
|
"panel": "shared"
|
||||||
|
|
@ -207,14 +201,11 @@
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
},
|
},
|
||||||
"problemMatcher": [
|
|
||||||
"$gcc"
|
|
||||||
],
|
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"reveal": "silent",
|
"reveal": "silent",
|
||||||
"panel": "shared"
|
"panel": "shared"
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
],
|
],
|
||||||
"inputs": [
|
"inputs": [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
375
FLASH_LOG.log
375
FLASH_LOG.log
|
|
@ -1,375 +0,0 @@
|
||||||
tft_manufacture_test git:(dev) ✗ uv run --directory tools/hil pyocd flash \
|
|
||||||
--target mimxrt1050_quadspi \
|
|
||||||
--base-address 0x60000000 -vvv \
|
|
||||||
--erase chip \
|
|
||||||
../../build/Debug/firmware_test_hab.bin
|
|
||||||
0000149 D Project directory: /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/hil [session]
|
|
||||||
0000150 D Project directory: /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/tools/hil [session]
|
|
||||||
0000152 D CMSIS-DAP v2 probe GUXFBWDJBWTGQ: firmware version 3.160, protocol version 2.1.1 [dap_access_cmsis_dap]
|
|
||||||
0000154 I Target type is mimxrt1050_quadspi [board]
|
|
||||||
0000157 D Running task load_svd [sequencer]
|
|
||||||
0000157 D Running task pre_connect [sequencer]
|
|
||||||
0000157 D Running task dp_init [sequencer]
|
|
||||||
0000157 D Running task lock_probe [sequencer]
|
|
||||||
0000157 D Running task get_probe_capabilities [sequencer]
|
|
||||||
0000157 D Running task connect [sequencer]
|
|
||||||
0000161 D Default wire protocol selected; using SWD [dap]
|
|
||||||
0000162 D Sending deprecated SWJ sequence to select SWD [swj]
|
|
||||||
0000164 I DP IDR = 0x0bd11477 (v1 MINDP rev0) [dap]
|
|
||||||
0000164 D Running task clear_sticky_err [sequencer]
|
|
||||||
0000164 D Running task power_up_debug [sequencer]
|
|
||||||
0000165 D Running task check_version [sequencer]
|
|
||||||
0000165 D Running task unlock_probe [sequencer]
|
|
||||||
0000165 D Running task unlock_device [sequencer]
|
|
||||||
0000165 D Running task create_discoverer [sequencer]
|
|
||||||
0000165 D Running task discovery [sequencer]
|
|
||||||
0000165 D Running task find_aps [sequencer]
|
|
||||||
0000169 D Running task create_aps [sequencer]
|
|
||||||
0000169 D Running task create_ap.0 [sequencer]
|
|
||||||
0000179 D AHB-AP#0 default HPROT=3 HNONSEC=0 [ap]
|
|
||||||
0000180 D AHB-AP#0 implemented HPROT=f HNONSEC=0 [ap]
|
|
||||||
0000181 I AHB-AP#0 IDR = 0x04770041 (AHB-AP var4 rev0) [discovery]
|
|
||||||
0000181 D Running task find_components [sequencer]
|
|
||||||
0000181 D Running task init_ap.0 [sequencer]
|
|
||||||
0000185 I AHB-AP#0 Class 0x1 ROM table #0 @ 0xe00fd000 (designer=00e:NXP part=88c) [rom_table]
|
|
||||||
0000187 I [0]<e00fe000:ROM class=1 designer=43b:Arm part=4c8> [rom_table]
|
|
||||||
0000187 I AHB-AP#0 Class 0x1 ROM table #1 @ 0xe00fe000 (designer=43b:Arm part=4c8) [rom_table]
|
|
||||||
0000191 I [0]<e00ff000:ROM class=1 designer=43b:Arm part=4c7> [rom_table]
|
|
||||||
0000191 I AHB-AP#0 Class 0x1 ROM table #2 @ 0xe00ff000 (designer=43b:Arm part=4c7) [rom_table]
|
|
||||||
0000194 I [0]<e000e000:SCS v7-M class=14 designer=43b:Arm part=00c> [rom_table]
|
|
||||||
0000207 I [1]<e0001000:DWT v7-M class=14 designer=43b:Arm part=002> [rom_table]
|
|
||||||
0000209 I [2]<e0002000:FPB v7-M class=14 designer=43b:Arm part=00e> [rom_table]
|
|
||||||
0000210 I [3]<e0000000:ITM v7-M class=14 designer=43b:Arm part=001> [rom_table]
|
|
||||||
0000210 D [4]<fff41002 not present> [rom_table]
|
|
||||||
0000210 D [5]<fff42002 not present> [rom_table]
|
|
||||||
0000214 I [1]<e0041000:ETM M7 class=9 designer=43b:Arm part=975 devtype=13 archid=4a13 devid=0:0:0> [rom_table]
|
|
||||||
0000219 I [2]<e0042000:CTI CS-400 class=9 designer=43b:Arm part=906 devtype=14 archid=0000 devid=40800:0:0> [rom_table]
|
|
||||||
0000219 D [3]<1ff02002 not present> [rom_table]
|
|
||||||
0000221 I [1]<e0040000:TPIU M7 class=9 designer=43b:Arm part=9a9 devtype=11 archid=0000 devid=ca1:0:0> [rom_table]
|
|
||||||
0000222 I [2]<e0043000:TSGEN class=15 designer=43b:Arm part=101> [rom_table]
|
|
||||||
0000222 D [3]<1ff03002 not present> [rom_table]
|
|
||||||
0000222 D Running task create_cores [sequencer]
|
|
||||||
0000239 I IMXRT Boot Mode: Internal Boot [target_imxrt]
|
|
||||||
0000242 I CPU core #0: Cortex-M7 r1p1, v7.0-M architecture [cortex_m]
|
|
||||||
0000243 I Extensions: [DSP, FPU, FPU_DP, FPU_V5, MPU] [cortex_m]
|
|
||||||
0000243 I FPU present: FPv5-D16-M [cortex_m]
|
|
||||||
0000244 D selected core #0 [soc_target]
|
|
||||||
0000244 D Running task create_components [sequencer]
|
|
||||||
0000244 D Creating DWT component [discovery]
|
|
||||||
0000246 I 4 hardware watchpoints [dwt]
|
|
||||||
0000246 D Creating FPB component [discovery]
|
|
||||||
0000247 I 8 hardware breakpoints, 1 literal comparators [fpb]
|
|
||||||
0000247 D fpb has been disabled [fpb]
|
|
||||||
0000247 D Creating ITM component [discovery]
|
|
||||||
0000249 D Creating TPIU component [discovery]
|
|
||||||
0000250 D Running task check_for_cores [sequencer]
|
|
||||||
0000250 D Running task halt_on_connect [sequencer]
|
|
||||||
0000250 D halting core 0 [cortex_m]
|
|
||||||
0000250 D Running task post_connect [sequencer]
|
|
||||||
0000250 D Running task post_connect_hook [sequencer]
|
|
||||||
0000250 D Running task create_flash [sequencer]
|
|
||||||
0000251 D Running task notify [sequencer]
|
|
||||||
0000251 I IMXRT Boot Mode: Internal Boot [target_imxrt]
|
|
||||||
0000252 I IMXRT Boot Device: 0 [target_imxrt]
|
|
||||||
0000252 D normal_set_reset_catch [target_imxrt]
|
|
||||||
0000252 D set reset catch, core 0 [cortex_m]
|
|
||||||
0000252 D removing all breakpoints before reset [manager]
|
|
||||||
0000252 D halting core 0 [cortex_m]
|
|
||||||
0000254 D reset, core 0, type=VECTRESET [cortex_m]
|
|
||||||
0000254 D no delegate or debug sequence handled reset; performing VECTRESET reset, core 0 [cortex_m]
|
|
||||||
0000364 D clear reset catch, core 0 [cortex_m]
|
|
||||||
0000365 D disabling hardware breakpoints after reset [manager]
|
|
||||||
0000366 I Loading /Users/von_akimow/Desktop/TFT_ENV/tft_manufacture_test/build/Debug/firmware_test_hab.bin at 0x60000000 [load_cmd]
|
|
||||||
0000367 D Using double buffer chip erase program [builder]
|
|
||||||
0000367 D 0 of 89 pages have erased data [builder]
|
|
||||||
0000367 D halting core 0 [cortex_m]
|
|
||||||
0000586 D resuming core 0 [cortex_m]
|
|
||||||
0000586 D added=[] removed=[] [manager]
|
|
||||||
0000586 D bps after flush={} [manager]
|
|
||||||
0000592 D resuming core 0 [cortex_m]
|
|
||||||
0000592 D added=[] removed=[] [manager]
|
|
||||||
0000593 D bps after flush={} [manager]
|
|
||||||
0031381 D resuming core 0 [cortex_m]
|
|
||||||
0031381 D added=[] removed=[] [manager]
|
|
||||||
0031381 D bps after flush={} [manager]
|
|
||||||
[ ] 1%0031383 D halting core 0 [cortex_m]
|
|
||||||
0031392 D resuming core 0 [cortex_m]
|
|
||||||
0031392 D added=[] removed=[] [manager]
|
|
||||||
0031392 D bps after flush={} [manager]
|
|
||||||
0031396 D resuming core 0 [cortex_m]
|
|
||||||
0031396 D added=[] removed=[] [manager]
|
|
||||||
0031396 D bps after flush={} [manager]
|
|
||||||
[= ] 3%0031404 D resuming core 0 [cortex_m]
|
|
||||||
0031404 D added=[] removed=[] [manager]
|
|
||||||
0031404 D bps after flush={} [manager]
|
|
||||||
[= ] 4%0031412 D resuming core 0 [cortex_m]
|
|
||||||
0031412 D added=[] removed=[] [manager]
|
|
||||||
0031412 D bps after flush={} [manager]
|
|
||||||
[== ] 5%0031419 D resuming core 0 [cortex_m]
|
|
||||||
0031419 D added=[] removed=[] [manager]
|
|
||||||
0031419 D bps after flush={} [manager]
|
|
||||||
[== ] 6%0031427 D resuming core 0 [cortex_m]
|
|
||||||
0031427 D added=[] removed=[] [manager]
|
|
||||||
0031427 D bps after flush={} [manager]
|
|
||||||
[=== ] 7%0031435 D resuming core 0 [cortex_m]
|
|
||||||
0031435 D added=[] removed=[] [manager]
|
|
||||||
0031435 D bps after flush={} [manager]
|
|
||||||
[==== ] 8%0031442 D resuming core 0 [cortex_m]
|
|
||||||
0031442 D added=[] removed=[] [manager]
|
|
||||||
0031442 D bps after flush={} [manager]
|
|
||||||
[==== ] 9%0031450 D resuming core 0 [cortex_m]
|
|
||||||
0031450 D added=[] removed=[] [manager]
|
|
||||||
0031450 D bps after flush={} [manager]
|
|
||||||
[===== ] 10%0031458 D resuming core 0 [cortex_m]
|
|
||||||
0031458 D added=[] removed=[] [manager]
|
|
||||||
0031458 D bps after flush={} [manager]
|
|
||||||
[===== ] 11%0031465 D resuming core 0 [cortex_m]
|
|
||||||
0031465 D added=[] removed=[] [manager]
|
|
||||||
0031465 D bps after flush={} [manager]
|
|
||||||
[====== ] 12%0031473 D resuming core 0 [cortex_m]
|
|
||||||
0031473 D added=[] removed=[] [manager]
|
|
||||||
0031473 D bps after flush={} [manager]
|
|
||||||
[====== ] 14%0031481 D resuming core 0 [cortex_m]
|
|
||||||
0031481 D added=[] removed=[] [manager]
|
|
||||||
0031481 D bps after flush={} [manager]
|
|
||||||
[======= ] 15%0031489 D resuming core 0 [cortex_m]
|
|
||||||
0031489 D added=[] removed=[] [manager]
|
|
||||||
0031489 D bps after flush={} [manager]
|
|
||||||
[======= ] 16%0031496 D resuming core 0 [cortex_m]
|
|
||||||
0031496 D added=[] removed=[] [manager]
|
|
||||||
0031497 D bps after flush={} [manager]
|
|
||||||
[======== ] 17%0031504 D resuming core 0 [cortex_m]
|
|
||||||
0031504 D added=[] removed=[] [manager]
|
|
||||||
0031504 D bps after flush={} [manager]
|
|
||||||
[========= ] 18%0031512 D resuming core 0 [cortex_m]
|
|
||||||
0031512 D added=[] removed=[] [manager]
|
|
||||||
0031512 D bps after flush={} [manager]
|
|
||||||
[========= ] 19%0031520 D resuming core 0 [cortex_m]
|
|
||||||
0031520 D added=[] removed=[] [manager]
|
|
||||||
0031520 D bps after flush={} [manager]
|
|
||||||
[========== ] 20%0031528 D resuming core 0 [cortex_m]
|
|
||||||
0031528 D added=[] removed=[] [manager]
|
|
||||||
0031528 D bps after flush={} [manager]
|
|
||||||
[========== ] 21%0031536 D resuming core 0 [cortex_m]
|
|
||||||
0031536 D added=[] removed=[] [manager]
|
|
||||||
0031536 D bps after flush={} [manager]
|
|
||||||
[=========== ] 22%0031544 D resuming core 0 [cortex_m]
|
|
||||||
0031544 D added=[] removed=[] [manager]
|
|
||||||
0031544 D bps after flush={} [manager]
|
|
||||||
[=========== ] 24%0031552 D resuming core 0 [cortex_m]
|
|
||||||
0031552 D added=[] removed=[] [manager]
|
|
||||||
0031552 D bps after flush={} [manager]
|
|
||||||
[============ ] 25%0031560 D resuming core 0 [cortex_m]
|
|
||||||
0031560 D added=[] removed=[] [manager]
|
|
||||||
0031560 D bps after flush={} [manager]
|
|
||||||
[============ ] 26%0031568 D resuming core 0 [cortex_m]
|
|
||||||
0031568 D added=[] removed=[] [manager]
|
|
||||||
0031568 D bps after flush={} [manager]
|
|
||||||
[============= ] 27%0031576 D resuming core 0 [cortex_m]
|
|
||||||
0031576 D added=[] removed=[] [manager]
|
|
||||||
0031577 D bps after flush={} [manager]
|
|
||||||
[============= ] 28%0031584 D resuming core 0 [cortex_m]
|
|
||||||
0031585 D added=[] removed=[] [manager]
|
|
||||||
0031585 D bps after flush={} [manager]
|
|
||||||
[============== ] 29%0031593 D resuming core 0 [cortex_m]
|
|
||||||
0031593 D added=[] removed=[] [manager]
|
|
||||||
0031593 D bps after flush={} [manager]
|
|
||||||
[=============== ] 30%0031601 D resuming core 0 [cortex_m]
|
|
||||||
0031601 D added=[] removed=[] [manager]
|
|
||||||
0031601 D bps after flush={} [manager]
|
|
||||||
[=============== ] 31%0031609 D resuming core 0 [cortex_m]
|
|
||||||
0031609 D added=[] removed=[] [manager]
|
|
||||||
0031609 D bps after flush={} [manager]
|
|
||||||
[================ ] 32%0031617 D resuming core 0 [cortex_m]
|
|
||||||
0031617 D added=[] removed=[] [manager]
|
|
||||||
0031617 D bps after flush={} [manager]
|
|
||||||
[================ ] 34%0031626 D resuming core 0 [cortex_m]
|
|
||||||
0031626 D added=[] removed=[] [manager]
|
|
||||||
0031626 D bps after flush={} [manager]
|
|
||||||
[================= ] 35%0031634 D resuming core 0 [cortex_m]
|
|
||||||
0031634 D added=[] removed=[] [manager]
|
|
||||||
0031634 D bps after flush={} [manager]
|
|
||||||
[================= ] 36%0031643 D resuming core 0 [cortex_m]
|
|
||||||
0031643 D added=[] removed=[] [manager]
|
|
||||||
0031643 D bps after flush={} [manager]
|
|
||||||
[================== ] 37%0031651 D resuming core 0 [cortex_m]
|
|
||||||
0031651 D added=[] removed=[] [manager]
|
|
||||||
0031651 D bps after flush={} [manager]
|
|
||||||
[================== ] 38%0031660 D resuming core 0 [cortex_m]
|
|
||||||
0031660 D added=[] removed=[] [manager]
|
|
||||||
0031660 D bps after flush={} [manager]
|
|
||||||
[=================== ] 39%0031668 D resuming core 0 [cortex_m]
|
|
||||||
0031668 D added=[] removed=[] [manager]
|
|
||||||
0031668 D bps after flush={} [manager]
|
|
||||||
[==================== ] 40%0031677 D resuming core 0 [cortex_m]
|
|
||||||
0031677 D added=[] removed=[] [manager]
|
|
||||||
0031677 D bps after flush={} [manager]
|
|
||||||
[==================== ] 41%0031686 D resuming core 0 [cortex_m]
|
|
||||||
0031686 D added=[] removed=[] [manager]
|
|
||||||
0031686 D bps after flush={} [manager]
|
|
||||||
[===================== ] 42%0031694 D resuming core 0 [cortex_m]
|
|
||||||
0031694 D added=[] removed=[] [manager]
|
|
||||||
0031694 D bps after flush={} [manager]
|
|
||||||
[===================== ] 44%0031703 D resuming core 0 [cortex_m]
|
|
||||||
0031703 D added=[] removed=[] [manager]
|
|
||||||
0031703 D bps after flush={} [manager]
|
|
||||||
[====================== ] 45%0031712 D resuming core 0 [cortex_m]
|
|
||||||
0031712 D added=[] removed=[] [manager]
|
|
||||||
0031712 D bps after flush={} [manager]
|
|
||||||
[====================== ] 46%0031721 D resuming core 0 [cortex_m]
|
|
||||||
0031721 D added=[] removed=[] [manager]
|
|
||||||
0031721 D bps after flush={} [manager]
|
|
||||||
[======================= ] 47%0031729 D resuming core 0 [cortex_m]
|
|
||||||
0031729 D added=[] removed=[] [manager]
|
|
||||||
0031729 D bps after flush={} [manager]
|
|
||||||
[======================= ] 48%0031738 D resuming core 0 [cortex_m]
|
|
||||||
0031738 D added=[] removed=[] [manager]
|
|
||||||
0031738 D bps after flush={} [manager]
|
|
||||||
[======================== ] 49%0031747 D resuming core 0 [cortex_m]
|
|
||||||
0031747 D added=[] removed=[] [manager]
|
|
||||||
0031747 D bps after flush={} [manager]
|
|
||||||
[========================= ] 50%0031756 D resuming core 0 [cortex_m]
|
|
||||||
0031756 D added=[] removed=[] [manager]
|
|
||||||
0031756 D bps after flush={} [manager]
|
|
||||||
[========================= ] 51%0031765 D resuming core 0 [cortex_m]
|
|
||||||
0031765 D added=[] removed=[] [manager]
|
|
||||||
0031765 D bps after flush={} [manager]
|
|
||||||
[========================== ] 52%0031773 D resuming core 0 [cortex_m]
|
|
||||||
0031773 D added=[] removed=[] [manager]
|
|
||||||
0031773 D bps after flush={} [manager]
|
|
||||||
[========================== ] 53%0031782 D resuming core 0 [cortex_m]
|
|
||||||
0031782 D added=[] removed=[] [manager]
|
|
||||||
0031782 D bps after flush={} [manager]
|
|
||||||
[=========================== ] 55%0031791 D resuming core 0 [cortex_m]
|
|
||||||
0031791 D added=[] removed=[] [manager]
|
|
||||||
0031791 D bps after flush={} [manager]
|
|
||||||
[=========================== ] 56%0031800 D resuming core 0 [cortex_m]
|
|
||||||
0031800 D added=[] removed=[] [manager]
|
|
||||||
0031800 D bps after flush={} [manager]
|
|
||||||
[============================ ] 57%0031809 D resuming core 0 [cortex_m]
|
|
||||||
0031809 D added=[] removed=[] [manager]
|
|
||||||
0031809 D bps after flush={} [manager]
|
|
||||||
[============================ ] 58%0031818 D resuming core 0 [cortex_m]
|
|
||||||
0031818 D added=[] removed=[] [manager]
|
|
||||||
0031818 D bps after flush={} [manager]
|
|
||||||
[============================= ] 59%0031827 D resuming core 0 [cortex_m]
|
|
||||||
0031827 D added=[] removed=[] [manager]
|
|
||||||
0031827 D bps after flush={} [manager]
|
|
||||||
[============================== ] 60%0031836 D resuming core 0 [cortex_m]
|
|
||||||
0031836 D added=[] removed=[] [manager]
|
|
||||||
0031836 D bps after flush={} [manager]
|
|
||||||
[============================== ] 61%0031845 D resuming core 0 [cortex_m]
|
|
||||||
0031845 D added=[] removed=[] [manager]
|
|
||||||
0031845 D bps after flush={} [manager]
|
|
||||||
[=============================== ] 62%0031854 D resuming core 0 [cortex_m]
|
|
||||||
0031854 D added=[] removed=[] [manager]
|
|
||||||
0031854 D bps after flush={} [manager]
|
|
||||||
[=============================== ] 63%0031863 D resuming core 0 [cortex_m]
|
|
||||||
0031863 D added=[] removed=[] [manager]
|
|
||||||
0031863 D bps after flush={} [manager]
|
|
||||||
[================================ ] 65%0031872 D resuming core 0 [cortex_m]
|
|
||||||
0031872 D added=[] removed=[] [manager]
|
|
||||||
0031872 D bps after flush={} [manager]
|
|
||||||
[================================ ] 66%0031881 D resuming core 0 [cortex_m]
|
|
||||||
0031881 D added=[] removed=[] [manager]
|
|
||||||
0031881 D bps after flush={} [manager]
|
|
||||||
[================================= ] 67%0031890 D resuming core 0 [cortex_m]
|
|
||||||
0031890 D added=[] removed=[] [manager]
|
|
||||||
0031890 D bps after flush={} [manager]
|
|
||||||
[================================= ] 68%0031899 D resuming core 0 [cortex_m]
|
|
||||||
0031899 D added=[] removed=[] [manager]
|
|
||||||
0031899 D bps after flush={} [manager]
|
|
||||||
[================================== ] 69%0031908 D resuming core 0 [cortex_m]
|
|
||||||
0031908 D added=[] removed=[] [manager]
|
|
||||||
0031908 D bps after flush={} [manager]
|
|
||||||
[=================================== ] 70%0031918 D resuming core 0 [cortex_m]
|
|
||||||
0031918 D added=[] removed=[] [manager]
|
|
||||||
0031918 D bps after flush={} [manager]
|
|
||||||
[=================================== ] 71%0031927 D resuming core 0 [cortex_m]
|
|
||||||
0031927 D added=[] removed=[] [manager]
|
|
||||||
0031927 D bps after flush={} [manager]
|
|
||||||
[==================================== ] 72%0031936 D resuming core 0 [cortex_m]
|
|
||||||
0031936 D added=[] removed=[] [manager]
|
|
||||||
0031936 D bps after flush={} [manager]
|
|
||||||
[==================================== ] 73%0031945 D resuming core 0 [cortex_m]
|
|
||||||
0031945 D added=[] removed=[] [manager]
|
|
||||||
0031945 D bps after flush={} [manager]
|
|
||||||
[===================================== ] 75%0031954 D resuming core 0 [cortex_m]
|
|
||||||
0031954 D added=[] removed=[] [manager]
|
|
||||||
0031954 D bps after flush={} [manager]
|
|
||||||
[===================================== ] 76%0031963 D resuming core 0 [cortex_m]
|
|
||||||
0031963 D added=[] removed=[] [manager]
|
|
||||||
0031963 D bps after flush={} [manager]
|
|
||||||
[====================================== ] 77%0031972 D resuming core 0 [cortex_m]
|
|
||||||
0031972 D added=[] removed=[] [manager]
|
|
||||||
0031972 D bps after flush={} [manager]
|
|
||||||
[====================================== ] 78%0031981 D resuming core 0 [cortex_m]
|
|
||||||
0031981 D added=[] removed=[] [manager]
|
|
||||||
0031981 D bps after flush={} [manager]
|
|
||||||
[======================================= ] 79%0031990 D resuming core 0 [cortex_m]
|
|
||||||
0031990 D added=[] removed=[] [manager]
|
|
||||||
0031990 D bps after flush={} [manager]
|
|
||||||
[======================================== ] 80%0032000 D resuming core 0 [cortex_m]
|
|
||||||
0032000 D added=[] removed=[] [manager]
|
|
||||||
0032000 D bps after flush={} [manager]
|
|
||||||
[======================================== ] 81%0032009 D resuming core 0 [cortex_m]
|
|
||||||
0032009 D added=[] removed=[] [manager]
|
|
||||||
0032009 D bps after flush={} [manager]
|
|
||||||
[========================================= ] 82%0032018 D resuming core 0 [cortex_m]
|
|
||||||
0032018 D added=[] removed=[] [manager]
|
|
||||||
0032018 D bps after flush={} [manager]
|
|
||||||
[========================================= ] 83%0032027 D resuming core 0 [cortex_m]
|
|
||||||
0032027 D added=[] removed=[] [manager]
|
|
||||||
0032027 D bps after flush={} [manager]
|
|
||||||
[========================================== ] 84%0032036 D resuming core 0 [cortex_m]
|
|
||||||
0032036 D added=[] removed=[] [manager]
|
|
||||||
0032036 D bps after flush={} [manager]
|
|
||||||
[========================================== ] 86%0032045 D resuming core 0 [cortex_m]
|
|
||||||
0032045 D added=[] removed=[] [manager]
|
|
||||||
0032045 D bps after flush={} [manager]
|
|
||||||
[=========================================== ] 87%0032054 D resuming core 0 [cortex_m]
|
|
||||||
0032054 D added=[] removed=[] [manager]
|
|
||||||
0032054 D bps after flush={} [manager]
|
|
||||||
[=========================================== ] 88%0032063 D resuming core 0 [cortex_m]
|
|
||||||
0032063 D added=[] removed=[] [manager]
|
|
||||||
0032063 D bps after flush={} [manager]
|
|
||||||
[============================================ ] 89%0032072 D resuming core 0 [cortex_m]
|
|
||||||
0032072 D added=[] removed=[] [manager]
|
|
||||||
0032072 D bps after flush={} [manager]
|
|
||||||
[============================================= ] 90%0032082 D resuming core 0 [cortex_m]
|
|
||||||
0032082 D added=[] removed=[] [manager]
|
|
||||||
0032082 D bps after flush={} [manager]
|
|
||||||
[============================================= ] 91%0032091 D resuming core 0 [cortex_m]
|
|
||||||
0032091 D added=[] removed=[] [manager]
|
|
||||||
0032091 D bps after flush={} [manager]
|
|
||||||
[============================================== ] 92%0032100 D resuming core 0 [cortex_m]
|
|
||||||
0032100 D added=[] removed=[] [manager]
|
|
||||||
0032100 D bps after flush={} [manager]
|
|
||||||
[============================================== ] 93%0032109 D resuming core 0 [cortex_m]
|
|
||||||
0032110 D added=[] removed=[] [manager]
|
|
||||||
0032110 D bps after flush={} [manager]
|
|
||||||
[=============================================== ] 94%0032118 D resuming core 0 [cortex_m]
|
|
||||||
0032119 D added=[] removed=[] [manager]
|
|
||||||
0032119 D bps after flush={} [manager]
|
|
||||||
[=============================================== ] 96%0032127 D resuming core 0 [cortex_m]
|
|
||||||
0032127 D added=[] removed=[] [manager]
|
|
||||||
0032127 D bps after flush={} [manager]
|
|
||||||
[================================================ ] 97%0032136 D resuming core 0 [cortex_m]
|
|
||||||
0032136 D added=[] removed=[] [manager]
|
|
||||||
0032136 D bps after flush={} [manager]
|
|
||||||
[================================================ ] 98%0032146 D resuming core 0 [cortex_m]
|
|
||||||
0032146 D added=[] removed=[] [manager]
|
|
||||||
0032146 D bps after flush={} [manager]
|
|
||||||
[================================================= ] 99%0032155 D resuming core 0 [cortex_m]
|
|
||||||
0032155 D added=[] removed=[] [manager]
|
|
||||||
0032155 D bps after flush={} [manager]
|
|
||||||
[==================================================] 100%
|
|
||||||
0032159 D resuming core 0 [cortex_m]
|
|
||||||
0032159 D added=[] removed=[] [manager]
|
|
||||||
0032159 D bps after flush={} [manager]
|
|
||||||
0032161 I Erased chip, programmed 22784 bytes (89 pages), skipped 0 bytes (0 pages) at 0.70 kB/s [loader]
|
|
||||||
0032162 D reset, core 0, type=VECTRESET [cortex_m]
|
|
||||||
0032162 D no delegate or debug sequence handled reset; performing VECTRESET reset, core 0 [cortex_m]
|
|
||||||
0032269 D uninit session <pyocd.core.session.Session object at 0x102979420> [session]
|
|
||||||
0032270 D uninit board <pyocd.board.board.Board object at 0x1048020e0> [board]
|
|
||||||
0032271 D cannot resume core 0: core is RUNNING [cortex_m]
|
|
||||||
152
TODO.md
Normal file
152
TODO.md
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
# Контекст проекта — состояние на текущий момент
|
||||||
|
|
||||||
|
## Проект
|
||||||
|
|
||||||
|
Монорепозиторий `tft_manufacture_test` для **MIMXRT1052CVJ5B** (Cortex-M7, NXP i.MX RT1052). Три firmware-проекта: `firmware/test` (входной контроль, bare-metal), `firmware/bootloader` (A/B обновление, bare-metal), `firmware/tft_app` (боевая прошивка, FreeRTOS). Сборка в devcontainer, операции с железом на хосте.
|
||||||
|
|
||||||
|
Стек: CMake + Ninja + CMakePresets, `just` как task runner (модули `build.just`, `host.just`, `ci.just`), `uv` для Python-зависимостей, devcontainer (VSCode + arm-none-eabi-gcc + clangd).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что сделано в этой сессии
|
||||||
|
|
||||||
|
### 1. Отладка через SWD — настроена и работает
|
||||||
|
|
||||||
|
**Архитектура:**
|
||||||
|
```
|
||||||
|
Хост: pyocd gdbserver :3333 ← MCU-Link (CMSIS-DAP) ← SWD ← MIMXRT1052
|
||||||
|
DevContainer: arm-none-eabi-gdb → host.docker.internal:3333
|
||||||
|
VSCode: cortex-debug (servertype: external)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Файлы:**
|
||||||
|
- `pyocd_debug.yaml` — конфиг pyOCD (`target_override: mimxrt1050_quadspi`, `frequency: 4000000`, `rtt.enabled: true`, `rtt.port: 4445`)
|
||||||
|
- `.vscode/launch.json` — три конфигурации cortex-debug
|
||||||
|
- `.vscode/tasks.json` — `preLaunchTask` для каждой конфигурации + `rtt:connect`
|
||||||
|
- `just/host.just` — рецепты `debug-server`, `flash-swd-*`
|
||||||
|
|
||||||
|
**Параметры из `.env`:**
|
||||||
|
```
|
||||||
|
GDB_PORT=3333
|
||||||
|
PYOCD_TARGET=mimxrt1050_quadspi
|
||||||
|
PYOCD_FREQUENCY=4000000
|
||||||
|
FCB_PATH=tools/host/dcd/w25q128_fdcb.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
**Конфигурации launch.json:**
|
||||||
|
- `🐛 Debug: firmware_test` — `build/Debug/firmware_test.elf`
|
||||||
|
- `🐛 Debug: bootloader` — `build/Debug/bootloader.elf`
|
||||||
|
- `🐛 Debug: tft_app (FreeRTOS)` — `build/Debug/app.elf`, `"rtos": "FreeRTOS"`
|
||||||
|
|
||||||
|
Все три: `loadFiles: []` (не перепрошивают), `runToEntryPoint: main`, SVD из `bsp/generated/startup/MIMXRT1052.xml`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Прошивка через SWD — настроена и работает
|
||||||
|
|
||||||
|
**Проблема:** GDB `load` не работает для XIP-прошивок на IMXRT — Boot ROM при cold-start читает FCB по `0x60000000`, а ELF-секции кладутся без FCB.
|
||||||
|
|
||||||
|
**Решение:** `tools/host/flash_swd.py` собирает итоговый образ:
|
||||||
|
```
|
||||||
|
0x60000000 w25q128_fdcb.bin (512 байт) — FCB для W25Q128 Quad SPI
|
||||||
|
0x60000200 0xFF × 3584 байт — padding
|
||||||
|
0x60001000 *_hab.bin — IVT + DCD + код
|
||||||
|
```
|
||||||
|
|
||||||
|
Всё в одном 64KB-секторе — стирается и записывается за одну транзакцию.
|
||||||
|
|
||||||
|
|
||||||
|
**Рецепты в host.just:**
|
||||||
|
```
|
||||||
|
just host::flash-swd-test-debug
|
||||||
|
just host::flash-swd-test-release
|
||||||
|
just host::flash-swd-bootloader-debug
|
||||||
|
just host::flash-swd-bootloader-release
|
||||||
|
just host::flash-swd-app-debug
|
||||||
|
just host::flash-swd-app-release
|
||||||
|
```
|
||||||
|
|
||||||
|
Скрипт читает конфигурацию из окружения (`PYOCD_TARGET`, `PYOCD_FREQUENCY`, `BUILD_DIR`, `FCB_PATH`). Относительные пути из `.env` автоматически разрешаются от `REPO_ROOT`.
|
||||||
|
|
||||||
|
**Рабочий цикл А (прошивка уже в Flash):**
|
||||||
|
```
|
||||||
|
just host::debug-server → VSCode: 🐛 Debug → F5
|
||||||
|
```
|
||||||
|
|
||||||
|
**Рабочий цикл Б (прошить через SWD + отладить):**
|
||||||
|
```
|
||||||
|
just build::hab-firmware-test-debug (в контейнере)
|
||||||
|
just host::flash-swd-test-debug (на хосте)
|
||||||
|
⚡ power cycle
|
||||||
|
just host::debug-server
|
||||||
|
VSCode: 🐛 Debug → F5
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. RTT — решено отказаться, заменить на UART
|
||||||
|
|
||||||
|
**Почему отказались от RTT:**
|
||||||
|
- `servertype: external` в cortex-debug официально не поддерживает RTT нативно
|
||||||
|
- Workaround через `postLaunchCommands` + `rtt_client.py` работает, но требует ручного запуска RTT-клиента после F5
|
||||||
|
- Автозапуск через `preLaunchTask` зависает (клиент стартует до того как pyOCD открыл порт)
|
||||||
|
- Итоговое UX: два ручных действия вместо одного
|
||||||
|
|
||||||
|
**Вывод:** RTT остаётся в проекте как возможность (код в `lib/SEGGER`, `SEGGER_RTT_ENABLED=ON` в Debug-пресете), но для логов использовать не будем.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. UART как канал логов — решение принято
|
||||||
|
|
||||||
|
**BSP:** `bsp/uart_host` — `bsp_uart_host_write_str()` / `bsp_uart_host_write()`.
|
||||||
|
|
||||||
|
TX — `LPUART_WriteBlocking` (blocking polling). Для логов это нормально:
|
||||||
|
- Bare-metal: нет проблем
|
||||||
|
- FreeRTOS: задача вытесняется по таймеру, но мьютекс нужен если несколько задач пишут лог
|
||||||
|
|
||||||
|
**Ограничения bsp_uart_host для логгера:**
|
||||||
|
- Нельзя вызывать из ISR
|
||||||
|
- В FreeRTOS-контексте нужен мьютекс на уровне логгера (не в bsp_uart_host)
|
||||||
|
|
||||||
|
**Мониторинг логов на хосте:**
|
||||||
|
```
|
||||||
|
just host::uart-monitor
|
||||||
|
```
|
||||||
|
Использует `python3 -m serial.tools.miniterm` (pyserial, кроссплатформенно — macOS/Linux/Windows). Порт и бод из `.env` (`HIL_VCOM_PORT`, `HIL_VCOM_BAUD`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Следующий шаг — слой логгирования
|
||||||
|
|
||||||
|
Планируется добавить логгер поверх `bsp_uart_host`. Кандидат — **log.c** (rxi, однофайловый, MIT). Нужно:
|
||||||
|
|
||||||
|
1. Вендоринг `log.h` / `log.c` в `lib/log/` или `utils/log/`
|
||||||
|
2. CMake-таргет
|
||||||
|
3. Callback-адаптер → `bsp_uart_host_write_str()`
|
||||||
|
4. Мьютекс для FreeRTOS (в callback, не в bsp)
|
||||||
|
5. Запрет вызова из ISR (задокументировать)
|
||||||
|
6. Красивый вывод: уровень, файл, строка, ANSI-цвета
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Структура файлов отладки (итог)
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── .env # GDB_PORT, PYOCD_*, RTT_PORT, FCB_PATH
|
||||||
|
├── .vscode/
|
||||||
|
│ ├── launch.json # 3 конфигурации cortex-debug
|
||||||
|
│ └── tasks.json # build:*, rtt:connect, uart-monitor
|
||||||
|
├── pyocd_debug.yaml # target, frequency, rtt config
|
||||||
|
├── bsp/generated/startup/MIMXRT1052.xml # SVD
|
||||||
|
├── just/host.just # debug-server, flash-swd-*, uart-monitor
|
||||||
|
└── tools/
|
||||||
|
├── hil/ # uv-проект: pyocd, pyserial, pytest
|
||||||
|
└── host/
|
||||||
|
├── flash_swd.py # FCB+HAB → pyocd flash
|
||||||
|
├── rtt_client.py # RTT TCP-клиент (retry-loop)
|
||||||
|
└── dcd/
|
||||||
|
└── w25q128_fdcb.bin # FCB для W25Q128 Quad SPI
|
||||||
|
```
|
||||||
228
docs/HOW_TO_DEBUG.md
Normal file
228
docs/HOW_TO_DEBUG.md
Normal file
|
|
@ -0,0 +1,228 @@
|
||||||
|
# Отладка прошивок через SWD + GDB
|
||||||
|
|
||||||
|
## Обзор архитектуры
|
||||||
|
|
||||||
|
Отладка построена на проброске GDB-сервера с хоста в devcontainer по TCP. Это позволяет держать весь инструментарий сборки и языковой сервер внутри контейнера, не проводя USB-пробник внутрь Docker.
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────┐ ┌──────────────────────────────────┐
|
||||||
|
│ Хост (macOS/Linux) │ │ DevContainer │
|
||||||
|
│ │ │ │
|
||||||
|
│ just host::debug-server │ │ VSCode + cortex-debug │
|
||||||
|
│ └─ pyocd gdbserver :3333 ──────────┼─────┼──► arm-none-eabi-gdb │
|
||||||
|
│ │TCP │ └─ символы из .elf │
|
||||||
|
│ MCU-Link (CMSIS-DAP) │3333 │ │
|
||||||
|
│ └─ SWD ──► MIMXRT1052 │ │ RTT Console (SEGGER RTT логи) │
|
||||||
|
│ Flash / SDRAM │ │ Peripherals (SVD регистры) │
|
||||||
|
│ SEGGER RTT буфер │ │ RTOS view (FreeRTOS задачи) │
|
||||||
|
└─────────────────────────────────────┘ └──────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ключевой принцип:** `pyocd gdbserver` запускается на хосте и слушает на `0.0.0.0:3333`. Из контейнера GDB подключается через `host.docker.internal:3333` — специальный DNS-алиас Docker, который резолвится в IP хост-машины.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Компоненты
|
||||||
|
|
||||||
|
### На хосте
|
||||||
|
|
||||||
|
| Компонент | Роль | Источник |
|
||||||
|
|---|---|---|
|
||||||
|
| `pyocd` | GDB-сервер + flash-программатор | `tools/hil/uv.lock` |
|
||||||
|
| `MCU-Link` | CMSIS-DAP v2 пробник | USB к плате |
|
||||||
|
| `just host::debug-server` | Запуск GDB-сервера | `just/host.just` |
|
||||||
|
| `just host::flash-swd-*` | Прошивка через SWD | `just/host.just` |
|
||||||
|
| `tools/host/flash_swd.py` | Сборка FCB+HAB образа и запись | `tools/host/` |
|
||||||
|
| `tools/host/dcd/w25q128_fdcb.bin` | FCB для W25Q128 (Quad SPI) | NXP SecureProvisioningTool |
|
||||||
|
|
||||||
|
### В devcontainer
|
||||||
|
|
||||||
|
| Компонент | Роль |
|
||||||
|
|---|---|
|
||||||
|
| `arm-none-eabi-gdb` | GDB клиент, подключается к серверу на хосте |
|
||||||
|
| `cortex-debug` (VSCode extension) | UI для GDB: брейкпоинты, стек, регистры |
|
||||||
|
| `.vscode/launch.json` | Конфигурации запуска отладки |
|
||||||
|
| `.vscode/tasks.json` | `preLaunchTask` — пересборка ELF перед стартом |
|
||||||
|
| `build/Debug/*.elf` | Символы для GDB (DWARF debug info) |
|
||||||
|
| `bsp/generated/startup/MIMXRT1052.xml` | SVD — описание регистров периферии |
|
||||||
|
|
||||||
|
### Конфигурация
|
||||||
|
|
||||||
|
Параметры отладки задаются в `.env` и автоматически экспортируются через `just` (`set export`), откуда наследуются скриптами:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# .env — секция Debug / SWD
|
||||||
|
GDB_PORT=3333
|
||||||
|
PYOCD_TARGET=mimxrt1050_quadspi
|
||||||
|
PYOCD_FREQUENCY=4000000
|
||||||
|
FCB_PATH=tools/host/dcd/w25q128_fdcb.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Прошивки, поддерживаемые отладкой
|
||||||
|
|
||||||
|
| Конфигурация VSCode | ELF | Особенности |
|
||||||
|
|---|---|---|
|
||||||
|
| `🐛 Debug: firmware_test` | `build/Debug/firmware_test.elf` | Bare-metal, входной контроль |
|
||||||
|
| `🐛 Debug: bootloader` | `build/Debug/bootloader.elf` | Bare-metal, A/B обновление |
|
||||||
|
| `🐛 Debug: tft_app (FreeRTOS)` | `build/Debug/app.elf` | FreeRTOS, task view |
|
||||||
|
|
||||||
|
Все три — XIP-прошивки, исполняются напрямую из QuadSPI NOR Flash (`0x60000000`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Режимы запуска отладки
|
||||||
|
|
||||||
|
### Режим А — прошивка уже в Flash
|
||||||
|
|
||||||
|
Стандартный ежедневный сценарий. Прошивка была залита ранее любым способом и исполняется на плате.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Хост — запустить GDB-сервер (оставить работать в отдельном терминале)
|
||||||
|
just host::debug-server
|
||||||
|
|
||||||
|
# 2. DevContainer — VSCode
|
||||||
|
# Run & Debug (Ctrl+Shift+D) → выбрать конфигурацию → F5
|
||||||
|
```
|
||||||
|
|
||||||
|
GDB сбрасывает MCU, загружает символы из ELF и останавливается на входе в `main`. Flash не перезаписывается.
|
||||||
|
|
||||||
|
### Режим Б — прошить через SWD, затем отладить
|
||||||
|
|
||||||
|
Когда нужно обновить прошивку без перевода платы в режим Serial Downloader. Удобно при итеративной разработке когда плата закреплена в стенде.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. DevContainer — собрать HAB-образ
|
||||||
|
just build::hab-firmware-test-debug
|
||||||
|
|
||||||
|
# 2. Хост — прошить через SWD (MCU-Link, без смены BOOT_MODE)
|
||||||
|
just host::flash-swd-test-debug
|
||||||
|
|
||||||
|
# 3. ⚡ Power cycle платы (обязательно — VECTRESET не реинициализирует FlexSPI)
|
||||||
|
|
||||||
|
# 4. Хост — запустить GDB-сервер
|
||||||
|
just host::debug-server
|
||||||
|
|
||||||
|
# 5. DevContainer — VSCode → 🐛 Debug: firmware_test → F5
|
||||||
|
```
|
||||||
|
|
||||||
|
### Режим В — прошить через USB SDP, затем отладить
|
||||||
|
|
||||||
|
Классический способ. Требует перевода платы в режим Serial Downloader (BOOT_MODE = 01).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. DevContainer — собрать
|
||||||
|
just build::build-firmware-test-debug
|
||||||
|
|
||||||
|
# 2. Хост — перевести плату в Serial Downloader mode, затем:
|
||||||
|
just host::flash-test-debug
|
||||||
|
|
||||||
|
# 3. Хост — запустить GDB-сервер
|
||||||
|
just host::debug-server
|
||||||
|
|
||||||
|
# 4. DevContainer — VSCode → 🐛 Debug: firmware_test → F5
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Почему flash через SWD требует FCB
|
||||||
|
|
||||||
|
При прошивке через USB SDP (режимы А и В) ROM-загрузчик сам инициализирует FlexSPI контроллер по DCD из HAB-образа — Flash Configuration Block ему не нужен.
|
||||||
|
|
||||||
|
При прошивке через SWD flash-алгоритм pyOCD записывает данные напрямую в NOR Flash. При cold-start Boot ROM первым делом читает FCB по адресу `0x60000000`, конфигурирует по нему FlexSPI, и только потом ищет IVT. Без FCB бутлоадер не может обратиться к Flash.
|
||||||
|
|
||||||
|
`flash_swd.py` решает это, собирая итоговый образ перед записью:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
0x60000000 w25q128_fdcb.bin (512 байт) — FCB: параметры W25Q128, Quad SPI
|
||||||
|
0x60000200 0xFF × 3584 байт — padding (значение стёртой ячейки)
|
||||||
|
0x60001000 firmware_test_hab.bin — IVT + DCD + код (ivtOffset = 0x1000)
|
||||||
|
```
|
||||||
|
|
||||||
|
Весь диапазон `0x60000000–0x6000FFFF` умещается в один 64KB-сектор Flash, поэтому стирается и записывается за одну транзакцию — FCB и HAB не перезаписывают друг друга.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## RTT-логи
|
||||||
|
|
||||||
|
SEGGER RTT включён только в Debug-сборках (`SEGGER_RTT_ENABLED=ON` в `CMakePresets.json`). В Release-сборках RTT отключён и символ `_SEGGER_RTT` в ELF отсутствует.
|
||||||
|
|
||||||
|
После старта отладки вкладка `TERMINAL → RTT` в VSCode принимает вывод из RTT-буфера канала 0. `cortex-debug` находит адрес буфера автоматически по символу `_SEGGER_RTT` из ELF (`address: auto` в `launch.json`).
|
||||||
|
|
||||||
|
Использование в коде:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "SEGGER_RTT.h"
|
||||||
|
|
||||||
|
SEGGER_RTT_printf(0, "value = %d\n", value);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FreeRTOS task view
|
||||||
|
|
||||||
|
Конфигурация `🐛 Debug: tft_app (FreeRTOS)` включает `"rtos": "FreeRTOS"` — cortex-debug разбирает внутренние структуры планировщика и показывает вкладку `RTOS` с таблицей задач: имя, состояние (`Running` / `Ready` / `Blocked` / `Suspended`), использование стека, приоритет. При паузе можно переключиться в контекст любой задачи и просмотреть её стек вызовов.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Просмотр регистров периферии
|
||||||
|
|
||||||
|
Вкладка `Peripherals` в панели отладки показывает все периферийные блоки MIMXRT1052 по SVD-файлу `bsp/generated/startup/MIMXRT1052.xml`. Значения регистров обновляются при каждой паузе. Можно раскрыть любой блок (GPIO, LPUART, USB, FlexSPI и т.д.) и просматривать поля побитово.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ограничения и важные замечания
|
||||||
|
|
||||||
|
**MCU-Link монопольный ресурс.** `debug-server` и `flash-swd` не могут работать одновременно — оба занимают пробник. Перед `flash-swd` остановите сервер (Ctrl+C), и наоборот.
|
||||||
|
|
||||||
|
**HIL-тесты vs отладка.** pyOCD также используется для HIL (загрузка ELF в RAM через `pyocd.yaml`). Перед запуском HIL-тестов (`just host::hil-run`) остановите GDB-сервер.
|
||||||
|
|
||||||
|
**Power cycle после flash-swd обязателен.** pyOCD завершает запись командой VECTRESET, которая не реинициализирует FlexSPI контроллер. Boot ROM при таком сбросе не может прочитать FCB и не стартует из Flash. Только полное отключение питания гарантирует корректный cold-start.
|
||||||
|
|
||||||
|
**Только Debug-сборки.** Отладка с символами возможна только для `Debug` CMake-пресета. Release-сборки компилируются с `-O2` без DWARF-символов.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Быстрый старт (первый запуск)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Убедиться что cortex-debug установлен в devcontainer
|
||||||
|
# .devcontainer/devcontainer.json → extensions: ["marus25.cortex-debug"]
|
||||||
|
|
||||||
|
# 2. Убедиться что в .devcontainer/devcontainer.json есть (для Linux-хостов):
|
||||||
|
# "runArgs": ["--add-host=host.docker.internal:host-gateway"]
|
||||||
|
|
||||||
|
# 3. Залить прошивку любым способом (один раз)
|
||||||
|
just host::flash-test-debug # USB SDP
|
||||||
|
# или
|
||||||
|
just host::flash-swd-test-debug # SWD (после just build::hab-firmware-test-debug)
|
||||||
|
|
||||||
|
# 4. Запустить GDB-сервер на хосте
|
||||||
|
just host::debug-server
|
||||||
|
|
||||||
|
# 5. В VSCode (devcontainer)
|
||||||
|
# Ctrl+Shift+D → 🐛 Debug: firmware_test → F5
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Дерево файлов отладки
|
||||||
|
|
||||||
|
```bash
|
||||||
|
.
|
||||||
|
├── .env # GDB_PORT, PYOCD_TARGET, PYOCD_FREQUENCY, FCB_PATH
|
||||||
|
├── .vscode/
|
||||||
|
│ ├── launch.json # Конфигурации cortex-debug (3 проекта)
|
||||||
|
│ └── tasks.json # preLaunchTask: build:*-debug
|
||||||
|
├── bsp/generated/startup/
|
||||||
|
│ └── MIMXRT1052.xml # SVD — регистры периферии
|
||||||
|
├── just/
|
||||||
|
│ └── host.just # debug-server, flash-swd-*
|
||||||
|
└── tools/
|
||||||
|
├── hil/ # uv-проект с pyocd
|
||||||
|
└── host/
|
||||||
|
├── flash_swd.py # FCB + HAB → Flash через pyOCD
|
||||||
|
└── dcd/
|
||||||
|
└── w25q128_fdcb.bin # FCB для W25Q128 Quad SPI
|
||||||
|
```
|
||||||
|
|
@ -1,10 +1,19 @@
|
||||||
# HOW TO FLASH
|
# HOW TO FLASH
|
||||||
|
|
||||||
Прошивка выполняется **на хосте** (вне devcontainer) через USB ROM (Serial Download Protocol).
|
Поддерживаются два независимых способа прошивки:
|
||||||
|
|
||||||
|
| Способ | Интерфейс | Требование | Инструмент |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **USB SDP** | USB ↔ ROM-загрузчик | BOOT_MODE = 01 (Serial Downloader) | `spsdk` (sdphost + blhost) |
|
||||||
|
| **SWD** | MCU-Link ↔ CMSIS-DAP | Плата в любом режиме загрузки | `pyocd` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Перевод платы в SDP-режим
|
## Способ 1 — USB SDP (Serial Download Protocol)
|
||||||
|
|
||||||
|
Стандартный производственный способ. ROM-загрузчик принимает образ по USB и записывает его во Flash через Flashloader. Требует физического переключения пина `BOOT_MOD_1`.
|
||||||
|
|
||||||
|
### 1.1 Перевести плату в SDP-режим
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
1. BOOT_MOD_1 → 3V3
|
1. BOOT_MOD_1 → 3V3
|
||||||
|
|
@ -19,11 +28,7 @@
|
||||||
BOOT_MOD_1 → GND → Reset
|
BOOT_MOD_1 → GND → Reset
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### 1.2 Подготовить HAB-образ (внутри devcontainer)
|
||||||
|
|
||||||
## 2. Подготовить HAB-образ (внутри devcontainer)
|
|
||||||
|
|
||||||
HAB-образ собирается из ELF-файла командой `nxpimage`. Выполнять в терминале VSCode:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
just build::hab-firmware-test-debug # → build/Debug/firmware_test_hab.bin
|
just build::hab-firmware-test-debug # → build/Debug/firmware_test_hab.bin
|
||||||
|
|
@ -33,53 +38,25 @@ just build::hab-app-release # → build/Release/app_hab.bin
|
||||||
just build::hab-all-release # все три Release за один раз
|
just build::hab-all-release # все три Release за один раз
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### 1.3 Прошить (хостовый терминал)
|
||||||
|
|
||||||
## 3. Прошивка (хостовый терминал)
|
|
||||||
|
|
||||||
### Запись во Flash
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Основной рецепт: just host::flash <project> <type>
|
# Запись во Flash
|
||||||
just host::flash firmware_test debug # разработка, итерации с отладчиком
|
just host::flash firmware_test debug
|
||||||
just host::flash firmware_test release # проверить как будет на сервере
|
just host::flash firmware_test release
|
||||||
just host::flash bootloader release
|
just host::flash bootloader release
|
||||||
just host::flash app release
|
just host::flash app release
|
||||||
```
|
|
||||||
|
|
||||||
### Загрузка в RAM (без записи во Flash)
|
# Загрузка в RAM (без записи во Flash — быстро, не изнашивает Flash)
|
||||||
|
|
||||||
Быстро, не изнашивает Flash. Плата стартует сразу после загрузки.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
just host::flash-ram firmware_test # default: debug
|
|
||||||
just host::flash-ram firmware_test debug
|
just host::flash-ram firmware_test debug
|
||||||
just host::flash-ram firmware_test release
|
|
||||||
```
|
|
||||||
|
|
||||||
### Быстрые алиасы
|
# Быстрые алиасы
|
||||||
|
just host::flash-test-debug # firmware_test debug → Flash
|
||||||
```bash
|
just host::flash-test-release # firmware_test release → Flash
|
||||||
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 (с подтверждением)
|
just host::flash-production # bootloader release + app release (с подтверждением)
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### 1.4 Что происходит при прошивке через USB SDP
|
||||||
|
|
||||||
## 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
|
```bash
|
||||||
Плата в SDP-режиме (1FC9:0130)
|
Плата в SDP-режиме (1FC9:0130)
|
||||||
|
|
@ -94,11 +71,102 @@ just host::check-deps # проверить версии just / uv / docke
|
||||||
└── reset
|
└── reset
|
||||||
```
|
```
|
||||||
|
|
||||||
|
ROM-загрузчик сам конфигурирует FlexSPI через DCD из HAB-образа, поэтому FCB
|
||||||
|
в образе не нужен — его пишет Flashloader отдельно.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Производственный сценарий (сервер)
|
## Способ 2 — SWD через MCU-Link
|
||||||
|
|
||||||
|
Прошивка через отладочный пробник (MCU-Link, CMSIS-DAP). Плата остаётся
|
||||||
|
в нормальном режиме загрузки — переключать `BOOT_MOD_1` не нужно. Удобно
|
||||||
|
при итеративной разработке когда плата закреплена в стенде, а также как
|
||||||
|
часть отладочного цикла.
|
||||||
|
|
||||||
|
**Ограничения:**
|
||||||
|
|
||||||
|
- После записи обязателен **power cycle** (не reset) — VECTRESET не реинициализирует FlexSPI, Boot ROM не стартует
|
||||||
|
- MCU-Link используется монопольно: нельзя запускать одновременно с `debug-server` или HIL-тестами
|
||||||
|
|
||||||
|
### 2.1 Подготовить HAB-образ (внутри devcontainer)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
just host::incoming # firmware_test release → HIL-тесты периферии
|
just build::hab-firmware-test-debug
|
||||||
|
just build::hab-bootloader-debug
|
||||||
|
just build::hab-app-debug
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 Прошить (хостовый терминал)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just host::flash-swd-test-debug # firmware_test Debug
|
||||||
|
just host::flash-swd-test-release # firmware_test Release
|
||||||
|
just host::flash-swd-bootloader-debug
|
||||||
|
just host::flash-swd-bootloader-release
|
||||||
|
just host::flash-swd-app-debug
|
||||||
|
just host::flash-swd-app-release
|
||||||
|
|
||||||
|
# После любого flash-swd — обязательно:
|
||||||
|
# ⚡ Отключить и подключить питание платы
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 Что происходит при прошивке через SWD
|
||||||
|
|
||||||
|
`flash_swd.py` собирает итоговый образ из двух частей перед записью:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
0x60000000 w25q128_fdcb.bin (512 байт) — FCB: параметры W25Q128, Quad SPI
|
||||||
|
0x60000200 0xFF × 3584 байт — padding (значение стёртой ячейки)
|
||||||
|
0x60001000 *_hab.bin — IVT + DCD + код (ivtOffset = 0x1000)
|
||||||
|
```
|
||||||
|
|
||||||
|
Весь диапазон умещается в один 64 KB сектор Flash — стирается и записывается
|
||||||
|
за одну транзакцию. FCB нужен потому что при cold-start Boot ROM читает его
|
||||||
|
первым, конфигурирует по нему FlexSPI, и только потом ищет IVT. При USB SDP
|
||||||
|
этим занимается ROM-загрузчик по DCD, FCB ему не нужен.
|
||||||
|
|
||||||
|
### 2.4 Зависимости
|
||||||
|
|
||||||
|
| Файл | Назначение |
|
||||||
|
|---|---|
|
||||||
|
| `tools/host/flash_swd.py` | Скрипт сборки образа и вызова pyOCD |
|
||||||
|
| `tools/host/dcd/w25q128_fdcb.bin` | FCB для W25Q128 в режиме Quad SPI |
|
||||||
|
| `tools/hil/` (uv-проект) | pyocd, вызывается через `uv run` |
|
||||||
|
|
||||||
|
FCB-бинарник (`w25q128_fdcb.bin`) генерируется в NXP SecureProvisioningTool
|
||||||
|
для W25Q128 в режиме Quad SPI и хранится в репозитории — пересоздавать не нужно.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Сравнение способов
|
||||||
|
|
||||||
|
| | USB SDP | SWD |
|
||||||
|
|---|---|---|
|
||||||
|
| Переключение BOOT_MODE | Нужно | Не нужно |
|
||||||
|
| Power cycle после записи | Не нужен | **Обязателен** |
|
||||||
|
| FCB в образе | Не нужен (Flashloader пишет сам) | **Обязателен** |
|
||||||
|
| Скорость записи | ~50–100 kB/s | ~8–10 kB/s |
|
||||||
|
| Совместимость с отладкой | Раздельно | MCU-Link монопольный |
|
||||||
|
| Производственный сценарий | ✓ | — |
|
||||||
|
| Итеративная разработка | Неудобно (смена режима) | ✓ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Диагностика
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just host::scan # найти подключённые NXP USB-устройства
|
||||||
|
just host::sdp-status # проверить связь с BootROM (плата в SDP-режиме)
|
||||||
|
just host::flashloader-status # проверить Flashloader (после jump-address)
|
||||||
|
just host::debug-list-targets # проверить что pyOCD видит mimxrt1050_quadspi
|
||||||
|
just host::check-deps # проверить версии just / uv / docker
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Производственный сценарий
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just host::incoming # firmware_test release → Flash → HIL-тесты периферии
|
||||||
just host::production # bootloader release + app release
|
just host::production # bootloader release + app release
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,31 @@
|
||||||
|
|
||||||
#include "board.h"
|
#include "board.h"
|
||||||
#include "bsp/led.h"
|
#include "bsp/led.h"
|
||||||
#include "bsp/tick.h"
|
#include "bsp/tick.h"
|
||||||
#include "bsp/uart_host.h"
|
#include "bsp/uart_host.h"
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
const uint16_t DELAY_MS = 1000;
|
const uint16_t DELAY_MS = 100;
|
||||||
const uint32_t UART_BAUDRATE = 115200;
|
const uint32_t UART_BAUDRATE = 115200;
|
||||||
board_hw_init();
|
board_hw_init();
|
||||||
bsp_led_init();
|
bsp_led_init();
|
||||||
bsp_tick_init();
|
bsp_tick_init();
|
||||||
|
|
||||||
if (bsp_uart_host_init(UART_BAUDRATE) != BSP_OK)
|
if (bsp_uart_host_init(UART_BAUDRATE) != BSP_OK)
|
||||||
{
|
{
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
bsp_led_toggle(LED_HEARTBEAT);
|
bsp_led_toggle(LED_HEARTBEAT);
|
||||||
bsp_delay(50);
|
bsp_delay(DELAY_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
uint32_t cycle = 0;
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
|
|
||||||
bsp_led_on(LED_HEARTBEAT);
|
bsp_led_on(LED_HEARTBEAT);
|
||||||
bsp_uart_host_write_str("We are here!");
|
bsp_uart_host_write_str("We are here!\n");
|
||||||
bsp_delay(DELAY_MS);
|
bsp_delay(DELAY_MS);
|
||||||
bsp_led_off(LED_HEARTBEAT);
|
bsp_led_off(LED_HEARTBEAT);
|
||||||
bsp_delay(DELAY_MS);
|
bsp_delay(DELAY_MS);
|
||||||
|
|
|
||||||
120
just/host.just
120
just/host.just
|
|
@ -206,10 +206,11 @@ bootstrap:
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# ГРУППА: flash
|
# ГРУППА: flash — USB Serial Downloader (SDP через ROM-загрузчик)
|
||||||
|
# Требует: плата в режиме Serial Downloader (BOOT_MODE = 01)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
[doc('Прошить образ во Flash через USB ROM: flash <firmware_test|bootloader|app> <debug|release>')]
|
[doc('Прошить образ через USB SDP: flash <firmware_test|bootloader|app> <debug|release>')]
|
||||||
[group('flash')]
|
[group('flash')]
|
||||||
flash project type="release":
|
flash project type="release":
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
@ -222,7 +223,7 @@ flash project type="release":
|
||||||
cd "{{ TOOLS_DIR }}" && UV_PROJECT_ENVIRONMENT={{ _venv }} uv run python flash_usb.py \
|
cd "{{ TOOLS_DIR }}" && UV_PROJECT_ENVIRONMENT={{ _venv }} uv run python flash_usb.py \
|
||||||
--firmware "{{ project }}" --build-type "${BUILD_TYPE}"
|
--firmware "{{ project }}" --build-type "${BUILD_TYPE}"
|
||||||
|
|
||||||
[doc('Прошить образ в RAM через USB ROM: flash-ram <firmware_test|bootloader|app> <debug|release>')]
|
[doc('Прошить образ в RAM через USB SDP: flash-ram <firmware_test|bootloader|app> <debug|release>')]
|
||||||
[group('flash')]
|
[group('flash')]
|
||||||
flash-ram project type="debug":
|
flash-ram project type="debug":
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
@ -235,23 +236,67 @@ flash-ram project type="debug":
|
||||||
cd "{{ TOOLS_DIR }}" && UV_PROJECT_ENVIRONMENT={{ _venv }} uv run python flash_usb.py \
|
cd "{{ TOOLS_DIR }}" && UV_PROJECT_ENVIRONMENT={{ _venv }} uv run python flash_usb.py \
|
||||||
--firmware "{{ project }}" --build-type "${BUILD_TYPE}" --ram-only
|
--firmware "{{ project }}" --build-type "${BUILD_TYPE}" --ram-only
|
||||||
|
|
||||||
[doc('Прошить firmware_test Debug во Flash')]
|
[doc('Прошить firmware_test Debug через USB SDP')]
|
||||||
[group('flash')]
|
[group('flash')]
|
||||||
flash-test-debug:
|
flash-test-debug:
|
||||||
@just host::flash firmware_test debug
|
@just host::flash firmware_test debug
|
||||||
|
|
||||||
[doc('Прошить firmware_test Release во Flash')]
|
[doc('Прошить firmware_test Release через USB SDP')]
|
||||||
[group('flash')]
|
[group('flash')]
|
||||||
flash-test-release:
|
flash-test-release:
|
||||||
@just host::flash firmware_test release
|
@just host::flash firmware_test release
|
||||||
|
|
||||||
[confirm("Flash bootloader + app (Release)?")]
|
[confirm("Flash bootloader + app (Release)?")]
|
||||||
[doc('Прошить bootloader + tft_app Release во Flash (производство)')]
|
[doc('Прошить bootloader + tft_app Release через USB SDP (производство)')]
|
||||||
[group('flash')]
|
[group('flash')]
|
||||||
flash-production:
|
flash-production:
|
||||||
@just host::flash bootloader release
|
@just host::flash bootloader release
|
||||||
@just host::flash app release
|
@just host::flash app release
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ГРУППА: flash-swd — прошивка через SWD (MCU-Link, без смены BOOT_MODE)
|
||||||
|
# Требует: GDB-сервер НЕ запущен (pyOCD занимает пробник монопольно)
|
||||||
|
# После записи — обязательный power cycle платы
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
_flash_swd := TOOLS_DIR / "flash_swd.py"
|
||||||
|
|
||||||
|
[doc('Прошить firmware_test Debug через SWD. Нужен power cycle после')]
|
||||||
|
[group('flash-swd')]
|
||||||
|
flash-swd-test-debug:
|
||||||
|
uv run --directory {{ HIL_DIR }} python {{ _flash_swd }} \
|
||||||
|
--firmware firmware_test --build-type Debug
|
||||||
|
|
||||||
|
[doc('Прошить firmware_test Release через SWD. Нужен power cycle после')]
|
||||||
|
[group('flash-swd')]
|
||||||
|
flash-swd-test-release:
|
||||||
|
uv run --directory {{ HIL_DIR }} python {{ _flash_swd }} \
|
||||||
|
--firmware firmware_test --build-type Release
|
||||||
|
|
||||||
|
[doc('Прошить bootloader Debug через SWD. Нужен power cycle после')]
|
||||||
|
[group('flash-swd')]
|
||||||
|
flash-swd-bootloader-debug:
|
||||||
|
uv run --directory {{ HIL_DIR }} python {{ _flash_swd }} \
|
||||||
|
--firmware bootloader --build-type Debug
|
||||||
|
|
||||||
|
[doc('Прошить bootloader Release через SWD. Нужен power cycle после')]
|
||||||
|
[group('flash-swd')]
|
||||||
|
flash-swd-bootloader-release:
|
||||||
|
uv run --directory {{ HIL_DIR }} python {{ _flash_swd }} \
|
||||||
|
--firmware bootloader --build-type Release
|
||||||
|
|
||||||
|
[doc('Прошить tft_app Debug через SWD. Нужен power cycle после')]
|
||||||
|
[group('flash-swd')]
|
||||||
|
flash-swd-app-debug:
|
||||||
|
uv run --directory {{ HIL_DIR }} python {{ _flash_swd }} \
|
||||||
|
--firmware app --build-type Debug
|
||||||
|
|
||||||
|
[doc('Прошить tft_app Release через SWD. Нужен power cycle после')]
|
||||||
|
[group('flash-swd')]
|
||||||
|
flash-swd-app-release:
|
||||||
|
uv run --directory {{ HIL_DIR }} python {{ _flash_swd }} \
|
||||||
|
--firmware app --build-type Release
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# ГРУППА: hil — запуск HIL-тестов на реальном железе
|
# ГРУППА: hil — запуск HIL-тестов на реальном железе
|
||||||
# Сборка target-прошивок — в devcontainer: just build::build-hil
|
# Сборка target-прошивок — в devcontainer: just build::build-hil
|
||||||
|
|
@ -284,6 +329,36 @@ hil-run-fast:
|
||||||
HIL_BUILD_DIR={{ _hil_build }} \
|
HIL_BUILD_DIR={{ _hil_build }} \
|
||||||
uv run --directory {{ HIL_DIR }} pytest -v --no-load
|
uv run --directory {{ HIL_DIR }} pytest -v --no-load
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ГРУППА: debug — GDB-сервер для отладки из VSCode (devcontainer)
|
||||||
|
#
|
||||||
|
# Рабочий цикл А — прошивка уже в Flash:
|
||||||
|
# just host::debug-server → VSCode: 🐛 Debug: <project> → F5
|
||||||
|
#
|
||||||
|
# Рабочий цикл Б — прошить и отладить без USB SDP:
|
||||||
|
# just host::flash-swd-test-debug → power cycle →
|
||||||
|
# just host::debug-server → VSCode: 🐛 Debug: firmware_test → F5
|
||||||
|
#
|
||||||
|
# Важно: debug-server и flash-swd используют MCU-Link монопольно.
|
||||||
|
# Остановите сервер (Ctrl+C) перед flash-swd и наоборот.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
_hil_dir := "tools/hil"
|
||||||
|
|
||||||
|
[doc('Запустить GDB-сервер. Оставить запущенным, подключаться из VSCode')]
|
||||||
|
[group('debug')]
|
||||||
|
debug-server:
|
||||||
|
uv run --directory {{ _hil_dir }} pyocd gdbserver \
|
||||||
|
--config {{ justfile_directory() }}/pyocd_debug.yaml \
|
||||||
|
--port {{ env('GDB_PORT', '3333') }} \
|
||||||
|
--persist \
|
||||||
|
--semihosting
|
||||||
|
|
||||||
|
[doc('Показать доступные builtin-таргеты pyOCD для MIMXRT')]
|
||||||
|
[group('debug')]
|
||||||
|
debug-list-targets:
|
||||||
|
uv run --directory {{ _hil_dir }} pyocd list --targets | grep -i mimx
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# ГРУППА: pipeline
|
# ГРУППА: pipeline
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
@ -327,30 +402,9 @@ upgrade-tools version:
|
||||||
cd "{{ TOOLS_DIR }}" && uv add "spsdk=={{ version }}" && uv sync
|
cd "{{ TOOLS_DIR }}" && uv add "spsdk=={{ version }}" && uv sync
|
||||||
@echo " ✅ spsdk upgraded to {{ version }}"
|
@echo " ✅ spsdk upgraded to {{ version }}"
|
||||||
|
|
||||||
# =============================================================================
|
[doc('Открыть UART монитор на MCU-Link VCOM (кроссплатформенный)')]
|
||||||
# ГРУППА: debug
|
[group('util')]
|
||||||
# =============================================================================
|
uart-monitor:
|
||||||
|
uv run --directory {{ HIL_DIR }} python3 -m serial.tools.miniterm \
|
||||||
_hil_dir := "tools/hil"
|
{{ env('HIL_VCOM_PORT') }} {{ env('HIL_VCOM_BAUD', '115200') }} \
|
||||||
|
--raw
|
||||||
[doc('Запустить GDB-сервер на порту 3333. Оставить запущенным, подключаться из VSCode')]
|
|
||||||
[group('debug')]
|
|
||||||
debug-server:
|
|
||||||
uv run --directory {{ _hil_dir }} pyocd gdbserver \
|
|
||||||
--config {{ justfile_directory() }}/pyocd_debug.yaml \
|
|
||||||
--port 3333 \
|
|
||||||
--persist \
|
|
||||||
--semihosting
|
|
||||||
|
|
||||||
[doc('Показать доступные builtin-таргеты pyOCD для MIMXRT')]
|
|
||||||
[group('debug')]
|
|
||||||
debug-list-targets:
|
|
||||||
uv run --directory {{ _hil_dir }} pyocd list --targets | grep -i mimx
|
|
||||||
|
|
||||||
[doc('Собрать HAB Debug и прошить через MCU-Link')]
|
|
||||||
[group('flash')]
|
|
||||||
flash-test-debug-swd:
|
|
||||||
uv run --directory {{ _hil_dir }} pyocd flash \
|
|
||||||
--config {{ justfile_directory() }}/pyocd_debug.yaml \
|
|
||||||
--base-address 0x60000000 \
|
|
||||||
{{ BUILD_DIR }}/Debug/firmware_test_hab.bin
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
# Используется: just host::debug-server
|
# Используется: just host::debug-server
|
||||||
# Не использовать для HIL-тестов — для них pyocd.yaml (target: cortex_m, RAM-режим)
|
# Не использовать для HIL-тестов — для них pyocd.yaml (target: cortex_m, RAM-режим)
|
||||||
#
|
#
|
||||||
# Первый запуск — установить CMSIS pack:
|
|
||||||
# just host::debug-pack-install
|
|
||||||
|
|
||||||
target_override: mimxrt1050_quadspi
|
target_override: mimxrt1050_quadspi
|
||||||
frequency: 4000000
|
frequency: 4000000
|
||||||
|
|
@ -0,0 +1,313 @@
|
||||||
|
# tools/hil — HIL-тесты и утилиты отладки MIMXRT1052
|
||||||
|
|
||||||
|
Изолированное Python-окружение на базе [uv](https://docs.astral.sh/uv/) для:
|
||||||
|
- **HIL-тестов** (Hardware-in-the-Loop) — загрузка ELF в RAM и pytest через UART
|
||||||
|
- **GDB-сервера** для отладки из VSCode (pyocd gdbserver)
|
||||||
|
- **SWD-прошивки** через `flash_swd.py` (вызывается из `tools/host/`)
|
||||||
|
|
||||||
|
Запускается на **хост-машине** — не внутри devcontainer.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Структура
|
||||||
|
|
||||||
|
```
|
||||||
|
tools/hil/
|
||||||
|
├── conftest.py — pytest-фикстуры: загрузка ELF, открытие UART, ожидание READY
|
||||||
|
├── env_config.py — конфигурация из переменных окружения (.env → just → pytest)
|
||||||
|
├── pyocd_utils.py — утилиты pyOCD: FLEXRAM, ELF-загрузка, запуск из векторов
|
||||||
|
├── load_and_run.py — CLI-обёртка: загрузить ELF в RAM и запустить вручную
|
||||||
|
├── test_uart.py — HIL-тесты bsp_uart_host (PING/ECHO/BUF_SIZE)
|
||||||
|
├── pyproject.toml — зависимости (pyocd, pyserial, pytest)
|
||||||
|
└── uv.lock — lockfile (коммитить)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Концепция HIL-тестов
|
||||||
|
|
||||||
|
Каждый HIL-тест — это пара:
|
||||||
|
|
||||||
|
```
|
||||||
|
tests/target/<name>/main.c ← C-прошивка с текстовым CLI через UART
|
||||||
|
tools/hil/test_<name>.py ← pytest-тесты, общаются с прошивкой по UART
|
||||||
|
```
|
||||||
|
|
||||||
|
pyOCD загружает `.elf` в RAM через MCU-Link (CMSIS-DAP). pytest общается с
|
||||||
|
прошивкой через MCU-Link VCOM (pyserial):
|
||||||
|
|
||||||
|
```
|
||||||
|
pytest → uart_cmd("PING\r\n") → MCU-Link VCOM → RT1052 → "PONG\r\n" → pytest
|
||||||
|
```
|
||||||
|
|
||||||
|
ELF загружается в **ITCM/DTCM** (не Flash) — быстро, не изнашивает Flash,
|
||||||
|
не требует HAB-образа.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Предварительные требования
|
||||||
|
|
||||||
|
### 1. uv — один раз на машину
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 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/hil
|
||||||
|
uv sync
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Настроить `.env` в корне репозитория
|
||||||
|
|
||||||
|
```bash
|
||||||
|
HIL_VCOM_PORT=/dev/tty.usbmodemGUXFBWDJBWTGQ3 # macOS
|
||||||
|
# HIL_VCOM_PORT=/dev/ttyACM0 # Linux
|
||||||
|
# HIL_VCOM_PORT=COM3 # Windows
|
||||||
|
HIL_VCOM_BAUD=115200
|
||||||
|
HIL_READY_TIMEOUT=5.0
|
||||||
|
HIL_PYOCD_FREQUENCY=1000000
|
||||||
|
HIL_BUILD_DIR=build/target-debug
|
||||||
|
```
|
||||||
|
|
||||||
|
Найти порт MCU-Link VCOM:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just host::scan # nxpdevscan — все NXP устройства
|
||||||
|
ls /dev/tty.usbmodem* # macOS
|
||||||
|
ls /dev/ttyACM* # Linux
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Запуск HIL-тестов
|
||||||
|
|
||||||
|
Сборка target-прошивок выполняется в devcontainer:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# devcontainer:
|
||||||
|
just build::build-hil # → build/target-debug/tests/target/*/test_*.elf
|
||||||
|
```
|
||||||
|
|
||||||
|
Запуск тестов — на хосте:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just host::hil-run # все HIL-тесты
|
||||||
|
just host::hil-smoke # только smoke-тесты (быстро)
|
||||||
|
just host::hil-run-fast # без перезагрузки ELF (прошивка уже запущена)
|
||||||
|
```
|
||||||
|
|
||||||
|
Или напрямую через pytest:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd tools/hil
|
||||||
|
|
||||||
|
# Все тесты
|
||||||
|
uv run pytest -v
|
||||||
|
|
||||||
|
# Только smoke
|
||||||
|
uv run pytest -v -m smoke
|
||||||
|
|
||||||
|
# Конкретный файл
|
||||||
|
uv run pytest test_uart.py -v
|
||||||
|
|
||||||
|
# Без перезагрузки ELF (прошивка уже запущена)
|
||||||
|
uv run pytest -v --no-load
|
||||||
|
|
||||||
|
# Другой ELF
|
||||||
|
uv run pytest test_uart.py -v --elf /path/to/custom.elf
|
||||||
|
|
||||||
|
# Другой VCOM-порт
|
||||||
|
uv run pytest -v --vcom /dev/ttyACM1
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Конфигурация
|
||||||
|
|
||||||
|
Конфигурация читается в `env_config.py`. Приоритет: CLI-опции pytest > `os.environ` > defaults.
|
||||||
|
|
||||||
|
| Переменная окружения | CLI pytest | Default | Описание |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `HIL_BUILD_DIR` | — | `build/target-debug` | Директория с target ELF-файлами |
|
||||||
|
| `HIL_VCOM_PORT` | `--vcom` | `/dev/ttyACM0` | UART-порт MCU-Link VCOM |
|
||||||
|
| `HIL_VCOM_BAUD` | — | `115200` | Скорость UART |
|
||||||
|
| `HIL_READY_TIMEOUT` | — | `5.0` | Таймаут ожидания `READY` от прошивки (сек) |
|
||||||
|
| `HIL_PYOCD_FREQUENCY` | — | `1000000` | Частота SWD для загрузки ELF в RAM |
|
||||||
|
|
||||||
|
При запуске через `just` все переменные из корневого `.env` автоматически
|
||||||
|
экспортируются в окружение (`set dotenv-load` + `set export`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Как работают фикстуры
|
||||||
|
|
||||||
|
### Цепочка зависимостей
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test_ping()
|
||||||
|
└── _setup (autouse, scope=function)
|
||||||
|
├── loaded_host_uart (scope=module) ← грузит ELF в RAM
|
||||||
|
└── uart (scope=module) ← открывает VCOM, ждёт READY
|
||||||
|
└── depends_on: loaded_host_uart
|
||||||
|
```
|
||||||
|
|
||||||
|
`scope=module` — ELF загружается один раз на весь файл с тестами, порт
|
||||||
|
открывается один раз. Все тесты внутри файла разделяют одно соединение.
|
||||||
|
|
||||||
|
### Порядок выполнения
|
||||||
|
|
||||||
|
```bash
|
||||||
|
1. loaded_<n>()
|
||||||
|
├── open_target() → pyOCD: подключиться к MCU через SWD
|
||||||
|
├── flexram_init() → настроить ITCM/DTCM/OCRAM
|
||||||
|
├── load_elf() → записать PT_LOAD сегменты по адресам
|
||||||
|
└── run_from_vectors() → SP/PC из 0x00000000/0x00000004 → resume
|
||||||
|
|
||||||
|
2. uart()
|
||||||
|
├── serial.Serial.open()
|
||||||
|
├── while readline() != "READY": ... ← ждём сигнал от прошивки
|
||||||
|
└── yield ser
|
||||||
|
|
||||||
|
3. test_ping(), test_echo(), ... ← тесты
|
||||||
|
|
||||||
|
4. uart teardown → ser.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Почему `uart` зависит от `loaded_<n>`
|
||||||
|
|
||||||
|
```python
|
||||||
|
def uart(request, loaded_host_uart): # ← явная зависимость в сигнатуре
|
||||||
|
```
|
||||||
|
|
||||||
|
Без этого pytest мог бы создать `uart` раньше чем ELF загружен — порт открылся
|
||||||
|
бы, но `READY` не пришёл. Явная зависимость гарантирует порядок.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## pyocd_utils — справочник
|
||||||
|
|
||||||
|
### `open_target(frequency)`
|
||||||
|
|
||||||
|
Контекстный менеджер, открывает pyOCD-сессию с первым найденным пробником.
|
||||||
|
Таргет — `cortex_m` (generic, без flash-алгоритма — для RAM-операций достаточно).
|
||||||
|
|
||||||
|
```python
|
||||||
|
with open_target(frequency=1_000_000) as target:
|
||||||
|
flexram_init(target)
|
||||||
|
load_elf(target, "test.elf")
|
||||||
|
run_from_vectors(target)
|
||||||
|
```
|
||||||
|
|
||||||
|
### `flexram_init(target)`
|
||||||
|
|
||||||
|
Настраивает FLEXRAM через `IOMUXC_GPR16/GPR17`:
|
||||||
|
|
||||||
|
- 128 KB ITCM (0x00000000) — код
|
||||||
|
- 128 KB DTCM (0x20000000) — данные, стек
|
||||||
|
- 256 KB OCRAM (0x20200000) — буферы
|
||||||
|
|
||||||
|
### `load_elf(target, elf_path)`
|
||||||
|
|
||||||
|
Записывает все `PT_LOAD` сегменты ELF по физическим адресам (`p_paddr`).
|
||||||
|
Использует `pyelftools`.
|
||||||
|
|
||||||
|
### `run_from_vectors(target)`
|
||||||
|
|
||||||
|
Читает SP и PC из таблицы векторов (ITCM `0x00000000`/`0x00000004`),
|
||||||
|
выставляет регистры, вызывает `target.resume()`. Проверяет:
|
||||||
|
|
||||||
|
- SP в диапазоне DTCM `[0x20000000, 0x20040000]`
|
||||||
|
- PC в диапазоне ITCM `[0x00000400, 0x00020000]`
|
||||||
|
|
||||||
|
### `load_and_run(elf_path, frequency)`
|
||||||
|
|
||||||
|
Комбо-функция: open_target + flexram_init + load_elf + run_from_vectors.
|
||||||
|
Для CLI и одиночных скриптов.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## load_and_run.py — ручная загрузка ELF
|
||||||
|
|
||||||
|
Для ручной отладки без запуска тестов:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Загрузить ELF в RAM и запустить
|
||||||
|
uv run python load_and_run.py build/target-debug/tests/target/host_uart/test_host_uart.elf
|
||||||
|
|
||||||
|
# Через just:
|
||||||
|
just host::hil-load
|
||||||
|
```
|
||||||
|
|
||||||
|
После этого можно подключиться к VCOM вручную:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just host::uart-monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Протокол CLI в target-прошивках
|
||||||
|
|
||||||
|
Все HIL target-прошивки (`tests/target/<name>/main.c`) реализуют единый
|
||||||
|
текстовый CLI через `bsp_uart_host`:
|
||||||
|
|
||||||
|
- Прошивка отправляет `READY\r\n` пока хост не открыл порт
|
||||||
|
- Хост посылает команду строкой с `\r\n`
|
||||||
|
- Прошивка отвечает одной строкой с `\r\n`
|
||||||
|
|
||||||
|
Минимальные команды в каждой прошивке:
|
||||||
|
|
||||||
|
| Команда | Ответ | Назначение |
|
||||||
|
|---|---|---|
|
||||||
|
| `PING` | `PONG` | Проверка канала |
|
||||||
|
| `<неизвестная>` | `ERR_UNKNOWN` | Прошивка не зависает |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Добавление нового HIL-теста
|
||||||
|
|
||||||
|
Подробный гайд — в `tests/HIL_CREATE_TEST.md`. Краткая схема:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
1. tests/target/<name>/main.c — C-прошивка с CLI
|
||||||
|
2. tests/target/<name>/CMakeLists.txt — сборка с bsp_boot_ram
|
||||||
|
3. tests/target/CMakeLists.txt — add_subdirectory(<name>)
|
||||||
|
4. CMakePresets.json — добавить test_<name> в target-debug-build
|
||||||
|
5. tools/hil/test_<name>.py — pytest-тесты
|
||||||
|
6. tools/hil/conftest.py — добавить loaded_<name> фикстуру
|
||||||
|
```
|
||||||
|
|
||||||
|
Фикстура для нового теста в `conftest.py`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def loaded_<name>(request: pytest.FixtureRequest) -> None:
|
||||||
|
_load_elf(
|
||||||
|
request,
|
||||||
|
Path(cfg.BUILD_DIR) / "tests/target/<name>/test_<name>.elf",
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Совместимость с GDB-сервером
|
||||||
|
|
||||||
|
pyOCD используется для двух независимых задач:
|
||||||
|
|
||||||
|
| Задача | Команда | Таргет | Порт |
|
||||||
|
|---|---|---|---|
|
||||||
|
| HIL-тесты (RAM) | `pyocd` через `pyocd_utils` | `cortex_m` | — |
|
||||||
|
| GDB-сервер (отладка) | `pyocd gdbserver` | `mimxrt1050_quadspi` | 3333 |
|
||||||
|
|
||||||
|
**MCU-Link монопольный** — нельзя запускать HIL и GDB-сервер одновременно.
|
||||||
|
Перед `just host::hil-run` остановите GDB-сервер (`Ctrl+C`), и наоборот.
|
||||||
|
|
||||||
|
Разные таргеты намеренны: HIL не нужен flash-алгоритм (`cortex_m` достаточно),
|
||||||
|
GDB-сервер нужен полноценный таргет для корректного reset и SVD.
|
||||||
|
|
@ -1,207 +0,0 @@
|
||||||
Name Vendor Part Number Families Source
|
|
||||||
------------------------------------------------------------------------------------------------------
|
|
||||||
air001 AirM2M Air001 builtin
|
|
||||||
air32f103xb AirM2M Air32F103xB builtin
|
|
||||||
air32f103xc AirM2M Air32F103xC builtin
|
|
||||||
air32f103xe AirM2M Air32F103xE builtin
|
|
||||||
air32f103xg AirM2M Air32F103xG builtin
|
|
||||||
air32f103xp AirM2M Air32F103xP builtin
|
|
||||||
ama3b1kk_kbr Ambiq Micro AMA3B1KK_KBR builtin
|
|
||||||
cc3220sf Texas Instruments CC3220SF builtin
|
|
||||||
cortex_m Generic CoreSightTarget builtin
|
|
||||||
cy8c64_sysap Cypress cy8c64_sysap builtin
|
|
||||||
cy8c64x5_cm0 Cypress cy8c64x5_cm0 builtin
|
|
||||||
cy8c64x5_cm0_full_flash Cypress cy8c64x5_cm0_full_flash builtin
|
|
||||||
cy8c64x5_cm4 Cypress cy8c64x5_cm4 builtin
|
|
||||||
cy8c64x5_cm4_full_flash Cypress cy8c64x5_cm4_full_flash builtin
|
|
||||||
cy8c64xa_cm0 Cypress cy8c64xA_cm0 builtin
|
|
||||||
cy8c64xa_cm0_full_flash Cypress cy8c64xA_cm0_full_flash builtin
|
|
||||||
cy8c64xa_cm4 Cypress cy8c64xA_cm4 builtin
|
|
||||||
cy8c64xa_cm4_full_flash Cypress cy8c64xA_cm4_full_flash builtin
|
|
||||||
cy8c64xx_cm0 Cypress cy8c64xx_cm0 builtin
|
|
||||||
cy8c64xx_cm0_full_flash Cypress cy8c64xx_cm0_full_flash builtin
|
|
||||||
cy8c64xx_cm0_nosmif Cypress cy8c64xx_cm0_nosmif builtin
|
|
||||||
cy8c64xx_cm0_s25hx512t Cypress cy8c64xx_cm0_s25hx512t builtin
|
|
||||||
cy8c64xx_cm4 Cypress cy8c64xx_cm4 builtin
|
|
||||||
cy8c64xx_cm4_full_flash Cypress cy8c64xx_cm4_full_flash builtin
|
|
||||||
cy8c64xx_cm4_nosmif Cypress cy8c64xx_cm4_nosmif builtin
|
|
||||||
cy8c64xx_cm4_s25hx512t Cypress cy8c64xx_cm4_s25hx512t builtin
|
|
||||||
cy8c6xx5 Cypress CY8C6xx5 builtin
|
|
||||||
cy8c6xx7 Cypress CY8C6xx7 builtin
|
|
||||||
cy8c6xx7_nosmif Cypress CY8C6xx7_nosmif builtin
|
|
||||||
cy8c6xx7_s25fs512s Cypress CY8C6xx7_S25FS512S builtin
|
|
||||||
cy8c6xxa Cypress CY8C6xxA builtin
|
|
||||||
hc32a448 HDSC HC32F448xC builtin
|
|
||||||
hc32a448xa HDSC HC32F448xA builtin
|
|
||||||
hc32a448xc HDSC HC32F448xC builtin
|
|
||||||
hc32a460 HDSC HC32F460xE builtin
|
|
||||||
hc32a460xe HDSC HC32F460xE builtin
|
|
||||||
hc32a4a0 HDSC HC32F4A0xI builtin
|
|
||||||
hc32a4a0xi HDSC HC32F4A0xI builtin
|
|
||||||
hc32f003 HDSC HC32F003 builtin
|
|
||||||
hc32f005 HDSC HC32F005 builtin
|
|
||||||
hc32f030 HDSC HC32F030 builtin
|
|
||||||
hc32f072 HDSC HC32F072 builtin
|
|
||||||
hc32f115 HDSC HC32F115x8 builtin
|
|
||||||
hc32f115x8 HDSC HC32F115x8 builtin
|
|
||||||
hc32f120 HDSC HC32F120x8TA builtin
|
|
||||||
hc32f120x6 HDSC HC32F120x6TA builtin
|
|
||||||
hc32f120x8 HDSC HC32F120x8TA builtin
|
|
||||||
hc32f155 HDSC HC32F155xC builtin
|
|
||||||
hc32f155xa HDSC HC32F155xA builtin
|
|
||||||
hc32f155xc HDSC HC32F155xC builtin
|
|
||||||
hc32f160 HDSC HC32F160xC builtin
|
|
||||||
hc32f160xa HDSC HC32F160xA builtin
|
|
||||||
hc32f160xc HDSC HC32F160xC builtin
|
|
||||||
hc32f190 HDSC HC32F190 builtin
|
|
||||||
hc32f196 HDSC HC32F196 builtin
|
|
||||||
hc32f334 HDSC HC32F334xA builtin
|
|
||||||
hc32f334x8 HDSC HC32F334x8 builtin
|
|
||||||
hc32f334xa HDSC HC32F334xA builtin
|
|
||||||
hc32f448 HDSC HC32F448xC builtin
|
|
||||||
hc32f448xa HDSC HC32F448xA builtin
|
|
||||||
hc32f448xc HDSC HC32F448xC builtin
|
|
||||||
hc32f451 HDSC HC32F451xE builtin
|
|
||||||
hc32f451xc HDSC HC32F451xC builtin
|
|
||||||
hc32f451xe HDSC HC32F451xE builtin
|
|
||||||
hc32f452 HDSC HC32F452xE builtin
|
|
||||||
hc32f452xc HDSC HC32F452xC builtin
|
|
||||||
hc32f452xe HDSC HC32F452xE builtin
|
|
||||||
hc32f460 HDSC HC32F460xE builtin
|
|
||||||
hc32f460xc HDSC HC32F460xC builtin
|
|
||||||
hc32f460xe HDSC HC32F460xE builtin
|
|
||||||
hc32f467 HDSC HC32F467xG builtin
|
|
||||||
hc32f467xg HDSC HC32F467xG builtin
|
|
||||||
hc32f472 HDSC HC32F472xE builtin
|
|
||||||
hc32f472xc HDSC HC32F472xC builtin
|
|
||||||
hc32f472xe HDSC HC32F472xE builtin
|
|
||||||
hc32f4a0 HDSC HC32F4A0xI builtin
|
|
||||||
hc32f4a0xg HDSC HC32F4A0xG builtin
|
|
||||||
hc32f4a0xi HDSC HC32F4A0xI builtin
|
|
||||||
hc32f4a2 HDSC HC32F4A0xI builtin
|
|
||||||
hc32f4a2xi HDSC HC32F4A0xI builtin
|
|
||||||
hc32l072 HDSC HC32L072 builtin
|
|
||||||
hc32l073 HDSC HC32L073 builtin
|
|
||||||
hc32l110 HDSC HC32L110 builtin
|
|
||||||
hc32l130 HDSC HC32L130 builtin
|
|
||||||
hc32l136 HDSC HC32L136 builtin
|
|
||||||
hc32l190 HDSC HC32L190 builtin
|
|
||||||
hc32l196 HDSC HC32L196 builtin
|
|
||||||
hc32m120 HDSC HC32M120 builtin
|
|
||||||
hc32m120x6 HDSC HC32M120 builtin
|
|
||||||
hc32m423xa HDSC HC32M423xA builtin
|
|
||||||
k20d50m NXP K20D50M builtin
|
|
||||||
k22f NXP K22F builtin
|
|
||||||
k22fa12 NXP K22FA12 builtin
|
|
||||||
k28f15 NXP K28F15 builtin
|
|
||||||
k32l2b3 NXP K32L2B3 builtin
|
|
||||||
k32w042s NXP K32W042S builtin
|
|
||||||
k64f NXP K64F builtin
|
|
||||||
k66f18 NXP K66F18 builtin
|
|
||||||
k82f25615 NXP K82F25615 builtin
|
|
||||||
ke15z7 NXP KE15Z7 builtin
|
|
||||||
ke17z7 NXP KE17Z7 builtin
|
|
||||||
ke18f16 NXP KE18F16 builtin
|
|
||||||
kinetis NXP Kinetis builtin
|
|
||||||
kl02z NXP KL02Z builtin
|
|
||||||
kl05z NXP KL05Z builtin
|
|
||||||
kl25z NXP KL25Z builtin
|
|
||||||
kl26z NXP KL26Z builtin
|
|
||||||
kl27z4 NXP KL27Z4 builtin
|
|
||||||
kl28z NXP KL28x builtin
|
|
||||||
kl43z4 NXP KL43Z4 builtin
|
|
||||||
kl46z NXP KL46Z builtin
|
|
||||||
kl82z7 NXP KL82Z7 builtin
|
|
||||||
kv10z7 NXP KV10Z7 builtin
|
|
||||||
kv11z7 NXP KV11Z7 builtin
|
|
||||||
kw01z4 NXP KW01Z4 builtin
|
|
||||||
kw24d5 NXP KW24D5 builtin
|
|
||||||
kw36z4 NXP KW36Z4 builtin
|
|
||||||
kw40z4 NXP KW40Z4 builtin
|
|
||||||
kw41z4 NXP KW41Z4 builtin
|
|
||||||
lpc11u24 NXP LPC11U24 builtin
|
|
||||||
lpc11xx_32 NXP LPC11XX_32 builtin
|
|
||||||
lpc1768 NXP LPC1768 builtin
|
|
||||||
lpc4088 NXP LPC4088 builtin
|
|
||||||
lpc4088dm NXP LPC4088dm builtin
|
|
||||||
lpc4088qsb NXP LPC4088qsb builtin
|
|
||||||
lpc4330 NXP LPC4330 builtin
|
|
||||||
lpc54114 NXP LPC54114 builtin
|
|
||||||
lpc54608 NXP LPC54608 builtin
|
|
||||||
lpc5526 NXP LPC5526 builtin
|
|
||||||
lpc55s16 NXP LPC55S16 builtin
|
|
||||||
lpc55s28 NXP LPC55S28 builtin
|
|
||||||
lpc55s36 NXP LPC55S36 builtin
|
|
||||||
lpc55s69 NXP LPC55S69 builtin
|
|
||||||
lpc800 NXP LPC800 builtin
|
|
||||||
lpc824 NXP LPC824 builtin
|
|
||||||
lpc845 NXP LPC845 builtin
|
|
||||||
m2354kjfae Nuvoton M2354KJFAE builtin
|
|
||||||
m252kg6ae Nuvoton M252KG6AE builtin
|
|
||||||
m263kiaae Nuvoton M263KIAAE builtin
|
|
||||||
m467hjhae Nuvoton M467HJHAE builtin
|
|
||||||
m487jidae Nuvoton M487JIDAE builtin
|
|
||||||
max32600 Maxim MAX32600 builtin
|
|
||||||
max32620 Maxim MAX32620 builtin
|
|
||||||
max32625 Maxim MAX32625 builtin
|
|
||||||
max32630 Maxim MAX32630 builtin
|
|
||||||
max32660 Maxim MAX32660 builtin
|
|
||||||
max32666 Maxim MAX32666 builtin
|
|
||||||
max32670 Maxim MAX32670 builtin
|
|
||||||
mimxrt1010 NXP MIMXRT1011xxxxx builtin
|
|
||||||
mimxrt1015 NXP MIMXRT1015xxxxx builtin
|
|
||||||
mimxrt1020 NXP MIMXRT1021xxxxx builtin
|
|
||||||
mimxrt1024 NXP MIMXRT1024xxxxx builtin
|
|
||||||
mimxrt1050 NXP MIMXRT1052xxxxB_hyperflash builtin
|
|
||||||
mimxrt1050_hyperflash NXP MIMXRT1052xxxxB_hyperflash builtin
|
|
||||||
mimxrt1050_quadspi NXP MIMXRT1052xxxxB_quadspi builtin
|
|
||||||
mimxrt1060 NXP MIMXRT1062xxxxA builtin
|
|
||||||
mimxrt1064 NXP MIMXRT1064xxxxA builtin
|
|
||||||
mimxrt1170_cm4 NXP MIMXRT1176xxxxx_CM4 builtin
|
|
||||||
mimxrt1170_cm7 NXP MIMXRT1176xxxxx_CM7 builtin
|
|
||||||
mps2_an521 Arm AN521 builtin
|
|
||||||
mps3_an522 Arm AN522 builtin
|
|
||||||
mps3_an540 Arm AN540 builtin
|
|
||||||
musca_a1 Arm MuscaA1 builtin
|
|
||||||
musca_b1 Arm MuscaB1 builtin
|
|
||||||
musca_s1 Arm MuscaS1 builtin
|
|
||||||
ncs36510 ONSemiconductor NCS36510 builtin
|
|
||||||
nrf51 Nordic Semiconductor NRF51 builtin
|
|
||||||
nrf51822 Nordic Semiconductor NRF51 builtin
|
|
||||||
nrf52 Nordic Semiconductor NRF52832 builtin
|
|
||||||
nrf52832 Nordic Semiconductor NRF52832 builtin
|
|
||||||
nrf52833 Nordic Semiconductor NRF52833 builtin
|
|
||||||
nrf52840 Nordic Semiconductor NRF52840 builtin
|
|
||||||
nrf54l Nordic Semiconductor NRF54L15 builtin
|
|
||||||
nrf91 Nordic Semiconductor NRF91XX builtin
|
|
||||||
rp2040 Raspberry Pi RP2040Core0 builtin
|
|
||||||
rp2040_core0 Raspberry Pi RP2040Core0 builtin
|
|
||||||
rp2040_core1 Raspberry Pi RP2040Core1 builtin
|
|
||||||
rp2350 Raspberry Pi RP2350 builtin
|
|
||||||
rtl8195am Realtek Semiconductor RTL8195AM builtin
|
|
||||||
rtl8762c Realtek Semiconductor RTL8762C builtin
|
|
||||||
s32k344 NXP S32K344 builtin
|
|
||||||
s5js100 Samsung S5JS100 builtin
|
|
||||||
stm32f051 STMicroelectronics STM32F051 builtin
|
|
||||||
stm32f103rc STMicroelectronics STM32F103RC builtin
|
|
||||||
stm32f412xe STMicroelectronics STM32F412xE builtin
|
|
||||||
stm32f412xg STMicroelectronics STM32F412xG builtin
|
|
||||||
stm32f429xg STMicroelectronics STM32F429xG builtin
|
|
||||||
stm32f429xi STMicroelectronics STM32F429xI builtin
|
|
||||||
stm32f439xg STMicroelectronics STM32F439xG builtin
|
|
||||||
stm32f439xi STMicroelectronics STM32F439xI builtin
|
|
||||||
stm32f767zi STMicroelectronics STM32F767xx builtin
|
|
||||||
stm32h723xx STMicroelectronics STM32H723xx builtin
|
|
||||||
stm32h743xx STMicroelectronics STM32H743xx builtin
|
|
||||||
stm32h750xx STMicroelectronics STM32H750xx builtin
|
|
||||||
stm32h7b0xx STMicroelectronics STM32H7B0xx builtin
|
|
||||||
stm32l031x6 STMicroelectronics STM32L031x6 builtin
|
|
||||||
stm32l432kc STMicroelectronics STM32L432xC builtin
|
|
||||||
stm32l475xc STMicroelectronics STM32L475xC builtin
|
|
||||||
stm32l475xe STMicroelectronics STM32L475xE builtin
|
|
||||||
stm32l475xg STMicroelectronics STM32L475xG builtin
|
|
||||||
w7500 WIZnet W7500 builtin
|
|
||||||
ytm32b1ld0 Yuntu Microelectronics YTM32B1LD0 builtin
|
|
||||||
ytm32b1le0 Yuntu Microelectronics YTM32B1LE0 builtin
|
|
||||||
ytm32b1md1 Yuntu Microelectronics YTM32B1MD1 builtin
|
|
||||||
ytm32b1me0 YTMicro YTM32B1ME0 builtin
|
|
||||||
|
|
@ -1,9 +1,16 @@
|
||||||
# tools/host — Окружение прошивки MIMXRT1052CVJ5B
|
# tools/host — Окружение прошивки MIMXRT1052CVJ5B
|
||||||
|
|
||||||
Изолированное Python-окружение на базе [uv](https://docs.astral.sh/uv/) для сборки
|
Изолированное Python-окружение на базе [uv](https://docs.astral.sh/uv/) для сборки
|
||||||
HAB-образов и прошивки платы через USB. Запускается на хост-машине разработчика —
|
HAB-образов и прошивки платы. Запускается на хост-машине разработчика —
|
||||||
**не внутри devcontainer**.
|
**не внутри devcontainer**.
|
||||||
|
|
||||||
|
Поддерживаются два независимых способа прошивки:
|
||||||
|
|
||||||
|
| Способ | Скрипт | Интерфейс | Требование |
|
||||||
|
|---|---|---|---|
|
||||||
|
| USB SDP | `flash_usb.py` | USB ↔ ROM-загрузчик | BOOT_MODE = 01 |
|
||||||
|
| SWD | `flash_swd.py` | MCU-Link ↔ CMSIS-DAP | Плата в любом режиме |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Структура
|
## Структура
|
||||||
|
|
@ -11,18 +18,28 @@ HAB-образов и прошивки платы через USB. Запуска
|
||||||
```bash
|
```bash
|
||||||
tools/host/
|
tools/host/
|
||||||
├── dcd/
|
├── dcd/
|
||||||
│ ├── dcd.bin ← DCD бинарник (инициализация SDRAM, коммитить как есть)
|
│ ├── dcd.bin ← DCD бинарник (инициализация SDRAM)
|
||||||
│ └── ivt_flashloader.bin ← NXP Flashloader (коммитить)
|
│ ├── ivt_flashloader.bin ← NXP Flashloader (USB SDP)
|
||||||
|
│ ├── w25q64_fdcb.bin ← FCB для W25Q64 (SWD flash)
|
||||||
|
│ ├── w25q128_fdcb.bin ← FCB для W25Q128 (SWD flash) ← используется
|
||||||
|
│ └── w25q512_fdcb.bin ← FCB для W25Q512 (SWD flash)
|
||||||
├── hab/
|
├── hab/
|
||||||
│ ├── hab_firmware_test.yaml ← HAB: Debug + DCD (входной контроль)
|
│ ├── hab_firmware_test_debug.yaml
|
||||||
│ ├── hab_bootloader.yaml ← HAB: Release, без DCD
|
│ ├── hab_firmware_test_release.yaml
|
||||||
│ └── hab_app.yaml ← HAB: Release + DCD (боевая прошивка)
|
│ ├── hab_bootloader_debug.yaml
|
||||||
├── flash_usb.py ← скрипт прошивки через USB
|
│ ├── hab_bootloader_release.yaml
|
||||||
├── pyproject.toml ← зависимости (spsdk==3.7.x)
|
│ ├── hab_app_debug.yaml
|
||||||
├── uv.lock ← lockfile (коммитить)
|
│ └── hab_app_release.yaml
|
||||||
|
├── flash_usb.py ← прошивка через USB ROM (SDP → blhost)
|
||||||
|
├── flash_swd.py ← прошивка через SWD (pyocd, FCB+HAB)
|
||||||
|
├── HAB_GUIDE.md
|
||||||
|
├── pyproject.toml
|
||||||
|
├── uv.lock
|
||||||
└── README.md
|
└── README.md
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Предварительные требования
|
## Предварительные требования
|
||||||
|
|
||||||
### 1. uv — один раз на машину
|
### 1. uv — один раз на машину
|
||||||
|
|
@ -44,13 +61,11 @@ uv sync
|
||||||
|
|
||||||
### 3. udev правила — только Linux, один раз на машину
|
### 3. udev правила — только Linux, один раз на машину
|
||||||
|
|
||||||
Без этого для работы с USB нужен `sudo`:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo tee /etc/udev/rules.d/99-nxp-mimxrt.rules << 'EOF'
|
sudo tee /etc/udev/rules.d/99-nxp-mimxrt.rules << 'EOF'
|
||||||
# NXP BootROM — SDP режим (BOOT_MOD_1 = 3V3)
|
# NXP BootROM — SDP режим
|
||||||
SUBSYSTEM=="usb", ATTR{idVendor}=="1fc9", ATTR{idProduct}=="0130", MODE="0666", GROUP="plugdev"
|
SUBSYSTEM=="usb", ATTR{idVendor}=="1fc9", ATTR{idProduct}=="0130", MODE="0666", GROUP="plugdev"
|
||||||
# NXP Flashloader — после jump-address
|
# NXP Flashloader
|
||||||
SUBSYSTEM=="usb", ATTR{idVendor}=="15a2", ATTR{idProduct}=="0073", MODE="0666", GROUP="plugdev"
|
SUBSYSTEM=="usb", ATTR{idVendor}=="15a2", ATTR{idProduct}=="0073", MODE="0666", GROUP="plugdev"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
@ -61,18 +76,20 @@ sudo usermod -a -G plugdev $USER
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Карта Flash (W25Q64FVSSIG, 8 MB)
|
## Карта Flash
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
0x60000000 ┌─────────────────────────────┐
|
0x60000000 ┌─────────────────────────────┐
|
||||||
│ FCB — Flash Config Block │ 512 байт
|
│ FCB — Flash Config Block │ 512 байт
|
||||||
│ (пишет Flashloader, │
|
│ При USB SDP: пишет │
|
||||||
│ не входит в HAB образ) │
|
│ Flashloader автоматически. │
|
||||||
|
│ При SWD: flash_swd.py │
|
||||||
|
│ берёт из dcd/*_fdcb.bin. │
|
||||||
0x60001000 ├─────────────────────────────┤
|
0x60001000 ├─────────────────────────────┤
|
||||||
│ IVT — Image Vector Table │ ← начало HAB образа
|
│ IVT — Image Vector Table │ ← начало HAB-образа
|
||||||
│ BDT — Boot Data Table │
|
│ BDT — Boot Data Table │
|
||||||
0x60001040 ├─────────────────────────────┤
|
0x60001040 ├─────────────────────────────┤
|
||||||
│ DCD — SDRAM init │ ~1088 байт (для fw_test и app)
|
│ DCD — SDRAM init │ ~1088 байт (fw_test, app)
|
||||||
0x60003000 ├─────────────────────────────┤
|
0x60003000 ├─────────────────────────────┤
|
||||||
│ Код прошивки │
|
│ Код прошивки │
|
||||||
│ (.text, .data, ...) │
|
│ (.text, .data, ...) │
|
||||||
|
|
@ -81,68 +98,39 @@ sudo usermod -a -G plugdev $USER
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Пайплайн: сборка HAB образа
|
## Способ 1 — USB SDP (`flash_usb.py`)
|
||||||
|
|
||||||
```bash
|
Прошивка через ROM-загрузчик. Требует перевода платы в режим Serial Downloader.
|
||||||
cd tools/host/hab
|
|
||||||
|
|
||||||
# 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 hab export --force \
|
|
||||||
-c hab_bootloader.yaml \
|
|
||||||
-o ../../../../build/Release/bootloader_hab.bin
|
|
||||||
|
|
||||||
# app (Release) — боевая прошивка
|
|
||||||
uv run nxpimage hab export --force \
|
|
||||||
-c hab_app.yaml \
|
|
||||||
-o ../../../../build/Release/app_hab.bin
|
|
||||||
```
|
|
||||||
|
|
||||||
### Проверка образа после сборки
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uv run nxpimage hab parse -b ../../../../build/Debug/firmware_test_hab.bin
|
|
||||||
```
|
|
||||||
|
|
||||||
Ожидаемый результат: IVT с корректным `entry`, DCD с тегом `0xD2`,
|
|
||||||
`csf = 0x00000000` (unsigned).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Пайплайн: прошивка через USB
|
|
||||||
|
|
||||||
### Подготовка платы
|
### Подготовка платы
|
||||||
|
|
||||||
Перевести плату в режим SDP (Serial Download Protocol):
|
|
||||||
|
|
||||||
1. Подтянуть `BOOT_MOD_1` к `3V3`
|
|
||||||
2. Reset
|
|
||||||
3. Подключить USB к ПК
|
|
||||||
|
|
||||||
В этом режиме плата определяется как `VID:PID = 1FC9:0130`.
|
|
||||||
|
|
||||||
### Запуск скрипта
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd tools/host
|
1. BOOT_MOD_1 → 3V3
|
||||||
|
2. Reset
|
||||||
# firmware_test
|
3. Подключить USB → плата определяется как VID:PID 1FC9:0130
|
||||||
uv run python3 flash_usb.py --firmware firmware_test --build-type Debug
|
|
||||||
|
|
||||||
# app
|
|
||||||
uv run python3 flash_usb.py --firmware app --build-type Release
|
|
||||||
|
|
||||||
# bootloader
|
|
||||||
uv run python3 flash_usb.py --firmware bootloader --build-type Release
|
|
||||||
```
|
```
|
||||||
|
|
||||||
После прошивки: вернуть `BOOT_MOD_1` к `GND`, Reset — плата стартует из Flash.
|
После прошивки: `BOOT_MOD_1 → GND → Reset`.
|
||||||
|
|
||||||
### Последовательность команд внутри скрипта
|
### Запуск
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Предпочтительно через just (на хосте):
|
||||||
|
just host::flash firmware_test debug
|
||||||
|
just host::flash firmware_test release
|
||||||
|
just host::flash bootloader release
|
||||||
|
just host::flash app release
|
||||||
|
|
||||||
|
# Или напрямую:
|
||||||
|
cd tools/host
|
||||||
|
uv run python3 flash_usb.py --firmware firmware_test --build-type Debug
|
||||||
|
uv run python3 flash_usb.py --firmware app --build-type Release
|
||||||
|
|
||||||
|
# Загрузка в RAM без записи во Flash (быстро, не изнашивает Flash):
|
||||||
|
uv run python3 flash_usb.py --firmware firmware_test --build-type Debug --ram-only
|
||||||
|
```
|
||||||
|
|
||||||
|
### Последовательность команд
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
Плата в SDP режиме (1FC9:0130)
|
Плата в SDP режиме (1FC9:0130)
|
||||||
|
|
@ -153,29 +141,117 @@ uv run python3 flash_usb.py --firmware bootloader --build-type Release
|
||||||
│ (ожидание до 10с пока Flashloader поднимется на 15A2:0073)
|
│ (ожидание до 10с пока Flashloader поднимется на 15A2:0073)
|
||||||
│
|
│
|
||||||
Flashloader (15A2:0073)
|
Flashloader (15A2:0073)
|
||||||
│
|
├─ configure-memory 0xC0000007 — инициализация FlexSPI NOR
|
||||||
│ Шаг 1 — инициализация FlexSPI NOR контроллера
|
├─ flash-erase-region 0x60000000
|
||||||
├─ blhost fill-memory 0x2000 4 0xC0000007 word
|
├─ configure-memory 0xF000000F — запись FCB в 0x60000000
|
||||||
├─ blhost configure-memory 9 0x2000
|
├─ write-memory 0x60001000 ← HAB-образ
|
||||||
│
|
└─ reset
|
||||||
│ Шаг 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 не в образе?**
|
FCB генерируется Flashloader'ом автоматически из параметров FlexSPI — отдельный
|
||||||
> Flashloader генерирует FCB автоматически из параметров FlexSPI (option word
|
`*_fdcb.bin` не нужен.
|
||||||
> `0xC0000007`). Запись FCB через `0xF000000F` — это отдельная команда,
|
|
||||||
> выполняемая строго после `flash-erase-region`, иначе FCB будет затёрт следующим
|
---
|
||||||
> стиранием.
|
|
||||||
|
## Способ 2 — SWD (`flash_swd.py`)
|
||||||
|
|
||||||
|
Прошивка через отладочный пробник (MCU-Link, CMSIS-DAP). Плата остаётся в
|
||||||
|
нормальном режиме загрузки — переключать `BOOT_MOD_1` не нужно.
|
||||||
|
|
||||||
|
**После записи обязателен power cycle** — VECTRESET не реинициализирует FlexSPI,
|
||||||
|
Boot ROM не стартует без холодного старта.
|
||||||
|
|
||||||
|
### Почему нужен FCB при SWD
|
||||||
|
|
||||||
|
При USB SDP FlexSPI конфигурируется ROM-загрузчиком через DCD. При SWD
|
||||||
|
flash-алгоритм pyOCD пишет данные напрямую — Boot ROM при cold-start читает FCB
|
||||||
|
первым и по нему конфигурирует FlexSPI. Без FCB плата не стартует.
|
||||||
|
|
||||||
|
`flash_swd.py` собирает итоговый образ перед записью:
|
||||||
|
|
||||||
|
```
|
||||||
|
0x60000000 *_fdcb.bin (512 байт) — FCB
|
||||||
|
0x60000200 0xFF × 3584 байт — padding (erased flash value)
|
||||||
|
0x60001000 *_hab.bin — HAB-образ (ivtOffset = 0x1000)
|
||||||
|
```
|
||||||
|
|
||||||
|
Всё умещается в один 64KB-сектор — стирается и записывается за одну транзакцию.
|
||||||
|
|
||||||
|
### FCB-файлы
|
||||||
|
|
||||||
|
| Файл | Микросхема | Режим |
|
||||||
|
|---|---|---|
|
||||||
|
| `w25q64_fdcb.bin` | Winbond W25Q64 | Quad SPI |
|
||||||
|
| `w25q128_fdcb.bin` | Winbond W25Q128 | Quad SPI |
|
||||||
|
| `w25q512_fdcb.bin` | Winbond W25Q512 | Quad SPI |
|
||||||
|
|
||||||
|
Активный FCB задаётся в `.env`: `FCB_PATH=tools/host/dcd/w25q128_fdcb.bin`.
|
||||||
|
|
||||||
|
FCB-файлы получены из NXP SecureProvisioningTool и хранятся в репозитории —
|
||||||
|
пересоздавать не нужно.
|
||||||
|
|
||||||
|
### Запуск
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Предпочтительно через just (на хосте):
|
||||||
|
just host::flash-swd-test-debug
|
||||||
|
just host::flash-swd-test-release
|
||||||
|
just host::flash-swd-bootloader-debug
|
||||||
|
just host::flash-swd-bootloader-release
|
||||||
|
just host::flash-swd-app-debug
|
||||||
|
just host::flash-swd-app-release
|
||||||
|
|
||||||
|
# Или напрямую:
|
||||||
|
cd tools/host
|
||||||
|
uv run --directory ../hil python3 flash_swd.py --firmware firmware_test --build-type Debug
|
||||||
|
uv run --directory ../hil python3 flash_swd.py --firmware app --build-type Release
|
||||||
|
|
||||||
|
# Собрать образ без записи (для проверки):
|
||||||
|
uv run --directory ../hil python3 flash_swd.py --firmware firmware_test --build-type Debug --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Конфигурация flash_swd.py
|
||||||
|
|
||||||
|
Приоритет: аргументы CLI > переменные окружения > defaults.
|
||||||
|
|
||||||
|
| Переменная | CLI-аргумент | Default |
|
||||||
|
|---|---|---|
|
||||||
|
| `PYOCD_TARGET` | `--target` | `mimxrt1050_quadspi` |
|
||||||
|
| `PYOCD_FREQUENCY` | `--frequency` | `4000000` |
|
||||||
|
| `BUILD_DIR` | — | `<repo>/build` |
|
||||||
|
| `FCB_PATH` | `--fcb` | `tools/host/dcd/w25q128_fdcb.bin` |
|
||||||
|
|
||||||
|
Переменные задаются в `.env` и экспортируются через `just` (`set export`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Сравнение способов
|
||||||
|
|
||||||
|
| | USB SDP | SWD |
|
||||||
|
|---|---|---|
|
||||||
|
| Переключение BOOT_MODE | Нужно | Не нужно |
|
||||||
|
| Power cycle после записи | Не нужен | **Обязателен** |
|
||||||
|
| FCB в образе | Не нужен (Flashloader пишет сам) | **Нужен** (`*_fdcb.bin`) |
|
||||||
|
| Скорость записи | ~50–100 kB/s | ~8–10 kB/s |
|
||||||
|
| Совместимость с отладкой | Раздельно | MCU-Link монопольный |
|
||||||
|
| Производственный сценарий | ✓ | — |
|
||||||
|
| Итеративная разработка | Неудобно (смена режима) | ✓ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Пайплайн: сборка HAB-образов
|
||||||
|
|
||||||
|
Выполняется **внутри devcontainer**:
|
||||||
|
|
||||||
|
```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-debug
|
||||||
|
just build::hab-bootloader-release
|
||||||
|
just build::hab-app-debug
|
||||||
|
just build::hab-app-release
|
||||||
|
just build::hab-all-release # все три Release за один раз
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -185,34 +261,30 @@ uv run python3 flash_usb.py --firmware bootloader --build-type Release
|
||||||
управления прошивке. Инициализирует PLL, CCM clock gates, SEMC контроллер
|
управления прошивке. Инициализирует PLL, CCM clock gates, SEMC контроллер
|
||||||
и микросхему SDRAM (MT48LCxxM4).
|
и микросхему SDRAM (MT48LCxxM4).
|
||||||
|
|
||||||
Файл хранится в репозитории в бинарном виде и **не требует пересборки** — он
|
Файл хранится в репозитории в бинарном виде и **не требует пересборки**.
|
||||||
меняется только при изменении схемотехники.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
Цель: MT48LC16M16A2P-6A, 32 MB, шина 16 бит, CS0
|
Цель: MT48LC16M16A2P-6A, 32 MB, шина 16 бит, CS0
|
||||||
SEMC BR0: base=0x80000000, size=32MB, VLD=1
|
SEMC BR0: base=0x80000000, size=32MB, VLD=1
|
||||||
SEMC BR1–BR3: VLD=0 (один чип, один CS)
|
SEMC BR1–BR3: VLD=0
|
||||||
```
|
```
|
||||||
|
|
||||||
Использование DCD по прошивкам:
|
|
||||||
|
|
||||||
| Прошивка | DCD | Причина |
|
| Прошивка | DCD | Причина |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `firmware_test` | ✓ | тесты работают с SDRAM |
|
| `firmware_test` | ✓ | тесты работают с SDRAM |
|
||||||
| `app` | ✓ | FreeRTOS heap и буферы LCDIF размещены в SDRAM |
|
| `app` | ✓ | FreeRTOS heap и буферы LCDIF в SDRAM |
|
||||||
| `bootloader` | ✗ | загрузчик не использует SDRAM, инициализация в app |
|
| `bootloader` | ✗ | загрузчик не использует SDRAM |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Flashloader
|
## Flashloader
|
||||||
|
|
||||||
`dcd/ivt_flashloader.bin` — NXP-программа, загружаемая в RAM через SDP.
|
`dcd/ivt_flashloader.bin` — NXP-программа, загружаемая в RAM через SDP.
|
||||||
Предоставляет `blhost` доступ к Flash, которого нет через BootROM напрямую.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
Entry point: 0x20002401
|
Entry point: 0x20002401
|
||||||
Загрузка по: 0x20001C00
|
Загрузка по: 0x20001C00
|
||||||
VID:PID после старта: 15A2:0073
|
VID:PID после: 15A2:0073
|
||||||
Источник: MCUXpresso Secure Provisioning Tool 25.12
|
Источник: MCUXpresso Secure Provisioning Tool 25.12
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -224,15 +296,14 @@ VID:PID после старта: 15A2:0073
|
||||||
# Найти подключённые NXP устройства
|
# Найти подключённые NXP устройства
|
||||||
uv run nxpdevscan
|
uv run nxpdevscan
|
||||||
|
|
||||||
# Проверить связь с BootROM через SDP
|
# Проверить связь с BootROM (плата в SDP-режиме)
|
||||||
sdphost -u 0x1FC9,0x0130 -- error-status
|
sdphost -u 0x1FC9,0x0130 -- error-status
|
||||||
|
|
||||||
# Проверить что Flashloader отвечает (после jump-address)
|
# Проверить что Flashloader отвечает
|
||||||
blhost -u 0x15A2,0x0073 -- get-property 1 0
|
blhost -u 0x15A2,0x0073 -- get-property 1 0
|
||||||
|
|
||||||
# Версии инструментов
|
# Проверить что pyOCD видит таргет (для SWD)
|
||||||
uv run nxpimage --version
|
just host::debug-list-targets
|
||||||
uv run blhost --version
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Типичные ошибки
|
### Типичные ошибки
|
||||||
|
|
@ -240,10 +311,11 @@ uv run blhost --version
|
||||||
| Симптом | Причина | Решение |
|
| Симптом | Причина | Решение |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `USB HID device not found: 1FC9:0130` | Плата не в SDP режиме | Проверить `BOOT_MOD_1` → 3V3 и Reset |
|
| `USB HID device not found: 1FC9:0130` | Плата не в SDP режиме | Проверить `BOOT_MOD_1` → 3V3 и Reset |
|
||||||
| Flashloader timeout после jump | `ivt_flashloader.bin` повреждён или не тот | Взять из SPT 25.12 |
|
| Flashloader timeout после jump | `ivt_flashloader.bin` повреждён | Взять из SPT 25.12 |
|
||||||
| Плата не стартует после прошивки | `BOOT_MOD_1` не переключён обратно | Вернуть `BOOT_MOD_1` → GND, Reset |
|
| Плата не стартует после USB SDP | `BOOT_MOD_1` не переключён обратно | `BOOT_MOD_1` → GND, Reset |
|
||||||
| Плата зависает сразу после старта | DCD завис (неверный `dcd.bin`) | Использовать только верифицированный `dcd.bin` |
|
| Плата не стартует после SWD flash | Power cycle не был выполнен | Отключить и подключить питание |
|
||||||
| Плата стартует, периферия не работает | CCM clock gates в DCD отключают нужные клоки | Проверить `dcd.bin` — только верифицированный вариант |
|
| Плата не стартует после SWD flash | Неверный FCB (другая Flash-микросхема) | Проверить `FCB_PATH` в `.env` |
|
||||||
|
| `skipped N bytes` при SWD flash | pyOCD считает содержимое актуальным | Добавить `--erase chip` или `--erase sector` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
BIN
tools/host/dcd/w25q128_fdcb.bin
Normal file
BIN
tools/host/dcd/w25q128_fdcb.bin
Normal file
Binary file not shown.
BIN
tools/host/dcd/w25q512_fdcb.bin
Normal file
BIN
tools/host/dcd/w25q512_fdcb.bin
Normal file
Binary file not shown.
BIN
tools/host/dcd/w25q64_fdcb.bin
Normal file
BIN
tools/host/dcd/w25q64_fdcb.bin
Normal file
Binary file not shown.
219
tools/host/flash_swd.py
Normal file
219
tools/host/flash_swd.py
Normal file
|
|
@ -0,0 +1,219 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
flash_swd.py — прошивка MIMXRT1052 через SWD (MCU-Link / CMSIS-DAP)
|
||||||
|
|
||||||
|
Отличие от flash_usb.py (spsdk):
|
||||||
|
flash_usb.py — USB Serial Downloader Protocol (SDP). Плата должна быть в
|
||||||
|
режиме Serial Downloader (BOOT_MODE = 01). Использует ROM-
|
||||||
|
загрузчик для разбора IVT/DCD/HAB — сам конфигурирует FlexSPI
|
||||||
|
по DCD, поэтому FCB в образе не нужен.
|
||||||
|
|
||||||
|
flash_swd.py — SWD через отладочный пробник (MCU-Link). Плата в любом
|
||||||
|
режиме загрузки; отладчик загружает flash-алгоритм в RAM
|
||||||
|
и пишет NOR Flash напрямую. Boot ROM при cold-start читает
|
||||||
|
FCB первым — поэтому этот скрипт объединяет FCB + HAB
|
||||||
|
перед записью в единый образ.
|
||||||
|
|
||||||
|
Структура образа в Flash (XIP NOR):
|
||||||
|
0x60000000 FCB (512 байт) — Flash Configuration Block (W25Q128, Quad SPI)
|
||||||
|
0x60000200 0xFF (padding) — до адреса IVT
|
||||||
|
0x60001000 IVT — начало HAB-образа (ivtOffset = 0x1000)
|
||||||
|
0x60001020 DCD — инициализация SDRAM (если есть)
|
||||||
|
0x60002000 .text / .data — код прошивки
|
||||||
|
|
||||||
|
Конфигурация (приоритет: аргументы CLI > переменные окружения > defaults):
|
||||||
|
PYOCD_TARGET — таргет pyOCD (default: mimxrt1050_quadspi)
|
||||||
|
PYOCD_FREQUENCY — частота SWD в Гц (default: 4000000)
|
||||||
|
BUILD_DIR — директория сборки (default: <repo_root>/build)
|
||||||
|
FCB_PATH — путь к FCB-бинарнику (default: tools/host/dcd/w25q128_fdcb.bin)
|
||||||
|
|
||||||
|
Переменные задаются в .env и автоматически экспортируются через just (set export).
|
||||||
|
|
||||||
|
Использование:
|
||||||
|
python flash_swd.py --firmware firmware_test --build-type Debug
|
||||||
|
python flash_swd.py --firmware bootloader --build-type Release
|
||||||
|
python flash_swd.py --firmware app --build-type Debug --dry-run
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# ── Корень репозитория ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
|
||||||
|
# ── Конфигурация из окружения (just экспортирует .env через set export) ───────
|
||||||
|
|
||||||
|
def _env(key: str, default: str) -> str:
|
||||||
|
return os.environ.get(key, default)
|
||||||
|
|
||||||
|
PYOCD_TARGET = _env("PYOCD_TARGET", "mimxrt1050_quadspi")
|
||||||
|
PYOCD_FREQUENCY = _env("PYOCD_FREQUENCY", "4000000")
|
||||||
|
BUILD_DIR = Path(_env("BUILD_DIR", str(REPO_ROOT / "build")))
|
||||||
|
FCB_PATH = Path(_env("FCB_PATH", str(REPO_ROOT / "tools/host/dcd/w25q128_fdcb.bin")))
|
||||||
|
if not FCB_PATH.is_absolute():
|
||||||
|
FCB_PATH = REPO_ROOT / FCB_PATH
|
||||||
|
HIL_DIR = REPO_ROOT / "tools" / "hil"
|
||||||
|
|
||||||
|
# IVT располагается по смещению 0x1000 от начала Flash (ivtOffset в HAB yaml)
|
||||||
|
IVT_OFFSET = 0x1000
|
||||||
|
|
||||||
|
# Маппинг имён прошивок → имена HAB-файлов (генерируются build::hab-*)
|
||||||
|
HAB_NAMES = {
|
||||||
|
"firmware_test": "firmware_test_hab.bin",
|
||||||
|
"bootloader": "bootloader_hab.bin",
|
||||||
|
"app": "app_hab.bin",
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Helpers ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def build_full_image(fcb_path: Path, hab_path: Path) -> bytes:
|
||||||
|
"""Объединить FCB + padding + HAB в единый образ для записи с 0x60000000."""
|
||||||
|
fcb = fcb_path.read_bytes()
|
||||||
|
hab = hab_path.read_bytes()
|
||||||
|
|
||||||
|
if len(fcb) > IVT_OFFSET:
|
||||||
|
raise ValueError(
|
||||||
|
f"FCB size {len(fcb)} bytes exceeds IVT_OFFSET {IVT_OFFSET:#x}"
|
||||||
|
)
|
||||||
|
|
||||||
|
padding = b"\xff" * (IVT_OFFSET - len(fcb)) # 0xFF = erased flash value
|
||||||
|
image = fcb + padding + hab
|
||||||
|
|
||||||
|
print(f" FCB: {len(fcb):>6} bytes @ 0x60000000")
|
||||||
|
print(f" Padding: {len(padding):>6} bytes @ 0x{0x60000000 + len(fcb):08X}")
|
||||||
|
print(f" HAB: {len(hab):>6} bytes @ 0x60001000")
|
||||||
|
print(f" Total: {len(image):>6} bytes")
|
||||||
|
|
||||||
|
return image
|
||||||
|
|
||||||
|
|
||||||
|
def run_pyocd_flash(image_path: Path, target: str, frequency: str) -> int:
|
||||||
|
"""Запустить pyocd flash через uv run из tools/hil."""
|
||||||
|
cmd = [
|
||||||
|
"uv", "run",
|
||||||
|
"--directory", str(HIL_DIR),
|
||||||
|
"pyocd", "flash",
|
||||||
|
"--target", target,
|
||||||
|
"--frequency", frequency,
|
||||||
|
"--base-address", "0x60000000",
|
||||||
|
"--erase", "sector",
|
||||||
|
str(image_path),
|
||||||
|
]
|
||||||
|
print(f"\n Running: {' '.join(cmd)}\n")
|
||||||
|
return subprocess.run(cmd, cwd=REPO_ROOT).returncode
|
||||||
|
|
||||||
|
|
||||||
|
# ── Main ──────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Flash MIMXRT1052 via SWD (MCU-Link). "
|
||||||
|
"Prepends FCB to HAB image before programming.",
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
epilog=__doc__,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--firmware",
|
||||||
|
choices=list(HAB_NAMES.keys()),
|
||||||
|
required=True,
|
||||||
|
help="Firmware project to flash",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--build-type",
|
||||||
|
choices=["Debug", "Release"],
|
||||||
|
default="Debug",
|
||||||
|
help="CMake build type (default: Debug)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--fcb",
|
||||||
|
type=Path,
|
||||||
|
default=FCB_PATH,
|
||||||
|
help=f"Path to FCB binary (default from FCB_PATH env or {FCB_PATH})",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--target",
|
||||||
|
default=PYOCD_TARGET,
|
||||||
|
help=f"pyOCD target (default from PYOCD_TARGET env or {PYOCD_TARGET})",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--frequency",
|
||||||
|
default=PYOCD_FREQUENCY,
|
||||||
|
help=f"SWD frequency in Hz (default from PYOCD_FREQUENCY env or {PYOCD_FREQUENCY})",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--dry-run",
|
||||||
|
action="store_true",
|
||||||
|
help="Build the combined image and save it, but do not flash",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# ── Проверить FCB ─────────────────────────────────────────────────────────
|
||||||
|
if not args.fcb.exists():
|
||||||
|
print(f" ❌ FCB not found: {args.fcb}", file=sys.stderr)
|
||||||
|
print(
|
||||||
|
" Hint: obtain w25q128_fdcb.bin from NXP SecureProvisioningTool\n"
|
||||||
|
" for W25Q128 in Quad SPI mode and place it in tools/host/dcd/\n"
|
||||||
|
" or set FCB_PATH in .env",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# ── Найти HAB-образ ───────────────────────────────────────────────────────
|
||||||
|
hab_name = HAB_NAMES[args.firmware]
|
||||||
|
hab_path = BUILD_DIR / args.build_type / hab_name
|
||||||
|
|
||||||
|
if not hab_path.exists():
|
||||||
|
print(f" ❌ HAB image not found: {hab_path}", file=sys.stderr)
|
||||||
|
fw_slug = args.firmware.replace("_", "-")
|
||||||
|
bt_slug = args.build_type.lower()
|
||||||
|
print(
|
||||||
|
f" Hint: run just build::hab-{fw_slug}-{bt_slug} "
|
||||||
|
"inside devcontainer first.",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
print(f"\n Firmware : {args.firmware} ({args.build_type})")
|
||||||
|
print(f" HAB : {hab_path}")
|
||||||
|
print(f" FCB : {args.fcb}")
|
||||||
|
print(f" Target : {args.target}")
|
||||||
|
print(f" Frequency : {args.frequency} Hz\n")
|
||||||
|
|
||||||
|
# ── Собрать объединённый образ ────────────────────────────────────────────
|
||||||
|
image = build_full_image(args.fcb, hab_path)
|
||||||
|
|
||||||
|
if args.dry_run:
|
||||||
|
out = BUILD_DIR / args.build_type / f"{args.firmware}_full.bin"
|
||||||
|
out.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
out.write_bytes(image)
|
||||||
|
print(f"\n Dry run — image saved to {out}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# ── Записать во Flash через pyOCD ─────────────────────────────────────────
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
suffix=f"_{args.firmware}_full.bin", delete=False
|
||||||
|
) as tmp:
|
||||||
|
tmp_path = Path(tmp.name)
|
||||||
|
tmp_path.write_bytes(image)
|
||||||
|
|
||||||
|
try:
|
||||||
|
rc = run_pyocd_flash(tmp_path, args.target, args.frequency)
|
||||||
|
finally:
|
||||||
|
tmp_path.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
if rc == 0:
|
||||||
|
print(f"\n ✅ {args.firmware} ({args.build_type}) flashed via SWD")
|
||||||
|
print(" ⚡ Power cycle the board to boot from Flash")
|
||||||
|
else:
|
||||||
|
print(f"\n ❌ pyocd flash failed (exit code {rc})", file=sys.stderr)
|
||||||
|
|
||||||
|
return rc
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
Loading…
Reference in a new issue