协慌网

登录 贡献 社区

编译用于高放射性环境的应用程序

我们正在编译嵌入式 C / C ++ 应用程序,该应用程序部署在受电离辐射轰击的环境中的屏蔽设备中。我们正在使用 GCC 和 ARM 进行交叉编译。部署后,我们的应用程序会生成一些错误的数据,并且比我们想要的更频繁地崩溃。硬件专为此环境而设计,我们的应用程序已在此平台上运行了数年。

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

答案

通过软件 / 固件开发和微型卫星环境测试工作约 4 - 5 年 *,我想在此分享我的经验。

*( 由于其电子元件的尺寸相对较小,尺寸有限,小型化卫星比大型卫星更容易发生单一事件干扰

非常简洁和直接:没有机制把由软件 / 固件本身检测,错误的情况中恢复没有 ,至少,软件 / 固件恢复目的地方最低工作版本的一个副本 - 与硬件支持恢复 (功能)。

现在,这种情况通常在硬件和软件级别处理。在这里,根据您的要求,我将分享我们在软件级别可以做的事情。

  1. ...... 恢复目的......提供在真实环境中更新 / 重新编译 / 刷新软件 / 固件的功能。对于高电离环境中的任何软件 / 固件,这几乎是必备功能。如果没有这个,您可以拥有任意数量的冗余软件 / 硬件,但在某一点上,它们都会爆炸。所以,准备这个功能!

  2. ... 最低工作版本...在代码中具有响应,多个副本,最低版本的软件 / 固件。这就像 Windows 中的安全模式。软件 / 固件的最低版本具有多个副本,而不是只有一个功能完整的软件版本。最小副本的大小通常比完整副本少得多,并且几乎总是只有以下两个或三个特征:

    1. 能够听取外部系统的命令,
    2. 能够更新当前的软件 / 固件,
    3. 能够监控基本操作的内务管理数据。
  3. ... 复制...... 某处... 在某处有冗余软件 / 固件。

    1. 无论是否有冗余硬件,您都可以尝试在 ARM uC 中使用冗余软件 / 固件。这通常通过在单独的地址中具有两个或更多相同的软件 / 固件来完成,这些地址彼此发送心跳 - 但是一次只有一个是活动的。如果已知一个或多个软件 / 固件没有响应,请切换到其他软件 / 固件。使用这种方法的好处是我们可以在发生错误后立即进行功能替换 - 无需与负责检测和修复错误的任何外部系统 / 方面进行任何联系(在卫星情况下,通常是任务控制中心( MCC))。

      严格地说,没有冗余硬件,这样做的缺点是你实际上无法消除所有单点故障。至少,您仍然会有一个单点故障,即交换机本身 (或通常是代码的开头)。然而,对于在高度电离环境(例如微微 / 毫微微卫星)中受到尺寸限制的设备,仍然值得考虑将单点故障减少到一点而无需额外的硬件。此外,切换的代码片段肯定比整个程序的代码要少得多 - 大大降低了获取单个事件的风险。

    2. 但是如果你不这样做,你的外部系统至少应该有一个副本可以与设备联系并更新软件 / 固件(在卫星的情况下,它又是任务控制中心)。

    3. 您也可以将副本放在设备的永久存储器中,可以触发该存储以恢复正在运行的系统的软件 / 固件
  4. ...... 可检测的错误情况..错误必须是可检测的 ,通常是由硬件纠错 / 检测电路或用于纠错 / 检测的一小段代码。最好将这些代码放在小型,多个和独立于主要软件 / 固件的位置。其主要任务用于检查 / 纠正。如果硬件电路 / 固件是可靠的 (例如它比辐射硬化更多 - 或者具有多个电路 / 逻辑),那么您可以考虑使用它进行纠错。但如果不是,最好将其作为错误检测。可以通过外部系统 / 设备进行校正。对于纠错,您可以考虑使用像 Hamming / Golay23 这样的基本纠错算法,因为它们可以在电路 / 软件中更容易实现。但它最终取决于你的团队的能力。对于错误检测,通常使用 CRC。

  5. ... 支持恢复的硬件现在,在这个问题上遇到了最困难的方面。最终,恢复需要负责恢复的硬件至少是功能性的。如果硬件永久损坏(通常在其总电离剂量达到一定水平后发生),那么(遗憾的是)软件无法帮助恢复。因此,对于暴露于高辐射水平(例如卫星)的设备而言,硬件是最重要的考虑因素。

除了上面因单个事件扰乱而预测固件错误的建议外,我还建议您:

  1. 子系统间通信协议中的错误检测和 / 或错误校正算法。这是另一个几乎必须具备的,以避免从其他系统接收的不完整 / 错误信号

  2. 过滤 ADC 读数。 不要使用 ADC 直接读取。通过中值滤波器,均值滤波器或任何其他滤波器对其进行滤波 - 绝不相信单个读数值。样品更多,而不是更少 - 合理。

美国宇航局有一篇关于辐射强化软件的论文。它描述了三个主要任务:

  1. 定期监视内存中的错误,然后清除这些错误,
  2. 强大的错误恢复机制,以及
  3. 如果某些东西不再有效,则能够重新配置。

请注意,内存扫描速率应该足够频繁,以至于很少发生多位错误,因为大多数ECC内存可以从单比特错误中恢复,而不是从多比特错误中恢复。

强大的错误恢复包括控制流传输(通常在错误之前的某个位置重新启动进程),资源释放和数据恢复。

他们对数据恢复的主要建议是通过将中间数据视为临时数据来避免对数据恢复的需要,以便在错误之前重新启动也将数据回滚到可靠状态。这听起来类似于数据库中 “交易” 的概念。

他们讨论了特别适用于面向对象语言(如 C ++)的技术。例如

  1. 用于连续内存对象的基于软件的 ECC
  2. 按合同编程 :验证前提条件和后置条件,然后检查对象以验证它仍处于有效状态。

而且,恰巧也是如此,NASA 已经将 C ++ 用于Mars Rover等重大项目。

C ++ 类抽象和封装支持多个项目和开发人员之间的快速开发和测试。

他们避免了可能产生问题的某些 C ++ 功能:

  1. 例外
  2. 模板
  3. Iostream(没有控制台)
  4. 多重继承
  5. 运算符重载(除了newdelete
  6. 动态分配(使用专用内存池和new放置以避免系统堆损坏的可能性)。

以下是一些想法和想法:

更有创意地使用 ROM。

在 ROM 中存储任何东西。而不是计算东西,在 ROM 中存储查找表。 (确保您的编译器将查找表输出到只读部分!在运行时打印出内存地址以进行检查!)将中断向量表存储在 ROM 中。当然,运行一些测试来看看你的 ROM 与你的 RAM 相比有多可靠。

使用最好的 RAM 作为堆栈。

堆栈中的 SEU 可能是最可能的崩溃源,因为它通常存在诸如索引变量,状态变量,返回地址和各种指针之类的内容。

实现 timer-tick 和看门狗定时器例程。

您可以在每个计时器滴答处运行 “健全性检查” 例程,以及处理系统锁定的监视程序例程。您的主代码也可以定期递增计数器以指示进度,并且完整性检查例程可以确保已经发生这种情况。

在软件中实现纠错码

您可以为数据添加冗余,以便能够检测和 / 或纠正错误。这将增加处理时间,可能使处理器暴露于辐射较长时间,从而增加出错的可能性,因此您必须考虑权衡。

记住缓存。

检查 CPU 缓存的大小。您最近访问或修改过的数据可能位于缓存中。我相信你可以禁用至少一些缓存(性能成本很高); 您应该尝试这样看看缓存对 SEU 的敏感程度。如果缓存比 RAM 更强大,那么您可以定期读取和重写关键数据,以确保它保持在缓存中并使 RAM 恢复正常。

巧妙地使用页面错误处理程序。

如果将内存页标记为不存在,则 CPU 将在您尝试访问时发出页面错误。您可以创建一个页面错误处理程序,在处理读取请求之前进行一些检查。 (PC 操作系统使用它来透明地加载已交换到磁盘的页面。)

将汇编语言用于关键事物(可能是一切)。

使用汇编语言,您可以知道寄存器中的内容以及 RAM 中的内容; 你知道 CPU 正在使用什么特殊的 RAM 表,你可以用迂回的方式设计东西以降低风险。

使用objdump实际查看生成的汇编语言,并计算出每个例程占用的代码量。

如果您使用像 Linux 这样的大型操作系统,那么您就会遇到麻烦; 有太多的复杂性和许多事情要出错。

请记住,这是一个概率游戏。

一位评论者说

为了捕获错误而编写的每个例程都会因同一原因而失败。

虽然这是真的,但是检查例程正常运行所需的 100 字节代码和数据中出现错误的可能性远远小于其他地方出错的可能性。如果您的 ROM 非常可靠并且几乎所有代码 / 数据实际上都在 ROM 中,那么您的赔率甚至更高。

使用冗余硬件。

使用 2 个或更多相同硬件设置和相同代码。如果结果不同,则应触发重置。使用 3 个或更多设备,您可以使用 “投票” 系统来尝试识别哪个设备已被入侵。