一,前言
这几天尝试用硬件SPI模拟SWD时序,以求达到更快的烧录速度。在调试过程中有一些细节分享给大家,给各位一些参考。
二,几个关键问题
- SWD的时序和SPI有什么区别和联系?
答:SWD可以兼容SPI的mode3,即在第二个沿接收数据,在第一个沿发送数据;空闲时钟拉高。不同之处在于,SWD只对上升沿敏感,即数据的发送和接收都发生在在上升沿,所以我们用硬件SPI模拟SWD接收数据时,数据会在上升沿被芯片写入数据线,并在下一个上升沿前被我们读取。 - SWD的具体时序?
答:我把SWD分为四个阶段。- 第一,调试器给芯片发送请求,这个阶段的数据是8位,恰好可以使用SPI完成。
- 第二,调试器接收芯片发来的ACK,这一阶段的长度是不确定的,读操作和写操作有不一样的长度,可能是Trn+ACK(读)或Trn+ACK+Trn(写)。我把转换周期(Trn)也一并算到这个阶段。
- 第三,数据阶段,SWD的数据都是32bit的整数,很容易用SPI接收和发送。
- 第四,校验位阶段。读操作需要读取1位的奇偶校验值,写操作需要把数据的1位奇偶校验值发送给芯片。这一阶段也是不定长的,可能是CRC+Trn(读)或CRC(写)。
三,我的硬件
我是用的是GD32F407VET6,主频160MHz,SWD最高频率40MHz(SYSCLK/4)。
GD32F407VET6价格很便宜,美中不足的是没有片上的HS PHY。板上加了USB3300,和单片机共用一颗24MHz晶振。
这颗料主频比较低,APB的速度也和STM32F407一样慢。
- SPI主机还是从机?
为了更加自由的时钟频率,使SWD频率不受限于SPI自身的粗糙的对数分频器,我没有使用SPI主机而是使用SPI从机,把SPI从机的时钟线连接到定时器的PWM输出上,用定时器产生频率精确的PWM脉冲作为时钟。 - 怎样用固定8bit的SPI发送不定长的数据?
我使用另一个定时器作为主时钟的输出使能信号,这个辅助定时器通过内部的定时器互联信号与主定时器连在一起并同时启动。发送前计算好需要发出的PWM脉冲数量,并在最后一个PWM脉冲的上升沿后关闭PWM的输出使能信号。这样,硬件SPI的时钟周期数和调试器发出到芯片的时钟周期数就有了不同,由此实现了定长SPI发送不定长数据。
四,我的SWD实现方式
在实现SWD时序之前,我们需要记住几个基本逻辑:
- SWD的传输方向发生变化时,需要加入空周期等待。
- SWD传输前和传输后,SWD的SWDIO信号的主动权都要掌握在调试器的手里,即传输方向是调试器到芯片。
接下来就可以实现SWD时序了:
- 在请求阶段,这一阶段是纯发送,我使用8bit的SPI发送请求数据,这一步很简单。
- 在ACK阶段,需要使用变长SPI。读操作时,由于数据阶段和ACK阶段之间传输方向没有发生改变,我只需要接收长度为一个Trn和3bit的ACK的数据。写操作时,由于数据阶段和ACK阶段之间传输方向发生了改变,我需要接收长度为两个Trn和3bit的ACK的数据。一般情况下,Trn周期长度为1bit,所以这个阶段可能会产生4或5位的数据。在收到ACK后需要根据不同的ACK进行不同的操作。
- 在数据阶段,直接接收或发送32bit的数据,这一步可以使用DMA进一步提高SPI收发速度,同样很简单。
- 在CRC阶段,需要接收或发送奇偶校验值,这一步的数据长度也是可变的。读操作时,由于传输后传输方向要发生改变,我们需要接收1bit奇偶校验值和一个Trn。写操作时,传输方向不发生改变,我只需要发送1bit奇偶检验值。
SWD读取GD32F407的IDCODE的:
总结
- 在10MHz的SWCLK时钟下,使用pyocd向同型号单片机下载500K左右的固件,表显速度差不多有160K/s,20MHz下差不多220K/s。
- 使用GD32F407VET6@160MHz,我利用位带操作加速寄存器读写,两次SPI操作之间的间隔差不多是1.3us,缩短这一时间加速效果不明显,这并不是制约烧录速度的主要因素。
- 单片机主频是更重要的制约因素,想要实现更快的烧录速度,高主频单片机必不可少,使用主频360MHz、480MHz甚至更高的单片机可以大大加快烧录速度。在10MHz的SWCLK下,GD32F407计算CRC和处理数据的时间甚至能超过SPI数据发送的时间。
代码
https://gitee.com/telehua/dap_gd32f407
使用了CherryUSB并根据GD32进行了一些魔改。