type
status
date
slug
summary
tags
category
icon
password
📌
Summary
这篇文章是对 OpenOCD(Open On-Chip Debugger)源码的深度分析。首先,概述了 OpenOCD 源码的结构,解释了程序的执行流程。然后详细分析了 OpenOCD 的初始化过程,解释了指令注册的作用和具体方法。之后,文章分析了 OpenOCD 的执行环节。在最后一部分,详细讲解了程序执行的过程,并以官方的 xilinx-xc7.cfg 文件为例,详细解释了指令的执行过程。

1 OpenOCD 源码结构概述

openocd 的源码流程图如下
notion image
由程序入口进入后会先注册 openocd 支持的所有调试指令,然后初始化配置和服务,如 RTT。之后进入调试部分的处理,即 openocd_threads 函数中,首先解析命令行参数以及配置文件;之后初始化服务如 telnet,然后进入服务主循环,监听其是否被调用,最终收到 exit 指令后推出服务,在 openocd_main 函数的最后会将程序中可能实例化的对象进行释放,结束执行。

2 OpenOCD 源码详解

本节依照流程图顺序对 OpenOCD 的源码进行详细的解释和分析。

2.1 初始化

程序初期的初始化中对 openocd 的指令进行了注册,初始化了 RTT(Real-Time Transfer) 服务。因为 RTT 使用了 SEGGER 提供的协议实现,这里先不做介绍。把重点放在指令注册部分。即 setup_comand_handler 函数。
如下代码段中的数组就是各个子系统定义的指令注册函数,openocd 会遍历各个子系统中的指令注册函数进行指令注册
其中每个指令注册函数会调用 register_commands 指令来注册该子系统下定义的函数。这里最需要关注的就是指令的数据结构,在 openocd 中,所有的指令都以相同的数据结构定义,以便在兼容后续加入的不同 adapter。指令的数据结构分为两种,一种是指令注册时的数据结构,一种是注册完成后的指令的数据结构,定义如下:
内容包括指令名称 name,回调函数 handler,带 jim 前缀的用于定义处理 TCL 脚本信息的指令的回调函数,以及指令模式 mode。关于 mode 的定义有四种,其含义是规定该条 openocd 指令在何时可以被执行,如 COMMAND_CONFIG 指在 openocd 程序启动时才可以执行,如 init; COMMAND_ANY 是指在任何时候都可以执行,绝大部分指令的 mode 都是 COMMAND_ANY。
registration 结构体多出的 help 和 usage 是向 tcl 处理函数提供的信息,在 openocd 调试终端处输入 help 或 usage 可以获得指令的帮助信息 chain 是用来链接其他需要注册的指令,比如某个子系统中分类定义了几组指令,可以将其他指令定义的地址添加在 chain 中。具体的注册流程如图所示:
notion image
可以看到 register_commands 是依靠 chain 完成一个子系统的指令注册,注册使用的是 jim-tcl 的 API。如下是 adapter 部分的指令生命代码示例:
经过上述的过程,基本的初始化就完成了,为 tcl 解释器注册了基本的指令支持。

2.2 openocd_threads

在初始化结束后,就进入到正式的执行环节。

2.2.1 解析命令行参数 parse_cmdline_args

解析命令行参数就不细说了,openocd -help 就能看到支持的所有参数和介绍,对参数的解析也是常规的 getopt_long 函数。

2.2.2 解析配置文件 parse_config_file

先贴一下这部分的源码:
openocd 会根据命令行中指定的 -f 参数来读入配置文件,并且可以读入多个,openocd 会将其全部存入 config_file_names 数组中,在解析时依次读入。command_run_line() 函数中,会调用 jim-tcl 解释器,以行为单位执行配置文件。上一步的指令注册也是为了这一步能够顺利进行。

2.2.3 初始化并监听服务 server_init & server_loop

server_init 中会启动 tcl server 和 telnet server,前者为 openocd 提供网络通信的能力,用于后续启动 telnet、remote bitbang 或者 gdb;后者是 openocd 默认的调试终端接口。启动服务后金辉进入到服务的主循环中,这里会监听之前启动的 server,当有读写或异常时调用对应的回调函数进行处理。
本小节以 telnet 为例,详细介绍上述过程。
首先是对 telnet 的初始化,代码如下(代码块中省略了一些指针检查,只保留了关键部分):
可以看出,主要定义了 telnet 处理输入和打开关闭的回调函数,并在 telnet_init 函数中添加了该服务。
在 server_loop 中,会使用 select 函数监听其文件描述符,检查是否有读写存在(省略了超时检查和异常处理,只保留了初始化和监听部分):
当 select 捕捉到输入事件时,telnet 就会调用启动服务时注册的回调函数 telnet_input 进行处理,在检测到终止符(如换行)时,就会认为输入结束,调用 telnet_exec_line 然后调用 command_run_line 来执行当前输入的指令。

2.2.4 退出 server_quit

当检测到推出命令或 CTRL-C 终止符,openocd 会退出主循环,销毁执行过程中实例化的对象及指针,然后退出程序。

2.3 程序执行的过程

前两节的讲述将 openocd 的基本执行过程介绍完成,但是在指令执行部分都只介绍到 command_run_line 就结束了,因为实际上指令的解析是交给了 jim-tcl 去完成。但是对于执行部分,仍然是由 openocd 的源码完成的,其原理就是前面提到的指令注册,通过指令添加的 API 将自定义指令写入了解释器,这一节来详细介绍一下具体的执行过程。
以官方给出的 xilinx-xc7.cfg 文件为例(省略了一些设备 id 的信息,源码路径是 openocd/tcl/xilinx-xc7.cfg):
可以看到配置文件定义了芯片名称,jtag 接口 tap,并将其声明为一个 virtex2 类型的 pld 设备,又设置了几个基本指令码,之后定义了一个 program 函数,用来向指定的 tap 中载入程序。其中 irscan 和 runtest 就是 openocd 在 jtag_register_commands 组中定义的两个 jtag 基本操作的指令。
runtest 代码相对简短,我们以 runtest 为例,查看其回调函数:
可以看到 runtest 指令就是让 JTAG 接口在 IDLE 状态等待 num_clocks。这里的 jtag_add_runtest 函数是将一条 run_test 的指令添加到一个 jtag_command 的链表结构中,链表中保存指令的名称和类型,当有些回调函数中要处理多条指令,可以添加到最后一起调用 jtag_execute_queue 执行。openocd 为 jtag 的基本命令都编写了 add 函数,作为 jtag 操作的最小单元,列举如下:
完成指令链表的添加后就可以打包一起执行(除去了一些 debug 模式的 log 输出):
可以看到真正执行的是 adapter_driver 的 execute_queue 函数,使用何种 adapter 可以用类似 adapter_driver remote_bitbang 类似的语句在 cfg 文件开头声明。openocd 就会取对应的参数为 adapter_driver 对象设置对应的值。
正如代码展示的,这里提供的是一个函数地址,实际上该函数定义在每一个 adapter_driver 的 c 文件中,这里我们以 usb_blaster 为例:
可以看到,在定义 usb_blaster_adapter_driver 时,已经将 execute_queue 指向了对应的执行函数。
在 usb_blaster 中也根据对应的类型定义了自己的实现。我们仍然以 runtest 为例:
在 jtag 命令函数中,runtest 被拆解成其他基本指令,其他基本指令被改写为最基本的 jtag 的 tms 操作。openocd 将这些操作定义在一个专门的 interface.c 文件中,如上述代码块最后所示,每个状态的 tms 序列为一列,通过索引可以方便的实现两个状态间的转移。
再分析上面的代码,usb_blaster 会将指令给出的信号序列换算成输出端口的 byte 信号,然后转移到输出的 buffer 中去再通过 flush buffer 的方式写出。真正的写操作是通过 info.drv 完成的,于是继续寻找代码,找到如下定义:
看出 drv 是一个 ublast_lowlevel 结构的指针继续寻找,发现 lowlevel 使用的是 ftdi 或 libusb 库。
到此,后面的写操作就由具体的硬件驱动接手了,由他们完成从 openocd 到硬件设备信号的转换。

总结

本文以 openocd 的执行过程为主,对 openocd 的源代码进行了详细的介绍和分析,可以对后续添加自定义的 adapter driver 起到参考作用。
OpenOCD 使用流程OpenOCD 原理总结
Loading...