实时嵌入式系统软件调试问题分析

发布者:peon1989最新更新时间:2015-10-21 关键字:实时  嵌入式  系统  系统软件 手机看文章 扫描二维码
随时随地手机看文章
本文将讨论常见的调试问题以及预防和检查这些故障问题的一些方法。

从历史角度上来看,嵌入式应用代码的调试流程可以分为两类。第一类调试流程是回答 “我的代码现在执行到哪里?” 的问题。当开发商依靠打印语句或者LED的闪烁来指示应用程序执行到某个节点的调试方法时,往往就属于这种情形。如果开发工具支持这种调试方法,可以沿着应用应当程序应当执行的路径插入断点。第二类调试流程是帮助回答“我看到的这一数值是从哪里来的?”这一问题。在这种情况下,人们往往依靠寄存器显示窗口观察变量信息、处理器内存的内容。人们还可以尝试单步执行,并且观察所有这些数据窗口以了解某个寄存器状态何时出现错误,内存位置何时得到错误的数据,抑或指针何时出现了误用。

当开发商写完全部代码后,如果无需了解网络基础设施,也没有操作系统的任务调度需要考虑,那么就可以利用这些调试方法使一个应用程序运行起来。然而,现在的情况并非如此。嵌入式处理器以超过600 MHz的速度运行,并且拥有可支持Ethernet和USB等协议的嵌入式外设,它们支持功能齐备的操作系统,例如uClinux,而且这些操作系统所调度的各种应用程序是由数千行代码构成。使用打印语句和利用LED来调试是不现实的,因为现在常常有如此之多的功能在执行是不可能的,或者它们会影响标准 I/O口,从而造成处理器性能大幅度下降。

也可能发生这样的情况:处理器的工作速度是如此之快,以至于LED的亮灭速度会快到人眼无法察觉。另外现代的嵌入式系统通常支持断点的设定,但是伴随这些处理器所运行的代码数量,使得这种类型的断点调试难以驾驭。中断和多线程系统在代码的任何一点上设置一个断点,可能都无法指示系统的正确状态。由于断点设置在物理内存的某个地址上,索引不必了解线程的状态。如果使用寄存器显示方法,那么局部变量窗口和内存窗口都将有助于隔离出所载入的不恰当的量值,但是,由于这些是静态化的工具,不能给出有意义的运行中的调试信息,其适用性也常常很有限。

实时嵌入式系统软件最常见的调试问题可以大致划分为如下几类:

1. 同步问题

2. 内存和寄存器讹误(corruption)

3. 与中断相关的问题

4. 硬件配置问题

5. 异常情况

同步问题

在任何系统中,只要有多串序线程或者进程都在运行,而且是异步共享数据,则系统必然存在同步问题。对于共享数据的全部操作必须是原子化的,也就是说,只有在一个线程或者进程完成对数据的操作后,其它的线程才能对数据进行操作。

以图1为例,线程A和线程B对共享变量“counter”进行操作,A让counter 增加,而B则让counter减少。下方示出了线程A的counter++和线程B counter—的汇编代码。假设线程B的优先级要高于线程A,而线程A目前正在运行,则线程B将被阻止。

 

举例来说,假设初始的计数值是2,而线程A是执行线程。则线程A读入计数值,并送入一个寄存器,在使其增加一个增量后,再将其写回计数器变量上。

在可抢先的多线程系统中,高优先级的线程的执行可以抢先于低优先级的线程。例如,假定线程A执行Reg1 = Reg1+1指令后,一个事件唤醒线程B。此时,Reg1储存量值3。现在线程B被唤醒(正如蓝线所标示的那样),并读入计数器的量值2(它尚未被线程A 刷新)并将其量值减小到1。正如棕色的线所显示的那样,经过一段时间,线程A恢复运行,将Reg1写入计数器中,而该计数器的储存量值为3。在这个过程中,线程B的减量操作结果被丢弃。计数器存储的量值变为2,即线程A进行一次增量后,线程B又进行了一次减量操作。被窜改的链接表则是另一个例子。如果数据被一个线程和中断例程共享,则也会出现上面的问题,因为中断的执行与线程的执行之间是异步关系。

同步化方面的问题常常是很难进行调试的,因为它们取决于时序,是随着软件对数据的操作而随机出现的。幸运的是,这些问题可以通过恰当地保护任何共享数据来避免。大多数的实时操作系统可以提供同步化原语。开发商可以使用最适当的机制来保护共享数据,而不至于影响系统的性能。如果数据在多个线程之间共享,则开发商将有如下的选择:

a. 关闭调度器以便当前的线程永远不会被其它线程抢先。(无调度区)

b. 使用信号两(Semaphore)或者互斥信号量(Mutex)来保护共享数据。

c. 利用关键区域来进行保护,即屏蔽所有的中断。

 

开发商必须从性能出发来选择恰当的技术选项。关闭调度器,将防止任何一种环境的切换,从而使得现在的线程能继续执行,直到调度器重新打开为止。这种方法有一个负面的影响:它将阻止任何准备好运行的高优先级的线程。这一现象被称为优先级倒置。将中断关闭是最安全的方法,对于执行时间短的情形来说是理想选择。于是,最差情况的中断延迟就是所有未发生中断的持续时间的总和。在硬实时系统中,一般来说,一个中断功能可以被关闭的时间存在上限。

调试的一个小窍门就是,如果共享的数据被破坏,则编程者就应当首先检查出任何一种多个线程或者中断对共享数据同时进行的操作。如果线程和中断共享了数据,那么在线程代码中必须将中断关闭。如果数据在多个中断例程之间共享的话,则中断也应当被关闭,因为高优先级的中断可以抢先于低优先级的中断。

在多线程的系统中,高优先级的线程可以抢在低优先级的线程之前执行。因此,如果数据在多个线程间共享的话,则必须采用某种恰当的机制来保护被共享的数据。

另外一个同步化问题则与线程优先级的不恰当的分配有关。应当确保系统的初始化线程在引导时间内就启动,并在生成其它的优先级更高的线程之前,完成整个系统的初始化。例如,如果一个用于配置一个器件的低优先级现场被一个使用该设备的高优先级的线程抢先后,配置可能会完成,并可能会造成设备的故障。为了避免这种情形,开发商应当使用操作系统所支持的信号量或者其它同步化的原语。

内存和寄存器的数据讹误

大多数的嵌入式系统都采用了平面化的内存模式,也并没有内存管理单元(MMU),于是没有硬件支持的内存保护机制。即使采用能提供这种功能的处理器,也需要由开发商来实现对某些内存区域的保护。进程和线程将对其它进程和线程的内存空间有完全的访问权限。这可能会造成下面所描述的、各种类型的内存讹误问题。

堆栈溢出

运行时堆栈是在函数调用进程中所使用的一种暂存空间,用于存储局部变量。硬件寄存器指针(SP)将跟踪堆栈指针的地址。如果你在高级的语言中编程,如C语音,则编译器所生成的代码将使用与C语言运行时间模型相一致的堆栈。运行时间模式定义了变量是如何存储在堆栈中的以及编译器将如何使用堆栈。局部的变量被放置在当前的堆栈中。下面给出的例子描述了在堆栈上采用的某些关键性的内存。

 

当堆栈指针超出了其所指定的边界时,就会出现堆栈溢出。这将造成内存的讹误,并最终造成系统的失效。在上述的实例中,如果总的堆栈内存区不足以容纳所有的局部变量,堆栈溢出就会发生。

调试的一个技巧就是,如果你担心溢出,一个好的做法,就是将堆栈安排在内存边界上,这样,如果在调试过程中出现了溢出,则仿真器将触发一个硬件异常提示。

开发商可以采用的一个技巧是,如果你担心堆栈的溢出,你就应当考虑把它放在有效的内存的边界上。这样,当堆栈溢出时,设备将报告硬件异常,而不是造成其它内存空间的讹误。

在独立运行的应用中,运行时间堆栈可能就已经够用。然而,在使用任何一种实时操作系统时,每个线程和过程都将有自己的堆栈。考虑到性能方面的原因,大多数嵌入式实时操作系统的堆栈尺寸都是事先确定的,无法在运行中动态扩展。这意味着,如果针对特定的线程/进程所选用的堆栈尺寸不恰当的话,堆栈溢出就会发生。

如果应用大量使用局部变量(如阵列和大的结构),则将不得不按比例为其分配堆栈的空间。人们可以利用malloc() 来分配内存,或者将其设置为静态的全局变量,具体是何种方法,则取决于实际应用。

有些实时操作系统可能会提供调试功能,例如保护位,以形成对堆栈溢出的防护。这些操作系统要么记录关于堆栈溢出的错误信息,要么提交一个异常报告,以便动态地增加堆栈。最起码当前的大多数实时操作系统都能报告堆栈以及已经被线程和进程所采用的堆栈的情况。

在任何中断驱动的系统中,堆栈的分配方式都必须考虑到中断服务例程所采用的空间。如果中断例程的设计目标是使用当前的执行对象栈,则在这种情况下,每一个线程或进程所拥有的最小的堆栈尺寸都应大于或者等于执行对象所要求的堆栈尺寸加上所有中断例程累积起来所需要的最大的堆栈尺寸。

嵌入式系统开发商必须掌握各种应用链接库。例如,第三方的库可能会认定堆栈上为其提供了空间。

中断服务例程代码编写时所出的问题:

嵌入式系统中,一般情况下,出于性能方面的考虑,中断服务例程是以汇编形式编写的。中断本质上是异步的,在应用执行中的任何时刻都有可能出现。汇编层次上的中断例程最常见的问题,是寄存器的讹误。在中断服务例程中所采用的寄存器所存储的数据,在寄存器被使用之前都必须被保存,而在从中断服务例程返回之前,这些数据将被恢复。开发商必须了解状态寄存器的情况,而任何一种ALU的操作都会改变其状态。在这种情形中,ISR应该保存其状态并进行恢复,仿佛它是一个已被使用的寄存器一般。

如果中断例程是用C语言编写的,它们的开发也是为了使用当前的堆栈,则开发商就应该针对堆栈溢出情况进行防护,即每个线程都应该拥有足够多的堆栈,来满足中断或者嵌套的中断堆栈的要求。最好的做法,就是让中断例程的规模尽可能小,推迟处理过程,交给一个线程或者优先级较低的中断。在开发过程中,开发商可以在中断的开始和结束部分添加诊断功能,对基础的架构中的寄存器的状态进行比较。

中断嵌套可以让一个高优先级的中断抢先于低优先级的中断例程执行。开发商应该考虑到堆栈要求的峰值,并为其分配充足的空间(考虑最差的情况,即你的系统中的每一个中断都被一个优先级更高的中断所抢先)。

而操作内存映射寄存器(MMR)时,人们常常采用在线汇编以改善性能。例如,你在屏蔽中断时,可能希望直接设定中断屏蔽寄存器(IMASK)而不是执行RTOS所提供的应用软件编程接口(API)。例如原子增加或减少操作常常是用汇编语言编写的。在C函数中,这些宏汇编可能会被调用,在这种情况下,编译器可能不了解在宏汇编中所使用的寄存器。因此这会导致寄存器的讹误。有些编译器具有汇编的扩展版,可以将关于这些函数的更多的信息传递给编译器,例如已被使用的寄存器、代码在内存中的位置等等。这将使得编译器可以生成恰当的代码。

有时,某些函数是以汇编语言编写的,将被C函数所调用。如果汇编代码并未按照C函数运行时间调用规范来编写,即按照编译器所要求的那样进行,则会导致参数传递(argument passing)无效和讹误。例如,C函数运行时间模型可以规定前两个参量必须通过寄存器R0和R1来传递,则汇编的实现方式就必须按照这种语法来编写。在另一种情况下,运行时间模型可能需要存储堆栈上的函数的返回地址。如果汇编的实现方法并不符合运行时间模型,则它可能会搅乱某些寄存器,并带来系统的故障。如果开发商使用混合模式的语言来避免这种类型的问题的话,开发商就必须清楚运行时间模型。

编译器:

编译器的优化,即使实现了逻辑上的正确性,有时也仍然会造成故障。采用低水平的设备驱动器时,这一问题特别关键。重排指令是实现更高性能的常用方法,因为处理器常常支持单个周期内执行多条指令。因此,编译器将试图调度指令,使得所有的指令时间片都得到充分的利用,即使这意味着在寄存器使用前很久就载入数据,或者在数值被计算完毕后很久,也让内存保持载入的数据。请看附图,其中描述了这种内存的移动是如何发生的。

 

例如,假设一个设备必须在向其发任何指令前就完成初始化。编译器可能会移动指令位置,以便改善性能。这可能会造成设备的故障。如果你的设备驱动器调试后的版本是可行的,而采用经过优化的版本时会出现故障,那么你会想查看设备的初始化中是否有被移动的指令。你可能不得不采用恰当的编译器指南以便指导编译器不去对每条基本函数执行这样的优化,而不至于损失性能。

有时,将代码从一个架构移植到另一种架构上,也会带来某种数据类型上的问题。例如,一种架构内的整数可能是32 bit的,而其它的架构中可能是48 bit或者64 bit的。这可能会导致数据的失效或者被截断。

异常所带来的问题

如果异常是与程序的执行相同步的,则这往往是一种不当的操作的结果,例如零作为除数所造成的异常。某些异常则是架构所特有的。处理异常的最佳方法是采用缺省的异常处理器,并在出现异常时检查异常出现的环境。异常所处的环境背景是寄存器量值的集合,包括状态寄存器。大多数架构将拥有一个指令地址寄存器,用来保存造成问题的指令地址。在多数情况下,要知道一个异常是如何发生的并不难,但是,是何种指令路径可以隔离出这一失效,则是调试时棘手的地方。有些架构支持跟踪,即让你可以看到程序顺序执行的指令的历史。这将给出造成异常的指令顺序的某些细节信息。内存和寄存器讹误则是造成异常及程序逻辑错误的主要原因。通过细致检查造成异常的内存指向或者寄存器,将可以缩小问题的范围。

不能执行错误检验的代码会造成内存的讹误

由于性能方面的原因,开发商可能会放弃对错误的检查。跳过错误检查将让内存泄漏等事件无法为人所知,而最终导致内存讹误。例如,如果 malloc()出现故障,而由于返回的值并未得到检验,则开发商将开始覆盖在内存的地址0x0地址所写入的量值,在很多嵌入式系统中,这则是一个有效的内存区域。一个技巧是,让某些地址0x0处的内存控制,以便排查出任何一种潜在的讹误。某些处理器架构就容许应用监测数据总线的活动,从而能抓住相应事件。

探寻架构特有的功能:

大多数嵌入式处理器都支持某种层次上的调试功能。内置的跟踪单元就是一种得到硬件支持的跟踪机制。例如,ADI公司的Blackfin处理器系列就具有硬件跟踪单元,它可以跟踪至少16路的时序控制器的访问。当硬件跟踪缓冲器充满后,就会产生跟踪异常。使用这种跟踪单元后,人们可以构建出完整的执行路径。所提供的跟踪输出来自于一种可以免费提供的工具(http://www.blackfin.org/) ,它可以构造完整的执行路径。

 

观察点:

观察点可以让你监测特定的内存位置或者内存块区正在被更改时出现的情况。观察点可以监测内部的数据总线传送,如果在观察点寄存器中,发现任何匹配的对象,则让处理器暂停。如果一个特定内存位置不断出现讹误,则观察点就非常有用。对内存块区进行观察以查看是否有任何正在损毁存储器数据的恶意代码。

大多数当前的调试环境都容许对内存和寄存器的内容进行修改。有时,修改寄存器的内容,可以让我们洞察何处出现了故障。例如,通过更改程序计数器,你可以迫使程序在特定函数出现时恢复执行。必须谨慎地对恰当的寄存器设定恰当的量值,具体方式则取决于处理器C函数的运行时间模型。另外一个有用的寄存器是IMASK,如果你正在调试任何一种实时操作系统,则调试(分步深入时)进程中任何时刻都会出现中断。由于调试后的代码不一定处于关键区,你可能几乎时时刻刻都要访问中断的例程。你可能无法屏蔽中断,因为它们让你的系统完成设定,并运行起来。例如,任何系统中的定时器的中断都可能会被触发。更好的方法是对IMASK寄存器进行编辑,将所有的中断都屏蔽掉,直到你调试完代码为止。

结论:

总之,由于调试是开发过程的最后步骤,因此它将对产品上市时间造成直接的影响。调试本身也是难以调度的,因为所发现的问题在复杂性和可避免性方面都大相径庭,上面所讨论的是一些在嵌入式系统开发期间常见的问题。这些调试技巧和提示旨在着重强调节省时间,因此在开发复杂的嵌入式系统时,应用现代的开发工具和拥有丰富调试功能的处理器能够改善投资收益。

关键字:实时  嵌入式  系统  系统软件 引用地址:实时嵌入式系统软件调试问题分析

上一篇:基于模型设计的嵌入式测试系统开发
下一篇:瑞萨电子推出 RZ/G 系列 HMI 解决方案

推荐阅读最新更新时间:2024-05-03 00:03

如何用分体设备来搭建BMS模拟系统
如今做充电桩协议测试最普遍的方法还是采用实车进行验证性测试,然而这种方法会带来诸多弊端。这里将大家介绍一种由分体设备自行搭建的BMS模拟系统的方案。 1.1实车测试的弊端 不具备代表性:目前市面上主流的电动汽车之间的BMS通讯协议仍存在差异,很难找到一款具有代表性的车型。 仅能做功能性验证:使用实车测试只能验证充电桩是否能够实现充电,对于是否严格符合国标27930-2015规范尚不能得知。 放电时间久:使用实车在长期测试后会导致电动汽车电池充满,需要漫长的用电过程进行放电。 图 1 使用实车进行充电桩通讯协议验证 1.2BMS模拟系统搭建 为了避免实车测试所带来的上述问题,我们可自行搭建BMS模拟系统,系统由一下几类设备组
[汽车电子]
如何用分体设备来搭建BMS模拟<font color='red'>系统</font>
英飞凌完成收购氮化镓系统公司 (GaN Systems),成为领先的氮化镓龙头企业
【 2023 年 10 月 25 日 , 德国慕尼黑和加拿大渥太华 讯 】 英飞凌科技股份公司今日宣布完成收购氮化镓系统公司(GaN Systems,以下同)。这家总部位于加拿大渥太华的公司,为英飞凌带来了丰富的氮化镓 (GaN) 功率转换解决方案产品组合和领先的应用技术。已获得所有必要的监管部门审批,交易结束后,GaN Systems 已正式成为英飞凌的组成部分。 英飞凌科技首席执行官 Jochen Hanebeck 表示,“氮化镓技术为打造更加低碳节能的解决方案扫清了障碍,有助于推动低碳化进程。收购 GaN Systems 将显著推进公司的氮化镓技术路线图,并让我们同时拥有所有主要的功率半导体技术,进一步增强英飞
[半导体设计/制造]
英飞凌完成收购氮化镓<font color='red'>系统</font>公司 (GaN Systems),成为领先的氮化镓龙头企业
PLC在充磁机控制系统的设计
1 引言 随着电机、家用电子、计算机、通信等技术日新月异的更新和发展,永磁材料需要量越来越大性能越来越高。目前,永磁材料大多采用钕铁硼、铁氧体、铝镍钴、钐钴等,并具有矫顽力大、性能稳定等特点,这些材料经充磁电源的高压大电流向螺线管瞬间脉冲放电,使其磁化。生产中要求充磁电源高效、稳定、精度高,同时,在机测试充磁后永磁材料的磁通量。文中介绍了的充磁和测量为一体高效自动充磁机,使用plc实现系统控制,触摸屏作为参数调整、工作显示等。 2 电磁交换 充磁机根据电容储能脉冲放电产生强大磁场,对铁磁性物质进行磁化。在电磁交换前,电容储存的能量 (1) 式中uc为储存电容的端电压,c为储存电容的容量。改变电容的电压或容量,可
[工业控制]
基于网络的电能质量监测系统设计
  0 引 言   随着电力系统运行管理的系统化、智能化、自动化和网络化,对电网的远程实时监控和自动化调试是电力系统发展的必然趋势。近年来,随着人们对电力能源需求的不断增长,电力电子设备应用越来越广泛,大量的非线性负荷、冲击性负荷的投运,使公用电网中产生了大量的谐波干扰以及电压波形畸变、电压波动和三相不平衡等问题,电能质量不断恶化。为实现对电力系统实时的监控和准确的调度,全面掌握电网中电能质量状况并对电力参数进行快速准确的测试就变得十分重要。本文提出了一种基于网络的电能质量监测系统(以下简称“监测系统”),不但能够实现对现场数据的实时采集与分析处理,而且还能够通过网络进行远程监测与控制,有助于解决现场环境恶劣而难以在现场进行精确
[测试测量]
基于网络的电能质量监测<font color='red'>系统</font>设计
智能电视系统升级 无惧乐视“挑衅”
    传统彩电厂商已经感受到乐视和小米电视的存在,但市场并未形成实质压力。近日,海信电器总经理刘洪新在广州“代言”体验VIDAATV时透露,7 月17日将对VIDAA TV进行第一次全面在线升级,今后定期升级。他称,乐视尚未给消费者带来真正的价值。而创维集团CEO杨东文此前接受记者采访时亦认为,乐视等互联网电视商的威胁比想象中小。   智能电视系统在线升级   刘洪新称,5月VIDAATV上市,海信随即启动产品后续升级计划,研发团队收集了1800多条用户意见,解决了700多个问题,并完成10个重大修订。基于用户提出的意见和问题,7月17日将对VIDAATV进行第一次全面在线升级,今后定期升级。海信因此成为手机家宣布智能电
[家用电子]
Wind River与McAfee合作推出嵌入式及移动解决方案
全球嵌入式及移动应用软件领导厂商风河(Wind River)今日宣布与安全防护技术领先者McAfee公司达成一项战略合作协议,针对各类非PC设备,尤其是嵌入式及移动设备,共同开发、营销专属的安全防护解决方案并提供相关支持。Wind River 与McAfee将携手针对市场上与日俱增的网上设备,推出最完备的安全防护解决方案,将安全防护能力拓展到远超过PC的更大范围。。 随着网上设备相关应用日趋普遍,其数量也将随之骤增,数量和种类都远远超过PC。根据McAfee公司内部预估,到2020年,全球网上设备总数量将达到50亿台之多,而这一成长趋势所造就的市场大饼中最大的一块将被嵌入式及移动设备应用占领,包括工业控制、能源管理、汽车
[手机便携]
16A欧标充电枪电流转换系统会出现哪些故障?
随着电动汽车的普及,16A欧标充电枪作为充电基础设施的重要组成部分,起着至关重要的作用。然而,在实际使用过程中,由于各种原因,充电枪的电流转换系统可能会遇到各种故障。 充电速度降低或停止 当电动汽车接入充电枪后,如果充电速度明显降低或者完全停止,可能是电流转换系统出现故障的迹象。 充电枪显示异常信息 充电枪屏幕上可能会显示各种故障代码或警告信息,例如“电流转换器故障”、“输出电压异常”等,这些信息是识别故障的重要线索。 充电枪发出异常声响 有时候,故障的充电枪可能会发出异常的声音,这可能与电流转换系统有关。 检查充电枪连接 首先,确认电动汽车与充电枪之间的连接是否牢固,检查充电线缆是否有明显的损伤或
[嵌入式]
16A欧标充电枪电流转换<font color='red'>系统</font>会出现哪些故障?
AnalogicTech推出使更多便携系统获益的高电压缩放效率降压转换器
领先的移动消费电子设备的功率管理半导体供应商 AnalogicTech 日前推出了一款用于便携应用的电压缩放 (voltage-scaling) 降压转换器 AAT1142 ,它是第一款可通过一个双线 I 2 C 接口和 AnalogicTech 的专利单线简易串行控制 (S 2 Cwire) 数字接口两种方式,来提供动态管理的电压缩放降压转换器,可提供高达 800mA 输出电流。 目前市场上大部分中高端手机都带有了 I 2 C ,所以他们能够很容易地利用电压缩放的功率效率。然而,更多价格敏感的手机机型不能承受实现 I 2 C 所带来的硬件和固件费用。而通过 AAT1142 的单
[新品]
小广播
最新嵌入式文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved