前言
ARM的文档写的已经很好了,但是关于上电以后的第一时间应该怎么操作,依然写的不够清晰,导致我第一次用的时候还是费了一些周折。今天做一个详细的梳理,希望能够帮助到更多的朋友。BTW, ARM_Cortex-M0/3 DesignStart系列会持续更新,只是中间可能会穿插其他知识点。
1 SWD概述
SWD是Serial Wire Debug的简称,翻译成中文是”串行线调试”。
SWD是ARM目前支持的两种调试端口之一,另一个调试端口叫做JTAG Debug Port,也就是我们常用的J-link上面的调试端口(JTAG模式下)。基于ARM CoreSight调试构架,SWD可以通过传输数据包来读写芯片的寄存器。SWD是用于访问ARM调试接口的双线协议。它是ARM调试接口规范(ARM Debug Interface Architecture Specification)的一部分,是JTAG的替代品。SWD的物理层由两条线组成:
SWDIO: 双向数据线
SWCLK: host驱动的时钟线
下图是SWD的简易连接图:
外部设备(如调试探针)通过连接到SWDIO/SWCLK,可以直接访问串行线调试端口(SW-DP)。SW-DP可以访问一个或多个接入端口(AP),通过AP可以访问系统的其余部分寄存器。 Cortex M系列CPU的一个重要AP是AHB-AP,它是内部AHB总线上的主机。换句话说,AHB-AP可以访问内部核心的内存映射。
由于内部闪存、SRAM、调试组件和外围设备都是内存映射,因此AHB-AP可以控制整个设备,包括对其进行编程。整个SWD操作过程是分级进行的,时钟信号由SWCLK 管脚输入,数据信号从SWDIO管脚输入输出。首先Debugger对SW-DP进行操作,确定AP寄存器的参数,达到对Cortex Memory Map进行操作。
下面三幅截图是ARM Debug Interface的连接实现示意图。如下图所示,DAP由两部分组成:DP和MEM-AP (就是通常所说的AP)。MEM-AP是一个统称,APB/AHB/AXI-AP都统称为MEM-AP。
2 SWD协议
SWCLK是始终由host驱动的时钟信号。双方将推动SWDIO线路发送数据,SWDIO上的高值表示逻辑“1”,低值表示逻辑的“0”。协议规定当双方都将驱动SWDIO线时,规定了三个不同的阶段。每个事务都以host发送请求为开始;Target随后以应答回应;最后是数据传输阶段。
数据阶段由谁控制线路,取决于host发出的请求类型。如果host发出的是写请求,host将驱动SWDIO线。在读请求时,target将驱动SWDIO线,将数据从target传输到host。在所有阶段,数据都是首先传输LSB(最低有效位)。不管是target从SWDIO线上采样数据,还是驱动数据到SWDIO线上,都是在时钟SWCLK的上升沿进行。
2.1 SWD命令
2.1.1 host发出packet request(包请求)
外部的host debugger发送一个request(请求)给DP, DP就是这个请求的target
主机host发出的请求由 8bit 组成,具体如下图所示:
Bit0:Start,固定为 1。
Bit1:APnDP,0表示访问 DP 寄存器,1 表示访问 MEM-AP 寄存器。
Bit2:RnW,0表示写请求,1表示读请求。
Bit[4:3]:地址值A[3:2],存放DP/AP寄存器的地址。
Bit5:Parity,用于表示由{APnDP,RnW, A[2:3]}组成这四位数的奇偶总个数。
Bit6:Stop,固定为0。
Bit7:Park,固定为1。
2.1.2 target返回ACK
Target返回的ACK值由3bit组成
ACK[0:2]==001,表示FAULT
ACK[0:2]==010,表示WAIT
ACK[0:2]==100,表示OK response
2.1.3 数据位的传输
数据由32bit有效数据 + 1bit数据的奇偶校验位组成
2.2 DAP寄存器
SWD通信的时候主要涉及的寄存器就两个,一个DP(即Debug port),一个AP(即Access port)。如果要访问内核寄存器,访问顺序是DP ->AP -> Core Register。
DP寄存器
DP寄存器如下图,地址就是我们在包头中的ADDR[2:3],这两位指示的地址。具体请参考《ARM Debug Interface v5 Architecture Specification》。
下图是根据ADI5的说明,网友自己制作的表格,更容易理解。
IDCODE寄存器
IDCODE:识别码寄存器,用于识别SW-DP。IDCODE寄存器提供有关SW-DP的标识信息。在具有Cortex-M3或Cortex-M4内核的设备上,该寄存器应为0x2BA01477。对于具有CortexM0+内核的设备,该寄存器应该为0x0BC11477。
ABORT寄存器
ABORT:中止寄存器,强制AP事务中止。在通信中,若发生粘性错 误位置1(ack = 4)。将0b1写入ABORT中,会生成一个DAP中止使当前AP事务中止。
ABORT寄存器的主要用途是强制DAP中止,在SW-DP上,它还用于清除错误和粘性标志条件。以下是AP ABORT寄存器每一位的定义。
如果希望找到导致标志设置为1的原因。通常:
对于STICKYCMP或STICKYARR标志,必须找到访问的位置,才能将标志设置为1。
对于WDATAERR标志,必须重新发送损坏的数据。
对于STICKYORUN标志,必须找到导致溢出的DP或AP事务。然后必须从那一点开始重复交易。
CTRL/STAT寄存器
CTRL/STAT:控制状态寄存器,用于控制和获取有关DP的状态信息。
SELECT:AHB-AP选择寄存器,选择要访问的端口(AP)和AP中的bank地址和DP的bank地址。
RDBUFF寄存器
RDBUFF:读缓冲寄存器,在SW-DP上,RDBUFF显示了在上一次AP读取期间捕获的数据,允许重复返回值而不生成新的AP访问。
值的说明的是,总线复位之后,需要先进行JTAG和SWD的切换操作(发0x79E7或者0xE79E),然后必须先读下IDCODE,判断下MCU的类型,然后才能继续别的操作。
2.2.2 AP(MEM-AP)寄存器
MEM-AP为DAP提供对内存子系统的访问。由于内存、外围设备和调试组件都是内存映射的,因此MEM-AP可用于对Cortex M0/M3等进行编程和调试。
AP寄存器相比较于DP寄存器复杂了很多。具体请参考《ARM Debug Interface v5 Architecture Specification》。
下图是根据ADI5的说明,网友自己制作的表格,更容易理解。
CSW寄存器
CSW:控制/状态字寄存器,CSW配置和控制通过MEM-AP访问连接的内存系统。size:32bit-0b010;16bit-0b001
TAR寄存器
TAR寄存器: 传送地址寄存器,保存要访问的内存地址。通过CSW寄存器的AddrInc字段中设置b01,成功访问DRW时,TAR的内容可以自动递增。
DRW寄存器
DRW:数据读/写寄存器,用于写入或读取TAR中地址的数据。DRW将AP访问中传递的值直接映射到TAR中指定的地址中的一个或多个内存访问。
打算将值写入内存地址时,首先将地址写入TAR寄存器,然后将值写入DRW。打算要读取内存地址的数据时,首先将地址写入TAR,然后读取DRW中的值。
IDR:识别寄存器,IDR标识访问的端口。要读取此寄存器时,APBANKSEL字段应设置为0xF。然后可以使用地址0x0C读取IDR寄存器(ADDR[3:2]=b11)。对于具有Cortex-M3或Cortex-M4内核的设备,IDR寄存器应返回值0x24770011。在带有Cortex-M0+的设备上,它应该返回0x0477003。
2.3 Debug and system Registers(调试寄存器)
下面截图中的是Cortex M3调试寄存器。使用调试寄存器,我们可以将Core设置为暂停模式,并可以更改Core寄存器,如SP或PC。
2.3.1 DHCSR寄存器
DHCSR=Debug Halting Control and Status Register
DHCSR控制处理器的暂停、单步和重启等动作。当C_DEBUGEN设置为1时,将启用暂停调试。更详细的信息,请参考《ARM Cortex-Mx权威指南》
2.3.2 DCRSR
DCRSR register = Debug Core Register Selector Register,provides debug access to the ARM core registers, special-purpose registers, and Floating-point extension registers. A write to DCRSR specifies the resister to transfer.
2.3.3 DCRDR
Debug Core Register Data Register, DCRDR寄存器提供对ARM核心寄存器、专用寄存器和浮点扩展寄存器的调试访问。DCRDR是这些访问的数据寄存器。
2.3.4 DEMCR
Debug Exception and Monitor Control Register, DEMCR register manages vector catch
behavior and DebugMonitor handling when debugging. Bits [23:16] provide DebugMonitor exception control. Bits [15:0] provide Debug state, halting debug, control.
2.3.5 AIRCR
Application Interrupt and Reset Control Register,AIRCR寄存器设置或返回中断控制数据。
2.4 SWD协议操作时序
2.4.1 成功的写时序
如下图所示,值的注意的是,trn(调转周期),因为我们是单总线通信,一根线上既有写又有读,而这个trn就是发生在写读切换的时候的一个延时。
2.3.2 成功的读时序
2.3.3 响应WAIT的时序
2.3.4响应FAULT的时序
2.3.5 Protocol error response
2.3.6 Sticky overrun behavior
3 SWD操作步骤
3.1 SWD初始化步骤
a) 初始化IO口,SWCLK和SWIO设置为输出模式,保持SWDIO=1,保证Host连续发送至少50个“1”,使得Target进行Line Reset。
b) 发送JTAG to SWD命令,0x79E7两个byte,随后再发送一次line reset操作。
c) 至少发送两个Idle信号,保持SWDIO=0。
d) 读取芯片的IDCODE,从而完成SWD初始化操作。此时只能访问DP寄存器组,AP寄存器组还无法访问。注意,复位之后必须读一次IDCODE(位于DP寄存器中)。将地址设置为00b,读DP操作,就可以读到IDCODE了,根据内核型号不同,一般第一数字不一样,我的是0x0BB11477。
ARM spec中的内容如下图所示,更多请参考《ARM Debug Interface v5 Architecture Specification ADIv5.0 to ADIv5.2》。
3.2 通过DP操作AP寄存器
通过往DP中的CTRL/STAT寄存器写入0x50000000 (第28位和第30位),开启debug port及其时钟;
CSYSPWRUPREQ:System powerup request
CDBGPWRUPREQ:Debug powerup request
通过写DP 中的SELECT寄存器,确定APBANK.
比如,写入的数据为0x000000F0。意味着APSEL=0x00,表示AHB访问;APBANKSEL=0xF,表示选择当前AP的bank地址;DPBANKSEL=0x0,表示DP.CTRL
读取IDR寄存器(addr = 0xFC)的数据并验证
IDR也就是AP寄存器的IDCODE,IDR寄存器是只读寄存器,IDR寄存器存在于AP寄存器空间的偏移地址0xFC处,IDR值为零表示不存在AP。
读取IDR寄存器的数值时,需要读两次,第一次是dummy read,第二次读取到的才是正确的数据。(第二次也可以读RDBUFF寄存器)。
CSW寄存器写入0x00000002,data_size:32bit
Size:访问MEM-AP的数据类型的大小
其中:如果寄存器的数据是32bit,CSW.Size=’b010,如果编程写入的数据是16bit, CSW.Size=’b001。
3.3 读写操作
如果想要写入/读出芯片内部Flash/Sram/Register的数值,需要用到两个AP寄存器:TAR(Transfer Address Register)和DRW(Data Read/Write Register)。
上面截图中说的很清楚,TAR寄存器中的地址,不是AP内部的地址,而是与AP连接的芯片系统中的物理地址。
DRW寄存器将AP访问直接映射到一个或多个芯片系统内部的memory访问。在memory访问完成之前,AP访问不会完成。
DRW寄存器是R/W(可读可写)的,它在MEM-AP寄存器空间中的偏移量是0x0C。
在写入模式下,DRW将当前传输的值写入TAR中指定的地址。
在读取模式下,DRW保存从TAR中指定的地址读取的当前传输值。
3.2.1 写操作
在TAR寄存器中写入要访问的内存地址数值 (比如addr = 0x08000000)
在DRW寄存器中写入要往芯片的memory中写入的数据 (比如data = 0x12345678)
3.2.2 读操作
在TAR寄存器中写入要访问的内存地址数值 (比如addr = 0x08000000)
从DRW寄存器中读取要访问的内存中存放的数据
从RDBUFF寄存器(DP)中读取数据,或者第二次从DRW寄存器(AP)中读取数据
3.4 读写时的注意事项:
由于读取AP寄存器的特殊性,当读取AP寄存器时,返回的数据是上一次传输的值。也就是说,有两种方式可以得到正确的 AP 寄存器的值。
a) 发送两次读DRW寄存器的操作;
b) 发送一次读DRW寄存器的操作(读取上一周期的数据),再发送一次读RDBUFF寄存器的操作(读取自己需要的数据)
总的来说,读写DP寄存器没有迟滞,不需要读写两次。读取AP寄存器,有迟滞,第一次读AP,是上一周期的值,第二次读AP才是想要的值。写AP寄存器,没有迟滞。如TAR,DRW只需要写一次即可写进去。
4 通过SWD对SRAM编程
本节将逐步演示如何通过SWD编程内部SRAM。
4.1 初始化
在使用SW-DP之前,必须执行初始化序列,以建立通信并使SW-DP进入已知状态。
a) 发送超过50个cycle的SWCLK/TCK,SWDIO/TMS=1。这确保SWD和JTAG都处于重置状态。
b) Send the 16-bit JTAG-to-SWD select sequence on SWDIOTMS
c) 发送超过50个cycle的SWCLK/TCK,SWDIO/TMS=1。这可以确保如果SWJ-DP已经处于SWD模式,则在发送选择序列之前,SWD会进行线路重置。
d) 执行READID以验证SW-DP是否已切换到SWD操作。
4.2 Halt CPU
在对内部SRAM或FLASH编程之前,应首先复位并Halt CPU。这主要是使CPU和外围设备进入已知状态,并禁止CPU在编写程序时意外运行部分代码。
具体操作步骤如下,
a) Write 0xA05F0001 to DHCSR, which halting debug enabled.
b) Write 0x01 to DEMCR. This enable Reset Vector Catch.
c) Write 0xFA050004 to AIRCR. This reset the core.
现在,内核将在第一条指令时暂停,所有外围设备和寄存器(调试寄存器除外)都将设置为复位值。
4.3 Memory and core register access.
4.3.1 Access memory
a) Set 32 bit width and auto increment in CSW register.
b) Write memory address in TAR register
c) Access DRW register for read/write data.
4.3.2 Access core registers
Read:
a) Set 32 bit width in CSW register
b) Write DCRSR address into TAR register.
c) Write core register index Rn into DRW register.
d) Write DCRDR address into TAR register.
e) Read core register value from DRW register.
Write:
a) Set 32 bit width in CSW register
b) Write DCRDR address into TAR register.
c) Write core value into DRW register.
d) Write DCRSR address into TAR register.
e) Write core register index Rn and REGWnR = 1 into DRW register.
5 SWD操作过程中的注意事项
5.1数据传输完成后,要额外多发8个cycle的时钟
如下面截图所示,主要是确保数据传输的稳定。
5.2 无效请求包的错误
如下图所示,一般来说出现这种状况时,都是HOST发送的请求包中奇偶校验出错,有可能是HOST本身出现了BUG,发出了一个错误的奇偶校验位,也可能是发送过程中传输错误,错误的可能性比较多,但最终都导致一点,主机在ACK phase发现target没有驱动SWDIO线,由于带有上拉电阻,所以主机最终会查看到接收的ACK为3’b111.一旦出现这种错误,主机只能line reset the DAP,然后读取DPIDR,建立同步机制后再进行通信。也就是从新开始。
5.3 写数据错误WDATAERR
如下图所示,DP会监测到这种状况,导致三个后果:
第一,写操作不会被触发,因为检测到校验错误;
第二,在接下来的两个周期置位错误标志WDATAERR;
第三,主机发起下一次传输时,会在其ACK phase接收到Fault ACK,即ACK=3’b001。
一旦某次ACK是ACK_FAULT,应立刻读取CTRL/STAT查看是何种原因导致了错误,如果查看到了是WDATAERR,则应向AP Abort寄存器指定位写1清楚错误状态,并再次发起前一次失败的写操作。
5.4 STIKYERR
出现的原因可能是:Debug power domain被power down了;或者是被调试资源出现了错误(例如内核错误);或者是对应的设计逻辑出现了问题;
一旦检测到这种错误,就要认真debug了,修改code,然后reconnect。
原作者:芯片验证日记 芯片验证那些事儿