Типичная ситуация. Компонент логирования и UART(Universal Asynchronous Receiver/Transmitter) не проинициализировался корректно или устройство где-то зависло в инициализации после reset. Или устройство бесконечно перезагружается после подачи питания. Как же понять на какой строчке возникла run-time ошибка? Классическое решение это пошаговая отладка через JTAG(Joint Test Action Group) или SWD(Serial Wire Debug).

Немного про железо. Работать буду с платой nRF5340-DK. Её блок-схема.

Отладочная плата 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%

Примерно вот такой должен быть лог успешной загрузки прошивки в терминале 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)


  1. randomsimplenumber
    22.10.2022 08:31
    +12

    всякие там циклопические и дорогущие IDE для пошаговой отладки не особо-то и нужны как бы

    Но если ide уже имеется - странно им не пользоваться.

    Можно вообще отлаживать без кода, просто получив по почте один лишь *.elf файл.

    Можно, но с исходниками как то проще. Странно, когда пишет код один, а отлаживает другой.

    абсолютно бесплатно

    Можно совершенно бесплатно вместо паяльника использовать зажигалку и канцелярскую скрепку. Но паяльник удобнее, хоть и дороже;)


    1. aabzel Автор
      24.10.2022 13:10
      +1

      Но если ide уже имеется - странно им не пользоваться.

      По моим и не только наблюдениям, люди, которые привыкли что-то ковырять внутри IDE имеют тенденцию становится очень слабыми программистами. Даже микроконтроллер прошить без IDE не могут.

      Эти IDE-наркоманы даже не догадываются о существовании файлов отличных от *.h и *.сpp. Коммитят в репозиторий всё даже *.i, *.obj(ты), *.a, *.hex, *.bin, *.elf.

      А когда IDE не открывается из-за несовместимости версий, то у таких случается приступ панической атаки.


  1. JackKatch
    22.10.2022 11:45
    +3

    Автору спасибо, полезная информация. Поддержу предыдущего комментатора. Меня тоже раздражает раздуваемая сложность на ровном месте. Для чего накручиваются всевозможные костыли. Вот 13 лет назад я сел за среду SiLabs. Нажал кнопку загрузить и работаю, не нужны ни какие конфигураторы, серверы, инициализаторы, ...-ры и т.д. Напоминает старый анекдот: "... Вот унитаз, вот моя жопа, продайте, наконец то, мне туалетную бумагу. ". Скоро что бы запрограммировать микроконтроллер специальные курсы посещать придётся.


  1. jaha33
    22.10.2022 11:46
    +2

    Так сейчас практически все производители MCU дают бесплатную IDE, обычно на базе эклипса, на кой эти консольные страдания?

    А "дорогие" IDE нужны не для стразиков, тот же IAR жмет код получше чем GNU Arm, для кого то это может оказаться критично. Плюс там misra и статический анализатор. Из популярных есть еще Keil, но за него как то нечего сказать.


    1. aabzel Автор
      24.10.2022 13:04

      MISRA можно бесплатно проверять в CodeComposerStudio



  1. e-zig
    22.10.2022 12:47

    На мой взгляд, все это может быть полезно только при удаленной отладки через gdb server.


  1. progchip666
    22.10.2022 14:29
    +1

    Эпично, но не думаю что быстро и удобно.

    Как пища для мозгов и в целях понимания процессов, думаю куда полезнее чем в практических целях.


  1. AntonSor
    22.10.2022 16:34
    +3

    До чего дошло разделение труда в программировании микроконтроллеров (сарказм). Один пишет код, отправляет второму elf по почте, и второй его в консоли отлаживает :)


    1. aabzel Автор
      22.10.2022 17:08

      Отличная шутка. Я давно так не смеялся.

      На самом деле я просто хотел подчеркнуть, что при отладке через консоль при помощи GDB зависимости c самими сорцами и репозиторием уже нет.

      Это для тех кто всё еще верует, что без IDE невозможна пошаговая отладка.

      *.elf самодостаточный файл с тонной полезной инфы.

      Можно отлаживаться в консоли, а на код поглядывать лежащий где-н в Web(e), например на github.com


  1. Dovgaluk
    22.10.2022 20:35
    +1

    Сокращение для target remote это tar rem :2331


    1. Mike-M
      23.10.2022 23:13

      Думается мне, что пробел перед двоеточием лишний.


      1. Dovgaluk
        24.10.2022 09:02

        Всегда писал с пробелом. Но если речь о том, чтобы максимально сократить команду, то лишний, да.


        1. Mike-M
          24.10.2022 12:24

          Всегда писал с пробелом.
          Не всегда )
          target remote localhost:2331

          если речь о том, чтобы максимально сократить команду
          Нет-нет, речь о том, воспримет ли консольный GDB-клиент параметр с пробелом (не возникнет ли ошибки).


  1. osmanpasha
    23.10.2022 12:28
    +1

    Разве gdb не умеет сам перезапускать прошивку командой run, если стартовать с target extended-remote?


  1. Mike-M
    23.10.2022 23:17
    +1

    Поэтому всякие там циклопические и дорогущие IDE для пошаговой отладки не особо-то и нужны как бы.
    Результаты голосования это опровергают: на втором вопросе в 6 раз, на третьем вопросе в… 35 (тридцать пять) раз.