Пошаговая GDB отладка ARM процессора из консоли в Win10

Типичная ситуация. Компонент логирования и UART не проинициализировался корректно или устройство где-то зависло в инициализации после reset. Или устройство бесконечно перезагружается после подачи питания. Как же понять на какой строчке возникла run-time ошибка? Классическое решение это пошаговая отладка через JTAG или SWD.

Немного про железо. Работать буду вот с этой платой

Отладочная плата nRF5340-DKОтладочная плата nRF5340-DK

Тут на board (e) как программатор U2 так и сам микроконтроллер U1 (Target). Оба 94pin (новые) nrf5340 в корпусе aQFN94. Соединены они интерфейсом SWD. Внутри каждого по 2 ядра Arm Cortex-M33 (Armv8-M). На улицу также выходит 2 UART (а) для интерфейса командной строки. Они же пробрасываются по USB (J2) на DeskTop.

Что понадобится из софта?

Утилита

Назначение

Path

arm-none-eabi-gdb.exe

GDB Client для ARM

C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2021.10\bin

JLinkGDBServer.exe

GDB Server

C:\Program Files (x86)\SEGGER\JLink\

nrfjprog.exe

Прошивальщик микроконтроллера

C:\Program Files (x86)\Nordic Semiconductor\nrf-command-line-tools\bin

ttermpro.exe

Терминал COM портов

C:\Program Files (x86)\teraterm

Этого должно быть достаточно.

Фаза 1 Залить прошивку в Target

Это можно проделать вот этим скриптом

echo off
cls

set com_port_num=10
set baudrate=115200
set project_name=headset_app
set project_dir=%~dp0
set NRF_SDK_DIR=C:/ncs/v2.1.0

set SDK_PROJECT_DIR=%NRF_SDK_DIR%/nrf/applications/nrf5340_dk_audio_w
IF "%1"=="" (
    echo set dflt SDK ver 
    set ARTEFACT_HEX=%SDK_PROJECT_DIR%\build\headset_app\zephyr\zephyr.hex
) ELSE (
    echo set dflt SDK from argument 
    set ARTEFACT_HEX=%1
)
echo Artefact hex: [%ARTEFACT_HEX%]


set tools_dir=%cd%\..\..\..\tool
echo Project Dir:%project_dir%

set FlashTool="C:\Program Files (x86)\Nordic Semiconductor\nrf-command-line-tools\bin\nrfjprog.exe"

set options=--iface USB --family NRF53 --coprocessor CP_APPLICATION --program %ARTEFACT_HEX%  --log --chiperase --verify --reset 
call %FlashTool% %options%
echo tools_dir=%tools_dir%

call %tools_dir%\launch_terminal.bat 9 %baudrate% %project_name%
call %tools_dir%\launch_terminal.bat 10 %baudrate% %project_name%

Отработав этот скрипт загрузит прошивку в микроконтроллер nrf5340.

Фаза 2 Запуск GDB сервера (Back-End)

GDB сервер это утилита, которая опрашивает программатор-отладчик. Именно она общается с устройством. Надо запустить GDB сервер. Вот скрипт запуска этой утилиты.

 
echo off
cls
set GDBServerOpt = -select USB -device nRF5340_xxAA_APP -endian little -if SWD -speed 400 -ir -LocalhostOnly -logtofile -log "C:\projects\code_base_workspace\code_base_firmware\tool\GdbServerLog.txt"

set GDBServerDir="C:\Program Files (x86)\SEGGER\JLink\"
set GDBServerPath=%GDBServerDir%JLinkGDBServer.exe"
cd %GDBServerDir%
call %GDBServerPath% %GDBServerOpt%

Надо установить интерфейс связи программатора и микроконтроллера который мы хотим отлаживать. В данном случае это интерфейс SWD.

df47e976222b1baccfbb5b515e08477f.png

J-link попросит выбрать микроконтроллер

f41950f6f7f7628ae37506733639b4bd.png

список поддерживаесых чипов очень большой

f63b7ee96287e23ff65ca74a2e520bd2.png

В моем случае чип nrf5340

31856a2cd58318555f74e7331df88d17.png

GDB сервер J-Link запущен локально. Программатор (S/N: 1050009032) подключен по USB. Target подключен к программатору по SWD.

1e8627ddc199d5c81667b252fd4368f9.png

вот полный лог ожидающего GDB сервера

Hidden text

SEGGER J-Link GDB Server V7.66a GUI Version

JLinkARM.dll V7.66a (DLL compiled May 19 2022 15:13:27)

-----GDB Server start settings-----
GDBInit file: none
GDB Server Listening port: 2331
SWO raw output listening port: 2332
Terminal I/O port: 2333
Accept remote connection: localhost only
Generate logfile: off
Verify download: off
Init regs on start: off
Silent mode: off
Single run mode: off
Target connection timeout: 5000 ms
------J-Link related settings------
J-Link Host interface: USB
J-Link script: none
J-Link settings file: none
------Target related settings------
Target device: Unspecified
Target interface: SWD
Target interface speed: 4000kHz
Target endian: little

Connecting to J-Link…
J-Link is connected.
Firmware: J-Link OB-nRF5340-NordicSemi compiled Dec 3 2021 15:46:49
Hardware: V1.00
S/N: 1050009032
Checking target voltage…
Target voltage: 3.30 V
Listening on TCP/IP port 2331
Connecting to target…
Connected to target
Waiting for GDB connection…

Сейчас GDB сервер просто ожидает подключения по TCP порту: 2331

На отладочной плате nRF5340-DK, есть встроенный программатор J-Link и там сейчас непрерывно светится зеленый LED, от отладчика.

Фаза 3 Запуск GDB клиента (Front-End )

Если GDB сервер можно метафорично считать Back-End (ом), то Front-End частью для пошаговой отладки выступает GDB клиент. Вот скрипт его запуска.


"C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2021.10\bin\arm-none-eabi-gdb.exe" --help
"C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2021.10\bin\arm-none-eabi-gdb.exe"  C:/ncs/v2.1.0/nrf/applications/nrf5340_dk_audio_w/build/headset_app/zephyr/zephyr.elf

GBD клиент смог подхватить *.elf файл с отладочными символами

0a1efa4c55472e56c4c38d6d8fc456ba.png

Настало время подключиться к GBD серверу. Это делается командой.

target remote localhost:2331

Сработало

b3274ce0d8beeedbdf793c049fb504bb.png

Подключился. Сейчас исполнение прошивки на паузе. HeartBeat LED не мигает, UART-Shell не отвечает.

Для запуска прошивки надо набрать команду continue

5b8a0a7375f90678751e69de91588eb0.png

Теперь HeartBeat LED мигает CLI отвечает. LED на программаторе тоже мигает только как-то апериодично.

Для того чтобы снова остановить исполнение программы надо в терминале GDB клиента нажать Ctrl+C.

Теперь можно установить точку останова. Меня интересует функция bool hw_init (void)

(gdb) break hw_init

Посмотреть список установленных точек останова можно командой

(gdb) info b

Вот это сейчас отображается

de3139680510f952439bef3013d5faf4.png

Снова запускает программу на исполнение. Команда c

(gdb) c

Но вот незадача. Точка останова установлена внутри функции инициализации, которая уже давно отработала. Надо как-то перезагрузить trarget, чтобы инициализация снова отработала.

Вот тут то нам как раз поможет еще один терминал. Это терминал командной строки поверх UART реализованный прямо внутри прошивки на Target (е). Я просто попрошу устройство перезагрузиться командой в UART CLI

-> kernel reboot cold

и устройство в самом деле перезагружается

e8f3435786ebfcd161b90b94c9adcf9d.png

При этом я даже от клавиатуры рук не отводил. И вот отладчик зацепил функцию bool hw_init(void). Это называется синергия GDB и UART-CLI. Успех.

945486148082188ba66a0df1ce190fbc.png

Выполнить одну строчку кода можно командой n. Посмотреть содержимое локальный переменный можно командой info locals

3c26abd9d1c087b50e8d6fa220e253dc.png

Зайти внутрь функции можно командой s.

Само собой, в консоли GBD клиента есть история команд и вы можете стрелками вверх и вниз на клавиатуре набирать уже используемые в прошлом команды.

Чтобы выйти из GDB клиента надо набрать в его консоли команду q. Target все еще suspended. Поэтому также надо закрыть GDB сервер. Только после этого Target станет resumed и продолжит исполнять свой код.

В последующем можно запустить GDB сервер и GDB клиент одним единственным скриптом.

echo off
cls

set FIRMWARE_FILE=C:/ncs/v2.1.0/nrf/applications/nrf5340_dk_audio_w/build/headset_app/zephyr/zephyr.elf
set GDB_CLIENT="C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2021.10\bin\arm-none-eabi-gdb.exe"

set tools_dir=%cd%\..\..\..\tool
start %tools_dir%\0_LaunchGdbServerNrfAppCore.bat


%GDB_CLIENT% --help
%GDB_CLIENT% %FIRMWARE_FILE%

Суммируя вышесказанное, весь процесс можно объяснить вот этой простой схемой

0b861832c42b9d40ebe3a4bb298baa50.png

Также небольшая шпаргалка по наиболее употребительным командам консольного GDB клиента

short

full

description

-

info args

Show current function arguments

i b

info b

List all breakpoints

bt

backtrace

show function call stack

n

next

Step over functions

f

finish

Execute the rest of the current function. Step out of the current function.

s

step

Step into functions

p count

Print the value of a variable count

c

continue

Continue execution up to the next breakpoint or until termination if no breakpoints are encountered

-

delete n

Delete breakpoint number n

-

info locals

show local variables

-

target remote localhost:2331

Connect to J-Link

q

quit

quit gdb

b function

Set a breakpoint at the beginning of function

Буду также обновлять реестр команд GDB клиента вот тут
https://docs.google.com/spreadsheets/d/1AWD8GsDfaA9dtdsfqgbB1klagou1yrREc1AAK9CRUik/edit#gid=0

Вывод

Как видите в GDB отладке через консоль нет вообще ничего сложного. Поэтому всякие там циклопические и дорогущие IDE для пошаговой отладки не особо-то и нужны как бы. Консоль даже лучше в том смысле, что внимание концентрируется на сути (коде), а не на стразиках из оформления IDE. Можно вообще отлаживать без кода, просто получив по почте один лишь *.elf файл.

При этом накладные расходы на установку Toolchain (а) для пошаговой отладки минимальные и всё абсолютно бесплатно.

Links

https://condor.depaul.edu/glancast/373class/docs/gdb.html#Setting_Breakpoints

https://habr.com/ru/post/181738/
https://habr.com/ru/post/535960/
https://habr.com/ru/post/546216/
https://mcuoneclipse.com/2015/03/25/command-line-programming-and-debugging-with-gdb/
https://www.electricmonk.nl/docs/gdb_debugging/gdb_debugging.html

Вопрос *

Как работает механизм установки точек останова JTAG под капотом?

© Habrahabr.ru