CISC处理器调试系统的设计实现,详细软硬件架构、流程、代码
- 前言
随着嵌入式的不断发展,在各个领域的应用越来越广泛,由于嵌入式系统的专用性很强,所以针对不同的应用的嵌入式软件的开发在嵌入式系统开发中所占的比重越来越大。嵌入式系统的开发主要包括两个部分硬件设计和软件设计。软件设计的步骤主要有源码编辑、源码编译、嵌入式操作系统的配置、程序下载和调试。由于嵌入式系统的资源有限不可能在嵌入式系统的硬件平台上集成自身的软件开发环境,所以嵌入式的软件开发的通常采用交叉编译和调试的方式。调试是保证程序质量的重要手段之一,可以验证程序是否满足预期的要求和检查程序存在的bug。交叉的开发方式采用宿主机和目标机的结构。宿主机一般采用PC机,将嵌入式系统的软件开发环境配置在PC机上。宿主机和目标机之间通过串口、并口和网线等建立连接,不仅仅是物理连接还有逻辑连接。在交叉开发过程中宿主机和目标机通过这种连接进行交互,完成目标程序的编译下载和调试。
嵌入式系统的开发主要针对硬件系统中的微处理器的嵌入式软件的开发,嵌入式系统的软件调试也是针对微处理器的调试。现在微电子技术飞速发展,处理器生产工艺也不断提高,微处理器芯片的集成度更高、运行频率更高,所以对调试的要求更高。单单采用PC机上的软件调试器和软件调试代理已经很难适应这些新的变化,将调试功能转移到硬件部分为嵌入式软件的开发提供更好的保障和支持,实现成本也比较低。微处理器芯片中集成支持调试的硬件逻辑已经逐渐成为趋势。
在板级的测试和调试领域,为了更好的支持对电路物理节点的访问,JTAG标准已经被很多芯片生产厂商所采用。JTAG标准最早的提出是为了检测印刷电路板的元件焊接问题,通过在电路中引入边界扫描单元所组成的边界扫描链来完成对电路节点的访问。现在JTAG标准也被应用于嵌入式软件的调试过程中,用于与硬件调试逻辑交互提供通用的调试接口。
- 总体方案设计
图1 系统结构框图
整个调试系统分为四部分PC机上层控制台,USB-JTAG协议转换器,片上调试器OCD(On Chip Debugger)以及调试的目标处理器CPU。
- 系统硬件
- USB传输
USB是以串行传输的方式传输数据,首先使用FTDI公司的USB串并转换芯片FT245R将USB串行数据转换为并行数据,然后USB-JTAG协议转换器根据接收到的数据按照JTAG协议输出TAP信号。USB-JTAG协议转换器的结构如图1所示。
图 1 协议转换器的结构
USB-JTAG协议转换器与FT245R之间除了有8位的数据Data[7:0]的传输,还有四个握手信号,分别是nRXF、nTXE、nRD和WR,如图1所示。nRXF是读FIFO_TX允许信号,nTXE是写FIFO_RX允许信号,还有读FIFO_TX信号nRD和写FIFO_RX信号WR。当nRXF为低电平时说明FIFO_TX不为空,USB-JTAG协议转换器可以从中读出数据,此时USB-JTAG协议转换器可以通过发送nRD信号从FIFO_TX读取数据;同样当nTXE信号有效的时候表明FIFO_RX还有空间,此时USB-JTAG协议转换器可以使WR信号有效往FIFO_RX中写入数据。USB-JTAG协议转换器对FIFO_TX和FIFO_RX的读写操作都是以字节为单位。
FT245R内部有两个FIFO分别为FIFO TX Buffer和FIFO RX Buffer,FIFO TX Buffer负责存放接收从PC机发送来的数据,FIFO RX Buffer负责接收从USB-JTAG协议转换器发送来的数据,为了区分称之为FIFO_TX和FIFO_RX。FT245R芯片的内部结构如图2所示。
图2 FT245R芯片的内部结构
- SPARTAN-3E开发板
采用xilinx的SPARTAN-3E开发板,将目标处理器与片上调试器的硬件逻辑还有协议转换器中的JTAG信号的生成功能都集成到该开发板的FPGA上。系统如图3所示。
图3
- 片上调试器结构
图4 片上调试器结构
TAP控制器掌管JTAG协议的状态机,控制OCD与USB-JTAG协议转换器之间的数据和指令的移入移出。TAP(Test Access Port)包含四个引脚用来控制指定的操作。这四个引脚分别为TMS,TCK,TDI,TDO。TAP控制器内部采用标准的JTAG协议状态机来控制TAP的数据通路,包括指令寄存器扫描链和数据寄存器扫描链的选择以及扫描链数据的传输。
寄存器包括边界扫描寄存器、BYPASS寄存器、断点寄存器和指令寄存器。边界扫描寄存器在此处是为读取处理器输入输出引脚预留的扩展寄存器;BYPASS寄存器是位宽为1的寄存器,当在多个器件或模块串联的时候用该寄存器来旁路当前器件或模块;指令寄存器主要用来存放对CPU进行调试的相应调试指令。
扫描链包括写PC扫描链和自定义数据扫描链。写PC扫描链是为了写入PC值而设计的扫描链,而通过自定义数据扫描链可以读出处理器内部的状态和数据。
处理器运行控制模块是片上调试器的核心部件,负责调试指令的译码以及读CPU寄存器、复位、断点设置检测和处理器运行控制,还有写CPU内部程序计数器PC等调试功能的实现。
- 处理器运行控制模块
处理器运行控制模块是片上调试器的核心部件,负责指令译码和根据当前指令和CPU的运行状态进行机器指令或者微指令的单步和断点的判定,并且根据判定结果控制CPU的运行。指令译码就是根据PC上层平台发送到指令寄存器中的指令来产生相应的控制信号,直接通过这些控制信号控制CPU的运行、停止和复位等。设计了9条指令,分别为:
1)SREAD:CPU停止,并且此时选通CPU内部扫描寄存器数据链读出CPU内部主要寄存器的值。
2)STEP:微指令的单步,CPU运行一条微指令然后停下来。
3)MSTP:机器指令的单步,CPU运行一条机器指令然后停下来。
4)BPSET:断点设置,该指令只负责产生断点设置所需要的相应控制信号,并不负责断点数据的写入。实际的断点数据是用户通过PC机的上层工具设置以后,再进行一个BPR的数据写入来完成的。
5)RUN:CPU运行指令。
6)BRUN:断点运行,在断点设置完成以后,发送该指令来使CPU运行。
7)RESET:复位指令,使CPU复位。
8)WPC:写PC指令,为用户提供对CPU内部程序计数器PC值进行写入的功能,以满足某些用户的特殊需求。
9)STOP:CPU停止指令。
上面所描述的9条指令, 其中BPSET和BPRUN指令是两条相互依赖的调试指令,两条指令联合完成断点调试功能。其他的指令都是一条指令完成相应的调试功能。
- 断点设置和检测
首先要通过外部来设置断点值,硬件部分要设计一个断点寄存器BPR用来存放用户设置的机器指令断点或者微指令断点值,该寄存器位宽为24,其中低18位为有效数据位,高6位作为预留的扩展位。通过边界扫描的方式将断点值串行的移入到断点寄存器BPR中。
BPR[17:16]两位为标志位用来进行断点类型的区分,当BPR[17:16]=00时表明要写入的是微指令断点,此时BPR[8:0]为要写入的微指令的断点值即断点微地址;当BPR[17:16]=01时表示要写入的是机器指令断点,此时BPR[15:0]为要写入的机器指令的断点值即内存机器指令的地址。
断点的检测首先根据标志位来区分断点类型,是微指令断点还是机器指令断点。当标志位为00时,将断点值与微指令地址进行比较,其值一致时产生断点触发信号;当标志位为01时,将断点值与PC寄存器中的机器指令地址进行比较,如果一致同样产生断点触发信号。
- PC值的写入
处理器调试的过程中,为了能够随时控制处理器所要执行的指令,需要设置PC(程序计数器)值即所要执行的指令的地址。采用边界扫描技术来实现该功能,设计一条扫描链通过该扫描链将要设置的PC值写入到PC寄存器中,扫描链的结构如图5所示。
图5 写PC扫描链
由于CISC处理器内部的寄存器在写入和读出时都有相应的使能信号,所以在写入PC值的时候需要使PC的使能信号PCce有效,而这些寄存器的使能信号的产生是由微指令译码得来的,如果要修改微指令会影响到其他寄存器的使能控制存在一定的风险。
为了解决这个问题可以有两种方法可供选择:
1、将uIR数据读出,PC写操作完成以后再写回
该方法是先将uIR内部的内容读出保存好,然后写入带有PC寄存器使能控制信号微指令,当PC值正确写入以后再将原来保存的uIR的值还原到uIR寄存器中。
2、引入伪微指令寄存器用来在写入PC值取代uIR输出微指令
该方法是在微指令寄存器uIR到微指令译码模块uIR_Decoder之间加入一个扫描寄存器BSC_uIR,称之为伪微指令寄存器,这个寄存器在保证不影响uIR的前提下产生PCce信号。当写入PC值时首先选中该扫描链,然后将要写入PC的数据移入BSC_PC_A中,PCce有效则将BSC_PC_A中数据更新到PC中,完成PC的写操作。
第一种方法操作起来比较繁琐并且对软件的设计要求也比较高,在PC机和硬件平台指令就会存在反复传送数据的局面增加了数据出错的几率。而第二种方法实施起来比较简单,并且只要将需要的微指令和要写入的PC值一起进行一次写操作就可以完成,还保证了uIR寄存器中原有的微指令保持不变。因此本文采用了第二种方法。
- 处理器内部状态读出
处理器内部寄存器反映处理器内部运行的状态和当前一些主要数据,在调试过程中必须能够将这些数据读回给用户。这样就需要在处理器内部构建一条自定义数据扫描链,当执行处理器内部数据读出调试功能时先选中该扫描链,然后将该扫描链内部数据读回到PC机上层控制台。处理器内部自定义数据扫描链的结构如图6所示。
图 6 自定义数据扫描链
构建数据扫描链可以考虑在在寄存器的输入或者输出线上加边界扫描单元的方法来捕获数据,而不直接对寄存器本身进行操作,这样就将片上调试器对CPU的侵入性降到最低,保证了CPU内部数据通路的独立性。
由于在处理器内部寄存器的值的更新都有严格的时序控制,所以将扫描寄存器加在寄存器的输入线和输出线上是有区别的。这些寄存器输入线上的数据要存入到寄存器中在下一个时钟周期输出才会生效,例如IR中指令输出到微控制部件去译码才会起作用。所以为了要了解当前正在起作用的这些寄存器的值,另外输入线上的值随时都可能变化,由于这两个原因在输入线上引入边界扫描寄存器才可以真实的反映处理器当前的状态和寄存器的值。
- 系统软件设计
调试系统不仅需要底层硬件的良好支持,还需要有一个便于用户使用的PC机上层的控制台。该控制台主要功能是发送数据到串并转换芯片FT245R,数据经过FT245R转换以后变为并行的8bit数据送入到USB-JTAG协议转换器,协议转换器将数据进行解析来相应的产生TAP信号。
PC机通过USB电缆与FT245R相连,所以PC机只要将数据发送到FT245R任务就完成了,其他的都由下面的硬件来实现。采用C++builer6.0为PC机控制台的开发平台,添加FT245R厂商提供的动态链接库,使用FT245R的厂商提供的API函数进行编程实现。
- 系统调试和测试
- 各调试功能的验证
- 测试程序
当OCD、USB-JTAG协议转换器和PC机控制台这三个片上调试系统的组成部分都设计好以后,下一步的工作就是来编写一段汇编程序作为目标测试程序,因为目标处理器是一款CSIC处理器,所以还必须设计好一段可用的微程序存放到处理器内部控制存储器中来完成整个处理器控制信号的产生。
由于处理器内存地址0000~002F作为堆栈使用,所以内存中存放的机器指令必须从地址0030开始存放,本文在xilinx的SPARTAN-3E开发板上实现处理器以及片上调试器,所以同时也将编写好的测试程序生成存储器初始化文件,然后采用xilinx的RAM核将编辑好的存储器文件初始化到该RAM核中。在处理器上电时的程序入口地址设置为测试程序的入口即可。
现在编写一段测试程序如下:
0030:0761 0100; MOV #100H,R1
0032:043A 0002; MOV R1,0002H
0034:2b68 0001; TEST #0001H, R0
0036:019a 000B; JZ 000BH
左边是在机器指令以及其在内存中的位置,右边是其对应的汇编程序。其中共有四条指令,包括对通用寄存器、内存等资源的访问,跳转指令是程序的跳跃执行,由于在此处关注的是处理器的内部状态,所采用的测试程序只要能够覆盖整个处理器的数据通路即可。
- 复位功能验证
复位功能是指当系统出现异常或者处于的状态用户不能确定时,可以使用该功能使处理器回到初始状态。由于处理器内部与处理器初始运行有关的寄存器初始值都是确定的值,所以要验证复位调试功能,只需将复位调试指令发送到OCD,然后读出处理器内部寄存器的值与寄存器的初始值是否相同来确定复位调试功能是否正确执行。
首先来看一下处理器内部一些寄存器的初始值,在处理器内部堆栈指针SP、程序计数器PC和中断允许寄存器MASK有特别的初始值,其他寄存器都为0值,SP初始值为0030H,PC值也为0030H,因为在处理器运行时SP从0030H往小地址减而PC值则是往大地址执行。而MASK初始值为5AA5,设置这样的初始值主要是为了容易检测数据在读出时是否出现错位的现象。
执行处理器复位调试功能,然后读出内部寄存器的值,看是否回到初始状态,PC机控制台显示出读出的寄存器值如图1,可以看出所有的寄存器都回到了初始状态,表示复位调试功能实现正确。
图 1 复位功能验证
- 微指令单步
微指令单步主要是在执行一条机器指令过程中,为了能更清楚详细的处理器内部具体操作涉及到具体的控制信号起作用以及某些寄存器的操作。本文的测试程序的第一条机器指0761 0100(MOV #100H,R1),是将立即数十六进制的100移动到通用寄存器R0中。根据第三章介绍的微程序的设计和微指令的转移方式,写出该机器指令所对应的微指令地址和微指令。
000:20080001;
001:00069002;
002:CC000003;
003:00000404;
004:00000A08;
00B:2008000F;
00F:00069C10;
014:C0080015;
015:00061016;
016:D0000006;
在单步调试过程中将数据读回与以上的分析相比较,将指令0761 0100的单步调试过程每一个单步的数据都读回,其中将微指令单步中的主要界面截图如图2、图3、图4和图5。
图2第一条微指令
图3 第二条微指令
图4 读数据微指令
图5 结果写入R1
通过观察UAR、DR、IR、R1、PC和AR六个寄存器值的变化,可知系统正常按照预期运行,并且单步调试功能正确实现。
- 微指令断点
微指令断点调试时,首先通过断点设置将微指令断点值写入到断点寄存器。其中断点寄存器BPR中为显示正是通过PC机控制台写入到BPR寄存器中值,说明断点值已经正确的写入。然后运行处理器,由于当处理器运行停止时读取处理器内部寄存器状态,结果返回如图6。其中UAR显示现在处理器停止的微指令的位置,读出的其他寄存器值也是运行到当前微指令断点的值。
图6 微指令断点返回结果
- 连续运行和停止
连续运行是处理器运行在正常情况下,此时并不需要片上调试器的干涉,至于停止运行则是让处理器运行完当前机器指令则停止。由于在内存中存储的程序很短,而处理器的运行速度很快,所以在发送完连续运行调试指令,可以马上执行停止运行调试指令内存中的测试指令已执行完成了,所以在停止运行指令之后再通过PC机控制台来读取处理器内部寄存器的值就不会改变了。如7、8两张截图是停止运行指令之后两次读取数据的结果,可以看出结果不再变化,证明停止调试指令正确实现。
图7 第一次读结果
图8 第二次读结果
- 总结
在嵌入式系统的开发过程中,调试是不可或缺的一环。一个好的调试器可以大大的提高系统开发的效率,缩短推出产品的时间,同时也提高了系统的可靠性。
本文设计了一个基于JTAG协议的片上调试器,该片上调试器实现了微指令和机器指令的单步、断点以及读取处理器内部寄存器和写入PC、处理器复位等常用调试功能。构建扫描链时采用独立的扫描通路不对处理器本身寄存器做任何修改,这样在保证实现调试功能的同时还最大程度的保护了处理器数据通路的独立性,将对处理器的侵入性降到一个比较低的水平。
评论