OpenOCD 代码学习(4)其它配置命令
前言
-
1)上一节我们学习了 adapter 与 transport 命令,这一节我们接着学习配置文件中的其它命令。本文主要是对配置文件中用到的命令(如下图)进行解析,以在命令行运行如下命令的结果为准:
openocd -d3 -f board/airm2m_air001.cfg -d3 也作 --debug-level=3。即指定输出日志级别。 -f 也作 --file。这里指定开发板为合宙 air001 芯片。
-
2)这一节主要是学习 air001.cfg 文件的内容。这里简单提一下加载的两个文件:swj-dp.tcl 和 mem_helper.tcl。
- swj-dp.tcl 文件首先执行了 transport select 命令(这个已经在 cmsis-dap.cfg 中执行过,所以无影响)。然后定义了 swj_memdap 调用过程。
- mem_helper.tcl 文件则定义了操作 memory 的多个调用过程:
- mrw/mrh/mrb 分别是 memory read word/halfword/byte,读取 memory。
- mmw 则是 memory modify word,修改 memory。
-
3)参考链接:
1 swj_newdap
- 1)完整的 swj_newdap 命令如下:
swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID
# 替换掉变量后为:
swj_newdap air001 cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id 0x2ba01477
-
2)其中使用的 swj_newdap 命令是一个定义在 /tcl/target/swj-dp.tcl 文件中的 proc 可调用过程,其实现为:
# /tcl/target/swj-dp.tcl proc swj_newdap {chip tag args} { if [using_jtag] { eval jtag newtap $chip $tag $args } elseif [using_swd] { eval swd newdap $chip $tag $args } else { echo "Error: transport '[ transport select ]' not supported by swj_newdap" shutdown } } # /src/target/startup.tcl proc using_jtag {} { set _TRANSPORT [ transport select ] expr { [ string first "jtag" $_TRANSPORT ] != -1 } }
- (1)transport select 命令当未指定传输方式时,OpenOCD 会在自动选择一种传输方式的同时,并通过 Jim_SetResultString() 函数会将当前选择的传输方式作为结果返回。
-
3)创建名为 air001.cpu 的 Tap 过程如下(尽管该命令名叫 swj_newdap,实际上却是创建的 Tap):
-
最终的结果是创建了 struct jtag_tap 对象,并追加到名为 __jtag_all_taps 的 Tap 链表中。
2 dap create
-
1)真正的创建 dap 过程如下:
-
2)dap create 命令:
- 首先通过 dap_name 指定待创建的 dap 名称;
- 其次通过 -chain-position tap_name 选项将上一小节中创建的 Tap 赋值给该 dap。
- 然后验证 dap 的合法性
- 最后会将 dap 的子命令复制给新创建的 dap(这里为 air001.dap) 一份,因此你既可以使用如 dap info 的命令,也可以使用 air001.dap info 命令。
-
3)最终的结果是创建 struct arm_dap_object 对象,并追加到名为 all_dap 的 dap 链表中。
3 target create
- 1)创建 target。
target create <name> <target_type> [<target_options> ...]
- (1)target_type:可取 arm7tdmi、cortex_m、arm11、esp32、stm8、riscv 等
- (2)target_options 可取:这些选项也可以后续通过 <target_name> configure 命令设置
-type -endian -event -coreid -work-area-virt -chain-position -work-area-phys -dbgbase -work-area-size -rtos -work-area-backup -defer-examine -gdb-port -gdb-max-connections
- 2)执行逻辑:
-
通过给定的 target_type 找到对应的 target 类型,这里为 cortexm_target
-
通过 target_configure() 函数处理该命令指定的选项
-
注册 target_type 的内置命令
-
重写并注册 air001.cpu 命令
4 <target_name> configure
-
1)创建完名为 air001.cpu 的 target 后,还需要进行配置:
target_name configure [options]
- (1)target_name:一般可以通过 target create 命令创建
- (2)options:
-type :一般指 MCU 架构,如 arm7tdmi、arm9tdmi、cortexm、 riscv 等 -event -work-area-virt -work-area-phys :工作空间的物理起始地址 -work-area-size :工作空间的大小 -work-area-backup :工作空间是否备份 -endian :大小端 -coreid -chain-position -dbgbase -rtos -defer-examine -gdb-port -gdb-max-connections
-
2)target 配置逻辑:
-
这里有一个疑问,如果使用 -chain-position 选项指定 air001.dap,则启动时报错;可如果使用要求的 -dap 选项,target_configure() 函数又不支持该选项。
5 flash bank
-
1)flash bank 一般用来声明芯片的 bank。
flash bank <name> <driver> <base> <size> <chip_width> <bus_width> <target> 参数: - name:bank name,一般可以自定义为 “芯片.flash” - driver:driver name,声明在 /src/flash/nor/drivers.c 文件中的,struct flash_driver 类型的芯片驱动。用户需要实现其中的读、写、擦除、探测等方法。 - base:base address,芯片 flash 的起始地址 - size:size bytes,芯片 flash 的大小。如果可以从芯片的某个寄存器中读取,建议在 flash_driver 中实现,则这里默认写 0 即可;否则需要填写芯片的准确大小,单位为:字节。 - chip\_width:默认为 0 - bus\_width:默认为 0 - target:通过 target create 命令创建的目标名称
-
2)flash bank 执行逻辑:
-
根据给定的 target 名称找到通过 target create 命令创建的 target
-
根据给定的 flash_driver 名称找到用户编写的 flash 驱动,并注册该驱动中的相关命令
-
创建 flash_bank 对象,并为其 name、target、driver 等属性赋值
-
调用 flash_driver 的 flash_bank_command() 函数指针
-
最后将该 flash_bank 对象添加到全局 flash_banks 链表中
总结
- 1)至此,我们基本掌握了配置文件中的相关命令,其实主要是创建了几个对象:
对象类型 对象名称 联系 struct adapter_driver cmsis-dap
st-link
jlink
ulinkstruct transport swd/jtag
dapdirect_swd/dapdirect_jtag
hla_swd/hla_jtag
swimadapter_driver->transports struct jtag_tap air001.cpu —— struct arm_dap_object air001.dap dap->dap.tap = air001.cpu struct target air001.cpu target->tap = air001.cpu(-dap 选项,存疑)