U-Boot 之二 详解使用 eclipse + J-Link 进行编译及在线调试
在上一篇博文 U-Boot 之一 零基础编译 U-Boot 过程详解 及 编译后的使用说明 中,最后使用阶段遇到了一些错误,然后发现不能调试(靠打印信息)实在是难受,就开始摸索如何调试 U-Boot,于是就有了这篇博文。
找了一下网上现有的资料,直接使用 GDB 命令行调试是一个选择,但是明显效率不高。于是开始探索直接使用 eclipse 对 U-Boot 进行编译及调试。发现网上有不少说明文章,但大多数都太老旧了,因此我决定重新整理一篇,使用的当前最新的工具环境如下:
- Ubuntu 20.04.3
- Eclipse IDE 2021-12 R Packages Eclipse IDE for Embedded C/C++ Developers
- U-boot 2021.10
- J-Link_Linux_V760b_x86_64.deb
下载安装 eclipse
首先从官网下载最新的针对嵌入式 C/C++ 的 tar 包(现在 eclipse 针对不同开发环境提供了不同的包,我记得之前并没有分开):Eclipse IDE for Embedded C/C++ Developers,地址:https://www.eclipse.org/downloads/packages/release/2021-12/r/eclipse-ide-embedded-cc-developers,然后进行安装(解压):
解压之后,我们在 /opt/eclipse/eclipse
目录(我这里先建立了 eclipse 文件夹导致多了一级,大家可以直接解压到 /opt
)下直接双击运行 eclipse
程序即可。这里我们直接下载嵌入式专用的版本,主要是出于以下几点:
- 根据官网提示 “The Eclipse Installer 2021‑12 R now includes a JRE for macOS, Windows and Linux.” ,我们不用再安装 Java 运行时 了。解压后直接使用就可以了。
- 包含了一些嵌入式使用的插件(例如 J-Link、OpenOCD 等调试插件 ),这省了我们在去安装这些插件。例如,选择 Eclipse IDE for C/C++ Developers,然后去安装各种插件。
进一步,我们可以选择建立一个 eclipse 的快捷方式,方便后续启动:
- 新建
sudo gedit /usr/share/applications/eclipse.desktop
- 输入以下内容:
[Desktop Entry] Encoding=UTF-8 Name=Eclipse Comment=Eclipse IDE Exec=/opt/eclipse/eclipse/eclipse Icon=/opt/eclipse/eclipse/icon.xpm Terminal=false StartupNotify=true Type=Application Categories=Application;Development;
- 赋予执行权限
sudo chmod u+x /usr/share/applications/eclipse.desktop
- 以上步骤之后我们就可以在桌面最左下角的菜单中找到 eclipse 了。当然还可以在进一步,将
/usr/share/applications/eclipse.desktop
复制到桌面,然后赋予执行权限(应该是还需要右键鼠标,选择 Allow Launching)。
附 tar 命令:
# 压缩tar -czvf ***.tar.gztar -cjvf ***.tar.bz2# 解压缩tar -xzvf ***.tar.gztar -xjvf ***.tar.bz2# 参数 -c :建立一个压缩档案的参数指令(create 的意思); -x :解开一个压缩档案的参数指令! -t :查看 tarfile 里面的档案!特别注意,在参数的下达中, c/x/t 仅能存在一个!不可同时存在!因为不可能同时压缩与解压缩。 -z :是否同时具有 gzip 的属性,亦即是否需要用 gzip 压缩 -j :是否同时具有 bzip2 的属性,亦即是否需要用 bzip2 压缩 -v :压缩的过程中显示档案!这个常用,但不建议用在背景执行过程! -f :使用档名。注意,在 f 之后要立即接档名,不要再加参数!例如使用『 tar -zcvfP tfile sfile 』就是错误的写法,要写成『 tar -zcvPf tfile sfile 』才对! -p :使用原档案的原来属性(属性不会依据使用者而变) -P :可以使用绝对路径来压缩! -N :比后面接的日期(yyyy/mm/dd)还要新的才会被打包进新建的档案中!
下载安装 J-Link 驱动
这个就相当简单了,SEGGER 提供了安装包,直接从 https://www.segger.com/downloads/J-Link/ 下载对应的的 linux 的安装包(例如我的 Ubuntu 对应 64-bit DEB Installer
),然后双击安装就可以了。安装之后默认位置是 ./opt/SEGGER
目录下。
里面的可执行程序都没有对应的桌面图标,运行后在左侧的工具栏上也是黑乎乎的。使用时直接使用命令行或者到目录下双击对应的可执行程序即可。
配置 eclipse
eclipse 的配置分为全局、工作区、项目,优先级前者最低后者最高(后者会覆盖前者)。对于一些通用的配置(例如我们安装的 J-Link 驱动,无论工作区还是项目,应该都用一个),eclipse 推荐直接在全局或者工作区中配置,项目直接继承使用全局或工作区的配置就可以了。 启动 eclipse 会提示选择工作区,默认可以选择不再提示。
启动之后,直接 菜单 --> Window --> Preferences
打开配置界面,我们主要是配置更改 MCU 这个目录(这个好像是由于我们用的嵌入式版 eclipse 自带了 CDT 插件才有这个)下的内容,其他的配置项则根据自己的需要自行修改即可。具体如下:
我这里仅仅配置了编译工具链和 J-Link 的安装位置。之所以这里选择对全局配置项进行配置,主要还是方便在后续建立项目之后,项目自动继承这些配置,方便减少很多操作。
这里再次说明,由于我们选择的是嵌入式专用的 eclipse(Eclipse IDE for Embedded C/C++ Developers ),因此,我们不需要安装 CDT 插件等操作。否则光是配置 eclipse 内容就挺多!
安装 CMSIS-Packs
CMSIS Pack 是 ARM 为 Cortex-M 核定义的一个规范,是一种有效的封装技术,目前支持近 9000 种不同的微控制器。它们为软件组件、设备参数和评估板支持提供了一种交付机制。软件包(文件集合)包括:
- 源代码、头文件和软件库
- 文档和源代码模板
- 设备参数以及启动代码和编程算法
- 示例工程
用过 MDK-ARM(Keil)的应该很熟悉,这个东西不仅可以在 MDK-ARM(Keil)中使用,现在 eclipse、IAR 都集成了 CMSIS Pack,通过 CMSIS Pack 我们可以在线安装一些芯片的包。
这里我们之所以安装 CMSIS Pack(确切的说是安装 CMSIS Pack 中提供的芯片的包),是因为从中我们可以获取到芯片的 SVD 文件,而 SVD 文件中记录了芯片中各种外设的寄存器,在调试时非常有用!如果不使用 SVD 文件,调试器将无法获取芯片中外设的寄存器,只能显示 ARM 核中的几个寄存器。
eclipse 中安装非常简单,但是由于访问的是国外的服务器,速度相当慢(那些包都挺大的),而且经常出现某些芯片下载不下来报错。如果出现报错直接选择忽略即可。具体操作如下图所示:
CMSIS Pack 的安装位置可以在 菜单--> Window-->Preferences --> C/C++ --> MCU Packages
下找到及更改。查看其中的内容,其实就是 Keil 中的那一套东西,因此一种变通的方法是将下载好的内容直接放到上面的目录中即可。SVD 文件就位于 CMSIS-Packs\Keil\STM32F7xx_DFP\2.14.1\CMSIS\SVD
目录下。
注意,上面的这个目录应该是个隐藏文件夹!
编译 U-Boot
新建项目
准备好以上环境及工具之后,就可以直接建立项目了,对于 U-Boot ,我们选择 Makefile Project with Existing Code
。我这里把步骤尽量把步骤合并到一张图,以节约篇幅。具体步骤如下图所示:
其中,我们选择的编译工具链是 Arm Cross GCC。这里的选择主要是让 eclipse 能够主动使用适合我们的一些配置。如果选择其他的,后续也可以再次进行更改(步骤会多一些)。
注意,如果选择在配置章节说的已经在配置了全局或者工作区,下面有些配置其实可以不用更改。我这里就以没有配置来进行演示(覆盖全局配置),对于全局配置有影响的地方单独说明一下。
点击 Finish 之后,默认在选择的源代码目录下新建两个(隐藏)文件:.cproject
和 .project
,这两个就是 eclipse 记录的项目配置信息文件(我之前以为会和 Windows 上的一些软件(例如 vs)似得放到选择的工作区目录下,要不然我选择工作区干啥!)。
项目配置
点击右键菜单最下面的 Properties (也可以通过 菜单 --> Project --> Properties
)之后,就打开了项目的配置页面,接下来就是更改一些项目专用的配置了。下面我们一步步说明需要做哪些更改:
新增环境变量。具体操作如下图所示:
环境变量下原有的两个CMD
、PWD
我们不用管它(PATH
变量默认也应该有,不知为何我这里没有)。我们最终需要新增三个环境变量ARCH
、CROSS_COMPILE
、PATH
(如果没有的话),其中,ARCH
和CROSS_COMPILE
就是我们使用命令行编译时指定的。PATH
主要是告诉 eclipse 我们是用的工具的位置。
这里需要特殊注意PATH
的值。我们需要先通过上面的 Variables 找到系统 PATH 变量,然后点击 OK,此时,eclipse 就会导入系统环境变量的值。我们需要编辑它,在其中添加我们自己的编译工具链位置(我这里是/usr/share/gcc-arm-none-eabi-10.3.2021.10/bin
,注意,bin 目录不止一个),否则在编译时会提示找不到相关工具。注意,如果在上面说的全局设置中已经添加了编译器的路径,这里不用再次添加。 最终添加后如下所示:
我们添加的编译器路径用于让 eclipse 找到编译器,其他原有的系统环境变量可以让 eclipse 找到 make 程序。此外,最下面的 Append xxx 这句不要去掉。更改我们使用的编译工具链以及选择使用的芯片。
如果没有配置全局编译器路径,这里必须单独配置;如果设置了这里就会自动读取全局设置的编译工具链,查看一下是否正确即可。如果这里选择了芯片,在调试的时候会省略一些配置。[可选] 不使用 CDT 内建的编译工具链的相关设置(因为我们更换了编译工具链),注意,这里不更换也没啥问题!
新增我们自己的编译工具链的头文件路径。每一个编译工具链下都会有
include
文件夹用来存放编译器使用的各种头文件。需要注意的是,include
可能会有好几个,最好都添加上。如果在全局中配置了编译器路径,eclipse 就会自动识别指定的编译工具的相关头文件,这里就可以不用设置。
[可选] 开启并行编译。就是指定
make -jn
参数(n 根据处理器来定) 。eclipse 配置如下:
[可选] 增加
make stm32f769-disco_defconfig
步骤。完整的构建 U-Boot 的步骤中,第一步是生成.config
文件,而后是make menuconfig
,最后才是make
。如果不添加到 eclipse ,则需要先用命令行执行以上步骤,否则 eclipse 会提示错误。
具体添加如下:
之后,我们就可以在菜单 --> Project --> Build Targets --> Build
中直接点击上面的配置,执行这一步了。[可选] 修改
make clean
。默认情况下,我们执行 Clean 时,使用是make clean
,而我们编译 U-Boot 一般使用make distclean
。这个是 eclipse 默认的设置,我们可以通过如下位置进行修改:
此外,还可以参考第 6 步中新建一个Build Targets
,只是使用起来没有上面这个方式简单。我第 6 步的图示中已经建立好了!
编译
经过以上步骤,完整的 U-Boot 编译环境就搭建好了,接下来就可以在 eclipse 中构建 U-Boot 了。
调试 U-Boot
J-Link 连接开发板
调试之前肯定是先把 J-Link 连接到开发板(开发板上有个 20 针脚的 JTAG/SWD 调试接口),确保连接正确。测试连接具体可以使用我们安装的 J-Link 驱动里相应的工具:JFlashExe
,新建一个 STM32F769 芯片的项目,然后连接一下:
提示 Connected successfully 即可。
eclipse 调试相关配置
连接没有问题之后,接下来就是配置 eclipse 了:菜单 --> Run --> Debug Configuration...
,总的来说配置并不麻烦,我这里使用是 J-Link,因此,选择 GDB SEGGER J-link Debugging
,然后点击上面的新建图标(或者直接双击 GDB SEGGER J-link Debugging
)就会出现一个新的配置,具体步骤如下图所示:
需要注意的就是选择 C/C++ Application
这一项,我们需要从众多执行程序中选择出我们最终需要 u-boot(这个是最终编译出的带调试信息的,不是 u-boot.bin。具体见博文U-Boot 之三 U-Boot 源码文件解析及移植过程详解)。接下来就是配置界面中剩下的Debugger
、Startup
、Source
、Common
、SVD Path
这 5 个 Tab 页面内容,下面我直接上图来说明每个页面需要的配置:
Main
、Debugger
、Startup
如果已经在全局配置中设置了J-Link 的路径,这里默认是会自动读取全局的配置的 J-Link,不用更改也可以。当然这里再选择一下肯定没有问题。芯片类型如果在构建时选择了芯片,这里也会自动填上选择的芯片。还有就是Startup --> Setbreakpoint at:
这个默认是 mian,但是 U-Boot 默认没有 main,这里根据实际情况选择。直接reset
,简单粗暴!还有可以使用_main
。Source
、Common
、SVD Path
如果按照前面说的安装了 CMSIS-Packs,那么这里就可以找到对应芯片的 SVD 文件,选择 SVD 文件之后,我们在调试时就可以查看外设寄存器的值,否则将无法查看!如下图所示:
注意,eclipse 寄存器分为两部分:Registers 窗口中是 ARM Cortex 核的寄存器(例如,R0、PC),Peripherals 下面就是我们的 SVD 文件中描述的外设寄存器。
U-Boot 调试修改
开启调试选项
默认情况下,U-Boot 的编译已经进行了优化,且默认并不开启调试的,因此我们需要更改一下 U-Boot 的配置。第一个是需要取消 General Setup --> Optimise for size
,在一个就是开启 General Setup --> Configure standard U-Boot features (expert users) -> enable debug information for tools
,具体如下图所示:
这里需要注意,如果使用 make distclean
会清理所有文件,这就会导致以上的配置被清理!所以除非必要,否则还是使用 make clean
好一些。目前来看 make menuconfig
这一步还是需要在终端中执行。
警告:目前我在测试中发现,去掉 General Setup --> Optimise for size 可能导致程序无法运行,暂时没找到解决方法!我目前是在仅开启了 General Setup --> Configure standard U-Boot features (expert users) -> enable debug information for tools 的情况进行调试的。就是偶尔会出现断点位置不正确,不影响正常调试!
这里有个比较严重的问题,去掉 General Setup --> Optimise for size
之后,会导致程序变大,从而原来默认的 SPL 的大小(0x8000)不能容纳实际 SPL 大小,进一步导致了 U-Boot 无法启动。因此,这里我们必须要修改 U-Boot 的基地址。目前,有如下地方需要修改(图里面的 stm32f769-eval 是我移植的,上文暂时还没有更换):
修改设备树配置
接下来还有个问题:在启动调试之后,调试的程序在调试时只能执行一部分代码,此后就会失败,而且如果不调试想要直接运行是无法直接启动!具体现象如下图所示:
根本原因是由于调试的程序有没有设备树!默认情况下,U-Boot 的可执行程序是 bin 后拼接上 dtb 组成的,而我们调试的程序只有 bin,没有 dtb!解决方法如下有如下两种:
- 将设备树直接编译进 U-Boot 的 bin 文件中,而不是将 设备树单独出来!U-Boot 本身就有这方面的配置,具体如下:
如果执行过make distclean
那么每次都需要重新配置上面的选项。 - 在 eclipse 中指定设备树地址。如果使用的是 U-Boot 默认的分离设备树模式,那么在编译成功之后,会在源码的根目录下生成
u-boot.dtb
,这个就是设备数编译之后的文件,而我们可执行程序的结束地址可以从源码的根目录下的u-boot.map
文件中的__end
符号找到。
有了u-boot.dtb
和__end
之后,我们就可以在 eclipse 中通过命令来解决:
如果 U-Boot 的可执行程序大小有变化,每次都需要更改这里!而且在刚开始启动调试时,eclipse 会报一个错误,稍等一会即可正常进入调试。
经过以上两种方法的任意一种,再次启动调试时,就可以正常执行 board_inti_f
这个函数了!注意,由于这个函数里包含很多接口,执行速度可能有些慢!
重定位配置
接下来的问题就是内存重定位,U-Boot 中存在一段内存重定位的代码,重定位之后将导致调试失败!这主要是因为,在重定位之后,eclipse 正确加载符号表中的各符号。
目前,我还没有找到比较好的解决方法,官网推荐的方法是 使用 gdb 命令行来手动加载符号表 。命令非常简单:add-symbol-file u-boot 重定位后的地址
。重定位后的地址就位于 gd
中的成员变量 unsigned long relocaddr;
中。
调试
经过上面的配置之后,我们就可以使用 eclipse 对 U-Boot 进行调试了。这里需要注意的是,我们调试的如果是 U-Boot 本身,需要现将 SPL 烧写好,当然我们也可以调试 SPL。启动调试如下图所示:
参考
- https://boundarydevices.com/debugging-using-segger-j-link-jtag/
- https://www.cnblogs.com/humaoxiao/p/4166230.html
- https://m2m-tele.com/blog/2021/09/19/how-to-debug-u-boot/
- https://community.element14.com/products/devtools/single-board-computers/riotboard/b/blog/posts/automate-uboot-build-with-eclipse
- https://programmerclick.com/article/68662419084/
U-Boot 之二 详解使用 eclipse + J-Link 进行编译及在线调试相关推荐
- Spring Boot 集成 FreeMarker 详解案例
年轻就不应该让自己过得太舒服" – From yong 一.Springboot 那些事 SpringBoot 很方便的集成 FreeMarker ,DAO 数据库操作层依旧用的是 Myba ...
- Java web 开发的概念、环境配置、创建项目过程详解(Eclipse)
Java Web 开发 Java Web 开发概念 搭建过程 详解 在eclipse创建Dynamic Web Project 创建时选择Tomcat的版本 创建项目成功 eclipse环境下如何配置 ...
- SpringBoot2.1.5(16)--- Spring Boot的日志详解
SpringBoot2.1.5(16)--- Spring Boot的日志详解 市面上有许多的日志框架,比如 JUL( java.util.logging), JCL( Apache Commons ...
- ABP VNext系列(二)-详解ABP的依赖注入
ABP VNext系列(二)-详解ABP的依赖注入 上一篇 : ABP VNext系列(一)-第一个ABP VNext 目录 ABP VNext系列(二)-详解ABP的依赖注入 一.什么是依赖注入 1 ...
- U-Boot 之三 详解使用 eclipse + J-Link 进行编译及在线调试
在上一篇博文 U-Boot 之二 移植过程详解. STM32F769I-EVAL 开发板适配 中,最后使用阶段遇到了一些错误,然后发现不能调试(靠打印信息)实在是难受,就开始摸索如何调试 U-Bo ...
- Spring Boot (4)---配置文件详解
Spring2.0 Boot配置文件详解 配置文件说明 Spring Boot 配置文件允许为同一套应用,为不同的环境用不同的配置文件.比如开发环境.测试环境.生成环境.你可以用 properties ...
- Spring Boot(3)---Spring Boot启动器Starter详解
Spring Boot的启动器Starter详解 Spring Boot 简化了 Spring 应用开发,不需要配置就能运行 Spring 应用, Spring Boot 管理 Spring 容器.第 ...
- Spring Boot事务管理详解
什么是事务? 我们在开发企业应用时,对于业务人员的一个操作实际是对数据读写的多步操作的结合.由于数据操作在顺序执行的过程中,任何一步操作都有可能发生异常,异常会导致后续操作无法完成,此时由于业务逻辑并 ...
- 企业分布式微服务云SpringCloud SpringBoot mybatis (二)Spring Boot属性配置文件详解...
相信很多人选择Spring Boot主要是考虑到它既能兼顾Spring的强大功能,还能实现快速开发的便捷.我们在Spring Boot使用过程中,最直观的感受就是没有了原来自己整合Spring应用时繁 ...
最新文章
- 用好idea这几款插件,可以帮你少写30%的代码!
- 这个冬天,将是共享单车最艰难的时刻
- 李彦宏喊你来坐出租车,无人驾驶的那种;百度还要继续搞芯片,联手华为的那种...
- OpenYurt — Yurtctl
- WPF 快速制作可拖拽的对象和窗体
- VS2010重构学习总结
- SAP MM 移动类型-入门篇
- vc连接mysql 查询_vc连接数据库中查询代码如何写呀 急急急!!!!!!
- 在WPF中处理Windows消息
- 怎样打开mysql进程数_mysql查看最大打开进程数
- .Net的类型构造器-static构造函数
- php 两个类 相互调用_如何在 PHP 和 Laravel 中使用 Traits
- 在 for 循环里写加号拼接字符串的那个同事,不见了~
- 【计算大于这个整数的最小质数】
- WebRTC-NACK、Pacer和拥塞控制和FEC
- 配置JAVA_HOME
- python moving average_Python实现滑动平均(Moving Average)的代码教程
- LFS(Linux From Scratch)构建过程全记录(一):准备工作
- oracle闪回空间满的原因,处理Oracle数据库闪回区空间满的问题
- revit二次开发2016
热门文章
- 企业级 SpringBoot 教程 (十七)上传文件
- C++运算符重载(10)
- Java 常见内存溢出异常与代码实现
- 某房产中介服务器托管及安全方案(下)
- pandas(二) -- Dataframe创建及索引
- java 图形库_OpenGL开放图形java库jogamp-all-platforms.7z
- 高可用延迟队列设计与实现
- go mod导入本地包的正确引入方法:require + replace
- Spark3.1.1 Docker镜像中修改/etc/hosts
- kubesphere添加新节点