type
status
date
slug
summary
tags
category
icon
password
📌
引言
在工作和学习中意识到调试的重要性,在硬件开发中,硬件也需要类似软件的调试手段,在 IC 设计中还会有专门的团队去做 DFT(可测性设计)工作。OpenOCD 就是一种通过软件控制特定接口,实现硬件设备的 debug,本文主要对其工作原理进行总结。

1 简介

OpenOCD 全称 Open On Chip Debugger,是为嵌入式设备提供调试,系统内编程和边界扫描测试的工具。
OpenOCD 需要 debug adapter(调试适配器)一起工作才能完成对应的功能,因为 debug host(即 OpenOCD)并不具备将调试命令(比如 halt, step 或 reg 等)转换为调试接口的电信号的功能,所以借助 adapter 来完成这一操作。
这样的 debug adapter 需要支持一个或者多个传输协议(如 JTAG),因为传输协议们大多拥有不同的电信号转换规则。比较常用的 adapter 就是 JTAG Adapter,其通信接口被称为 TAP(Test Access Port),有特定的指令和数据定义。比较新的 ARM 架构的处理器或 MCU 有一个特有的 adapter 是 SWD,他的通信接口被称为 Serial Wire Debug。
OpenOCD 也可以将调试器的调试命令转换为 OpenOCD 的命令传递给 adapter。比如 GDB 和 Telnet。
列举一些目前使用比较广泛的硬件 adapter:
  • USB-JTAG:类似于各类 FPGA 的下载器
  • USB J-LINK
  • USB ST-LINK:只支持 STM 系列的 MCU
  • USB TI:TI(德州仪器)公司专用
  • USB CMSIS-DAP:适配由 ARM 公司发布的 CMSIS-DAP 接口标准,可以方便的连接 ARM Cortex 系列的核
在 OpenOCD 的源码中,还提供了丰富的,各种架构各种型号核的软件 adapter,用于在没有硬件时的仿真调试。
TODO: Source Code analysis.

2 Debug 总体结构

2.1 连接方式

OpenOCD 借助 debug adapter 将调试命令转换成对应接口的电信号,那么相应的,就需要在调试的硬件上,加入控制整个设备的 Debug 模块以及与 adapter 相连的接口。这里我们就以大多数可测性设计都会使用的 JTAG 为例。
notion image
如图中所示,OpenOCD 会作为 Debug Host 对外的结构与 Debugger Transport Hardware 也就是 JTAG 接口相连,JTAG 接口会和待测设计的 DTM 连通。DTM 的作用就是对外提供一个调试接口,支持 reset/halt 的基本操作和一些抽象指令。DTM 的 reset/halt 控制器直接与核相连,控制核的执行进程,抽象命令模块与核内控制 debug 模式的模块相连,获取一些 debug 信息以及执行抽象指令。

2.2 抽象命令简述

OpenOCD 的抽象命令分为两部分,common command 和 ISA command。
顾名思义,common command 就是所有 DTM 都应该支持的命令,包括基本的控制命令 reset/halt/resume,寄存器读写命令,内存读写命令,断点设置命令等。
而 ISA command 就会因为不同的指令集甚至同一指令集的不同架构而产生区分,比如 ARM 专用的命令,RISCV 专用的命令,CPLD/FPGA 专用的命令等。大多是一些查看核内状态以及与架构相关的命令。
TODO: Explain command usually used.

3 OpenOCD 的使用

OpenOCD 使用 Jim-Tcl 解释器,支持 Tcl 语言。可以通过 Tcl 脚本来写 OpenOCD 的执行脚本。

3.1 配置文件

OpenOCD 的执行过程是以配置文件为单位的,每一次运行会执行一个会多个 cfg 文件。如使用命令 openocd -f file.cfg -f file1.cfg 即可执行两个 cfg 文件中的 Tcl 脚本。所以 cfg 文件是理解 OpenOCD 工作过程的关键。
下面以两个简单的 config 文件的例子来解释一下有官方支持的调试和没有官方支持的调试两种情况。

3.1.2 有官方支持的调试

先看一下配置文件:
notion image
对于很多市面上常见的内核,官方已经将常用的诸如 initial, reset 等操作写成了脚本封装在对应的 config 文件中。在使用 OpenOCD 时,我们只需要 source 对应的脚本,就可以进行使用。
执行 openocd -f stm_test.cfg 脚本会自动完成初始化等操作,后续可以在 cfg 文件中继续添加命令并调试,也可以使用 telnet 连接本地端口输入命令进行调试。
TODO: OpenOCD debug detail.

3.1.3 没有官方支持的配置文件模板

如果我们的设备没有获得官方支持,比如我们自己实现的 RISCV CPU 核,希望通过 OpenOCD 进行调试。我们可以这样写配置文件。
可以看到我们将初始化和复位并停止核写成了两个 Tcl 函数,如果你将官方的 config 文件打开就会发现其实官方的脚本也是这样写的。接下来我们逐行解释配置文件的含义。
adapter_khz 1000 是定义了 adapter 的工作频率,不设置也无妨,openocd 会以默认的低速模式(100 khz)去进行与 JTAG 的 debug 信号传输。
set _CHIPNAME riscv set 命令,是 Tcl 脚本命令,格式为 set <变量名> <变量值>,本句意为设置变量 _CHIPNAME=riscv。
jtag newtap $_CHIPNAME cpu -irlen 5 这里是给 _CHIPNAME 声明一个 JTAG 的 TAP 指令接口,名叫 cpu,指令接口位宽为 5 bit。
set _TARGETNAME riscv.cpu 设置 _TARGETNAME 为 riscv.cpu。这一步是定义名称。
target create $_TARGETNAME riscv -chain-position $_TARGETNAME -coreid 0 这一步是可以开始调试前的最后一步,创建一个 target,命令格式是 target create <target 名字> <target ISA> -chain-position <target 名字> <ARGS>,-chain-position 是指定 TAP 的位置,后面的 ARGS 可有可无,命令中为手动设置了一个 coreid
在上述配置做完后实际上核的创建就完成了,如果是一个多核系统只需要多设置几个 cpu0,cpu1 这样的 target 并在 create 时指示不同的 coreid 就可以了。
接下来的命令是先初始化核,再复位并停止核。之所以这样做是因为在 init 命令执行的过程中,核其实已经向后执行一段时间了,如果不是 reset halt 而是普通的 halt,我们无法判断和的位置,所以将核复位并停止比较保险。
 
本文的内容就基本上结束了,后续会补充文中的几个 TODO,从使用教程到源码分析贯穿下来。
 
OpenOCD 源码分析Difftest 分析与拓展
Loading...