Типичная ситуация. Компонент логирования и 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 сервера
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 файл с отладочными символами
Настало время подключиться к GBD серверу. Это делается командой.
target remote localhost:2331
Сработало
Подключился. Сейчас исполнение прошивки на паузе. HeartBeat LED не мигает, UART-Shell не отвечает.
Для запуска прошивки надо набрать команду 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).
Само собой, в консоли GBD клиента есть история команд, и вы можете стрелками вверх/ вниз на клавиатуре набирать уже используемые в прошлом команды.
Чтобы выйти из 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
Там проще навигация благодаря наличию сортировки по столбцам и раскраске ячеек
Вывод
Как видите в GDB отладке через консоль нет вообще ничего сложного. Как по мне, дак, всякие там циклопические и дорогущие IDE(Integrated development environment) для пошаговой отладки не особо-то и нужны как бы. Консоль даже лучше в том смысле, что внимание концентрируется на сути (коде), а не на стразиках из оформления GUI(ни) от 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 под капотом?
Комментарии (16)
JackKatch
22.10.2022 11:45+3Автору спасибо, полезная информация. Поддержу предыдущего комментатора. Меня тоже раздражает раздуваемая сложность на ровном месте. Для чего накручиваются всевозможные костыли. Вот 13 лет назад я сел за среду SiLabs. Нажал кнопку загрузить и работаю, не нужны ни какие конфигураторы, серверы, инициализаторы, ...-ры и т.д. Напоминает старый анекдот: "... Вот унитаз, вот моя жопа, продайте, наконец то, мне туалетную бумагу. ". Скоро что бы запрограммировать микроконтроллер специальные курсы посещать придётся.
jaha33
22.10.2022 11:46+2Так сейчас практически все производители MCU дают бесплатную IDE, обычно на базе эклипса, на кой эти консольные страдания?
А "дорогие" IDE нужны не для стразиков, тот же IAR жмет код получше чем GNU Arm, для кого то это может оказаться критично. Плюс там misra и статический анализатор. Из популярных есть еще Keil, но за него как то нечего сказать.
e-zig
22.10.2022 12:47На мой взгляд, все это может быть полезно только при удаленной отладки через gdb server.
progchip666
22.10.2022 14:29+1Эпично, но не думаю что быстро и удобно.
Как пища для мозгов и в целях понимания процессов, думаю куда полезнее чем в практических целях.
AntonSor
22.10.2022 16:34+3До чего дошло разделение труда в программировании микроконтроллеров (сарказм). Один пишет код, отправляет второму elf по почте, и второй его в консоли отлаживает :)
aabzel Автор
22.10.2022 17:08Отличная шутка. Я давно так не смеялся.
На самом деле я просто хотел подчеркнуть, что при отладке через консоль при помощи GDB зависимости c самими сорцами и репозиторием уже нет.
Это для тех кто всё еще верует, что без IDE невозможна пошаговая отладка.
*.elf самодостаточный файл с тонной полезной инфы.Можно отлаживаться в консоли, а на код поглядывать лежащий где-н в Web(e), например на github.com
Dovgaluk
22.10.2022 20:35+1Сокращение для target remote это tar rem :2331
Mike-M
23.10.2022 23:13Думается мне, что пробел перед двоеточием лишний.
Dovgaluk
24.10.2022 09:02Всегда писал с пробелом. Но если речь о том, чтобы максимально сократить команду, то лишний, да.
Mike-M
24.10.2022 12:24Всегда писал с пробелом.
Не всегда )
target remote localhost:2331если речь о том, чтобы максимально сократить команду
Нет-нет, речь о том, воспримет ли консольный GDB-клиент параметр с пробелом (не возникнет ли ошибки).
osmanpasha
23.10.2022 12:28+1Разве gdb не умеет сам перезапускать прошивку командой run, если стартовать с target extended-remote?
Mike-M
23.10.2022 23:17+1Поэтому всякие там циклопические и дорогущие IDE для пошаговой отладки не особо-то и нужны как бы.
Результаты голосования это опровергают: на втором вопросе в 6 раз, на третьем вопросе в… 35 (тридцать пять) раз.
randomsimplenumber
Но если ide уже имеется - странно им не пользоваться.
Можно, но с исходниками как то проще. Странно, когда пишет код один, а отлаживает другой.
Можно совершенно бесплатно вместо паяльника использовать зажигалку и канцелярскую скрепку. Но паяльник удобнее, хоть и дороже;)
aabzel Автор
По моим и не только наблюдениям, люди, которые привыкли что-то ковырять внутри IDE имеют тенденцию становится очень слабыми программистами. Даже микроконтроллер прошить без IDE не могут.
Эти IDE-наркоманы даже не догадываются о существовании файлов отличных от *.h и *.сpp. Коммитят в репозиторий всё даже *.i, *.obj(ты), *.a, *.hex, *.bin, *.elf.
А когда IDE не открывается из-за несовместимости версий, то у таких случается приступ панической атаки.