14.1.1.4. 펌웨어 디버깅¶
온하드웨어 디버깅이란 프로세서를 정지시키고, C 소스에 중단점을 설정하며, 한 단계씩 실행하고, 변수와 메모리, 레지스터, 주변장치를 검사하는 것을 의미합니다 – 모두 VS Code 내부에서 수행합니다. 이를 위해서는 세 가지가 필요합니다: 디버그 빌드, SWD 디버그 프로브(Segger J-Link), 그리고 J-Link GDB 서버를 대상으로 arm-none-eabi-gdb를 구동하는 Cortex-Debug 확장.
14.1.1.4.1. 디버깅용 빌드¶
항상 DEBUG=1로 타깃을 다시 빌드하십시오:
make -j$(nproc) TARGET=<TARGET> DEBUG=1
릴리스(DEBUG=0) 이미지는 -O2로 컴파일됩니다. 디버거에서는 많은 변수가 <optimized out>으로 표시되고, 인라인된 함수는 호출자 안으로 합쳐지며, 단계 실행이 예측할 수 없게 건너뜁니다. DEBUG=1은 -Og -ggdb3으로 빌드되어 디버깅이 가능하면서도 카메라에서 여전히 부팅됩니다. 디버거가 가리키는 ELF는 다음과 같습니다:
build/<TARGET>/bin/firmware.elf
(Alif AE3의 경우 build/OPENMV_AE3/bin/firmware_M55_HP.elf를 디버깅하십시오 – 고성능 코어입니다.)
14.1.1.4.2. 하드웨어: SWD를 통한 J-Link¶
Segger J-Link를 카메라의 SWD 핀(SWDIO, SWCLK, GND, 그리고 참조용 타깃 VCC. 카메라는 평소대로 USB로 전원이 공급됩니다)에 연결하십시오. J-Link EDU / Base / Pro 모두 작동합니다. 디버그 핀이 노출되는 위치는 카메라마다 다릅니다 – 많은 보드에는 전용 JTAG/SWD 커넥터가 있고, 다른 보드는 I/O 헤더나 테스트 패드에 SWD를 노출합니다 – 따라서 OpenMV 하드웨어 문서에서 해당 보드의 핀아웃 다이어그램과 회로도를 확인하여 어느 핀을 연결할지 확인하십시오. 프로브가 물리적으로 연결된 머신에 segger.com 에서 J-Link Software and Documentation Pack을 설치하십시오. 합리적으로 최신 상태로 유지하십시오 – 오래된 J-Link 소프트웨어는 최신 장치 이름(STM32N6, MIMXRT, Alif)을 인식하지 못합니다.
각 MCU는 프로브가 올바른 플래시 로더와 메모리 맵을 불러오도록 정확한 J-Link 장치 이름이 필요합니다:
카메라 ( |
MCU |
J-Link |
|---|---|---|
|
STM32F427 |
|
|
STM32F765 |
|
|
STM32H743 |
|
|
STM32N657 |
|
|
MIMXRT1062 |
|
|
Alif Ensemble (M55-HP) |
|
|
STM32H747 |
|
14.1.1.4.3. VS Code Cortex-Debug 설정¶
저장소에 .vscode/launch.json을 생성하십시오. 가장 간단한 경우 – VS Code, J-Link, 빌드가 모두 동일한 Linux / macOS 머신에 있는 경우 – 는 servertype: "jlink"를 사용하며, 이는 Cortex-Debug가 직접 J-Link GDB 서버를 시작하도록 합니다:
{
"version": "0.2.0",
"configurations": [
{
"name": "OpenMV J-Link",
"type": "cortex-debug",
"request": "launch",
"cwd": "${workspaceFolder}",
"executable": "${workspaceFolder}/build/OPENMV4/bin/firmware.elf",
"servertype": "jlink",
"device": "STM32H743VI",
"interface": "swd",
"runToEntryPoint": "main",
"armToolchainPath": "${env:HOME}/openmv-sdk-1.6.0/gcc/bin",
"gdbPath": "${env:HOME}/openmv-sdk-1.6.0/gcc/bin/arm-none-eabi-gdb"
}
]
}
보드에 맞게 executable과 device를 변경하십시오(위 표 참조). F5를 눌러 빌드-플래시-실행하여 main에서 멈추도록 하십시오.
팁
디버깅을 시작할 때마다 자동으로 다시 빌드하려면 .vscode/tasks.json에 빌드 작업을 추가하고 launch 구성에서 "preLaunchTask"로 이를 참조하십시오. 예를 들어 make -j$(nproc) TARGET=OPENMV4 DEBUG=1을 실행하는 "build-firmware"라는 이름의 작업과, 위 구성의 "preLaunchTask": "build-firmware"를 사용하면 F5가 다시 빌드하고, 플래시하고, 디버거를 단일 단계로 시작합니다.
경고
Cortex-Debug에는 arm-none-eabi-gdb가 필요합니다. 이는 SDK의 ~/openmv-sdk-<version>/gcc/bin에 포함되어 있지만 기본적으로 PATH에 있지 않으므로, 디버깅이 “GDB executable ‘arm-none-eabi-gdb’ was not found” 오류로 실패합니다. 위에서 보인 대로 armToolchainPath / gdbPath를 설정하거나, ~/openmv-sdk-<version>/gcc/bin을 PATH에 추가하여(printenv PATH로 확인하면 표시되어야 함) 해결하십시오.
14.1.1.4.4. 주변장치 레지스터 보기 (SVD)¶
Cortex-Debug가 CMSIS SVD 파일을 가리키도록 하여 주변장치 레지스터(타이머, DMA, 카메라 인터페이스 등)를 이름과 비트필드별로 디코딩한 보기를 얻으십시오:
"svdFile": "/path/to/STM32H743.svd"
STM32 및 MIMXRT의 경우 ST / NXP CMSIS 팩이나 Cortex-Debug SVD 레지스트리에서 SVD를 받으십시오. Alif SVD는 펌웨어 저장소의 lib/micropython/lib/alif_ensemble-cmsis-dfp/Debug/SVD/에 벤더링되어 있습니다(AE3 HP 코어에는 ..._CM55_HP_View.svd를 사용하십시오).
14.1.1.4.5. Windows: WSL ↔ Windows J-Link 브리지¶
WSL 2는 J-Link의 USB 장치를 직접 볼 수 없으므로 다음과 같이 분리합니다: Windows가 프로브를 제공하고(연결된 곳), VS Code + gdb는 WSL에서 실행되어 TCP를 통해 도달합니다.
Windows에서 Segger J-Link 팩을 설치하고 J-Link를 Windows USB 포트에 꽂으십시오.
Windows에서 J-Link Remote Server를 시작하십시오(J-Link 팩에 포함되어 있습니다): J-Link를 연결한 상태로 실행하고 OK를 클릭하십시오. 프롬프트가 나타나면 Windows 방화벽을 통과시키십시오. 창에는 프로브를 제공하는 IP 주소가 표시됩니다 – 메모해 두십시오.
WSL에서
DEBUG=1로 빌드하고arm-none-eabi-gdb에 도달할 수 있는지 확인하십시오(위와 같이armToolchainPath설정).WSL VS Code에서
servertype: "jlink"를 유지하십시오 – GDB 서버는 WSL에서 실행되어 Remote Server를 통해 프로브에 도달합니다 – 그리고serverpath+ipAddress를 추가하십시오:{ "name": "OpenMV J-Link (Windows host)", "type": "cortex-debug", "request": "launch", "cwd": "${workspaceFolder}", "executable": "${workspaceFolder}/build/OPENMV4/bin/firmware.elf", "servertype": "jlink", "serverpath": "/opt/SEGGER/JLink/JLinkGDBServer", "ipAddress": "192.168.x.x", "device": "STM32H743VI", "interface": "swd", "runToEntryPoint": "main", "armToolchainPath": "${env:HOME}/openmv-sdk-1.6.0/gcc/bin" }
ipAddress를 Remote Server 창에 표시된 주소로 설정하십시오. 이것이 브리지의 전부입니다.
팁
GDB 서버 브리지의 대안: usbipd-win. Windows에서 서버를 실행하는 대신 usbipd-win 을 사용하여 J-Link의 USB 장치를 WSL에 직접 연결할 수 있습니다. 관리자 PowerShell에서:
winget install usbipd
usbipd list
usbipd bind --busid <busid>
usbipd attach --wsl --busid <busid>
(<busid>는 usbipd list에서 얻은 J-Link의 버스 ID입니다.) 그러면 프로브가 WSL 내부에 나타나며, IP 주소나 별도의 Windows 서버 없이 VS Code Cortex-Debug setup 의 일반 동일-머신 servertype: "jlink" 구성을 사용합니다. GDB 서버 브리지는 가끔 사용할 때 설정이 더 적게 필요하고, usbipd-win은 일상적인 개발에 더 편리합니다.
팁
재설정이나 재플래시 없이 이미 실행 중인 상태 그대로 펌웨어를 디버깅하려면 "request": "attach"를 사용하십시오 – 현장에서 발생하는 멈춤을 잡는 데 이상적입니다. 재설정하고, ELF를 플래시하고, runToEntryPoint에서 새로 시작하려면 "request": "launch"를 사용하십시오.
14.1.1.4.6. gdbrunner를 사용한 명령줄 디버깅¶
임베디드 타깃을 대상으로 GDB 세션을 수동으로 설정하는 것은 다섯 단계의 번거로운 작업입니다: 한 창에서 올바른 장치, 포트, 인터페이스 플래그로 J-Link / ST-Link GDB 서버를 시작하고, Waiting for GDB connection이 출력될 때까지 기다리고, 두 번째 창에서 arm-none-eabi-gdb를 실행하고, target remote localhost:<port>를 입력하고, gdb가 ELF를 가리키도록 합니다. gdb 세션이 끝나면 서버 창을 종료하는 것을 잊지 마십시오. gdbrunner 는 이 모든 것을 하나의 포그라운드 명령으로 합치는 작은 CLI입니다. OpenMV SDK의 Python 환경에 포함되어 있어 설치할 것이 없습니다. 일반적인 진입점은 펌웨어 저장소의 make debug 타깃입니다:
make -j$(nproc) TARGET=<TARGET> DEBUG=1 debug
이는 보드 구성의 디버거 인수 – J-Link 장치 이름과, 필요한 경우 ST-Link 외부 플래시 로더 – 로 gdbrunner를 실행하며, SDK의 arm-none-eabi-gdb가 이미 PATH에 있습니다. 기본 백엔드는 J-Link입니다. make DEBUGGER=STLINK debug는 대신 ST-Link 프로브로 작동합니다.
gdbrunner는 직접 호출할 수도 있습니다(SDK 외부에서는 pip install gdbrunner):
gdbrunner jlink --device STM32H743VI build/OPENMV4/bin/firmware.elf
gdbrunner stlink build/OPENMV4/bin/firmware.elf
gdbrunner qemu --machine mps2-an500 build/MPS2_AN500/bin/firmware.elf
첫 번째 위치 인수는 서버 백엔드(jlink, stlink, qemu)를 선택하고, 나머지는 OpenMV 캠에 맞는 기본값과 함께 해당 백엔드로 전달됩니다. gdbrunner --help는 백엔드별 전체 플래그 목록을 나열합니다. 각 백엔드의 인수 테이블은 JSON 기반(src/gdbrunner/backends.json)이므로, 새 서버를 추가하는 것은 코드 수정이 아니라 구성 편집입니다.
gdbrunner가 명령줄 작업을 위해 하는 일:
하나의 프로세스, 깔끔한 수명 주기. 서버가 시작되고, 포트가 열리면 gdb가 연결되며, gdb가 종료되면 서버가 깔끔하게 종료됩니다. 세션 이후에 살아남는 고아
JLinkGDBServer도 없고, 관리할 두 개의 터미널도 없습니다.STM32CubeProgrammer 자동 검색.
stlink백엔드는 STM32CubeProgrammer 도구를 찾기 위해 일반적인 설치 위치(~/STM32CubeProgrammer/,/opt/st/, STM32CubeIDE 플러그인 트리)를 검색하므로, 긴--cube-prog경로를 매번 입력할 필요가 없습니다. SDK는~/openmv-sdk-<version>/stcubeprog/bin에 자체 복사본을 번들로 제공합니다 – 시스템 설치가 없으면--cube-prog를 그곳으로 가리키십시오.프로젝트별 gdbinit 적용. 현재 디렉터리의
.gdbinit는-ix로 로드되어 – 사용자 전체의~/.gdbinit를 재정의하므로 – 프로젝트별 gdb 스크립팅(프리티 프린터, 보드별 매크로, 중단점 집합)이 작업 디렉터리에 존재하는 것만으로 적용됩니다.make debug는 저장소 루트에서 실행되므로 그곳의.gdbinit가 적용됩니다.드라이 런.
--dryrun은 서버 명령을 실행하지 않고 출력하며, 호출을 래퍼 스크립트에 맞추거나, IDE 런처 구성에 복사하거나, gdbrunner가 어떤 인수를 구성하는지 확인하는 데 유용합니다.서버 출력 표시.
--show-output은 서버의 stdout / stderr를 표시된 상태로 유지합니다. 기본값은 이를 억제합니다(gdb의 UI가 깔끔하게 유지되도록). 서버 자체가 오작동할 때 이 플래그를 켜십시오.QEMU 백엔드.
qemu-system-arm은 보드를 꽂지 않은 상태로 펌웨어 빌드를 디버깅합니다.MPS2_AN500타깃은 보드 구성에서 이 백엔드를 선택하므로,make TARGET=MPS2_AN500 DEBUG=1 debug는 QEMU의mps2-an500머신용으로 빌드하고 플랫폼 독립적인 코드 – 캠 전용 주변장치를 건드리지 않는 모든 것 – 를 비행 중에 단계 실행합니다. (qemu-system-arm은 SDK의 일부가 아니라 호스트 설치입니다.)
중단점 거터와 주변장치 레지스터 보기를 갖춘 소스 수준 단계 실행에는 위의 VS Code Cortex-Debug 설정이 더 나은 도구입니다. gdbrunner는 명령줄에 사는 모든 것에 적합한 도구입니다.
14.1.1.4.7. 디버거 사용하기¶
세션이 실행되면(프로세서가 main에서 정지된 상태):
중단점 – C 줄 옆의 거터를 클릭하거나, Debug Console에서
break <file>:<line>/break <function>을 사용하십시오. Cortex-M 코어에는 소수의 하드웨어 중단점 비교기가 있습니다(일반적으로 M7 / H7에서 6–8개, M55에서 8개). 플래시에 있는 코드에서 이를 초과하면 조용히 실패합니다 – 활성 중단점 수를 적당하게 유지하십시오.단계 실행 – F10 스텝 오버(
next), F11 스텝 인투(step), Shift+F11 스텝 아웃(finish), F5 계속. 명령어 수준 단계 실행은 Debug Console에서stepi/nexti입니다.변수 / 감시 / 호출 스택 – Variables와 Call Stack 창은 지역 변수와 백트레이스를 표시합니다. Watch에 표현식을 추가하십시오. 소스에서 변수 위에 마우스를 올리면 값을 볼 수 있습니다.
<optimized out>으로 표시되는 것은DEBUG=1빌드에 있지 않다는 의미입니다.감시점(데이터 중단점) –
watch <expr>는 변수가 기록될 때 정지하고,rwatch는 읽기 시,awatch는 둘 중 어느 쪽이든 정지합니다. Cortex-M DWT 유닛은 약 4개의 하드웨어 감시점을 지원합니다 – 누가 변수를 손상시켰는지 잡는 데 매우 유용합니다.레지스터와 주변장치 – Cortex Registers 보기는 코어 레지스터를 표시합니다.
svdFile이 설정되면 Peripherals 보기가 모든 주변장치 레지스터와 비트필드(DMA, 타이머, 카메라 / CSI 인터페이스, XSPI 등)를 디코딩합니다 – 드라이버가 왜 오작동하는지 가장 빠르게 확인하는 방법입니다.메모리 – Cortex-Debug 메모리 뷰어나 gdb
x/를 사용하여 프레임 버퍼, DMA 버퍼, 구조체를 직접 검사하십시오.중단 없는 printf (SWO/RTT) – 타이밍에 민감한 문제의 경우, Segger의 RTT 또는 SWO를 사용하면 타겟이 실행되는 동안 거의 오버헤드 없이
printf를 사용할 수 있습니다.DEBUG_PRINTF=1로 빌드하고 Cortex-Debug의rttConfig(RTT) 또는swoConfig(SWO, 코어 클럭 필요)를 추가하세요. 브레이크포인트가 관찰하려는 타이밍을 변경시킬 수 있는 상황에서 적합한 도구입니다.연결 해제 –
launch세션에서 Stop은 타깃을 정지시키고,attach세션에서 Disconnect는 카메라를 계속 실행 상태로 둡니다. 이후 정상 동작으로 되돌리려면 카메라의 전원을 껐다 켜십시오.
14.1.1.4.8. 디버깅 함정¶
최적화로 제거된 변수. 모든 것이
<optimized out>으로 표시됩니다 –DEBUG=0으로 빌드한 것입니다.DEBUG=1로 다시 빌드하십시오.“GDB executable not found” – SDK
gcc/bin이PATH에 없습니다.armToolchainPath/gdbPath를 설정하십시오.“Cannot connect” / 잘못된 메모리 맵 – 잘못되었거나 누락된
device이름. 표의 정확한 문자열을 사용하십시오.중단점이 조용히 적중하지 않음 – 플래시에 상주하는 코드에 하드웨어 중단점이 너무 많습니다. 줄이십시오.
소스 경로가 일치하지 않음(Docker로 빌드한 ELF) – Docker
build-firmware-dev타깃으로 빌드하거나(컨테이너 내부와 외부에서 동일한 절대 경로) gdbset substitute-path를 설정하십시오.