第三章 OpenOCD配置文件结构:接口、目标、板级与CFI闪存

好,咱们进入第三章。这一章我打算把OpenOCD的配置文件结构彻底讲透。很多初学者拿到OpenOCD,第一反应就是找个现成的配置文件,改两下就跑。结果呢?连不上、报错、不知道哪里出了问题。

我个人习惯是,先搞清楚OpenOCD到底在加载什么。说白了,它的配置文件就四大块:接口配置、目标配置、板级配置,还有CFI闪存配置。咱们一个一个来拆。

核心观点:OpenOCD的配置文件不是随便拼凑的,它遵循严格的层级结构。理解这个结构,你才能写出稳定可靠的调试环境。

OpenOCD 配置文件层级结构 OpenOCD 核心 接口配置文件 目标配置文件 板级配置文件 CFI闪存配置 接口配置文件 定义调试器硬件接口 FTDI/ST-Link/J-Link等 目标配置文件 定义芯片内核 Cortex-M/RISC-V等 板级配置文件 整合接口+目标 定义板级外设 CFI NOR Flash 加载顺序 接口配置 → 目标配置 → 板级配置(可选)→ CFI闪存配置(可选) OpenOCD -f interface.cfg -f target.cfg -f board.cfg

3.1 接口配置文件(interface)

接口配置文件,顾名思义,就是告诉OpenOCD你用的是哪种调试器。是J-Link?ST-Link?还是FT2232自己做的调试器?

我记得刚入行那会儿,公司有个项目用的是FT2232H做的调试器,结果我拿了个ST-Link的配置去跑,折腾了一下午没连上。后来才发现,接口配置错了,JTAG信号根本对不上。

接口配置文件里主要定义这几样东西:

  • 传输协议:JTAG还是SWD?现在ARM芯片基本都用SWD,但老一些的芯片还是JTAG。
  • 适配器驱动:比如 ftdi、jlink、stlink 等。
  • 信号映射:TDI、TDO、TCK、TMS这些引脚怎么接的。
  • 速度设置:adapter speed 命令,单位是kHz。

我的经验:速度别一上来就设太高。我一般先设1000kHz,能连上再慢慢往上加。有些板子布线不好,高频下信号反射严重,反而连不上。

来看一个典型的FTDI接口配置:

# interface/ftdi.cfg
adapter driver ftdi
ftdi vid_pid 0x0403 0x6010
ftdi layout_init 0x0018 0x001b
ftdi layout_signal nTRST -data 0x0010
ftdi layout_signal nSRST -data 0x0020
adapter speed 1000
transport select jtag

这里 ftdi layout_init 后面两个十六进制数,分别对应GPIO的初始状态和方向。我第一次看到这个也是一脸懵,后来查了FT2232H的数据手册才搞明白。说白了,就是告诉OpenOCD哪些引脚是输出,哪些是输入,初始电平是什么。

3.2 目标配置文件(target)

目标配置文件定义的是你调试的芯片内核。是Cortex-M3?M4?还是RISC-V?不同的内核,调试逻辑不一样。

目标配置文件里最重要的几个命令:

命令 作用 示例
set CHIPNAME 定义芯片名称 set CHIPNAME stm32f4
set ENDIAN 设置字节序 set ENDIAN little
target create 创建目标 target create $_TARGETNAME cortex_m -endian $_ENDIAN -chain-position $_CHIPNAME.cpu
$_TARGETNAME configure 配置目标参数 $_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size 0x10000

我曾经遇到过一个坑:有个客户用的芯片是Cortex-M0,但我配置里写的是cortex_m3。结果OpenOCD能连上,但单步调试时总跳飞。后来查了半天,发现是内核类型写错了。M0和M3的调试逻辑有细微差别,不能混用。

来看一个STM32F4的目标配置:

# target/stm32f4x.cfg
set CHIPNAME stm32f4
set ENDIAN little

if { [info exists CHIPNAME] } {
   set  _CHIPNAME $CHIPNAME
} else {
   set  _CHIPNAME stm32f4
}

if { [info exists ENDIAN] } {
   set  _ENDIAN $ENDIAN
} else {
   set  _ENDIAN little
}

# 创建目标
target create $_CHIPNAME.cpu cortex_m -endian $_ENDIAN -chain-position $_CHIPNAME.cpu

# 配置工作区
$_CHIPNAME.cpu configure -work-area-phys 0x20000000 -work-area-size 0x10000

# 配置复位
$_CHIPNAME.cpu configure -event reset-init {
   # 初始化代码
}

注意:工作区地址一定要选对。我见过有人把工作区设在Flash地址上,结果调试时写数据把程序给覆盖了。工作区一般用SRAM,比如STM32F4就是0x20000000开始的。

3.3 板级配置文件(board)

板级配置文件,说白了就是把接口配置和目标配置整合到一起,再加上板子特有的东西,比如外部Flash、LED、按键这些。

我个人习惯是,如果只是调试核心板,直接用接口+目标两个文件就够了。但如果是完整的开发板,最好用板级配置文件,因为它能帮你把板子上的外设都初始化好。

板级配置文件里常见的内容:

  • source命令:引用接口和目标配置文件
  • 复位配置:比如使用硬件复位还是软件复位
  • Flash配置:内部Flash或外部Flash的编程算法
  • 板级初始化:比如配置时钟、GPIO等

来看一个STM32F4 Discovery板的配置:

# board/stm32f4discovery.cfg
source [find interface/stlink-v2.cfg]
source [find target/stm32f4x.cfg]

# 复位配置
reset_config srst_only srst_nogate

# 内部Flash配置
flash bank $_CHIPNAME.flash stm32f2x 0x08000000 0x1000000 0 0 $_CHIPNAME.cpu

# 板级初始化
$_CHIPNAME.cpu configure -event reset-init {
   # 使能HSE时钟
   mww 0x40023800 0x00000180
   # 配置GPIO
   mww 0x40023830 0x00000004
}

这里有个细节:reset_config srst_only srst_nogate 是什么意思?我曾经也困惑过。srst_only表示只用系统复位,不用JTAG复位。srst_nogate表示复位信号不经过门控,直接拉低。不同的板子,复位电路设计不一样,这个配置得根据实际硬件来。

3.4 CFI闪存配置

CFI(Common Flash Interface)是NOR Flash的一种标准接口。如果你的板子上有外部NOR Flash,就需要配置CFI。

说实话,现在用NOR Flash的项目不多了,大部分都用NAND或者eMMC。但有些工业控制板、通信设备还在用NOR Flash,因为它的可靠性高、读取速度快。

CFI配置的核心命令是 flash bank:

# CFI NOR Flash配置示例
flash bank cfi_flash cfi 0x60000000 0x2000000 2 4 $_TARGETNAME

参数说明:

参数 含义 示例值
bank名称 Flash bank的名字 cfi_flash
驱动类型 固定为cfi cfi
基地址 Flash在内存中的映射地址 0x60000000
大小 Flash的总大小(字节) 0x2000000(32MB)
总线宽度 数据总线宽度(字节) 2(16位)
页大小 编程页大小(KB) 4
目标 关联的目标 $_TARGETNAME

避坑指南:我曾经在一个项目里,CFI Flash怎么都擦除不了。后来用逻辑分析仪抓波形,发现是总线宽度配错了。芯片是16位的,我配成了8位。OpenOCD发出去的地址和命令都不对,Flash当然不响应。

CFI配置还有一个好处:OpenOCD会自动读取Flash的CFI信息,验证配置是否正确。如果基地址或总线宽度不对,OpenOCD会报错:

Error: CFI query failed at 0x60000000
Error: auto_probe failed

看到这个错误,先检查基地址和总线宽度。我一般会先读一下芯片手册,确认Flash的映射地址和总线宽度,再填到配置里。

3.5 配置文件的加载顺序

最后说一个容易忽略的点:配置文件的加载顺序。

OpenOCD加载配置文件的顺序是:接口 → 目标 → 板级 → CFI。这个顺序不能乱,因为后面的配置可能依赖前面的。

比如板级配置文件里 source 了接口和目标文件,那它就必须在最后加载。如果你先加载板级文件,再加载接口文件,OpenOCD会报错说找不到适配器。

正确的启动命令:

openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg -f board/stm32f4discovery.cfg

或者直接用板级文件,它会自动引用接口和目标:

openocd -f board/stm32f4discovery.cfg

总结一下:接口配置管硬件连接,目标配置管芯片内核,板级配置管板子整合,CFI配置管外部Flash。四者各司其职,组合起来就是一个完整的调试环境。