Chinaunix首页 | 论坛 | 博客
  • 博客访问: 754648
  • 博文数量: 98
  • 博客积分: 4934
  • 博客等级: 上校
  • 技术积分: 1151
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-12 19:55
文章分类

全部博文(98)

文章存档

2014年(1)

2013年(2)

2012年(4)

2011年(25)

2010年(33)

2009年(33)

分类: 嵌入式

2009-09-12 22:33:56

探讨ARM调试技术
 
 
   真正要搞懂一种机制,就需要至少搞清楚一个实际的场景,也就是一个宏观的过程。而倘若一个过程是由程序驱动的,那么最好的途径就是阅读它的源代码了。(毛德操、胡希明)
 
    自从深入到bootloader的开发中,遇到很多困难。而这些困难,大都是因为基础技术储备不足导致的。有时候也会后悔,本科的时候没有努力学习。其实静下心来想一下,根本原因并不在于此。我想,主要是因为自己并没有确定的方向,只是满足于课程安排内容的掌握。没有目标,或者目标较低,自然前进的动力也就有限,很多的时间也就浪费掉了。而现在算是有了喜欢的方向,有了比较高的目标,所以觉得时间太少,需要学习的东西太多了。另外一个原因,嵌入式系统本来就是一个多学科交叉的方向,要求的基础比较高。没有2到3年的技术储备期,是很难真正成长起来的,在这个过程中,如果有人指导,或者经过大的项目锻炼,成长得会更快一些。但是,现在没有人指导,只能自己摸索,心理上也不要老是依赖外界,最主要的还是要靠自己的努力。经过这么长时间的努力,虽然基础还是比较薄弱,但是已经进步了很多了。后面的路会越走越坎坷,因为技术上越来越深入,一个人做终究还是吃力。不过不要紧,尽自己的最大努力,做到最大限度的提升。
 
    毛德操的书教导了我几个学习方法:一是从历史的角度去探讨技术;二是首先从理论上建立宏观的概念;三是如果要深入那就要进行场景分析。在实践中,这三个都是非常有帮助的。因为嵌入式设计的面太广,不可能所有都深入去学,要有自己的定位。在这个定位的基础上,与之相关的理论基础要利用前两个方法去探讨,开阔自己的技术视野,对定位的核心则要同时采用三个方法,尽可能的去深入分析,以求精通。我的定位还是基于ARM+Linux的系统软件开发,偏重于底层。bootloader算是第一个重点,基于Linux的驱动开发是第二个重点。这两个都是嵌入式系统核心MCU相关,打牢了基础之后可以考虑向上层发展。
 
    迟早需要跨越这个阶段,没什么捷径,努力攀登吧。
 
--------------------------------------
 
    在bootloader的深入学习中,深感调试的重要性。毛德操书中曾提过,在软件开发的“生命周期”中,程序调试(debug)以及调试手段的重要性是“怎么强调也不为过”的。诚然,尤其是在bootloader这样的底层软件开发中,调试能起到的作用就更为突出了。下面进行具体分析。
 
一、最简单的调试方法---状态灯
 
    一般地,在ARM开发板上总会留几个Led灯,在开发bootloader或者学习不带OS的程序编写时,就要充分利用这几个Led灯了。也就是说,把Led灯作为状态灯。如果有n个可以自由支配的Led灯,那么可以表达2^n个状态。比如,现在的EDUKIT-III实验箱上,有四个led,有GPF[7:4]来控制,低电平点亮,高电平熄灭,电路就不必画了,最简单的部分。在写led灯的驱动时,不妨采用查表法,把led的状态做成一个表,这样在表达的时候要方便的多。这部分实验在前面已经做过,ARM汇编和C版本都有,不再详述。下面主要谈几点方法:
 
    ·把流程按照功能分为几块,在每块的结束放一个状态灯。这样通过状态灯可以显示到了第几个功能块,一般要逐块测试,放状态灯后,加个死循环。
 
    ·利用条件判断语句来进行逻辑测试。比如有一个寄存器状态,假设为register1,正常应该是value1,那么就可以如下判断:
 
if (register1 == value1) {
        led_state1; /* 设置灯状态1 */
} else {
        led_state2; /* 设置灯状态2 */
}
while (1) {
        ;
}
 
 
    还有很多其他方法,不过核心还是靠自己的逻辑分析。
 
二、打印信息,辅助调试
 
    这种方法在PC的程序调试中也比较常用。比如熟悉的printf,在Linux内核的printk,不过这些都是打印到终端上。在嵌入式系统中则一般是没有终端的,特别是在bootloader阶段,就是“裸机”啊。不过可以借助于这种思想,利用串口来实现打印信息(当然,得有串口支持了。现在ARM一般都会有UART外设,而且很常用的手段就是把一个rs232串口作为debug口)。在bootloader阶段,可以利用状态灯法先简单的实现一个串口驱动程序,可以采用简单的实现方式,只实现查询发送就可以了。这样,就可以需要了解的信息,通过debug口来输出到相应的串口调试助手上。根据这些信息,来确定程序问题所在,从而尽快排除故障。
 
    这种方式就是串口作为底层来实现的printf,在ARM中应用比较广泛。在C语言中,可以通过一个调试开关开控制发行版/调试版,如下所示:
  #define DEBUG
//#undef DEBUG

#ifdef DEBUG
    uart_printf("debug info\n");
#endif
 
 
三 现代调试技术
 
    调试的基础是检测,而检测有“无损(non-intrusive)”和“无损(intrusive)”之分。理想的检测手段应该是不使被测对象的状态(包括时序)因此而造成任何改变,否则测到的数据就不准确了。可是,严格意义上的无损检测不可能实现,因此实际上是“测不准”的。人们所能做到的只是尽量减少对被测对象的状态改变,将误差减少到可以接受的程度。这还是比较容易理解的。不过原来看ARM资料的时候,调试手段一大堆,但是都没有深刻的印象,以致于以前还一直在怀疑,为什么下载的jflash-s3c2410,不能通过仿真器下载呢?现在想想,其实对于什么是调试器,什么是仿真器,调试的模型是什么都是非常模糊的,所以下决心首先从概念上理解调试模型。之后随着技术深入,可以逐步深入理解调试技术。
 
    现代调试技术大致可以归为指令级仿真调试和硬件仿真调试两种。
 
1 指令级仿真调试
 
    我想这个比较容易理解。指令集仿真调试属于纯软件仿真,比如ARM公司的ARMulator。因为有的时候嵌入式软件的开发需要在目标系统(硬件)并不存在的条件下进行,所以需要这种通过软件来模拟目标系统的CPU。现在有个开源项目skyeye,也是这样一个指令级仿真调试工具。这一系列的软件以数据结构来模拟目标机CPU中各个寄存器和其他资源,以及目标系统的有关资源(比如内存等),并且通过软件模拟,即逐条指令地解释执行目标机可执行映象中的程序。例如,“mov r1, #0”,就代表往寄存器r1的数据结构中写0,如此。模拟执行的速度当然慢一些,但是可以验证逻辑,在某些条件下是一种重要的手段。
 
2 硬件仿真调试
 
    这才是探讨的重头戏。在这里,就要从历史的角度去看一下了。
 
    从前的时候,元器件的面积和pcb的面积普遍较大,pcb板的层数较少,并且元器件只是安装于pcb的一面,也就是单面板。这样调试并不困难,加工工艺不复杂。pcb的加工制造者可以专门为具体的pcb配套制造用于测试的模板。模板上有许多触针,将pcb板和配套的测试模板叠在一起,模板上的触针正好与pcb“焊接面”许多监测点接触,从而可以监测pcb板上各点的逻辑电平及其变化。另一方面,pcb板的“元件面”也可以方便的将示波器或逻辑分析仪的探针连接到元器件的引线上。还有,当时的许多芯片,像cpu一类的大规模芯片,都不是直接焊接在pcb上,而是采用芯片插座。这样就可以作出特殊插头,冒充芯片插到其插座,而用外部的仪器来“仿真”这种芯片(通常是cpu)在整个目标系统中的作用和运行,就是“在线仿真ICE(In-Circuit Emulation)”。无论对于软件还是硬件,ICE在当时都是一种非常有效的开发/调试手段。我在本科时用的51单片机,但是就是采用ICE,记得有个仿真头,插到插座上。ICE还有一种方式,就是使用真实的cpu芯片,可以用特制的插头骑到芯片背上,以监测芯片的各条引线上的逻辑电平。
 
    可以随着pcb板和元器件的面积越来越小,pcb板层数也越来越多,特别是“表面贴焊”式芯片的出现打破了元件面和焊接面的划分,并且直接将芯片的引线焊接(而不是穿透),在pcb上,以前的那些手段都用不上了。
 
    在到后来,“集成电路级”向“集成系统级”发展,现在的芯片大多是SoC,原有的测试手段就更用不上了。这些芯片设计者都是重点考虑的问题。专业上成为DFT(design-for-test,可测试性设计),也就是在设计一款SoC,必须要考虑它的可测试性能。另外,为什么要在对资源要求比较苛刻的情况下,把调试电路集成到SoC中,看似矛盾,实际上,SoC集成调试电路已经成了普遍的共识:只有这样,才能减少调试成本,方便开发者进行调试,从而吸引开发者选择使用SoC。如果没有良好的调试手段,我想很少有开发者会选择使用的。好了,现在介绍调试检测技术。现在比较成熟的技术有如下四种:
 
    ·Ad-hoc test
    ·Scan-based test
    ·Build-in-self test
    ·Boundary-scan test
 
    因为不是专业研究这个方向,对这四个测试技术的区别也没大有必要深究。现在重点来看第四种技术,Bounary-scan test,即边界扫描技术。ARM普遍采用了边界扫描技术,协议标准为IEEE 1149.1,简介原来的文章也谈到过了,就不必多说了。关于JTAG,它的作用主要有如下两个方面:
 
    ·映象的下载。嵌入式系统软件的开发,在Host上完成目标映象之后,要在“裸机”上实现第一个程序的“自举”下载固化到flash中,就需要通过JTAG接口下载。当然,完成bootloader之后,也可以采用JTAG接口下载,另外也可以采用USB或者TFTP下载。后续的下载方式就多了。但是现在看来,“自举”下载方式还是比较单一的,ARM一般还得依赖于JTAG接口下载bootloader。
 
    ·软件(以及硬件)的调试。更重要的是,目标系统的调试也要在宿主机的控制/辅助下进行,而JTAG接口为宿主机与目标系统之间的通信、控制提供了重要的手段。【但是需要注意的是,这并不是绝对的。通信手段倒是还可以借助于网络实现,调试也可以通过gdb远程串口调试。这里的意思是,它是当前采用的调试的一种重要的手段。】
 
    现在可以分析基于JTAG技术的ARM Debug Architecture。下面给出一个结构图:
 
    ·Debug Host
    ·    Protocol converter
    ·        Debug Target
 
    也就是ARM Debug Architecture分为三层,这也可以认为是一个完整的调试系统。当然这是理论上的划分,在实际实现中也可以在形式上有所变通。下面先解释一下这三层结构。
 
    ·Debug Host
    Host computer running ARM or third party toolkit.
    这是调试系统的Host前端。也就是我们常见到的IDE的debugger,比如ARM提供的ADW(ARM Debugger for Windows,相应的unix版本为ADU,即ARM Debugger for Unix)。它所提供的功能是提供一个良好的图形交互界面,使用户可以方便的进行调试操作,也可以完成下载等。另外,它实际上完成调试命令的驱动部分。ARM制定了一个协议,就是ADP(Angel Debug Protocol),这是一个比较复杂的协议,分为三层,主要就是保证可靠稳定的与目标机进行通信。
 
    · Protocol converter
    for example, multi-ICE。
    这部分完成host端协议到jtag协议的转换。转换过程是双向的。这个在这里也不太容易说,可以通过后面仿真器来进行分析。
 
    ·debug target
    development system containing an ARM7TDMI processor
    也就是你的目标SoC。
 
    有了这个模型作为基础,就可以对现有的调试工具进行一个大体的分析了。在这里,仿真器又可以称为调试代理,它充当了protocol converter的角色。
 
    ·第一层次 简易JTAG小板
 
    市场上有不少提供简易JTAG小板,大多只是具备下载功能。其中,debug host可以是jflash-s3c2410这样的工具,它实际上就是用软件实现JTAG时序,完成“串并”和“并串”转换。即把目标JTAG向量中的每个字节都逐位地转换成JTAG接口各条引线上的串行的波形,或者反过来。而jtag小板,只是完成一个基本的电平转换电路,最差的只有几个电路,普通的就是一个74HC244做驱动。简单的说,就是:host(jflash-s3c2410等)--> 并口 --> jtag小板 --> SoC JTAG接口。这样完成了下载功能。如果在host端软件增强,可以完成一些调试功能。相对而言,这样技术是用纯软件去实现JTAG时序,效率比较低。市场上的wiggle、H-jtag、2410jtag等都是如此,只是在对应的引脚设置和host软件上有所不同。
 
    ·第二层次 cpld/fpga实现jtag状态机
 
    用软件模拟jtag时序效率低,那么可以通过cpld等可编程器件实现jtag时序,那么host的软件只要简单的写字节就可以了。这样,效率就高的多了。
 
    ·第三层次 mcu+cpld/fpga实现基于以太网/USB的仿真器
 
    其中,mcu用来实现tcp/ip协议,完成host调试协议的解析,cpld/fpga实现jtag状态机。这种组合方式的效率又远远高于第二层次。现在的realview就是采用这种方案,原有的ads系列则不提供更新了。
 
    我把使用的powerICE拆开看了一下,发现里面核心芯片就是cpld。由此可见,这个简化版的仿真器也只是第二层次的产品,硬件实现jtag时序,host IDE进行调试协议处理。原来的jflash-s3c2410不能够使用就很明显了,不同层次,也不会兼容。
 
    如果要深入研究,可以参考:
 
    ·IEEE 1149.1协议
    ·《嵌入式系统- -采用公开源代码和StrongARM/XScale处理器》
    ·OpenJTAG论坛
    ·ARM7TDMI Datasheet
 
---------------------------------------
 
几个名词:
 
spp: standard parallel protocol 标准并口协议
epp: enhanced parallel protocol 增强并口协议
dft: design for test  可测试性设计
tap: test access port 测试访问口
tdi: test data input  测试数据输入
tdo: test data output 测试数据输出
tms: test mode select 测试模式选择
tck: test clock  测试时钟
trst: test reset  测试复位
wdm: win32 driver model win32驱动模型
isp: in system programming 在系统编程
isc: in system config  在系统配置
adp: angel debug protocol 调试代理协议
rsp: remote serial protocol gdb的远程串行协议
rdi: remote debug interface ARM的远程调试接口

文章出处:
阅读(832) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~