引言
ARM嵌入式系统的大致开发流程是:先在PC机上编写程序源代码;由ARM的交叉编译器编译、链接生成一个目标文件;然后将目标文件转化成可以下载到目标机运行的二进制代码,在目标机上调试运行;最终将调试成功的二进制文件固化在目标机中。这一系列过程中,最关键的步骤就是在PC机上调试在目标机上运行的程序。这就需要采用远程调试的方式,即在PC机上运行调试器软件,被调试程序在目标机上运行。
本文描述了这样一个远程调试系统:利用USB批量传输的可靠性,将远程调试协议EmRDI的报文封装在USB数据包中;协议转换器EmJTAG再根据接收到的USB数据包中的协议信息,转换成适当的JTAG信号来控制和调试ARM核。协议转换器EmJTAG由ST公司的ST72651芯片外加一片接口驱动芯片74LVC244来实现。ST72651内部集成了USB模块和数据传输协处理器部件(DTC),通过对DTC的编程来产生控制ARM核的边界扫描序列,以产生相应的JTAG信号。
文章最终实现了PC机对ARM核的目标机的调试功能:PC机的调试器软件可以下载程序至目标机,实时控制目标机上代码的单步/全速运行或停止,以及设置断点和观察点、读写寄存器/存储器等。
1 系统组成及原理
如图1所示,整个调试系统包括三部分: PC机(调试器)、协议转换器(EmJTAG)以及目标板(基于ARM核)。调试的目的是实现在PC机上控制目标板单步或全速运行,并可以实时地察看或修改寄存器及存储器的值。
系统设计实现了一个远程调试协议EmRDI,它采用点对点的连接方式,以请求响应的方式进行交互。调试请求由PC机上的调试器软件发出,经由USB总线传输至协议转换器EmJTAG;EmJTAG解读该协议请求,在执行相应的操作之后,以USB数据包的形式发送回PC机(即响应过程)。
EmRDI协议定义了如下一些调试请求: 读/写核寄存器、读/写存储器、设置/清除硬件断点、设置/清除软件断点、设置/清除数据观察点、复位、停止运行、全速/单步运行和返回处理器状态/设备标识符。经验证,这些请求的组合,可以实现PC机对目标机上ARM核的调试和控制功能。
EmRDI协议请求格式(16字节)如下:
EmRDI协议响应格式(8字节)如下:
ARM核的芯片基本上都遵循JTAG规范,并带有边界扫描功能,此类芯片内集成了一个JTAG电路结构。JTAG电路由TAP(测试访问端口)、TAP控制器、指令寄存器、测试数据寄存器组成。另外,ARM7TDMI核还包含了一个提供在线调试支持的EmbeddedICE宏单元。
图1 系统框图
EmJTAG和PC机通过USB总线连接,与目标板则遵循JTAG调试接口标准。JTAG规范中,使用称为“边界扫描单元”的基本结构。边界扫描单元将JTAG电路与处理器逻辑电路联系起来,通过它可以将一个输入信号加载到处理器逻辑中而不影响逻辑的其他电路,也可以将处理器逻辑的输出信号采样到边界扫描单元中。若干个边界扫描单元构成边界扫描寄存器(BSR),也称为“边界扫描链”。边界扫描寄存器的数据通过JTAG接口的TDI引脚和TDO引脚串行移进移出。
PC机上需要实现:EmJTAG的USB驱动程序和调试器软件。
EmJTAG上需要实现:USB固件程序和用于产生JTAG信号的边界扫描程序。
2 硬件设计和实现
系统的硬件部分主要是指协议转换器EmJTAG的实现。协议转换器的功能,就是接收PC机上调试器软件发送的远程调试协议信息,将它转换成JTAG时序信号发送到目标板,然后把目标板的反馈信息或者数据返回给PC机的调试器软件。
EmJTAG使用意法半导体(ST)公司的ST72651芯片,原理框图如图2所示。它集成了1个全速(12 Mbps)USB接口模块,1个时钟频率为24 MHz的数据传输协处理器(DTC),32 KB程序存储空间,5 KB RAM,硬件I2C和SPI接口,多达47个可编程I/O口;工作电压为2.7~5.5 V,由USB直接供电。
图2 EmJTAG原理框图
与PC机通信方面,USB接口部分只需要将DP脚上拉一个电阻,使PC机识别到USB设备。DTC模块产生4个时钟信号作为JTAG时钟信号(TDI、TDO、TMS和TCK),由I/O口引出,外部再加一个接口驱动芯片74LVC244,将这4个信号连接到标准的JTAG接口上。TAP控制器复位信号nTRST和系统复位信号nSRST通过I/O逻辑直接控制。
EmJTAG采用USB供电方式,同时在TCK引脚上连接一个指示灯BUSY,显示系统是否处于忙状态。接口驱动芯片74LVC244由内部集成的电压校准器产生3.3 V来提供电压。
3 软件设计和实现
软件部分主要包括: PC机上的调试器软件,EmJTAG协议转换器的PC端驱动程序以及EmJTAG中的固件程序。
3.1 PC机上的调试器软件
采用VC++ 6.0 MFC。其主要功能是与EmJTAG的PC端驱动程序通信,将调试命令通过USB总线发送至EmJTAG。
3.2 EmJTAG的PC端驱动程序
使用Driverstudio3.1开发Windows下的USB驱动程序,使EmJTAG和PC机的应用程序可以进行数据交互。
3.3 EmJTAG中的固件程序
固件程序使用意法半导体(ST)开发工具STVD7 for InDART STX进行开发。这一部分也是调试系统最重要的部分,即如何产生JTAG时序信号控制ARM核。EmJTAG中的固件分成以下多个模块实现。
3.3.1 DTC软件插件程序模块
DTC功能框图如图3所示。DTC是ST72651内部集成的数据传输协处理器,也是一个通用的串行/并行通信接口。通过改变其内运行的插件程序,DTC可以方便地完成不同的高速数据传输操作。插件程序(最大为256字节)开始时存放在ST72651的Flash中,芯片运行的程序会将插件程序加载到DTC SW RAM中,然后控制DTC运行这段插件程序。ST7核也可以读写DTC的数据传输缓冲区,因此需要有一个仲裁电路用于防止ST7核和DTC访问该缓冲区时发生冲突。DTC有一套简单的指令集。ST7核通过DTC控制寄存器DTCCR控制DTC的操作,通过DTC状态寄存器DTCSR查询DTC的状态,通过DTC指针寄存器DTCPR改变DTC的操作指针。
图3 DTC功能框图
DTC有4种操作状态: 空闲状态(IDLE),加载插件程序状态(RAMLOAD),运行状态(RUNNING)和改变操作指针状态(POINTERCHANGE)。通过改变DTCCR寄存器的RUN、INIT和LOAD位可以实现4种状态间的转换。
将插件程序加载到DTC SW RAM中的步骤如下:
◆ 清除DTCCR寄存器的RUN位,使DTC进入IDLE状态,停止DTC;
◆ 设置DTCCR寄存器的LOAD位,使DTC进入RAMLOAD状态,以允许写DTC RAM;
◆ 将插件程序复制到DTC SW RAM中;
◆ 清除DTCCR寄存器的LOAD位,使DTC返回到IDLE状态,恢复DTC RAM写保护。
使DTC运行其RAM(即DTC SW RAM)中的插件程序的步骤如下:
◆ 清除DTCCR寄存器的RUN位,使DTC进入IDLE状态,停止DTC;
◆ 将插件程序的起始地址填入DTCPR寄存器中;
◆ 设置DTCCR寄存器的INIT位,使DTC进入POINTERCHANGE状态,将DTCPR寄存器中的值复制到DTC中;
◆ 清除DTCCR寄存器的INIT位,使DTC返回到IDLE状态;
◆ 设置DTCCR寄存器的RUN位,使DTC进入RUNNING状态,开始运行。
DTC运行的插件程序实现的功能是: 将数据传输缓冲区内的数据快速移出I/O口,或者从I/O快速移入数据到数据传输缓冲区中。DTC操作的I/O口作为4个JTAG信号: TCK、TMS、TDI和TDO。TCK作为移入/移出数据的触发时钟;TDI作为移出数据出口;TDO作为移入数据进口;TMS作为输入信号,与TCK一起决定TAP控制器状态的转移过程。插件程序用DTC汇编指令编写,然后通过ST公司提供的一个简单汇编器将汇编源代码转化成DTC机器指令,烧写到ST72651的Flash存储器中。
3.3.2 扫描链操作模块
扫描链操作模块调用DTC软件插件程序,完成初始化TAP控制器、扫描链1操作和扫描链2操作。
(1) 初始化TAP控制器
TAP控制器的基本功能是产生时钟信号和控制信号。它包括一个带有16个状态的有限状态机: Test-Logic-Reset状态、Run-Test/Idle状态、Select-DR-Scan状态、Select-IR-Scan状态、Capture-DR状态、Shift-DR状态、Exit1-DR状态、Pause-DR状态、Exit2-DR状态、Update-DR状态、Capture-IR状态、Shift-IR状态、Exit1-IR状态、Pause-IR状态、Exit2-IR状态和Update-IR状态。
初始化TAP控制器的目的是使TAP控制器进入Select-DR-Scan状态。无论当前TAP控制器处于什么状态,只要在TMS为1时产生5个TCK信号,TAP控制器就进入Test-Logic-Reset状态。因此,使TAP控制器进入Select-DR-Scan状态的步骤为: 首先使TMS为1,产生5个TCK信号,使TAP控制器进入Test-Logic-Reset状态;然后使TMS为0,产生1个TCK信号,使TAP控制器进入Run-Test/Idle状态;最后使TMS为1,产生1个TCK信号,使TAP控制器进入Select-DR-Scan状态。
(2) 扫描链1操作
扫描链1是JTAG电路中的一种测试数据寄存器,主要用于内核测试和调试过程。
扫描链1有33位,按扫描先后顺序依次为: BREAKPT位(输入),D31~D0(输入/输出)。扫描链1操作的目的是移入BREAKPT位,同时向/从数据总线移入/移出 32位数据。操作步骤如下:
◆ 初始化TAP控制器,使TAP控制器进入Select-D-RScan状态;
◆ 使TMS为1,产生1个TCK信号,使TAP控制器进入Select-IR-Scan状态;
◆ 使TMS为0,产生2个TCK信号,使TAP控制器进入Shift-IR状态;
◆ 往TAP控制器的指令寄存器移入JTAG指令SCAN_N(b0010);
◆ 使TAP控制器退出Shift-IR状态,进入ShiftDR状态;
◆ 往扫描链选择寄存器移入b0001,选择扫描链1;
◆ 使TAP控制器退出Shift-DR状态,进入Shift-IR状态;
◆ 往TAP控制器的指令寄存器移入JTAG指令INTEST(b1100);
◆ 使TAP控制器退出Shift-IR状态,进入Shift-DR状态;
◆ 移入BREAKPT位,移入数据D31~D0(或移出数据D31~D0);
◆ 使TAP控制器退出Shift-DR状态,进入Select-DR-Scan状态。
(3) 扫描链2操作
扫描链2也是一种测试数据寄存器,用于访问EmbeddedICE宏单元的寄存器。
扫描链2有38位,按扫描先后顺序依次为:EmbeddedICE寄存器的D0~D31,EmbeddedICE寄存器的A0~A4,读/写位。扫描链2操作的目的是读写EmbeddedICE宏单元的寄存器。操作步骤如下:
◆ 初始化TAP控制器,使TAP控制器进入Select-DR-Scan状态;
◆ 使TMS为1,产生1个TCK信号,使TAP控制器进入Select-IR-Scan状态;
◆ 使TMS为0,产生2个TCK信号,使TAP控制器进入Shift-IR状态;
◆ 往TAP控制器的指令寄存器移入JTAG指令SCAN_N(b0010);
◆ 使TAP控制器退出Shift-IR状态,进入Shift-DR状态;
◆ 往扫描链选择寄存器移入b0010,选择扫描链2;
◆ 使TAP控制器退出Shift-DR状态,进入Shift-IR状态;
◆ 往TAP控制器的指令寄存器移入JTAG指令INTEST(b1100);
◆ 使TAP控制器退出Shift-IR状态,进入Shift-DR状态;
◆ 移入欲写的数据D0~D31;
◆ 移入欲读写的EmbeddedICE寄存器的地址;
◆ 移入读/写位(读为0,写为1);
◆ 如果为读,则移出读到的EmbeddedICE寄存器的值D0~D31;
◆ 使TAP控制器退出Shift-DR状态,进入Select-DR-Scan状态。
3.3.3 EmbeddedICE寄存器操作模块
EmbeddedICE寄存器的读写通过对扫描链2操作实现。读时,将欲读的EmbeddedICE寄存器地址作为参数调用扫描链2操作函数;写时,将欲写的值和EmbeddedICE寄存器地址作为参数调用扫描链2操作函数。
3.3.4 高层调试命令模块
通过调用扫描链操作模块和EmbeddedICE寄存器操作模块,实现了读写核寄存器、读写存储器、断点及观察点操作、单步或全速运行程序等高级调试控制命令。这些命令供主程序状态机模块调用。
3.3.5 USB接口驱动模块
USB接口驱动模块主要包含5个函数: 函数InitUSB()用于初始化USB模块;函数USB_Polling()处理USB主机发来的USB标准设备请求,实现USB枚举阶段的传输过程,同时根据状态寄存器的标志位调用相应的端点处理函数;CTR(正确传输)中断服务程序用于处理端点上发生的各种中断;函数USB_RecvDataEP2(unsigned char *dp, uint8 len)和USB_SendDataEP2(unsigned char *dp, uint8 len)用于从端点2接收和发送USB包。
3.3.6 主程序状态机模块
主程序状态机模块循环调用USB接口驱动模块的USB_RecvDataEP2()函数,接收调试器发来的命令报文,根据命令报文的主命令号和次命令号调用高层调试命令模块的相应命令函数,并将命令函数返回的数据通过调用USB_SendDataEP2()函数返回给调试器,最终根据命令函数返回的状态通过调用USB_SendDataEP2()函数发确认报文给调试器。
结语
本文构造了一个支持ARM核的嵌入式系统调试工具。它首先定义了一个轻量级的远程调试协议EmRDI,然后使用ST72651芯片实现了协议转换器EmJTAG。目前该调试工具已经应用于实际嵌入式系统开发中,PC机上的调试器软件可以通过EmJTAG下载程序到基于ARM核的目标机上,并实现设置/清除断点、设置/清除观察点、全速/单步/停止运行程序等基本的调试功能。