2. JTAG基础原理

好,咱们进入正题。OpenOCD 这东西,说白了就是一个「翻译官」——它把你的 GDB 调试命令,翻译成 JTAG/SWD 协议能听懂的电信号。但翻译官也有自己的脾气,你得先摸清它的底细。

我个人习惯把 OpenOCD 拆成四块来看:架构、配置文件语法、适配器驱动、目标芯片支持。这四块搞明白了,后面调板子、定位问题就顺了。

2.1 OpenOCD 架构:三层分工

OpenOCD 的架构其实不复杂,就三层。我画了张图,你一看就明白。

OpenOCD 三层架构图 OpenOCD 三层架构 GDB / Telnet / Tcl 客户端 (远程调试接口) 命令引擎 / 目标管理层 (断点、内存、寄存器、Flash 操作) 适配器驱动层 (FTDI / CMSIS-DAP / J-Link / ST-Link ...) JTAG / SWD 硬件接口 用户空间 (软件层) 核心层 (逻辑处理) 硬件抽象层 (驱动 + 物理)

顶层是 GDB 或 Telnet 客户端,你在这敲命令。中间层是命令引擎,负责解析、调度。底层是适配器驱动,直接跟硬件打交道。每一层只管自己的事,耦合度很低。

关键点:OpenOCD 本身不直接控制 JTAG 时序,它通过适配器驱动调用底层库(比如 libftdi、HIDAPI)来操作硬件。所以驱动选对了,就成功了一半。

2.2 配置文件语法:.cfg 文件怎么写

OpenOCD 的配置文件是 Tcl 脚本。嗯,你没看错,就是 Tcl。我第一次接触时也觉得别扭,但用久了发现,Tcl 的语法其实很简洁,适合做配置。

一个典型的配置文件分三部分:

  • 适配器配置:指定调试器型号、接口协议(JTAG/SWD)、速率。
  • 目标芯片配置:指定芯片内核、工作模式、内存映射。
  • 板级配置:把适配器和目标芯片绑定,加上一些板级特殊设置(比如复位方式)。

看个例子,这是我给 STM32F4 写的一个配置:

# 适配器配置:使用 ST-Link,SWD 模式,速率 4MHz
source [find interface/stlink.cfg]
transport select swd
adapter speed 4000

# 目标芯片配置:STM32F407,Cortex-M4
source [find target/stm32f4x.cfg]

# 板级配置:复位方式、Flash 参数
reset_config srst_only srst_nogate
flash bank bank1 stm32f4x 0x08000000 0x00100000 0 0 0

这里有几个坑,我踩过:

  • source [find ...] 是 OpenOCD 的搜索命令,它会去安装目录下的 interface/ 和 target/ 文件夹找文件。别自己瞎写路径。
  • transport select swd 必须放在适配器配置之后,否则 OpenOCD 不知道用哪种协议。
  • adapter speed 别设太高。我曾经设过 10MHz,结果线缆一长,通信直接挂掉。一般 4MHz 以内比较稳。

小技巧:如果你不确定适配器支持多高速率,先设 1MHz 试跑,稳定了再往上加。我习惯用 adapter speed 1000 做初始调试。

2.3 适配器驱动:选对驱动,少走弯路

OpenOCD 支持的调试器很多,但驱动方式不一样。我整理了个表,方便你对照:

调试器 驱动方式 配置文件 注意事项
FTDI 系列(FT2232H/FT232H) libftdi / ftd2xx interface/ftdi/... 需要安装 libftdi 驱动,Linux 下注意权限
CMSIS-DAP HIDAPI / libusb interface/cmsis-dap.cfg Windows 下可能需要 Zadig 换驱动
J-Link J-Link 驱动(SEGGER) interface/jlink.cfg 需要安装 SEGGER 驱动,OpenOCD 通过 TCP 连接
ST-Link libusb / 自带驱动 interface/stlink.cfg 新版 ST-Link 固件可能不兼容,需要降级
USB-JTAG(Altera/ Xilinx) libusb / 私有驱动 interface/altera-usb-blaster.cfg 部分需要额外配置 VID/PID

选驱动时,我建议优先用 libusb 或 HIDAPI 的版本,跨平台兼容性好。FTDI 的 ftd2xx 虽然性能高,但闭源,Linux 下折腾起来挺烦的。

避坑指南:我曾经在 Windows 下用 CMSIS-DAP 调试器,结果 OpenOCD 死活认不到设备。折腾了半天,发现是驱动没换——Windows 默认把 CMSIS-DAP 识别成了 HID 设备,但 OpenOCD 需要 WinUSB 驱动。用 Zadig 工具把驱动换成 WinUSB 后,问题解决。

2.4 目标芯片支持:内核与 Flash 算法

OpenOCD 对目标芯片的支持,核心在于两点:内核识别和Flash 算法。

内核识别靠的是 target/ 目录下的配置文件。比如 target/stm32f4x.cfg 里定义了:

  • 芯片的内核类型(Cortex-M4)
  • 调试寄存器地址(DHCSR、DEMCR 等)
  • 复位序列(硬件复位还是软件复位)
  • 内存映射(SRAM、Flash 的起始地址和大小)

Flash 算法则是 OpenOCD 用来擦除、编程、校验 Flash 的代码片段。每个芯片系列都有自己的算法文件,放在 flash/ 目录下。

举个例子,STM32F4 的 Flash 算法文件是 flash/stm32f4x.c,它实现了:

// 伪代码示意
int stm32f4x_write_block(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) {
    // 1. 解锁 Flash 控制寄存器
    // 2. 按页擦除
    // 3. 写入数据(半字/字)
    // 4. 校验
    // 5. 锁定 Flash
    return ERROR_OK;
}

如果你用的芯片比较冷门,OpenOCD 可能没有现成的支持。这时候就得自己写目标配置文件,甚至写 Flash 算法。嗯,这确实是个大活,但别怕,后面章节我会手把手教你。

核心原则:目标芯片支持好不好,看两点——内核配置是否完整,Flash 算法是否稳定。我建议拿到新板子后,先用 flash probe 0 命令检查 Flash 能不能正常识别,这是第一步。

2.5 配置文件加载顺序

OpenOCD 启动时,配置文件的加载顺序是有讲究的。搞反了,可能直接报错。

  1. 先加载适配器配置:告诉 OpenOCD 你用的是什么调试器。
  2. 再选择传输协议:JTAG 还是 SWD。
  3. 然后加载目标芯片配置:告诉 OpenOCD 你要调试什么芯片。
  4. 最后加载板级配置:复位方式、Flash 分区等。

为什么是这个顺序?因为目标芯片配置里可能会用到适配器的一些功能(比如复位信号),如果适配器还没初始化,就会出错。

我见过有人把 transport select 写在最前面,结果 OpenOCD 报错说「没有适配器,无法选择传输协议」。嗯,顺序很重要。

个人习惯:我会把配置文件拆成三个文件:adapter.cfg、target.cfg、board.cfg,然后在启动时用 -f 参数依次加载。这样模块化,好维护。