我们正在编译一个嵌入式C++应用程序,它部署在电离辐射轰击的环境中的屏蔽设备中。我们正在为ARM使用GCC和交叉编译。在部署时,我们的应用程序会生成一些错误的数据,并且比我们希望的更频繁地崩溃。硬件是为这个环境而设计的,我们的应用程序已经在这个平台上运行了几年。

我们是否可以对代码进行更改,或对编译时进行改进,以识别/纠正由单个事件扰乱导致的软错误和内存损坏?是否有其他开发人员成功地减少了软错误对长时间运行的应用程序的有害影响?


当前回答

如何运行应用程序的许多实例。如果崩溃是由于随机的内存位变化造成的,那么你的一些应用程序实例很可能会通过并产生准确的结果。(对于有统计背景的人来说)很容易计算出在给定的比特翻转概率下需要多少个实例才能实现所希望的最小总体错误。

其他回答

如何运行应用程序的许多实例。如果崩溃是由于随机的内存位变化造成的,那么你的一些应用程序实例很可能会通过并产生准确的结果。(对于有统计背景的人来说)很容易计算出在给定的比特翻转概率下需要多少个实例才能实现所希望的最小总体错误。

使用循环调度程序。这使您能够增加定期维护时间,以检查关键数据的正确性。最常遇到的问题是堆栈损坏。如果您的软件是周期性的,您可以在周期之间重新初始化堆栈。不要为中断调用重用堆栈,请为每个重要的中断调用设置一个单独的堆栈。

与看门狗概念类似的是最后期限计时器。在调用函数之前启动硬件计时器。如果函数在截止时间计时器中断之前未返回,则重新加载堆栈并重试。如果在3/5次尝试后仍然失败,则需要从ROM重新加载。

将软件拆分为多个部分,并将这些部分隔离开来,以使用单独的内存区域和执行时间(尤其是在控制环境中)。示例:信号采集、预处理数据、主要算法和结果实现/传输。这意味着某一部分的失败不会导致整个程序的失败。因此,当我们修复信号采集时,其余任务将继续处理过时的数据。

一切都需要CRC。如果您在RAM中执行,甚至您的.txt也需要CRC。如果使用循环调度程序,请定期检查CRC。有些编译器(不是GCC)可以为每个部分生成CRC,有些处理器有专用硬件来进行CRC计算,但我想这将超出您的问题范围。检查CRC还会提示内存上的ECC控制器在出现问题之前修复单位错误。

使用看门狗进行启动,而不仅仅是一次操作。如果您的启动遇到问题,您需要硬件帮助。

您可能还对有关算法容错的丰富文献感兴趣。这包括旧的赋值:编写一个排序,当恒定数量的比较将失败时(或者,更糟糕的版本,当失败的比较的渐近数量为n次比较的log(n)时),正确地对其输入进行排序。

开始阅读黄和亚伯拉罕1984年的论文《矩阵运算的基于算法的容错》。他们的想法隐约类似于同态加密计算(但实际上并不相同,因为他们正在尝试在操作级别进行错误检测/纠正)。

该论文的一个较新的后代是Bosilca、Delmas、Dongarra和Langou的“基于算法的容错应用于高性能计算”。

首先,围绕失败设计应用程序。确保作为正常流程操作的一部分,它需要重置(取决于您的应用程序和软或硬故障类型)。这很难做到完美:需要某种程度的事务性的关键操作可能需要在组装级别进行检查和调整,以便关键点的中断不会导致不一致的外部命令。一旦检测到任何不可恢复的内存损坏或控制流偏差,就立即失败。如果可能,记录故障。

第二,如果可能,纠正腐败并继续下去。这意味着经常检查和修复常量表(如果可以的话,还包括程序代码);可能在每个主要操作之前或在定时中断上,并将变量存储在自动校正的结构中(同样在每个主要运算之前或在计时中断上,从3中获得多数票,如果是单个偏差,则进行校正)。如果可能,记录更正。

第三,测试失败。设置一个可重复的测试环境,随机翻转内存中的位。这将允许您复制损坏情况,并帮助围绕它们设计应用程序。

有一点似乎没有人提到。你说你在GCC中开发,并在ARM上交叉编译。你怎么知道你的代码中没有关于空闲RAM、整数大小、指针大小、执行某个操作需要多长时间、系统将持续运行多长时间等的假设?这是一个非常普遍的问题。

答案通常是自动单元测试。编写在开发系统上执行代码的测试线束,然后在目标系统上运行相同的测试线束。寻找差异!

还要检查嵌入式设备上的勘误表。您可能会发现“不要这样做,因为它会崩溃,所以启用编译器选项,编译器会解决它”。

简而言之,崩溃的最可能来源是代码中的错误。在你确定这不是事实之前,不要担心更深奥的故障模式。