头图

前言

  • 1)上一节中,我们知道 parse_config_file() 函数会边解析配置文件,边执行其中的命令,那么接下来我们将主要学习一下 OpenOCD 配置文件中涉及到的命令。
  • 2)我们知道,要编写一个 OpenOCD 驱动,除了要实现烧录算法外,还要编写关于 MCU 的配置文件。OpenOCD 主要有 3 类配置文件(具体内容见官方文档第 6 节 配置文件参考),这里我们简单看一下。
  • 3)<font color="red">interface</font>:指定调试器的适配器驱动。

    • (1)该驱动实现 OpenOCD 为调试器们定义的统一接口,将来自 OpenOCD 的操作,转换为调试器的操作。
    • (2)常用的调试器都有对应的驱动,如 jlink、st-link、cmsis-dap、ftdi 等。
    • (3)当你根据公有(或私有)的调试协议设计出了自已的调试器,就需要在此为其添加一个驱动。如沁恒 WCHLink 的调试器适配驱动为 wlinke。
    • (4)可以通过以下命令来查看一下 OpenOCD 支持调试器的适配器驱动:

      grep -rn "adapter driver " | awk -F ':' '{print $3}' | uniq -u
      
      adapter driver ftdi
      adapter driver cmsis-dap
      adapter driver jlink
      adapter driver rlink
      adapter driver st-link
      adapter driver hla
      adapter driver ulink
      ......
  • 4)<font color="red">board</font>:指定开发板特定的初始化项。比如,同样的 MCU 内核,但 SRAM 和 FLASH 的大小、起始地址可能不同,此时可以在 board 配置文件中指定这些不同项。不过大部分情况下,只是在该配置文件中指定引用的 interfact 和 target 配置文件及其它选项,然后在烧录时指定该配置文件。
  • 5)<font color="red">target</font>:指定 MCU 中需要交由 OpenOCD 控制的测试访问端口(Test Access Port, TAP)。
  • 6)还有一些其它的配置文件,用于指定一些公共的调用过程,芯片信息等。本文主要是对配置文件中用到的命令(如下图)进行解析,以在命令行运行如下命令的结果为准:

    openocd -d3 -f board/airm2m_air001.cfg
    -d3 也作 --debug-level=3。即指定输出日志级别。
    -f  也作 --file。这里指定开发板为合宙 air001 芯片。
  • 7)参考链接:

1 adapter driver

1.1 前置知识

1.1.1 struct adapter\_driver

  • 1)在解析 adapter driver 命令之前,让我们先来了解一些前置知识。
  • 2)在 interfaces.c 文件中,OpenOCD 会根据编译选项来引入一些 struct adapter\_driver。比如说我们通过以下命令使能 ftdi,将在生成的 config.h 文件中声明 “#define BUILD\_FTDI 1” 宏定义,最终使 interfaces.c 文件中的 ftdi\_adapter\_driver 生效(当然有些 adapter 是自动使能的,如 cmsis\_dap)。

    ../configure --enable-ftdi
    • (1)adapter\_driver 既实现了对调试器的驱动,又是 OpenOCD 与调试器交互的适配器。
    • (2)每个调试器都有自已支持的传输方式,初始化、退出、复位过程,以及设置速度等功能,所以 OpenOCD 提供一个接口(interface,struct adapter\_driver),来定义出 .transports, .init, .quit, .reset, .speed 等接口函数指针,让不同的调试器自己去实现,而 OpenOCD 上层只需要调用这些接口函数,即为适配器模式。
    • (3)最后,OpenOCD 支持多少种调试器,interfaces.c 文件中就会有多少 adapter\_driver。
  • 3)cmsis-dap 的 adapter\_driver 实现如下:
struct adapter_driver cmsis_dap_adapter_driver = {
    .name = "cmsis-dap",
    .transports = cmsis_dap_transport,
    .commands = cmsis_dap_command_handlers,

    .init = cmsis_dap_init,
    .quit = cmsis_dap_quit,
    .reset = cmsis_dap_reset,
    .speed = cmsis_dap_speed,
    .khz = cmsis_dap_khz,
    .speed_div = cmsis_dap_speed_div,
    .config_trace = cmsis_dap_config_trace,
    .poll_trace = cmsis_dap_poll_trace,

    .jtag_ops = &cmsis_dap_interface,
    .swd_ops = &cmsis_dap_swd_driver,
};

1.1.2 transport

  • 1)上一小节中,我们知道 cmsis\_dap\_adapter\_driver 的 .transports 指定了 swd 和 jtag 传输方式(如下),这一小节我们来看一下 transport 的相关内容。
static const char * const cmsis_dap_transport[] = { "swd", "jtag", NULL };

struct adapter_driver cmsis_dap_adapter_driver = {
    .name = "cmsis-dap",
    .transports = cmsis_dap_transport,
    ......
}

/************** 分隔线 **********************************************/
static struct transport jtag_transport = {
    .name = "jtag",
    .select = jtag_select,
    .init = jtag_init,
};

static struct transport swd_transport = {
    .name = "swd",
    .select = swd_select,
    .init = swd_init,
};
  • 2)在 transport.c 中有一个 transport\_register() 函数,顾名思义——注册 transport。接下来,我们看一下哪些地方调用了这个函数,就可以知道 OpenOCD 支持哪些传输方式了。

  • 3)最终,我们可以知道 OpenOCD 支持这些传输方式(官方文档 8.3 Transport Configuration):

    • jtag
    • swd
    • hla\_jtag
    • hla\_swd
    • dapdirect\_jtag
    • dapdirect\_swd
    • swim
  • 4)swd 和 jtag 分别是通用的传输方式;而 hla\_jtag 和 hla\_swd 则是来自于使用了一些高级抽象 API 的 hla\_adapter\_driver;dapdirect\_jtag 和 dapdirect\_swd 则提供了仅支持 ST-Link 的直接访问 ADIv5 DAP 方式。
  • 5)注:以下代码结构表示,在 GCC 中,项目启动时调用该函数,所以在 OpenOCD 启动时,这些 transport 将会被注册:
static void swd_constructor(void) __attribute__((constructor));
static void swd_constructor(void)
{
    transport_register(&swd_transport);
}

1.2 adapter driver cmsis-dap

  • 1)上一小节前置知识中,我们知道 OpenOCD 在编译期加载调试器的驱动适配器(adapter\_driver),在启动时注册所有的传输(transport)。下面我们来看一下 “adapter driver cmsis-dap” 命令的执行逻辑。
  • 2)adapter driver cmsis-dap 命令的执行逻辑如下:

    • (1)首先执行该命令将会回调到 handle\_adapter\_driver\_command() 函数。在该函数中将遍历所有的 adapter\_driver,并根据该命令指定的驱动名(这里为 cmsis-dap)找到对应的 adapter\_driver。
    • (2)然后,注册该 adapter\_driver 定义的命令,并设置其允许的传输方式(transport)
    • (3)最后,如果该 adapter\_driver 只支持一种传输方式,则直接调用该传输方式的 select() 函数来选择传输方式;如果支持多种,则 OpenOCD 使用 allowed\_transports 记录所有支持的传输(等待用户后续选择)。
  • 3)在前置知识小节中我们知道,cmsis-dap 驱动支持 swd 和 jtag 两种传输,所以上图中虚线框的内容不会执行,即这里不会直接确定传输方式。

2 transport select

  • 1)顾名思义,transport select 命令即为传输选择。在前置知识中我们知道,transport 目前一共有 7 种:swd、jtag、swim、hla\_swd、hla\_jtag、dapdirect\_jtag、dapdirect\_swd。
  • 2)transport select 命令执行逻辑:

    • 根据指定的传输方式不同,将进行不同的选择逻辑。不过殊途同归,最终的逻辑都是注册与指定传输方式相关的命令。
  • 2)transport 相关的命令见官方文档:https://openocd.org/doc-release/html/Debug-Adapter-Configurat...

送南阳马生序
7 声望3 粉丝

余之业有不精、德有不成,非天质之卑,则心不若他之专耳,岂他人之过哉!