ARM调试端口简介

ARM的调试是基于DAP实现的,ARM的调试端口一般要实现以下一些功能:

  1. 更改处理器的状态
  2. 读取处理器状态
  3. 设定调试事件,比如搞个断点什么的
  4. 强制处理器进入或者离开Debug状态
  5. 查看当前处理器是不是在Debug状态
  6. 追踪程序执行状态

DAP的基本结构

一般DAP的结构如下图所示:

4069788318.png

Debug Port(DP)是直接和外部接口的,可以通过JTAG和SWD两种形式实现。
Access Port(AP)是位于DP和芯片之间的一个桥梁,一般AP会有一个识别标准,当Debugger扫描AP时候,如果碰到不支持的AP可以直接跳过。

下图是一个详细的DAP的结构图

1304885120.png

对于DP和AP,debugger在访问的时候都会涉及到两个地址,一个是公用的A[3:2],对于DP来讲A[3:2]和SELECT.DPBANKSEL共同决定了目标DP寄存器的地址;对于AP则是通过 SELECT.APSEL 选择一个AP寄存器,A[3:2]则是选择不同的bank,比如要访问AP 0 的0x14寄存器:

  1. 首先通过DP写相应的寄存器SELECT.APSEL写成0x00,SELECT.APBANKSEL 写 0x1.
  2. 访问AP,访问的时候要将A[3:2]写为0x01

DAP的读写操作

DAP的结构看起来还挺复杂的,不过JLINK工具里面其实已经集成这个工具了,比如Jlink Commander工具就支持以下一些命令

  1. writedp <index> <data>
  2. writeap <index> <data>
  3. readdp <index>
  4. readap <index>

比如要读AP[0] BANK[15] 的IDR,就可以通过如下的命令执行:

WriteDP 2, 0x000000F0 // Select AP[0] bank 15 
ReadAP 3 // Read AP[0] IDR

上面语句中WriteDP是写DP的指令,index=2代表写SELECT寄存器,后面的数据代表AP[0] BANK 15,SELECT寄存器的定义如下:

262051663.png

  1. APSEL代表AP的序号,如果访问了一个没有连接的AP序号,那么后续的读操作是全零,而写操作会直接忽略。
  2. APBANKSEL代表AP BANK的标号
  3. DPBANKSEL代表DP BANK的标号,这个具体含义和DAP实现的版本有关,DPv0中要保持写0,新一点的版本中会有特殊含义,但是我做测试的时候一般直接写0。

后面的ReadAp 3则是表示读AP的第四个寄存器(寄存器序号从0开始),所以对应的就是IDR寄存器了。

另外在读DP,AP的时候最好多读几遍,因为里面的扫描链一般会有滞后,所以读AP的时候至少要读两次,一般第一次读的结果是不可靠的,这里建议读三次,当后面两次结果相同时认为结果可靠。

通过DP,AP访问Memory

作为一个调试端口,读写Memory的功能是必不可少的,DAP形式的结构中,读写Memory可以直接通过MEM-AP实现。

MEM-AP定义如下图所示:

2241227428.png

图中红框中就是相应寄存器的定义,可以看到里面有地址和数据寄存器,我们就是通过对这两个寄存器的读写实现对memory的访问的。

下面就是一个Memory访问的例子

# 首先通过正常命令读memory, 实际这块memory都写了0xA5A5A5A5
J-Link>mem32 20000000 1                                         
20000000 = A5A5A5A5   
# 更改第一个值,方便后面验证读操作                                          
J-Link>w4 20000000 12345678                                     
Writing 12345678 -> 20000000                                    
J-Link>mem32 20000000 1                                         
20000000 = 12345678  
# 写DP,选AP[0] Bank[0]                                           
J-Link>writedp 2 0                                              
Writing DP register 2 = 0x00000000 (0 write repetitions needed) 
# 写AP[0] BANK[0] Address寄存器
J-Link>writeap 1 20000000                                       
Writing AP register 1 = 0x20000000 (0 write repetitions needed) 
# 读操作代表读memory, 可以看到结果已经变化
J-Link>readap 3                                                 
Reading AP register 3 = 0x12345678 (0 read repetitions needed)  
# 连续读操作的时候,地址会自动累加
J-Link>readap 3                                                 
Reading AP register 3 = 0xA5A5A5A5 (0 read repetitions needed)  
J-Link>readap 3                                                 
Reading AP register 3 = 0xA5A5A5A5 (0 read repetitions needed)
# 写操作时,我们再把地址改回来  
J-Link>writeap 1 20000000                                       
Writing AP register 1 = 0x20000000 (0 write repetitions needed) 
J-Link>readap 3                                                 
Reading AP register 3 = 0x12345678 (0 read repetitions needed)
# 写AP就代表写memory  
J-Link>writeap 3 87654321                                       
Writing AP register 3 = 0x87654321 (0 write repetitions needed) 
J-Link>readap 3                                                 
Reading AP register 3 = 0xA5A5A5A5 (0 read repetitions needed)  
J-Link>writeap 1 20000000                                       
Writing AP register 1 = 0x20000000 (0 write repetitions needed) 
J-Link>readap 3                                                 
Reading AP register 3 = 0x12345678 (0 read repetitions needed)  
J-Link>readap 3                                                 
Reading AP register 3 = 0x87654321 (0 read repetitions needed)  
# 可以看到Memory内容已经改变
J-Link>mem32 20000000 6                                         
20000000 = 12345678 87654321 A5A5A5A5 A5A5A5A5                  
20000010 = A5A5A5A5 A5A5A5A5                                    
J-Link>                                                         

最后的一点说明,Jlink Commander这个工具虽然支持了这些指令,但是如果使用这些命令过程中一旦出现错误,后续的命令都不能正常执行,只能通过USB命令或者connect命令重新连接之后才能正常工作。

参考资料


本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。

相关文章

发表新评论