也谈SWD接口协议分析

2019-12-10 18:32发布

本帖最后由 czdavid 于 2014-9-5 16:47 编辑

    这几日看到坛里有几个关于SWD协议相关的文章,自己也尝试了下,有点体会,也有些疑惑,写出来与大家分享和交流下。
    以下我的模拟SWD接口的板子简称为Host,目标MCU(即我要连接的板子)简称为Target。
SWD协议
         故名思议,串行总线调试接口。我们需要3根线与目标MCU相连,SWDIO,SWDCLK和GND。
        -SWDIO 为双向Data口,主机到目标的数据传送。
        -SWDCLK 为时钟口,主机驱动。
        -GND  GND脚。
        首先参考《ARM Debug Interface V5》(注:该文档已有更新版本,并且对V5版本做了勘误),对一些相关的协议相关说明有了较浅的认识。那接下来便找了个带SWD接口的板子,我这首先选了STM32F030,因为以后可以为生产线做离线编程器,当然随后也出现了一些问题,下文会说明。
        连上相关物理连线,开始折腾。
        看手册中有几个相对较重要的时序说明。
Trn-Trn:即Line turn-round,当总线上的数据传输方向发送改变时(比如由Host->Target变为Target->Host),需要插入Trn,Trn为一个CLK时序,关于对于Trn的理解自己也有些疑问。
Idle  cycles:在一个总线完成后,可以立即进入下一个总线操作或者是勒令总线进入Idle 状态,此时可以插入Idle cycle。在这我用连续送出8个’0b0’来使得总线进入Idle状态。
Parity :校验位,这个比较简单。分两个内容对命令头进行校验和对数据进行校验。命令头下文会说明。数据校验是对Data的0b0-0b31进行校验,如果‘1’的个数为奇数那校验位就为‘0b1’,如果‘0b1’的个数为偶数校验位就为‘0b0’。
        理解了这几个,我们接下来看读写命令。
        每个读写命令之前都会有个Host->Target的数据头。每个数据头为1Byte.
-Start     起始位,始终为1,这也是Target判断总线从空闲状态退出的条件。
-APnDP  选择要访问的是DP寄存器还是AP寄存器。
- Rnw    选择是读还是写。
-A[2:3]   DP或者AP寄存器的地址,注意它是低位在前。比如寄存器DP寄存器 Select 它的地址为0x08,那这儿的A为C(0b1000),显然A[2:3]就为01。
-Praity   校验位,它是APnDP、RnW和A[2:3]共4个bit的校验位。
-Stop    停止位。始终为0。
-Park     该位确切来说应该始终为1,ADI V5中描述此位由总线上拉,但由于总线的上拉能力不足,会导致Target识别不了这个1。该勘误在ADI V5.2中有说明。
STM32F030下载.JPG (314.2 KB, 下载次数: 0) 下载附件 2014-9-5 16:21 上传

先写到这,有空再继续分享。上传几份相关ARM的说明,欢迎拍砖。
ADIV5.pdf (1.67 MB, 下载次数: 386) 2014-9-5 16:24 上传 点击文件名下载附件


ADIV5.2.pdf (2.21 MB, 下载次数: 413) 2014-9-5 16:24 上传 点击文件名下载附件

coresight_components_trm.pdf (1.8 MB, 下载次数: 426) 2014-9-5 16:24 上传 点击文件名下载附件


友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
98条回答
huangqi412
2019-12-18 15:35
请教楼主, 周末拜读你的帖子,加上看STM32中文参考手册和CM3权威指南中文版,试了下,已经能用,但是有问题不明白。  
按你的方式  每个周期为先高后低能读DP_ID,但是写CTRL通不过, 示波器抓了JLINK波形也是跟你差不多。  后来参考DAP时序,每个周期为先低后高,全部通信都没问题,能下载程序了。


#if 0         //周期先高后低 读DP_ID没问题 其他有问题
        #define swd_clk_cyc()           {swd_clk_h();clk_delay();swd_clk_l();clk_delay();}                //发一个脉冲
        #define swd_w_h()       {swd_dio_h();swd_clk_cyc()}                                //写1
        #define swd_w_l()        {swd_dio_l();swd_clk_cyc()}                                //写0
        #define swd_w(x)        {if(x&bv(0)) swd_dio_h(); else swd_dio_l(); swd_clk_cyc()}        //写一位
        #define swd_r_bit(x)         {swd_clk_h();clk_delay();x=swd_dio_r();swd_clk_l();clk_delay();}        //读一位
#else        //周期先低后高 所有操作正常
        #define swd_clk_cyc()        {swd_clk_l();clk_delay();swd_clk_h();clk_delay();}                //发一个脉冲
        #define swd_w_h()       {swd_dio_h();swd_clk_cyc()}                                //写1
        #define swd_w_l()          {swd_dio_l();swd_clk_cyc()}                                        //写0
        #define swd_w(x)        {if(x&bv(0)) swd_dio_h(); else swd_dio_l(); swd_clk_cyc()}        //写一位
        #define swd_r_bit(x)         {swd_clk_l();clk_delay();x=swd_dio_r();swd_clk_h();clk_delay();}        //读一位
#endif

一周热门 更多>