我们正在编译一个嵌入式C++应用程序,它部署在电离辐射轰击的环境中的屏蔽设备中。我们正在为ARM使用GCC和交叉编译。在部署时,我们的应用程序会生成一些错误的数据,并且比我们希望的更频繁地崩溃。硬件是为这个环境而设计的,我们的应用程序已经在这个平台上运行了几年。
我们是否可以对代码进行更改,或对编译时进行改进,以识别/纠正由单个事件扰乱导致的软错误和内存损坏?是否有其他开发人员成功地减少了软错误对长时间运行的应用程序的有害影响?
我们正在编译一个嵌入式C++应用程序,它部署在电离辐射轰击的环境中的屏蔽设备中。我们正在为ARM使用GCC和交叉编译。在部署时,我们的应用程序会生成一些错误的数据,并且比我们希望的更频繁地崩溃。硬件是为这个环境而设计的,我们的应用程序已经在这个平台上运行了几年。
我们是否可以对代码进行更改,或对编译时进行改进,以识别/纠正由单个事件扰乱导致的软错误和内存损坏?是否有其他开发人员成功地减少了软错误对长时间运行的应用程序的有害影响?
当前回答
既然您专门要求软件解决方案,而且您使用的是C++,为什么不使用运算符重载来创建自己的安全数据类型呢?例如:
不要使用uint32_t(以及double、int64_t等),而是制作自己的SAFE_uint32-t,其中包含uint32/t的倍数(最小值为3)。重载您想要执行的所有操作(*+-/<<>>==!=等),并使重载的操作对每个内部值独立执行,即不要执行一次并复制结果。在之前和之后,检查所有内部值是否匹配。如果值不匹配,可以将错误的值更新为最常见的值。如果没有最常见的值,您可以安全地通知存在错误。
这样,即使ALU、寄存器、RAM或总线上发生损坏也无所谓,您仍然可以多次尝试并很好地捕获错误。然而,请注意,这只适用于您可以替换的变量-例如,堆栈指针仍然是易受影响的。
附带故事:我遇到了一个类似的问题,也是在一个旧的ARM芯片上。结果发现,这是一个使用旧版本GCC的工具链,与我们使用的特定芯片一起,在某些边缘情况下触发了一个错误,这会(有时)破坏传递到函数中的值。在将设备归咎于无线电活动之前,确保设备没有任何问题,是的,有时是编译器错误=)
其他回答
既然您专门要求软件解决方案,而且您使用的是C++,为什么不使用运算符重载来创建自己的安全数据类型呢?例如:
不要使用uint32_t(以及double、int64_t等),而是制作自己的SAFE_uint32-t,其中包含uint32/t的倍数(最小值为3)。重载您想要执行的所有操作(*+-/<<>>==!=等),并使重载的操作对每个内部值独立执行,即不要执行一次并复制结果。在之前和之后,检查所有内部值是否匹配。如果值不匹配,可以将错误的值更新为最常见的值。如果没有最常见的值,您可以安全地通知存在错误。
这样,即使ALU、寄存器、RAM或总线上发生损坏也无所谓,您仍然可以多次尝试并很好地捕获错误。然而,请注意,这只适用于您可以替换的变量-例如,堆栈指针仍然是易受影响的。
附带故事:我遇到了一个类似的问题,也是在一个旧的ARM芯片上。结果发现,这是一个使用旧版本GCC的工具链,与我们使用的特定芯片一起,在某些边缘情况下触发了一个错误,这会(有时)破坏传递到函数中的值。在将设备归咎于无线电活动之前,确保设备没有任何问题,是的,有时是编译器错误=)
你需要3台以上的从机,在辐射环境外有一台主机。所有I/O都通过包含表决和/或重试机制的主机。每个从设备必须有一个硬件监视器,并且撞击它们的调用应该被CRC等包围,以降低非自愿撞击的概率。转发应该由主机控制,因此与主机的连接丢失等于几秒钟内重新启动。
此解决方案的一个优点是,您可以对主机和从机使用相同的API,因此冗余成为一种透明的特性。
编辑:从评论中,我觉得有必要澄清“CRC的想法”。如果你用CRC来围绕碰撞,或者对来自主设备的随机数据进行摘要检查,那么从设备碰撞它自己的看门狗的可能性接近于零。只有当受监视的从设备与其他设备对齐时,才从主设备发送随机数据。随机数据和CRC/摘要在每次碰撞后立即清除。主从缓冲频率应超过看门狗超时的两倍。每次从主机发送的数据都是唯一生成的。
我真的读了很多很棒的答案!
这是我的2美分:通过编写软件检查内存或执行频繁的寄存器比较,建立内存/寄存器异常的统计模型。此外,以虚拟机的形式创建一个仿真器,您可以在其中试验该问题。我想,如果你改变结尺寸、时钟频率、供应商、外壳等,你会观察到不同的行为。
即使我们的台式电脑内存也有一定的故障率,但这不会影响日常工作。
如果你的硬件出现故障,你可以使用机械存储来恢复它。如果你的代码库很小,并且有一些物理空间,那么你可以使用一个机械数据存储。
材料表面不会受到辐射的影响。将有多个档位。机械读卡器将在所有齿轮上运行,并且可以灵活地上下移动。向下表示为0,向上表示为1。从0和1可以生成代码库。
使用循环调度程序。这使您能够增加定期维护时间,以检查关键数据的正确性。最常遇到的问题是堆栈损坏。如果您的软件是周期性的,您可以在周期之间重新初始化堆栈。不要为中断调用重用堆栈,请为每个重要的中断调用设置一个单独的堆栈。
与看门狗概念类似的是最后期限计时器。在调用函数之前启动硬件计时器。如果函数在截止时间计时器中断之前未返回,则重新加载堆栈并重试。如果在3/5次尝试后仍然失败,则需要从ROM重新加载。
将软件拆分为多个部分,并将这些部分隔离开来,以使用单独的内存区域和执行时间(尤其是在控制环境中)。示例:信号采集、预处理数据、主要算法和结果实现/传输。这意味着某一部分的失败不会导致整个程序的失败。因此,当我们修复信号采集时,其余任务将继续处理过时的数据。
一切都需要CRC。如果您在RAM中执行,甚至您的.txt也需要CRC。如果使用循环调度程序,请定期检查CRC。有些编译器(不是GCC)可以为每个部分生成CRC,有些处理器有专用硬件来进行CRC计算,但我想这将超出您的问题范围。检查CRC还会提示内存上的ECC控制器在出现问题之前修复单位错误。
使用看门狗进行启动,而不仅仅是一次操作。如果您的启动遇到问题,您需要硬件帮助。