Типичная ситуация. Компонент логирования и UART(Universal Asynchronous Receiver/Transmitter) не проинициализировался корректно или устройство где-то зависло в инициализации после reset. Или устройство бесконечно перезагружается после подачи питания. Как же понять на какой строчке возникла run-time ошибка? Классическое решение это пошаговая отладка через JTAG(Joint Test Action Group) или SWD(Serial Wire Debug).
Немного про железо. Работать буду с платой 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%
Примерно вот такой должен быть лог успешной загрузки прошивки в терминале cmd.exe
set dflt SDK ver
Artefact hex: [C:/ncs/v2.1.0/XXX/zephyr/zephyr.hex]
Project Dir:C:/XXX/
Parsing image file.
Verifying programming.
Verified OK.
Applying system reset.
Run.
tools_dir=C:/XXX/tools
>
>
Отработав, этот скрипт загрузит прошивку в On-Chip NorFlash микроконтроллера nrf5340 U1.
Фаза 2. Запуск GDB сервера (Back-End)
GDB(GNU DeBugger) сервер это утилита, которая опрашивает программатор-отладчик. Именно она общается с устройством. Надо запустить 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.
J-link попросит выбрать микроконтроллер
список поддерживаесых чипов очень большой
В моем случае чип nrf5340
GDB сервер J-Link запущен локально. Программатор (S/N: 1050009032) подключен по USB. Target подключен к программатору по SWD.
вот полный лог ожидающего GDB сервера
Скрытый текст
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
GDB клиент смог подхватить *.elf файл с отладочными символами
Настало время подключиться к GDB серверу. Это делается командой.
target remote localhost:2331
Сработало
Подключился. Сейчас исполнение прошивки на паузе. HeartBeat LED не мигает, UART-Shell не отвечает.
Самая полезная команда консольного GDB это команда bt (backtrace). Именно эту команду надо выполнить первой. Так как скорее всего вы запустили GDB чтобы выявить причину зависания. И команда bt сразу вам покажет на какой строчке прошивка свалилась в исключение.
Для запуска прошивки надо набрать команду continue
Теперь HeartBeat LED мигает CLI отвечает. LED на программаторе тоже мигает только как-то апериодично.
Для того чтобы снова остановить исполнение программы надо в терминале GDB клиента нажать Ctrl+C.
Теперь можно установить точку останова. Меня интересует функция bool hw_init(void)
(gdb) break hw_init
Посмотреть список установленных точек останова можно командой
(gdb) info b
Вот это сейчас отображается
Снова запускает программу на исполнение. Команда c
(gdb) c
Но вот незадача. Точка останова установлена внутри функции инициализации, которая уже давным-давно исполнилась. Что же делать? Надо как-то перезагрузить Target, чтобы инициализация снова отработала.
Вот тут-то нам как раз поможет еще один терминал. Это терминал командной строки поверх UART реализованный прямо внутри прошивки на Target(е). Я просто открою TeraTerm.exe и попрошу Zephyr-based прошивку перезагрузиться встроенной командой из UART CLI.
-> kernel reboot cold
и устройство в самом деле перезагружается!
При этом я даже от клавиатуры рук не отводил. И вот как раз отладчик и зацепил функцию bool hw_init(void). Это называется синергия GDB и UART-CLI. Успех.
Выполнить одну строчку кода можно командой n (next). Посмотреть содержимое локальный переменный можно командой info locals
Зайти внутрь функции можно командой s (step).
Само собой, в консоли GDB клиента есть история команд, и вы можете стрелками вверх/ вниз на клавиатуре набирать уже используемые в прошлом команды.
Чтобы выйти из GDB клиента достаточно набрать в его консоли команду q (quit). 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%
Суммируя вышесказанное, весь процесс можно объяснить вот такой простецкой схемой на одном листе.
Также небольшая шпаргалка по наиболее употребительным командам консольного 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 |
tar rem:2331 | target remote localhost:2331 | Connect to J-Link |
q | quit | quit gdb |
b function | Set a breakpoint at the beginning of function |
Буду также обновлять реестр команд консольного GDB клиента google spreadsheets.
https://docs.google.com/spreadsheets/d/1AWD8GsDfaA9dtdsfqgbB1klagou1yrREc1AAK9CRUik/edit#gid=0
Там проще навигация благодаря наличию сортировки по столбцам и раскраске ячеек
Вывод
Вот теперь вы умеете делать пошаговую DBG отладку из консоли и можете учить этому других. Как видите в GDB отладке через консоль нет вообще ничего сложного. Как по мне, дак, всякие там циклопические и дорогущие IDE(Integrated development environment) для пошаговой отладки не особо-то и нужны как бы.
Консоль даже лучше в том смысле, что внимание концентрируется на сути (коде), а не на стразиках из оформления GUI(ни) от IDE. Можно вообще отлаживать без кода, просто получив по почте один лишь *.elf файл, а все сорцы собирать на удаленном защищенном сервере.
Тем более в консольном отладчике, как правило, не надо проводить много времени. Подключился, выполнил bt понял где зависла программа, отключился. Вот и всё. Easy.
Потом, написав один набор скриптов запуска GDB, в консоли можно пошагово отлаживать микроконтроллеры абсолютно любого вендора: STM32(ST), nRF5x(NS), CC26x2(TI), MDR32(M), LPC21xx (NXP). Меняться будут только GDB серверы. Всё остальное одинаково у всех.
При этом накладные расходы на установку Toolchain(а) для консольной пошаговой отладки минимальные и всё абсолютно бесплатно. Вам уже нравится GDB?
Links
Дайте мне 15 минут, и я изменю ваш взгляд на GDB
https://condor.depaul.edu/glancast/373class/docs/gdb.html#Setting_Breakpoints
https://habr.com/ru/post/259205/
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 под капотом?
Акроним | Расшифровка |
GDB | The GNU Project Debugger |
GUI | graphical user interface |
IDE | integrated development environment |
JTAG | Joint Test Action Group |
UART | universal asynchronous receiver / transmitter |
CLI | command-line interface |
SWD | Serial Wire Debug |
ARM | Advanced RISC Machine |
RISC | reduced instruction set computer |