概述
文章目录
- 前言:
- 一、嵌入式系统调试原理
- 1.1 嵌入式系统调试模型
- 1.2 ARM CoreSight (Debug and Trace) 简介
- 二、嵌入式系统调试工具
- 2.1 J-LINK GDB Server 命令调试
- 2.2 VS Code Cortex-Debug 可视化调试
- 2.3 IDE 与Ozone 调试跟踪工具
- 2.4 SystemView 调试跟踪工具
- 更多文章:
前言:
前文介绍了GDB 调试原理,我们在进行嵌入式开发调试时,受限于嵌入式芯片资源和性能,一般采用远程调试。
GDB 远程调试需要在目标机上运行gdbserver 程序,对于裸机或RTOS 来说,gdbserver 占用的资源太多。而且在gdbserver 进程启动之前,我们无法对其进行调试,也就是说系统或驱动等底层代码的调试,单靠gdbserver 就无能为力了。我们如何调试嵌入式系统的底层代码呢?
有过嵌入式底层开发经验的朋友,都用过J-LINK、ST-LINK、CMSIS-DAP 等硬件调试器,借助这些硬件调试器可以访问嵌入式芯片的指令数据、内存数据、系统及外设寄存器等,而不要求目标芯片上提前执行什么代码。硬件调试器既然不依赖目标芯片的软件环境,自然是目标芯片在硬件上能处理这些调试跟踪信号,以ARM Cortex 架构芯片为例,目标芯片是如何处理这些调试跟踪信号的呢?
一、嵌入式系统调试原理
1.1 嵌入式系统调试模型
我们在GDB 远程调试模型的基础上,增加一个硬件调试器层级,得到嵌入式系统底层代码的调试模型如下:
嵌入式开发的目标芯片主流是ARM Cortex,本文以ARM Cortex 架构芯片为例简单介绍其调试模型:
- GDB 调试工具:ARM Cortex 架构使用ARM GNU 工具链,又可细分为两类,Cortex -M/R 架构常用arm-none-eabi-gdb 调试工具,Cortex-A 架构常用arm-none-linux-eabihf-gdb 调试工具,可从ARM 开发者网站下载;
- GDB Server 调试工具:这里的GDB Server 不是运行在目标机内的,也是运行在调试机内的程序(可以和GDB 调试工具运行在不同的调试机内,两个调试机通过网线相连),作为GDB 程序与硬件调试器之间沟通的桥梁,解释来自GDB 程序的调试命令和硬件调试器返回的调试数据。该程序通常与硬件调试器驱动程序一起发布,比如J-LINK 驱动程序包含的J-LINK GDB Server(仅支持J-LINK 调试器);也有一些开源的,比如OpenOCD(Open On-Chip Debugger) (可以支持多种硬件调试器,比如对ST-LINK 的支持更友好) 包含的OpenOCD GDB Server;
- 硬件调试器:是一个内部烧录好程序的独立硬件设备,作为GDB Server 与目标芯片之间沟通的桥梁,翻译来自GDB Server 的调试命令和目标芯片返回的调试跟踪信号。硬件调试器需要配合相应的驱动程序使用,且不同调试器支持的目标芯片型号不同,常见的硬件调试器有J-LINK、ST-LINK、CMSIS-DAP 等;
- 目标芯片:目标芯片为了方便开发人员调试代码,一般内部都集成了调试跟踪模块,比如ARM Cortex 架构集成了ARM CoreSight 调试跟踪组件,对内可以控制并访问几乎所有系统及外设存储空间,对外提供统一的Debug Port(支持SWD 和JTAG),开发者可借助硬件调试器对目标芯片进行调试跟踪。
GDB 和GDB Server 的调试原理在前篇博文已经介绍过了,这里简单介绍后两部分。我们进行软件开发调试的基础是目标芯片,硬件调试器应该选择支持目标芯片的型号,因此从目标芯片的调试跟踪模块及其接口介绍。
1.2 ARM CoreSight (Debug and Trace) 简介
目前在嵌入式领域ARM 架构是主流,RISC-V 架构是新锐(MIPS 架构也转投RISC-V 阵营),ARM Cortex 架构使用的调试跟踪系统是ARM CoreSight。
我们做嵌入式开发调试,只需要简单了解调试跟踪系统的大概组成结构和基本工作原理,帮助我们能善加利用这个强大的调试跟踪系统,可以大幅加快程序开发调试效率。硬件调试器配合调试软件已经帮我们封装了大量的细节,因此我们并不需要了解其过多的细节,下面以比较简单的单核Cortex-M3/M4 为例,展示ARM CoreSight 概貌。
在博文:ARM 代码烧录方案与原理详解 中已经介绍过ARM Cortex 提供的Debug Port,下图中颜色较深的模块都是ARM CoreSight 的组成部分,对外提供了Debug 和Trace 两个接口:
ARM Cortex-M 调试跟踪模块主要包含以下几个部分:
- FPB(Flash Patch and Breakpoint) Unit:闪存地址重载指的是将Code空间的指令或字面量重载到SRAM 中,断点单元可以产生断点事件,从而使处理器进入调试模式(暂停处理器模式或者调试监视器异常模式,后者不暂停处理器而是执行异常处理程序);
- DWT(Data Watchpoint and Trace) Unit:数据观察点单元可以产生数据观察点事件,让处理器进入调试模式(halting mode or debug monitor exception mode)。数据跟踪单元可以产生跟踪数据包(包括数据值、数据地址、当前PC 值等信息),汇入到Advanced Trace Bus,经Trace Port 输出;
- ITM(Instrumentation Trace Macrocell):测量跟踪单元(也称软件调试跟踪单元),可以将软件生成的调试信息封装到跟踪数据包中,并生成时间戳插入到跟踪流中,以帮助调试器重建事件的时序信息。也可以用作处理器内部的跟踪数据包合并设备,将DWT 数据包、软件调试数据包、时间戳数据包合并在一起(有一个FIFO 缓冲区以减少跟踪数据包的溢出),经Trace Port 输出;
- ETM(Embedded Trace Macrocell):用于提供指令跟踪功能,可以生成包含指令执行流的跟踪数据包,以帮助调试器重建处理器执行的指令序列。ETM 还提供了一个FIFO 缓冲区,以便为TPIU 提供足够的时间去处理和序列化这些跟踪数据包;
- TPIU(Trace Port Interface Unit):跟踪端口接口单元,用于把DWT、ITM 和ETM 汇聚来的跟踪数据包格式化,并输出到片外捕获设备(比如J-Trace、ULINKPro 等)。TPIU 有两种输出模式,一种是使用单根Serial Wire Output (SWO) 的Serial Wire Viewer (SWV) mode,另一种是使用四根parallel data output 加一根时钟信号引脚的Clocked mode。
从上面对ARM CoreSight 的简单介绍可以看出,Debug 和Trace 是两个不同的通路,对外也分别提供了Debug Port 和Trace Port。从调试过程是否会显著影响程序原来的执行流程看,调试跟踪可分为两种类型:
- 侵入式调试:可通过设置断点、数据观察点等让程序暂停执行或单步执行(可访问存储器及寄存器数据)。对于不能停止的程序(比如电机控制程序,突然暂停可能烧坏电机),还提供了调试监视器模式(中断号为12的系统异常Debug monitor),当发生调试事件(比如断点事件、数据观察点事件、外部调试请求事件等)时,去执行调试监视器异常处理程序(可更改内存堆栈数据),而不影响高优先级中断的响应;
- 非侵入式调试:可通过指令跟踪、数据跟踪、软件生成的跟踪数据(可即时访问存储器及外设数据),获知处理器执行的指令序列、事件发生的时序信息等,分析软件执行过程中出现的Bug 或瓶颈。
了解了ARM CoreSight 调试跟踪系统的大概组成结构和基本工作原理,我们重点看下Debug Port 和Trace Port:
我们经常使用的硬件调试器比如J-LINK、ST-LINK、CMSIS-DAP 等都支持SWD Debug Port + SWO Trace Port,支持Clocked Trace Port 的调试器比较少,比如J-Trace、ULINKPro 是支持这种Parallel Trace Port 的。5线的并行Trace Port 传输数据的速度更快,但占用的引脚更多,一般情况下都使用SWO Trace Port 以节省引脚。SWD Debug Port 也比JTAG Debug Port 使用更少的引脚数,因此上图最左边的组合更常用。
二、嵌入式系统调试工具
了解了调试原理,更重要的是会借用强大的调试工具,更高效的获取代码执行的详细信息,更快速的解决代码运行的bug 和瓶颈。本文使用nRF5 SDK 中的示例代码展示不同的调试工具和方法,开发调试环境搭建可参考博文:如何实现扫码连接BLE 设备的功能?
- 硬件平台:nRF52 开发板,芯片型号为nRF52832_QFAA;
- 软件平台:nRF5 SDK 版本 nRF5_SDK_17.0.2_d674dde,SoftDevice(BLE protocol stack)版本 s132_nrf52_7.20,基于Windows 10_x64;
- 编译调试工具:代码编辑VSCodeUserSetup-x64-1.54.2,代码编译gcc-arm-none-eabi-9-2019-q4-major-win32-sha2,代码调试跟踪JLink_Windows_V696、Ozone_Windows_V322b_x64、SystemView_Windows_V330_x64;
- 示例工程代码:.nRF5_SDK_17.0.2_d674ddeexamplesble_peripheralble_app_uart
2.1 J-LINK GDB Server 命令调试
接着从前篇博文的末尾:GDB 远程调试开始介绍,ARM Cortex-M 一般使用arm-none-eabi-gcc 编译器进行编译,我们先找到工程代码的Makefile 文件 .examplesble_peripheralble_app_uartpca10040s132armgccMakefile,然后使用make 命令编译工程代码,并将其烧录到nRF52 DK 内,图示如下:
按照前篇博文GDB 远程调试的方法,启动调试代码,首先打开J-Link GDB Server.exe 程序,配置界面如下:
J-Link GDB Server 启动后,监听TCP/IP Port 为localhost:2331,我们在Terminal(cmd/shell) 执行arm-none-eabi-gdb 程序后,输入target remote localhost:2331 即可连接上J-Link GDB Server,再读入带调试信息的ELF 文件,就可以开始远程调试嵌入式代码了:
GDB 命令调试并不直观,而且嵌入式系统更多的需要查看处理器及外设寄存器的值、内存数据、反汇编代码等比较靠近硬件底层的信息,为方便查看这些信息,常使用图形界面来进行调试跟踪。即便使用可视化调试界面,底层依然需要GDB 命令支持,GDB 查询寄存器数据、内存数据、反汇编代码的命令如下:
查询内存数据与反汇编代码 | 命令描述 |
---|---|
disassemble location | 显示当前调试环境中指定位置location 的反汇编代码,location 可以是函数名,也可以是存储地址; |
info registers | 打印当前调试环境中处理器内的寄存器数据; |
x/fmt address | Examine memory 查看内存,/fmt 为显示内存的长度和格式,比如/20xw 表示以十六进制显示20个字(也即4个字节32位)的内存数据,address 可以是函数名也可以是存储地址,指定查看内存的起始地址。 |
2.2 VS Code Cortex-Debug 可视化调试
VS Code 作为一款代码编辑器,提供了丰富的扩展库,对于Cortex-M 嵌入式芯片的调试也提供了一个扩展库Cortex-Debug,我们可以使用这个库实现嵌入式调试功能,Cortex-Debug 的下载和配置界面如下(参见博文:Cortex-debug 调试器使用介绍):
# settings.json
{
"terminal.integrated.shell.windows": "C:\WINDOWS\System32\cmd.exe",
"cortex-debug.armToolchainPath": "C:\Program Files (x86)\GNU Tools Arm Embedded\9 2019-q4-major\bin",
"cortex-debug.armToolchainPrefix": "arm-none-eabi",
"cortex-debug.JLinkGDBServerPath": "C:\Program Files (x86)\SEGGER\JLink\JLinkGDBServerCL.exe",
}
配置好编译工具链路径cortex-debug.armToolchainPath 和JLinkGDBServerCL.exe 路径cortex-debug.JLinkGDBServerPath,就可以创建被调试工程的启动文件launch.json 了,创建过程如下:
启动文件launch.json 中需要配置包含调试信息的ELF 文件路径executable,调试器类型servertype及调试接口interface,目标芯片型号device,目标芯片svd 文件路径svdFile 这几个属性值(还可以配置runToMain 为true,表示执行到main 函数起始位置暂停):
# launch.json
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Cortex Debug",
"cwd": "${workspaceRoot}",
"executable": ".\examples\ble_peripheral\ble_app_uart\pca10040\s132\armgcc\_build\nrf52832_xxaa.out",
"request": "launch",
"type": "cortex-debug",
"servertype": "jlink",
"svdFile": "C:\Users\paul\AppData\Local\Arm\Packs\NordicSemiconductor\nRF_DeviceFamilyPack\8.35.0\SVD\nrf52.svd",
"interface": "swd",
"device": "nRF52832_xxAA",
"runToMain": true
}
]
}
到这里Cortex-Debug 就配置好了,可以开始调试了,点击Start Debugging(Cortex Debug) 按钮,程序会暂停到main函数起始位置(设置了"runToMain": true),单步调试,我们可以从VS Code 界面获得的调试信息如下:
2.3 IDE 与Ozone 调试跟踪工具
我们开发调试嵌入式系统也经常使用IDE(Intergreated Development Environment) 工具,比如调试开发Cortex-M 芯片的Keil MDK(Microcontroller Development Kit)、IAR Embedded Workbench、SEGGER Embedded Studio 等。比如nRF5 SDK 就支持四种开发调试工具,除了ARMGCC 需要配合代码编辑器比如VS Code外(前面我们已经展示了VS Code 配合ARMGCC 如何实现代码编辑、编译、调试的),其余三个都是兼具代码编辑、编译链接、调试跟踪的IDE 工具:
Nordic 官方推荐使用SEGGER Embedded Studio,这里就简单展示下该IDE 的调试跟踪界面(打开.examplesble_peripheralble_app_uartpca10040s132sesble_app_uart_pca10040_s132.emProject 工程,然后点击菜单Debug–>Go 或按F5 键):
如果不想使用IDE,也可以使用比较轻便的调试信息展示工具,比如SEGGER 开发的Ozone,该工具安装包只有20MB,可单独配置使用,也可由SEGGER Embedded Studio 通过菜单Debug–> Debug With Ozone 启动。Ozone 不仅可以图形化显示前面介绍的调试信息(比如变量、断点、数据观察点、寄存器、内存、反汇编、调用栈等),还可以展示数据采样信息、指令跟踪信息、甚至是实时功耗信息等,可以帮助我们分析代码的覆盖率和执行效率,帮助我们定位软件执行bug 与瓶颈。Ozone 调试信息图形化显示界面如下(可参考博文Ozone Analyzer或点击Help–> User Guide 查看说明文档):
这里重点关注下 Trace 信息,主要是上图View 菜单Advanced 部分的内容。来自DWT、ITM、ETM 的跟踪数据可以帮助我们了解处理器执行的指令序列、数据或事件的时许、功耗的变化曲线等信息,配合ITM 为跟踪数据包添加的时间戳信息,我们可以查看包含Data、Power、Code 的Timeline 界面如下:
2.4 SystemView 调试跟踪工具
如果在目标芯片上运行了RTOS 操作系统或者通信协议栈这类相对复杂的任务调度或事件驱动代码,也可以使用SystemView 展示嵌入式系统内实时发生的任务切换或事件回调时序信息,帮助我们了解系统或协议栈运行时的细节,从而更高效的定位系统运行过程中的bug 或瓶颈。
SystemView 包含两部分代码(仅可用于J-LINK 或J-Trace 调试器,仅使用Debug Port,不需要Trace Port):
- PC 上运行的可视化应用程序SystemView,安装包只有不到7MB,独立使用;
- 目标芯片上运行的系统信息监测代码,在开发嵌入式系统代码时需要移植并集成到目标系统内,需要占用约2KB 的ROM 空间和约0.6 KB 的RAM 空间。
使用SystemView 的关键是向目标系统内添加移植SystemView 目标芯片端的代码,需要移植的代码可以到SystemView 安装目录.Program FilesSEGGERSystemViewSrc 找到,移植说明可以参考文档.SEGGERSystemViewDocUM08027_SystemView.pdf,需要移植的文件如下:
这里就不展示SystemView 目标芯片端代码移植过程了,可以参考pdf 文档逐步操作,SystemView 以图形化方式展现出来的任务调度或事件触发回调时序图如下:
通过SystemView,可以分析执行了哪些中断、任务和软件计时器?它们使用了多少次、何时执行以及使用了多少时间?SystemView 向我们展示了嵌入式系统内到底发生了什么、以什么顺序?哪个中断触发了哪个任务切换?哪个中断和任务调用了底层模块的哪个API函数?这些信息的实时采集精度可以精确到一个CPU 周期,然后以时序图的形式呈现出来,放大时序图可以看到细节。
更多文章:
- 《代码调试跟踪与优化(三)— 如何调试Fault 异常?》
- 《代码调试跟踪与优化(一)— 如何用GDB 调试代码?》
- 《如何实现扫码连接BLE 设备的功能(以nRF5 为例)?》
- 《Cortex-debug 调试器使用介绍》
- 《RT-Thread SystemView 使用指南》
最后
以上就是幸福火车为你收集整理的代码调试跟踪与优化(二)--- 如何调试嵌入式代码?前言:一、嵌入式系统调试原理二、嵌入式系统调试工具更多文章:的全部内容,希望文章能够帮你解决代码调试跟踪与优化(二)--- 如何调试嵌入式代码?前言:一、嵌入式系统调试原理二、嵌入式系统调试工具更多文章:所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复