如何实现RISC-V架构的交叉调试系统设计

描述

0  引言

国内外各类构的嵌入式芯片在竞争激烈的市场环境不断突破技术壁垒,飞速提升性能。芯片配套的集成开发环境(IDE,Integrated Development Environment)作为开发中的关键工具,其提供的交叉编译和交叉调试功能解决了嵌入式开发中目标机的运算能力和存储空间有限的缺陷,对芯片嵌入式开发的重要性不言而喻。

近年来,各芯片厂商都在大力研发自己芯片的配套IDE,力图打破 KEIL和IAR两个行业标杆的垄断局面。这些免费IDE(如ST的 CubeIDE和TI的CCS)大部分是基于Eclipse深度定制的,包含 Eclipse、SDK插件、代码生成插件、编译工具链,以及调试工具链的开发环境。目前,这些IDE存在仅针对各自产品的定制化开发、源码封闭导致二次开发困难等问题。基于上述原因,新上市的硬件产品移植适配工作量巨大,不利于IDE的推广使用。

本文主要由Eclipse、GDB、OpenOCD(采用JTAG标准)组成的自主方案实现了IDE。鉴于交叉调试的重要性,本文重点介绍基于GDB和OpenOCD的技术选型,实现了目标机(芯片型号为基于RISC架构的RAC102)的交叉调试方案。在该方案中,对GDB和OpenOCD源码进行自主编译移植后,两者采用远程通信协议(RSP,Remote SerialProtocol)进行通信,完成可执行程序的调试操作。通过此案例验证了IDE设计方案中交叉调试的可行性,为后续其余架构的IDE适配提供了指导思路。在此技术基础上,提供支持多架构芯的通用且开放的嵌入式开发集成环境。

1  设计思路

1.1   器件选型      

1.1.1 编辑器选型

Eclipse平台为更好地为C/C++开发人员服务,衍生出了Eclipse CDT 扩展套件。该套件可以为嵌入式开发者提供C/C+ +程序的编辑、编 译、运行及本地调试等功能[1]作为利用机器接口(MI,Machine Interface)支持C/C++源码调试最好的开源工具,采用CDT套件作 为交叉调试的前端,通过MI接口使CDT和GDB相互通信,构建了可视化的嵌入式远程调试软件,提高了开发效率。

1.1.2 调试器选型

GDB是GNU工具集中的开源且高适配性的调试器,根据可执行程序的运行位置分为本地调试和远程调试两种模式。在远程调试模式下,GDB与调试代理(OpenOCD、GDB Server等)通过RSP协议进行信息交互,完成目标机上可执行文件的调试控制。注意,若目标机架构与宿主机架构不同,则称为交叉调试。此外,GDB支持两种外部调试工具接口:MI和CLI(CommandLineInterface),其多样化的接口、完善的目标处理机制等特点为后续IDE的架构适配打下了基础。

1.1.3 调试代理选型

OpenOCD(Open On Chip Debugger)是一个开源、通用的片上调试器,并且可作为支持JTAG标准的调试代理。目前,可与GDB联合为RISC V、ARM、MIPS等架构的芯片提供在线调试工具[2]。OpenOCD开源的好处在于,可自主改写函数源码进而适配目标芯片的特性。因此,基于GDB和OpenOCD搭建的调试系统具备功能拓展简单灵活、架构兼容性强的优势。此外,OpenOCD支持各种商用JTAG仿真器、直接读写物理地址和核内部寄存器等特点也是技术选型时考虑的因素。

1.1.4 调试标准选型

JTAG(JointTest Action Group)边界扫描调试标准主要提供微处理器的电气特性测试和目标机可执行程序调试两大功能,适用于配置了JTAG接口的芯片电路。基于JTAG标准的微处理器在调试模式下中断可执行程序,上层的GDB和OpenOCD发送调试命令到调试模块,完成后续程序调试工作。采用JTAG标准的主要原因是:①基于ARM、RISC V、Intel等主流架构的微处理器实现了JTAG调试接口,该标准适配性高。②基于该标准的调试具备程序无侵入、依赖简单、高稳定性的特点。

1.2   整体框架      

本文自主设计的IDE以主流嵌入式开发类似的宿主机目标机通用结构为基础进行整体框架设计,如图1所示。考虑到目标机存储资源的局限性,目标机端放置调试代理(GDB Server或GDB STUB)的传统方案被摒弃。本框架方案中,目标机端仅运行可执行程序,宿主机端由Eclipse、GDB和OpenOCD组成,其中OpenOCD完成了传统方案下目标机端的调试代理工作。在框架中,首先CDT图形界面通过UI操作来调用MI接口与GDB,进行调试命令发送和调试信息获取。GDB通过MI接口获取调试命令后,在RSP序列包封装和解析机制下与Open-OCD进行调试命令传递和调试信息反馈。

最后,JTAG仿真器通过宿主机通信端口接收到OpenOCD的指令,将其转换为标准的JTAG信号并通过调试访问端口(DAP,Debug Access Port)发送给目标机端。JTAG进入调试模式读取内存和寄存器,按照需求进行可执行程序调试,目标机完成调试后再将调试结果反馈给宿主机。通过上述流程,IDE完成目标机可执行程序的交叉调试。

寄存器

图1 IDE设计框图

2  实现方式

2.1   驱动适配      

为了目标机的可执行程序被顺利调试,首先要保证目标机和宿主机之间的传输正常。由于使用默认驱动时,通过USB端口与仿真器 通信会发生异常错误,因此,采用Zadig工具更新宿主机的USB驱动,从而保证目标机可以与宿主机进行正常通信。其驱动安装步骤如下:

① 在菜单栏中选择“Options”->“List All Devices”,查看当前连接到计算机上的所有USB设备。

② 在接口复合框中分别选中目标机对应的“Dual RS232-HS (Interface 0)”接口,并在驱动复合框选择“WinUSB”驱动。

③ 选择“Replace Driver”进行驱动更新即可。

④ 重复上述3个步骤,步骤②中接口选择“Dual RS232-HS (Interface 1)”,完成该接口的驱动安装。

2.2   操作系统适配      

由于IDE设计方案中交叉调试的调试器选用了GDB,因此运行在 Unix/Linux系统下的GDB适配宿主机系统(Windows系统)是需要完成的目标。为了实现上述目的,可以采用Cygwin工具,其功能就是 在Windows上仿真Linux操作系统,使得GNU工具链可以运行在Linux模拟环境。在运行Windows的同时,仅依赖Cygwin核心的动态 库(如cygwin1.dll)也可以使用VIM、GCC、make、GDB等Linux工具。注意,安装Cygwin时并没有缺省安装GNU工具链,应根据需求安装 相应的工具。本文根据操作系统的配置需要安装了binutils、gcc-core、gcc-g++、gdb、make等工具。

2.3   源码编译调试      

2.3.1 GDB工作目录建立

首先通过Cygwin进入GDB源码根目录,新建文件夹build,用来存储 源码编译后的可执行文件。注意,Win- dows目录在Cygwin进行了挂 载,如D盘路径变成/cyg- drive/d等。因此,进入GDB源码根目录时要注意目录路径的变换。命令行操作如下:

//查看磁盘挂载情况 ,识别挂载后磁盘路径

#df- h

//进入 GDB源码根目录

# cd /cygdrive/d/gdb- source/gdb- 10. 2

//创建 build工作目录

# mkdirbuild

2.3.2 GDB源码编译

编译源码的主要流程由./configure(脚本文件配置)、make(工具编译)、make install(工具安装)3个步骤构成。具体而言,首先,运行configure脚本检查当前的配置选项和系统环境设置,检查正确 后根据源码中Makefile.in模版文件的引导生 成 Makefile文件;然后,make工具根据Makefile文件的进行预处理、编译、链接等项目构建工作,生成二进制文件;最后,make install命令根据执行con-figure脚本时传递的prefix等参数将生成的二进制文件安装到指定路径。

执行./configure需要进行各种参数配置,GDB交叉编译中使用的参数配置如表1所列。

表1 configure脚本参数说明

寄存器

GDB源码编译的操作如下:

//建立交叉调试环境之前检查 config. sub脚本是否支持目标机架构的编译 。若支持 ,则返回相应的架构类型 ,否则会报错

# sh config. sub riscv64   unknown   elf  gcc

//执行 configure脚本 ,其中 CFLAGS= " g"的作用为后续调试 GDB源码显示调试信息。build和 host参数默认均为

本机架构,不再显性指定

#./configureCFLAGS=" g"

target=riscv64 unknown elf

prefix=/cygdrive/d/gdb source/gdb 10.2/build

//make编译与安装

#make

#makeinstall

2.3.3 GDB源码调试

前面的工作已经编译生成了基于RISC V架构的GDB可执行程序,可以进行目标机上被调试程序的交叉调试工作。若GDB调试过程中出现了BUG或者需要进一步理解内部运行机制,则需要进入GDB源码调试模式。为了方便阅读调试源码,选择VSCode进行GDB源码调试,需配置launch.json文件,该配置文件主要作用是添加GDB调试任务并运行可执行文件。launch.json文件配置流程主要分为以下几个步骤:

①生成launch.json文件。菜单栏选择“运行”->“添加配置”->“C++(GDB/LLDB)”,即可生成一个配置为空的launch.json文件。

②添加默认配置模版。在"configurations"字段域中输入“GDB”可以自动生成默认配置。

③修改默认配置。"program"字段代表调试器的路径,该字段设置为Cygwin的GDB路径;"miDebuggerPath"字段代表被调试的可执行文件路径,该字段设置为前面编译生成的GDB程序。

完成launch.json文件配置后,在VSCode中选择“gdb启动”就可以在调试模式下运行GDB调试器。

此外,调试代理OpenOCD的编译和调试与GDB源码编译和调试工作步骤基本一致,在此不再重复说明。目标机的被调试的可执行程序为RAC102芯片自带示例程序。

2.4   交叉调试      

首先,运行OpenOCD程序后,会弹出运行终端窗口,显示JATG标准选择、芯片架构信息、监听端口等第一阶段信息,表示OpenOCD运行正常,与目标机的RAC102芯片连接成功。

之后,以调试模式或者运行模式打开GDB程序,弹出运行终端窗口,在终端中依次设置指定RAC102芯片的架构、远程调试的连接端口以及加载调试信息文件,命令执行如下:

(gdb) set architecture riscv:rv32

The target architecture is assumed to be riscv:rv32

(gdb) target extended-remote localhost:3333

Remote debugging using localhost:3333

(gdb)file D:\led\led_debug.elf

Reading symbols from D:\led\led_debug.elf…

完成上述操作后,OpenOCD运行终端会输出端口连接成功、中断地址等第二阶段信息,表示OpenOCD与GDB连接成功。此时可以在GDB调试终端中输入continue、step、break等常用的调试命令进行交叉调试。

3  功能验证与协议分析

在交叉调试中,GDB所有的调试命令和调试信息反馈均通过RSP协议来实现。该协议通过串口或网口等媒介传输ASCII消息。RSP包的基本格式为:$数据包#校验和[3-4]。其中,数据包由若干ASCII数据组成,该校验和是数据包所有字符的ASCII码之和取256的模后得到的值,用两位十六进制ASCII表示。若OpenOCD正确接收数据,则会返回‘+’,否则返回‘-’表示发生错误,要求重新发送消息[5]。

由GDB和OpenOCD源码调试分析可知,GDB作为RSP客户端,在remote.c源文件中完成了读写、运行控制、查询及设置、追踪点以及停止通知等RSP常用类型通信包的接口实现;OpenOCD作为RSP服务端,在server.cc源文件完成了GDB发送的通信包的解析与处理。在此基础上,通过RSP通信包的组合实现常用的GDB调试命令。

在上述前提下,通过调试命令对应的RSP协议分析来验证在GDB+OpenOCD的方案中基于RISC-V架构的RAC102芯片交叉调试的功能正确性。参照第2.4小节的交叉调试流程,区别在于在执行target extended-remote localhost:port命令之前,需要使用set remotelogfile filename命令配置好远程串行通信记录日志,日志文件名为filename,GDB会将与OpenOCD的交互数据写入该日志文件中。通过表2给出了验证后常用的调试命令以及对应RSP协议指令集,证明了交叉调试方案设计的正确性与可行性。

表2 GDB命令与RSP协议对照指令集

G D B 命 令  RSP请求序列  RSP应答序列
target extended-
remotelocalhost:
port

①qSupported
②vMustReplyEmpty
③QStartNoAckMode
④!
⑤Hg thread-id
⑥qXferread
⑦qTStatus
⑧?
⑨qXferread
⑩Hc thread-id

⑪qC
⑫qAttached
⑬qOffsets
⑭g
⑮qXferread
⑯qSymbol
①qSymbol::Size;
qXferread;
QStartNoAckMode;
vContSupported
②空
③OK
④OK
⑤OK
⑥m data
⑦空
⑧S signal-id
⑨1 data
⑩OK
⑪QC thread-id

⑫1(注:数字1)
⑬Text =xx;Data=yy;
Bss=zz
⑭XX..(注:寄存
器数据内容)
⑮1 data
⑯OK
file filename ①qSymbol
②g
①OK
②XX..
run
①vKill;pid
②k和?组合包
③vRun
④qC
⑤qAttached
⑥qOffsets
⑦Hg thread-id
⑧qXferread
⑨g
⑩vCont;c
⑪x03
⑫g
⑬qXferread
①空
②W00
③S signal-id
④QC thread-id
⑤1(注:数字1)
⑥Text=xx;Data=yy;
Bss=zz
⑦OK
⑧m data
⑨XX..
 onds>
⑪T siganal-id
⑫XX..
⑬1 data
continue ①m
②ZO
③vCont?
④yCont;c
⑤g
⑥qXferread
⑦z0
①XX..
②OK
③vCont;c;C;s:S
④T signal-id
⑤XX..
⑥1 data
⑦OK
next ①m
②ZO
③vCont;s:thread -
id;c
④g
⑤qXferread
⑥z0
①XX..
②OK
③T thread-id
④XX..
⑤1 data
⑥OK
step
break lineNo ①m(若干包) ①XX..(若干包)
watchval
print yal
backtrace
diassemble
quit ①D
②?
①OK
②S siganal-id

4  结语

本文自主设计了一套集成开发环境方案,该方案中基于自行编译的GDB和OpenOCD实现了基于RSIC-V架构的RAC102芯片的交叉调试,并且通过RSP协议分析进行了调试功能的验证。交叉调试方案的实现为后续其余架构芯片的集成开发环境设计与实现提供了指导思路。进一步,为自研架构芯片设计开发的自主可控提供了保障。







审核编辑:刘清
 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分