系统架构设计笔记(60)—— 嵌入式开发平台与调试环境

嵌入式系统的应用支撑软件近年来发展迅速。通常,应用支撑软件包括窗口系统 、 数据库管理系统及 Java 虚拟机等几个部分。应用支撑软件的出现大大改变了应用软件的开发条件,同时也使得应用系统的功能不断增强。

1 嵌入式系统软件开发平台

嵌入式系统的软件开发方法采用的不是通用的开发方法,而是交叉式开发方法。

1.1 交叉平台开发环境

嵌入式系统的软件开发采用交叉平台开发方法( Cross Platform Development , CPD ),即软件在一个通用的平台上开发,而在另一个嵌入式目标平台上运行。这个用于开发嵌入式软件的通用平台通常叫作宿主机系统,被开发的嵌入式系统称为目标机系统。

而当软件执行环境和开发环境一致时的开发过程则称为本地开发( Native Development , ND )。图 1 是一个典型的交叉平台开发环境,通常包含三个高度集成的部分:
(1)运行在宿主机和目标机上的强有力的交叉开发工具和实用程序。
(2)运行在目标机上的高性能 、 可裁剪的实时操作系统。
(3)连接宿主机和目标机的多种通信方式,例如,以太网,串口线, ICE ( In Circuit Emulator ,在线仿真器)或 ROM 仿真器等。

1.2 交叉编译环境

宿主机提供的基本开发工具是交叉编译器 、 交叉链接器和源代码调试器。作为目标机的嵌入式系统则可能提供一个动态装载器 、 链接装载器 、 监视器和一个调试代理等。在目标机和宿主机之间有一组连接,通过这组连接程序代码映像从宿主机下载到目标机,这组连接同时也用来传输宿主机和目标机调试代理之间的信息。

嵌入式系统开发人员需要完全了解目标机系统如何在嵌入式系统上存储程序映像 、 可执行映像如何下载到内存及执行控制如何传给应用,以及运行期间如何和何时装载程序映像,如何交叉式地开发和调试应用系统。这些方面对代码如何开发 、 编译 、 链接等步骤都有影响。

图 2 是一个典型的交叉编译环境的示例,显示了如何使用开发工具处理各种输入文件,并产生最终目标文件的过程。其中,交叉编译器将用户编写的 C/C++/Java 源代码文件根据目标机的 CPU 类型生成包含二进制代码和程序数据的目标文件。在此过程中,交叉编译器会建立一个符号表,包含所产生的目标文件中指向映像地址的符号名,当建立重定位输出时,编译器为每个相关的符号产生地址。

通常,还可以使用归档工具将这些目标文件收集到一起形成一个库。

最后,链接器将这些目标文件作为输入来产生一个可执行的映像。一般,用户会先编辑一个链接脚本文件,用来指示链接器如何组合和重定位各个代码段以便生成最终文件。

此外,链接器还可以将多个目标文件组合成一个更大的重定位目标文件或一个共享目标文件。

目前,嵌入式系统中常用的目标文件格式是 COFF ( Common Object File Format ,公共对象文件格式)和 ELF ( Executable Linking Format ,可执行链接格式)。另外,一些系统还需要有一些专门的工具将上述格式转换成二进制代码格式才可使用。

通常,一个目标文件包含:
(1)关于目标文件的通用信息,如文件尺寸 、 启动地址 、 代码段和数据段等。
(2)机器架构特定的二进制指令和数据。
(3)符号表和重定位表。
(4)调试信息。

2 嵌入式开发调试

通用系统与嵌入式系统的软件调试过程存在着明显的差异。对于通用系统,调试工具与被调试的程序位于同一台计算机上,调试工具通过操作系统的调试接口来控制被调试的程序。但是在嵌入式系统中,由于资源的限制,不能直接在其上开发应用程序,调试过程通常也以交叉方式进行的。在实际开发实践中,经常采用的调试方法有直接测试法 、 调试监控法 、 在线仿真法 、 片上调试法及模拟器法等。

2.1 直接调试法

直接调试法就是将目标代码下载到目标机上,让其执行,通过观察指示灯来判断程序的运行状态。

在嵌入式系统发展的早期一般采用这种方式进行调试,其基本步骤是:
(1)在宿主机上编写程序。
(2)在宿主机上编译 、 链接生成目标机可执行程序代码。
(3)将可执行代码写入目标机的存储器中。
(4)在目标机运行程序代码。
(5)判断程序的运行情况,如有错误则纠正错误,重复以上步骤,直到正确为止。
(6)将可执行代码固化到目标机,开发完成。这种方法是最原始的调试方法,程序运行时产生的问题,只有通过检查源代码来解决,因而开发效率很低。

2.2 调试监控法

调试监控法也叫插桩法。目标机和宿主机一般通过串行口 、 并行口或以太网相连接,采用这种方法还需要在宿主机的调试器内和目标机的操作系统上分别启动一个功能模块,然后通过这两个功能模块的相互通信来实现对应用程序的调试。

在目标机上添加的模块叫作桩,也叫调试服务器或调试监控器,主要有两个作用:
其一,监视和控制被调试的程序;
其二,跟宿主机上调试程序通信,接收控制指令,返回结果等。

在进行调试的时候,宿主机上的调试器通过连接线路向调试监控器发送各种请求,实现目标机内存读 / 写和寄存器访问 、 程序下载 、 单步跟踪和设置断点等操作。来自宿主机的请求和目标机的响应都按照预定的通信协议进行交互。

使用插桩法作为调试手段时,开发应用程序的基本步骤是:
(1)在宿主机上编写程序的源代码。
(2)在宿主机编译 、 链接生成目标机可执行程序。
(3)将目标机可执行代码下载到目标机的存储器中。
(4)使用调试器进行调试。
(5)在调试器帮助下定位错误。
(6)在宿主机上修改源代码,纠正错误,重复上述步骤直到正确为止。
(7)将可执行代码固化到目标机上。相对于直接测试法,插桩法明显地提高了开发效率,降低了调试的难度,缩短了产品的开发周期,有效降低了开发成本。

但是插桩法仍有明显的缺点,主要体现在以下几个方面:
(1)调试监控器本身的开发是个技术难题。
(2)调试监控器在目标机中要占用一定的系统资源,如 CPU 时间 、 存储空间及串口或网络接口等外设资源。
(3)调试时,不能响应外部中断,对有时间特性的程序不适用。
(4)在调试过程中,被调试的程序实际上在调试监控器所提供的环境中运行,这个环境可能会与实际目标程序最终的运行环境有一定的差异,这种差异有可能导致调试通过的程序最后仍不能运行。

为了克服插桩法的缺点,出现了一种改良的方法,即 ROM 仿真器法。

ROM 仿真器可以被认为是一种用于替代目标机上 ROM 芯片的硬件设备, ROM 仿真器一端跟宿主机相连,一端通过 ROM 芯片的引脚插座与目标机相连。对于嵌入式处理器来说, ROM 仿真器像是一个只读存储器,而对于宿主机来说,像一个调试监控器。 ROM 仿真器的地址可以实时映射到目标机的 ROM 地址空间里,所以它可以仿真目标机的 ROM 。

ROM 仿真器在目标机和宿主机之间建立了一条高速信息通道,其典型的应用就是跟插桩法相结合,形成一种功能更强的调试方法。该方法具有如下优点:
(1)不必再开发调试监控器。
(2)由于是通过 ROM 仿真器上的串行口 、 并行口或网络接口与宿主机连接,所以不必占用目标机上的系统资源。
(3) ROM 仿真器代替了目标机上原来的 ROM ,所以不必占用目标机上的存储空间来保存调试监控器。
(4)另外,即使目标机本身没有 ROM ,调试依然可以进行,并且不需要使用专门工具向 ROM 写入程序和数据。

2.3 在线仿真法

ICE 是一种用于替代目标机上 CPU 的设备。对目标机来说,在线仿真器就相当于它的 CPU ,在线仿真器本身就是一个嵌入式系统,有自己的 CPU 、 内存和软件。

在线仿真器的 CPU 可以执行目标机的所有指令,但比一般的 CPU 有更多的引脚,能够将内部信号输出到被控制的目标机上,在线仿真器的存储器也被映射到用户的程序空间,因此,即使没有目标机,仅用在线仿真器也可以进行程序的调试。

在线仿真器和宿主机一般通过串行口 、 并行口或以太网相连接。在连接在线仿真器和目标系统时,用在线仿真器的 CPU 引出端口替代目标机的 CPU 。在用在线仿真器调试程序时,在宿主机运行一个调试器界面程序,该程序根据用户的操作指令控制目标机上的程序运行。

在线仿真器能实时地检查运行程序的处理器的状态,设置硬件断点和进行实时跟踪,所以提供了更强的调试功能。在线仿真器,支持多种事件的触发断点,这些事件包括内存读写 、 I/O读写及中断等。

在线仿真器的一个重要特性就是实时跟踪,在线仿真器上有大容量的存储器用来保存每个指令周期的信息,这个功能使用户可以知道事件发生的精确时序,特别适于调试实时应用 、 设备驱动程序和对硬件进行功能测试。但是,在线仿真器的价格一般都比较昂贵。

2.4 片上调试法

片上调试( In Circuit Debugger , ICD )是 CPU 芯片内部的一种用于支持调试的功能模块。按照实现的技术,片上调试可以分为仿调试监控器 、 后台调试模式( Background Debugging Mode , BDM ) 、 连接测试存取组( Joint Test Access Group , JTAG )和片上仿真( On Chip Emulation , OnCE )等几类。

仿调试监控器类的 ICD 芯片产品有 Motorola 的 CPU 16 、 CPU 32和 ColdFire 等系列, BDM 类的主要有 Motorola 的 MPC5xx 和 MPC8xx 系列等, OnCE 类的主要有 Motorola 的 DSP 芯片系列, JTAG 类的主要有PPC6xxx 、 PPC4xx 、 ARM7TDMI 、 ARM9TDMI及 Intel1960 等。

目前,使用较多的是采用 BDM 技术的 CPU 芯片。这种芯片的外面有跟调试相关的引脚,这些引脚在调试的时候被引出,形成一个与外部相连的调试接口,这种 CPU 具有调试模式和执行模式两种不同的运行模式。当满足了特定的触发条件时, CPU 进入调试模式,在调试模式下, CPU 不再从内存中读取指令,而是通过其调试端口读取指令,通过调试端口还可以控制 CPU 进入和退出调试模式。这样在宿主机上的调试器就可以通过调试端口直接向目标机发送要执行的指令,使调试器可以读 / 写目标机的内存和寄存器,控制目标程序的运行及完成各种复杂的调试功能。

该方法的主要优点是:

  1. 不占用目标机的通信端口等资源;
  2. 调试环境和最终的程序运行环境基本一致;
  3. 无须在目标机上增加任何功能模块即可进行;
  4. 支持软 、 硬断点;
  5. 支持跟踪功能,可以精确计量程序的执行时间;
  6. 支持时序逻辑分析等功能。

该方法的缺点是:

  1. 实时性不如在线仿真器法强;
  2. 使用范围受限,如果目标机不支持片上调试功能,则该方法不适用;实现技术多样,标准不完全统一,工具软件的开发和使用均不方便。

2.5 模拟器法

模拟器是运行于宿主机上的一个纯软件工具,它通过模拟目标机的指令系统或目标机操作系统的系统调用来达到在宿主机上运行和调试嵌入式应用程序的目的。

模拟器适合于调试非实时的应用程序,这类程序一般不与外部设备交互,实时性不强,程序的执行过程是时间封闭的,开发者可以直接在宿主机上验证程序的逻辑正确性。当确认无误后,将程序写入目标机上就可正确运行。

模拟器有两种主要类型:一类是指令级模拟器,在宿主机模拟目标机的指令系统;另一类是系统调用级模拟器,在宿主机上模拟目标操作系统的系统调用。

指令级的模拟器相当于宿主机上的一台虚拟目标机,该目标机的处理器种类可以与宿主机不同,如宿主机是英特尔的 x86 系列机,而虚拟机可以是 ARM 、 PowerPC 、 MIPS 等。比较高级的指令级模拟器还可以模拟目标机的外部设备,如键盘 、 串口 、 网络接口等。

系统调用级的模拟器相当于在宿主机上安装了目标机的操作系统,使得基于目标机的操作系统的应用程序可以在宿主机上运行。被模拟的目标机操作系统的类型可以跟宿主机的不同。两种类型的模拟器相比较,指令级模拟器所提供的运行环境与实际目标机更为接近。

使用模拟器的最大好处是在实际的目标机不存在的条件下就可以为其开发应用程序,并且在调试时利用宿主机的资源提供更详细的错误诊断信息,但模拟器有许多不足之处:
(1)模拟器环境和实际运行环境差别很大,无法保证在模拟条件下通过的应用程序也能在真实环境中正确运行。
(2)模拟器不能模拟所有的外部设备,嵌入式系统通常包含诸多外设,但模拟器只能模拟少数部分。
(3)模拟器的实时性差,对于实时类应用程序的调试结果可能不可靠。
(4)运行模拟器需要较高的宿主机配置。尽管模拟器有很多的不足之处,但在项目开发的早期阶段,其价值是不可估量的,尤其对那些实时性不强的应用,模拟器调试不需要特殊的硬件资源,是一种非常经济的方法。


你可能感兴趣的:(系统架构设计笔记(60)—— 嵌入式开发平台与调试环境)