Продолжение предыдущей статьи, в которой мы ускорили разработку под embedded linux. Рабочая станция + sftp сервер + nfs сервер ускорили на порядок (10х) доставку изменений кода на целевое железо. Теперь не нужно часами компилировать код. В этой статье продолжаем очеловечивать разработку. На этот раз прикручиваем полноценную графическую IDE и пошаговую отладку кода на целевом железе с помощью программатора J-Link. Но пока только загрузчика U-boot. И автоматизируем развертывание рабочей среды разработчика с помощью Docker.
Микропроцессор, используемый в статье - imx6ull (ArmV7), программатор - j-link. В случае, если ваш процессор есть в списке поддерживаемых архитектур, - то у вас есть все шансы повторить все шаги, описанные в статье и получить возможность полноценной отладки программного кода на удаленном железе. По крайней мере, с ARM-ами затруднений возникнуть не должно. В данной статье рассматривается только отладка загрузчика (u-boot). Надеюсь, что и до ядра доберемся в одной из следующих статей.
Подготовка железа
Подключаем программатор по JTAG к целевой железке.
подключаем программатор по USB к хосту (рабочему ПК).
подключаем железку к хосту с помощью COM-порта.
подаем питание на железку.
Может случиться такое, что, в ядре выводы JTAG-интерфейса переинициализируются на другие функции. В этом случае, сразу после подачи питания нужно не дать загрузчику начать загрузку ядра. Для этого нужно в консоли нажать любую клавишу до окончания обратного отсчета и остаться в консоли загрузчика.
Установка драйверов
Устанавливаем gdb-multiarch:
$ sudo apt-get install gdb-multiarch
Скачиваем драйвера j-link и устанавливаем их:
$ dpkg -i /drivers/JLink_Linux_V754b_x86_64.deb
Сборка toolchain
В вашем случае он может быть уже предустановлен/предскомпилирован. В нашем случае компилируем:
$ mkdir /opt/eclipse/
$ cd /opt/elcipse/
$ git clone https://github.com/wireless-road/imx6ull-openwrt.git
$ cd imx6ull-openwrt
$ ./compile.sh flexcan_wifi
Установка IDE и создание проекта
Скачиваем Eclipse IDE for Embedded C/C++ Developers и распаковываем архив в /opt/eclipse.
Нажимаем File --> New Project и выбираем Makefile Project with Existing Code:
![](https://habrastorage.org/getpro/habr/upload_files/c5d/3b5/521/c5d3b5521a8ae68800f054607edc3760.png)
Задаем каталог с исходниками U-boot. В нашем случае, их можно найти в build_dir/target-arm_cortex-a7+neon-vfpv4_musl_eabi/u-boot-wirelessroad_ecspi3/u-boot-2017.07:
![](https://habrastorage.org/getpro/habr/upload_files/ae8/8af/50e/ae88af50eebbcc92734efa308d11e96f.png)
Далее, необходимо задать переменные окружения для корректной работы eclipse с вашим toolchain. Для этого кликаете правой кнопкой мыши на проект в окне Eclipse, нажимаете Properties, а далее - C/C++ Build --> Environment и создаете несколько переменных:
ARCH=arm
CROSS_COMPILE=arm-openwrt-linux-muslgnueabi-
STAGING_DIR=/opt/eclipse/imx6ull/imx6ull-openwrt/staging_dir/
Переменная STAGING_DIR специфична для OpenWrt, - вам она, возможно, и не нужна.
Далее редактируем уже имеющуюся переменную PATH, добавив в ее начало следующее:
/opt/eclipse/imx6ull-openwrt/build_dir/target-arm_cortex-a7+neon-vfpv4_musl_eabi/linux-imx6ull_cortexa7/linux-4.14.199/scripts/dtc/:/opt/eclipse/imx6ull-openwrt/staging_dir/target-arm_cortex-a7+neon-vfpv4_musl_eabi/host/bin:/opt/eclipse/imx6ull-openwrt/staging_dir/hostpkg/bin:/opt/eclipse/imx6ull-openwrt/staging_dir/toolchain-arm_cortex-a7+neon-vfpv4_gcc-7.5.0_musl_eabi/bin:/opt/eclipse/imx6ull-openwrt/staging_dir/host/bin:/bin:
Здесь, несколько каталогов тулчейна, разделенных двоеточием. В итоге, должно получиться следующее:
![](https://habrastorage.org/getpro/habr/upload_files/fa0/974/7e8/fa09747e8abf994d4beb52367fdc0cb1.png)
Следующим шагом отключаем дефолтный компилятор:
![](https://habrastorage.org/getpro/habr/upload_files/b29/f55/757/b29f55757edf6eda8d1d69b7ef54bd18.png)
и задаем пути к заголовочным файлам нашего тулчейна:
/opt/eclipse/imx6ull-openwrt/staging_dir/toolchain-arm_cortex-a7+neon-vfpv4_gcc-7.5.0_musl_eabi/arm-openwrt-linux-muslgnueabi/include/
/opt/eclipse/imx6ull-openwrt/staging_dir/toolchain-arm_cortex-a7+neon-vfpv4_gcc-7.5.0_musl_eabi/include/
/opt/eclipse/imx6ull-openwrt/staging_dir/toolchain-arm_cortex-a7+neon-vfpv4_gcc-7.5.0_musl_eabi/lib/gcc/arm-openwrt-linux-muslgnueabi/7.5.0/include/
![](https://habrastorage.org/getpro/habr/upload_files/f04/fe4/0c7/f04fe40c7f33fd00e8b269a81a1b63c0.png)
Не забываем включить параллельную сборку:
![](https://habrastorage.org/getpro/habr/upload_files/bb6/987/aee/bb6987aeed08887c588fe9cff48757c5.png)
Готово. Теперь можно попробовать собрать u-boot. Нажмите правой кнопкой мыши на проект и затем Clean Project для очистки проекта, Build Project для сборки:
![](https://habrastorage.org/getpro/habr/upload_files/eca/417/f15/eca417f15f1901a3e95e4e3e595d9672.png)
![](https://habrastorage.org/getpro/habr/upload_files/fa2/793/a13/fa2793a13266c995763c396445800fb2.png)
Далее, необходимо, либо включить в концигурации u-boot опцию CONFIG_TOOLS_DEBUG, либо с помощью утилиты make menuconfig, либо в ручную задав в .config файле следующее:
CONFIG_TOOLS_DEBUG=y
Возможно, в ide вы не найдете этого файла, поскольку он скрыт. Включить отображение скрытых файлов можно следующим образом:
![](https://habrastorage.org/getpro/habr/upload_files/b13/463/40f/b1346340fefd483a7ee40eea8287baca.png)
![](https://habrastorage.org/getpro/habr/upload_files/794/5f1/d90/7945f1d901db004e9eb6f45dc8484859.png)
После этого пересоберите проект, чтобы отладочные символы попали в итоговый бинарник.
Осталось совсем чуть-чуть, а именно настроить конфигурацию программатора. Нажмите Run --> Debug Configurations и задайте настройки:
![](https://habrastorage.org/getpro/habr/upload_files/b32/595/a44/b32595a4407d64822e27e217c7a39472.png)
![](https://habrastorage.org/getpro/habr/upload_files/349/78b/52b/34978b52baba1e738c89cc73b764fd52.png)
![](https://habrastorage.org/getpro/habr/upload_files/8a3/d6f/033/8a3d6f0333c08bb1d1ad9a4afb88f7c6.png)
Автоматизируй это.
Довольно длительная последовательность действий и практически каждый клиент, использующий наше BSP, каждый новый разработчик на каком-нибудь этапе да и споткнется, проверено на практике. Поэтому, логично будет автоматизировать развертнывание всей среды разработки, включая скачивание исходных кодов, сборку тулчейна и установку IDE. При этом, упаковка в docker контейнер дает надежку, что вся процедура развертывания будет более-менее стабильно разработать на максимально большом количестве компьютеров. Таким образом, вся выше-описанная инструкция заменяется четырьмя консольными командами:
$ git clone https://github.com/wireless-road/openwrt-ide.git
$ docker-compose run --name openwrt openwrt
И уже из консоли контейнера:
$ ./setup_ide.sh
$ /opt/eclipse/eclipse/eclipse
Исходники среды и более подробную инструкцию с некоторыми пояснениями можно найти тут. Eclipse, к сожалению, имеет ограниченные возможности для работы из консоли, но, к счастью, он portable. Таким образом, один раз настроив проект в eclipse, и упаковав его обратно в архив, можно переносить пред-настроенную IDE с машины на машину. Использование docker, в добавок к изоляции среды позволяет также решить возможные ошибки при распаковке архива по другому пути. В теории должно работать, практика покажет как оно на самом деле.
И последнее, - грязный хак, без которого, в нашем случае, u-boot не хотел работать под отладчиком. Вначале, в консоли u-boot вводим:
=> printenv fdtcontroladdr
fdtcontroladdr=8ef216d8
и данное значение хард-кодим в u-boot/lib/fdtdec.c в функции fdtdec_setup():
![](https://habrastorage.org/getpro/habr/upload_files/ece/fb6/a82/ecefb6a82bb71139c33caabc2c0401a8.png)
Как этот костыль заменить корректными методами мы пока, к сожалению, не разобрались. Надеюсь, осилим в ближайшее время, но пока так. В вашем случае, если eclipse не сможет стартануть отладку кода, может оказаться полезным запустить отладку в консольном режиме. Для этого запускаете GDBServer:
$ JLinkGDBServer -device MCIMX6Y2 -if JTAG -speed 1000
а следом запускаете консольный дебаггер, где u-boot - скомпилированный ранее образ загрузчика:
$ gdb-multiarch u-boot --nx
(gdb) target remote localhost:2331
(gdb) monitor reset
(gdb) monitor halt
(gdb) monitor sleep 200
(gdb) load
а далее шагаете до участка кода, на котором все рушится:
(gdb) s
reset () at arch/arm/cpu/armv7/start.S:40
40 b save_boot_params
(gdb) s
save_boot_params () at arch/arm/cpu/armv7/start.S:116
116 b save_boot_params_ret @ back to my caller
(gdb) s
save_boot_params_ret () at arch/arm/cpu/armv7/start.S:56
56 mrs r0, cpsr
(gdb) s
57 and r1, r0, #0x1f @ mask mode bits
(gdb) s
58 teq r1, #0x1a @ test for HYP mode
Тут можно ознакомиться с тем, как мы искали проблемный код.
Собственно, отладка
Задаем точку остановки, например функцию initcall_run_list, которая в цикле запускает функции инициализации перферии:
![](https://habrastorage.org/getpro/habr/upload_files/09f/22a/9a4/09f22a9a4f76d6704b68e5d3f57310bf.png)
И нажимаем на кнопку Debug, а следом на кнопку Resume:
![](https://habrastorage.org/getpro/habr/upload_files/ce0/d9d/f06/ce0d9df061ad21d7c77fe79f7d156a43.png)
В итоге, должны попасть в выбранную нами функцию. Попробуйте нажать на кнопку Step Over несколько раз - курсор текущего положения должен начать перемещаться по коду:
![](https://habrastorage.org/getpro/habr/upload_files/053/221/291/05322129128bbf4bce3eb425d0609c40.png)
Давайте попробуем начать знакомство с u-boot. Дойдя до строчки подсвеченной зеленым цветом на предыдущем скриншоте поставьте на ней точку остановки (двойной щелчок левой кнопкой мыши по синему полю слева напротив нужной строки), а далее нажимайте на кнопку Resume. После каждого нажатия на Resume, наводите курсор init_fnc_ptr, - в появляющемся всплывающем окне будет появляться название очередной функции, которая будет вызвана. В итоге, можно получить предстваление о том, какими функциями собтсвенно, и происходит инициализация периферии:
setup_mon_len
fdtdec_setup
initf_malloc
initf_bootstage
init_console_record
arch_cpu_init
march_cpu_init
initf_dm
arch_cpu_init_dm
board_early_init_f
get_clocks
timer_init
board_post_clk_init
env_init
init_baud_rate
serial_init
console_init_f
display_options U-Boot 2017.07 (Sep 19 2021 - 11:24:07 +0300)
display_text_info
print_cpuinfo CPU: Freescale i.MX6ULL rev1.1 528 MHz (running at 396 MHz)
CPU: Industrial temperature grade (-40C to 105C) at 70C
Reset cause: unknown reset
show_board_info Model: WirelessRoad ecspi3
Board: MX6ULL WirelessRoad ECSPI3 module
init_func_i2c I2C: ready
announce_dram_init DRAM:
dram_init
setup_dest_addr
reserve_round_4k
reserve_mmu
reserve_video
reserve_trace
reserve_uboot
reserve_malloc
reserve_board
setup_machine
reserve_global_data
reserve_fdt
reserve_bootstage
reserve_arch
reserve_stacks
dram_init_banksize
show_dram_config 256MiB
display_new_sp
reloc_fdt
reloc_bootstage
setup_reloc Setup fec 0
NAND: 0 MiB
MMC: FSL_SDHC: 0
board_spi_cs_gpio bus 2 cs 0
SF: Detected w25q256 with page size 256 Bytes, erase size 4 KiB, total 32 MiB
In: serial
Out: serial
Err: serial
Net: FEC [PRIME]
Hit any key to stop autoboot: 0
Здесь, левая колонка - вызывающиеся функции, правая - логи в консоли, выдаваемые функцией.
Продолжение
Тут и тут можно ознакомиться с удаленной отладкой приложений пользовательского уровня. В следующей же статье надеюсь добраться до пошаговой отладки ядра.