玩转SWD:基于MCU实现媲美FPGA波形的SWD调试协议

描述

致谢:本文内容整理自社区开发者的两篇原创技术文章。感谢作者对先楫 SEI 外设的深入研究和无私分享,让我们得以一窥 HPM 单片机在高速 SWD 调试器领域的潜力。


 

原文章链接:

玩转先楫单片机之:使用HPM6P00系列的SEI外设模拟SWD协议

https://www.bilibili.com/opus/1213437323262296083


 

玩转先楫单片机之:继续优化SEI外设模拟的SWD协议 
 

https://www.bilibili.com/opus/1214894163302023192

一、引言

最近开始玩 HPM 单片机,原因是低价入手的几片 HPM6P41 一直吃灰。本着不能浪费的原则,研究了一下能用在什么地方。本来我对先楫的单片机不太感冒,但是在看了手册之后,对芯片里这些奇奇怪怪的外设产生了一些兴趣。

mcu
 

二、奇怪的好胜心

前段时间,群里开始了赛博斗蛐蛐。为了做出最强的 CMSIS-DAP,群里大佬们各显神通,甚至把 FPGA 都搬出来了。我用的是 STM32H750,靠的定时器和 SPI 模拟 SWD,每次通信都要重新配置几次,接收 ACK 后还要停下靠 CPU 做判断,速度始终快不起来。经过优化后,也才在 30M 时钟下跑出 1600K 的 RAM 读写速度。


 

三、强大的 SEI

先楫的 SEI 外设专为模拟其他通信协议而生,属于电机控制系统的一部分,主要用于模拟编码器通信协议,读写编码器位置信息,能够支持同步/异步通信,并且能够完成简单的数值匹配和程序跳转。

mcu

在先楫的手册中,对 SEI 的介绍比较少,只是简单描述了一些功能,然后给出了一些编码器协议的通讯配置实例。

mcu

SEI 有 17 个数据寄存器(DAT),每个寄存器都有位指针,可以以特定的初始比特位置、特定的字节顺序、特定的字长收发数据,非常灵活。其中:

  • DAT0:可以当垃圾桶,收到的任何数据都会被丢掉。
  • DAT1:命令寄存器,其中的值可以进行匹配跳转。
  • DAT2 ~ DAT17:普通数据寄存器,可以用于收发数据、计算 CRC、数据匹配。
     

手册中给出了 8 个指令,主要分为数据收发、跳转、等待、停止这几类,执行时按所在地址顺序依次执行。SEI 中有四个状态机,能够根据特定条件跳转,状态跳转信号可以作为触发信号输出,与其他外设精确对齐时间点。

数据收发指令可以收发 0~32 位任意长度的数据,而且可以将多次收发的数据按位拼接到一个数据寄存器中,而不会冲掉原来的数据,CRC 计算也可以连续进行。

SEI 的跳转匹配条件列表有 8 个,每个匹配条件都能设置掩码、比较上限、比较下限,如都不匹配则会跳转到最后一个。

SEI 内部的时钟信号和数据收发时机都是由一个小的计数器设置的,类似 PWM 的比较/匹配点。时钟的上升/下降沿、数据的收/发点都可以在分频值内任意设置,高速时或许可以对通信延迟进行补偿。SEI 还支持 DMA,这点没有仔细研究,不再多说。


 

四、从 SEI 到 SWD

熟知以上知识的话,并且恰巧熟悉 SWD 协议的话,就可以快乐地模拟 SWD 了。以读操作为例:

我们使用 DAT2 保存 request,DAT3 保存 data,DAT4 计算 request 的奇偶校验值,DAT5 计算 data 的奇偶校验值。SWD 是以 LSB 传输的,我们要把 DAT2~DAT5 数据格式设置为 lsb,DAT2 长度 4,DAT3 长度 32。SEI 的奇偶校验功能是异步通信使用的,同步通信时我们使用 CRC-1 计算奇偶校验值,结果长度为 1,初始值为 0,多项式为 1。

具体指令如下:

指令操作
INSTR0接收 1bit 高电平,trn 信号
INSTR1发送 1bit 高电平,start 信号
INSTR2发送 4bit DAT2,同时把 CRC 值保存到 DAT4
INSTR3发送 1bit 奇偶校验位
INSTR4发送 1bit 低电平,stop 信号
INSTR5发送 1bit 高电平,park 信号
INSTR6接收 1bit 忽略,trn 信号
INSTR7接收 3bit ACK,保存到 CMD 寄存器
INSTR8根据 CMD 寄存器匹配跳转
INSTR9接收 32bit data,计算 CRC 保存到 DAT5
INSTR10接收 1bit 奇偶校验位,与 DAT5 做比较
INSTR11接收 1bit,trn
INSTR12halt,停止
INSTR13接收 32bit data,DAT0 丢弃
INSTR14接收 2bit 奇偶校验 + trn,DAT0 丢弃
INSTR15halt,停止

指令从 INSTR1 开始执行,ACK 跳转列表如下:

条件CMD 值动作
OK0x1跳转到 INSTR9,正常结束
WAIT0x2跳转到 INSTR0,重新执行
FAULT0x4跳转到 INSTR0,重新执行
大保底0x7跳转到 INSTR15,结束

上述指令仅为简化版,WAIT 和 FAULT 的处理方式可能与原程序有所不同;另外 idle cycle 也没有考虑,实际指令更复杂。


 

实现效果

读 IDCODE:

mcu


 

WAIT 自动重试:

mcu


 

多次重试:

mcu


 

在 30M 时钟下跑出了约 1700KB 左右的速度,但还是不出意料地遇到了一些问题。在经过评论区疑似 HPM 人士的大佬指点后,我重新优化了程序,解决了大部分问题。

mcu


 

五、BUG 之读取 ACK 返回值

上回说到,SEI 程序中接收到了芯片回应的 ACK[0:3] 值,存储在 DAT_CMD 寄存器中,用于程序的条件跳转。但是在 SEI 的程序结束后,我们还是需要读取到 ACK 值返回给上层的函数作判断,否则在调试时会出现很多意外的错误。

上次发文时,我误以为 ACK 的值存储在 DAT_1 寄存器,读取到了错误的结果。虽然官方在手册把 DAT_CMD 排列在 DAT_1 的位置,它在 SEI 程序中的定义值也与 DAT_1 一致:

mcumcumcu

但是实际上,DAT_CMD 寄存器是每个控制器独有的,只有通过 CTRL[x].CMD 才能正确读取。


 

六、高频时序优化

SEI 外设的时钟频率与 AHB 同频,默认值也是最大值为 200MHz。

mcumcu


 

在官方库中,第一个跳变点(即下降沿)在周期的 1/4 处,第二个跳变点(即上升沿)在 3/4 处。

mcu


 

理论上,SWD 协议中,芯片端时序只对上升沿敏感,所有数据的收发都在上升沿完成,芯片会在绿色标记处发送数据,我们在蓝色或紫色处都可以接收数据。但在实际测试中(例如 30MHz 下),蓝色标记和紫色标记处都收不到数据,需要把采样点向后继续移动几个采样点才能接收到数据。

mcu

在高频下,时钟分频值很小,如果要提高延迟周期数,只能将下降沿(p0)和上升沿(p1)同时前移,在上升沿后留出足够空间。所以我们在 20M 以上频率时,将 AHB 超频到 300M(小超个 50%),并按照下图的方法计算采样点,成功将 SWD 跑到 30MHz。

mcu


 

最后,我们选取多点进行测速,使用群友的 openocd 测速脚本,多次结果取平均,得到如下的折线图:

mcu

相比于低频,高频下速度提升并不成比例,最终呈现的曲线略凸。


 

七、冲高实验(国铁既视感)

HPM 的片内 FLASH 为 QSPI 形式,虽然每个内核有着 32K + 32K 的巨大 cache,但运行速度仍不够极限。所以我们修改链接脚本,使用 ram.icf,由此测试最高速度(但实际上此时的变量默认存放在 AXI_RAM,虽然有 Cache 加持,应该还是比 DLM 稍慢一点)。

最终在 30MHz 下达到了:

写入速度:1820 KB/s读取速度:1650 KB/s
 

距离理论速度 30/45×32/8=2.6667 MB/s30/45×32/8=2.6667 MB/s 仍有较大差距。这其中还有 USB 应答延时的开销不可忽略——每次 SWD 读写事务之间,上位机(OpenOCD)通过 USB 下发命令、等待响应都需要额外的往返时间,这部分延迟在高速通信中占比相当可观,也是制约实际速度逼近理论极限的重要因素。

mcu


 

八、总结

SEI 外设功能强大,能够模拟各种同步/异步协议。虽然不是专门为高速通信设计的,但在我们的调教下,最终成功实现了 30MHz 频率下的通信。

我们仍然对 SEI 所知甚少,手册中还有很多寄存器没有介绍。HPM6P00 系列的 FGPIO 更加强大,600M 主频下 FGPIO 的翻转速度能够达到 300M。使用 FGPIO 模拟 SWD/JTAG 协议有望实现更快的速度以及SEI模拟JTAG时序,这将是下一步探索的方向。

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分