我们正在编译一个嵌入式C / C ++应用程序,该应用程序部署在受ionizing radiation轰炸的环境中的屏蔽设备中。我们正在使用GCC和ARM进行交叉编译。部署后,我们的应用程序会生成一些错误的数据,并且比我们想要的更频繁地崩溃。硬件专为此环境而设计,我们的应用程序已在此平台上运行了数年。
我们可以对代码进行哪些更改,还是可以进行编译时改进,以识别/纠正由soft errors引起的single event upsets和内存损坏?有没有其他开发人员能够成功地减少软错误对长期运行的应用程序的有害影响?
答案 0 :(得分:770)
使用miniaturized satellites *的软件/固件开发和环境测试工作约4 - 5年,我想在此分享我的经验。
*(微型卫星比大型卫星更容易发生单一事件干扰,因为它的电子元件尺寸相对较小,尺寸有限)
要非常简洁和直接:没有从可检测,错误中恢复的机制 情况由软件/固件本身没有,至少是一个 复制 最低工作版本软件/固件某处 恢复目的 - 以及硬件支持恢复(功能)。
现在,这种情况通常在硬件和软件级别处理。在这里,根据您的要求,我将分享我们在软件级别可以做的事情。
......恢复目的...... 。提供在真实环境中更新/重新编译/刷新软件/固件的功能。这是高度电离环境中任何软件/固件的几乎必须功能。如果没有这个,你可以拥有你想要的冗余软件/硬件,但是在某一点上,它们都会爆炸。所以,准备这个功能!
...最低工作版本... 在代码中拥有响应式,多份副本,最低版本的软件/固件。这就像Windows中的安全模式。软件/固件的最低版本具有多个副本,而不是只有一个功能完整的软件版本。最小副本的大小通常比完整副本少得多,并且几乎总是只有 以下两个或三个功能:
...复制...某处...... 在某处拥有冗余软件/固件。
您可以使用没有冗余硬件的或尝试在ARM uC中使用冗余软件/固件。这通常通过在单独的地址中具有两个或更多相同的软件/固件来完成,这些地址相互发送心跳 - 但一次只有一个处于活动状态。如果已知一个或多个软件/固件没有响应,请切换到其他软件/固件。使用这种方法的好处是我们可以在发生错误后立即进行功能替换 - 无需与负责检测和修复错误的任何外部系统/方面进行任何联系(在卫星情况下,通常是任务控制中心( MCC))。
严格地说,没有冗余硬件,这样做的缺点是你实际上无法消除所有单点故障。至少,您仍然会有一个单点故障,即交换机本身(或通常是代码的开头)。然而,对于在高度电离环境(例如微微/毫微微卫星)中受尺寸限制的设备,单点故障减少到一点而没有额外硬件仍然值得考虑。此外,切换的代码肯定会远远少于整个程序的代码 - 大大降低了获取单个事件的风险。
但如果你不这样做,你的外部系统至少应该有一个副本可以与设备联系并更新软件/固件(在卫星的情况下,它再次是任务控制中心)。
...可检测到的错误情况.. 错误必须可检测,通常由硬件错误纠正/检测电路或者通过一小段代码进行纠错/检测。最好将这些代码从主软件/固件中放入小型,多个和独立的。它的主要任务是仅用于检查/纠正。如果硬件电路/固件是可靠(例如它比辐射硬化更多 - 或者具有多个电路/逻辑),那么您可以考虑使用它进行纠错。但如果不是,最好将其作为错误检测。可以通过外部系统/设备进行校正。对于纠错,您可以考虑使用像Hamming / Golay23这样的基本纠错算法,因为它们可以在电路/软件中更容易实现。但这最终取决于你团队的能力。对于错误检测,通常使用CRC。
...支持恢复的硬件现在,在这个问题上遇到了最困难的方面。最终,恢复需要负责恢复的硬件至少功能。如果硬件永久性损坏(通常在总电离剂量达到一定水平后发生),那么(遗憾的是)软件无法帮助恢复。因此,对于暴露于高辐射水平(例如卫星)的设备而言,硬件是最重要的考虑因素。
除了上述由于单一事件扰乱导致预期固件错误的建议外,我还建议您:
子系统间通信协议中的错误检测和/或错误纠正算法。这是另一个必须具备的,以避免从其他系统
过滤您的ADC读数。 不直接使用ADC读数。通过中值滤波器,均值滤波器或任何其他滤波器对其进行过滤 - 从不信任单个读取值。样本更多,而不是更少 - 合理。
答案 1 :(得分:384)
NASA拥有a paper on radiation-hardened软件。它描述了三个主要任务:
请注意,内存扫描速率应该足够频繁,以至于很少发生多位错误,因为大多数ECC内存可以从单位错误中恢复,而不是从多位错误中恢复。
强大的错误恢复包括控制流转移(通常在错误之前的某个位置重新启动进程),资源释放和数据恢复。
他们对数据恢复的主要建议是通过将中间数据视为临时数据来避免对数据恢复的需要,以便在错误之前重新启动也将数据回滚到可靠状态。这听起来类似于"交易"在数据库中。
他们讨论了特别适用于面向对象语言(如C ++)的技术。例如
而且,恰巧如此,NASA已将C ++用于Mars Rover等重大项目。
C ++类抽象和封装支持多个项目和开发人员之间的快速开发和测试。
他们避免了可能产生问题的某些C ++功能:
new
和delete
除外)new
以避免系统堆损坏的可能性。)答案 2 :(得分:112)
以下是一些想法和想法:
更有创意地使用ROM。
将所有内容存储在ROM中。而不是计算东西,在ROM中存储查找表。 (确保您的编译器将查找表输出到只读部分!在运行时打印出内存地址以进行检查!)将中断向量表存储在ROM中。当然,运行一些测试来看看你的ROM与你的RAM相比有多可靠。
使用最好的RAM作为堆栈。
堆栈中的SEU可能是崩溃的最可能源,因为它是索引变量,状态变量,返回地址和各种指针通常存在的地方。
实现timer-tick和看门狗定时器例程。
您可以在每个计时器时间点运行“健全性检查”例程,以及用于处理系统锁定的监视程序例程。您的主代码也可以定期递增计数器以指示进度,并且完整性检查例程可以确保已经发生这种情况。
在软件中实施error-correcting-codes。
您可以为数据添加冗余,以便能够检测和/或纠正错误。这将增加处理时间,可能使处理器暴露于辐射较长时间,从而增加了出错的可能性,因此您必须考虑权衡。
请记住缓存。
检查CPU缓存的大小。您最近访问或修改过的数据可能位于缓存中。我相信你可以禁用至少一些缓存(性能成本很高);您应该尝试这样看看缓存对SEU的敏感程度。如果缓存比RAM更强大,那么您可以定期读取和重写关键数据,以确保它保留在缓存中并使RAM恢复正常。
巧妙地使用页面错误处理程序。
如果将内存页面标记为不存在,则CPU会在您尝试访问时发出页面错误。您可以创建一个页面错误处理程序,在处理读取请求之前执行某些检查。 (PC操作系统使用它来透明地加载已交换到磁盘的页面。)
使用汇编语言处理关键事物(可能是一切)。
使用汇编语言,您知道寄存器中的内容以及RAM中的内容;您知道 CPU正在使用哪些特殊的RAM表,并且您可以以迂回的方式设计事物以降低风险。
使用objdump
实际查看生成的汇编语言,并计算出每个例程占用的代码量。
如果您使用像Linux这样的大型操作系统,那么您就会遇到麻烦;有太多的复杂性和许多事情要出错。
请记住,这是一场概率游戏。
一位评论者说为了捕捉错误而编写的每个例程都会因同一原因而失败。
虽然这是真的,但是检查例程正常运行所需的100字节代码和数据中出现错误的可能性远远小于其他地方出错的可能性。如果你的ROM非常可靠,而且几乎所有的代码/数据都在ROM中,那么你的几率就更好了。
使用冗余硬件。
使用2个或更多相同硬件设置和相同代码。如果结果不同,则应触发重置。使用3个或更多设备,您可以使用“投票”系统来尝试识别哪个设备已被入侵。
答案 3 :(得分:96)
您可能还对有关算法容错主题的丰富文献感兴趣。这包括旧的赋值:编写一个排序,当常数的比较失败时,它会正确地对其输入进行排序(或者,稍微更邪恶的版本,当失败的比较的渐近数量缩放为log(n)
n
时比较)。
开始阅读的地方是黄和亚伯拉罕1984年的论文“Algorithm-Based Fault Tolerance for Matrix Operations”。他们的想法与同态加密计算模糊地相似(但实际上并不相同,因为他们在操作级别尝试进行错误检测/纠正)。
该论文的最新后裔是Bosilca,Delmas,Dongarra和Langou的“Algorithm-based fault tolerance applied to high performance computing”。
答案 4 :(得分:38)
为放射性环境编写代码与为任何关键任务应用程序编写代码没有任何不同。
除了已经提到的内容之外,还有一些其他提示:
重要提示:您必须确保内部MCU寄存器的完整性。所有控制和可写的硬件外围设备的状态寄存器可以位于RAM存储器中,因此易受攻击。
为了保护自己免受寄存器损坏,最好选择一个带有内置"一次写入"的微控制器。寄存器的功能。此外,您需要在NVM中存储所有硬件寄存器的默认值,并定期将这些值复制到寄存器中。您可以以相同的方式确保重要变量的完整性。
注意:始终使用防御性编程。这意味着您必须在MCU中设置所有寄存器,而不仅仅是应用程序使用的寄存器。你不希望一些随机硬件外设突然醒来。
有各种方法可以检查RAM或NVM中的错误:校验和,行走模式",软件ECC等等。现在最好的解决方案是不使用任何这些,但要使用内置ECC和类似检查的MCU。因为在软件中执行此操作非常复杂,因此错误检查本身可能会引入错误和意外问题。
理解并接受防御性编程的概念。这意味着您的程序需要处理所有可能的情况,甚至是理论上不可能发生的情况。 Examples。
高质量关键任务固件会检测尽可能多的错误,然后以安全的方式忽略它们。
重要提示:不要依赖静态存储持续时间变量的默认值。也就是说,不要信任.data
或.bss
的默认内容。从初始化点到实际使用变量的点之间可能有任何时间,RAM可能有足够的时间被破坏。相反,编写程序以便在运行时从NVM设置所有这些变量,就在第一次使用这样的变量之前。
实际上,这意味着如果变量是在文件范围或static
声明的,那么你永远不应该使用=
来初始化它(或者你可以,但它没有意义,因为你不能依赖在任何方面的价值)。始终在使用前将其设置为运行时。如果可以从NVM重复更新这些变量,那么就这样做。
同样在C ++中,不要依赖构造函数来获取静态存储持续时间变量。让构造函数调用公共"设置"例程,您也可以在运行时直接从调用者应用程序调用它。
如果可能,请删除" copy-down"启动代码,它完全初始化.data
和.bss
(并调用C ++构造函数),这样如果编写依赖于此类的代码,就会出现链接器错误。许多编译器可以选择跳过这个,通常称为"最小/快速启动"或类似的。
这意味着必须检查任何外部库,以便它们不包含任何此类依赖。
为程序实施并定义一个安全状态,以便在出现严重错误时恢复。
答案 5 :(得分:31)
可以使用C编写在这种环境中表现强大的程序,但前提是禁用大多数形式的编译器优化。优化编译器旨在用“更高效”的编译模式替换许多看似冗余的编码模式,并且可能不知道程序员在编译器知道x==42
可能无法进行x
时测试x
的原因保留其他任何东西是因为程序员想要阻止执行某些代码volatile
保留一些其他值 - 即使在系统收到某种电子故障的情况下它能保持该值的唯一方法也是如此
将变量声明为... code that checks system state
if (system_state_favors_activation)
{
prepare_for_activation();
... code that checks system state again
if (system_state_is_valid)
{
if (system_state_favors_activation)
trigger_activation();
}
else
perform_safety_shutdown_and_restart();
}
cancel_preparations();
通常很有帮助,但可能不是灵丹妙药。
特别重要的是,请注意安全编码通常需要危险
操作具有硬件互锁,需要多个步骤才能激活,
并且使用模式编写代码:
prepare_for_activation()
如果编译器以相对字面的方式翻译代码,如果全部翻译
在prepare_for_activation()
之后重复检查系统状态,
该系统可以抵抗几乎任何似乎合理的单故障事件,
甚至那些会随意破坏程序计数器和堆栈的程序。如果
在调用prepare_for_activation()
之后发生故障,这意味着
激活是合适的(因为没有其他原因
在故障之前会被调用prepare_for_activation()
。如果
故障导致代码不恰当地达到trigger_activation()
,但在那里
没有后续的故障事件,随后的代码就没有办法了
在没有通过验证检查或首先调用cancel_preparations的情况下到达trigger_activation()
[如果堆栈出现故障,执行可能会在调用prepare_for_activation()
的上下文返回后继续cancel_preparations()
之前的某个位置,但是在prepare_for_activation()
和trigger_activation()
的调用之间会发生对{{1}}的调用,从而导致后者调用无害。
此类代码在传统C中可能是安全的,但在现代C编译器中则不然。这种编译器在这种环境中可能非常危险,因为他们努力只使用代码,这些代码在通过某种明确定义的机制可能产生的情况下是相关的,并且其结果后果也将被很好地定义。在某些情况下,其目的是在故障后检测和清理的代码最终会使事情变得更糟。如果编译器确定尝试恢复在某些情况下会调用未定义的行为,则可能会推断出在这种情况下可能无法进行此类恢复的条件,从而消除了将检查它们的代码。
答案 6 :(得分:27)
这是一个非常广泛的主题。基本上,您无法从内存损坏中恢复,但至少可以尝试及时失败。以下是您可以使用的一些技巧:
校验和常量数据。如果您有任何长时间保持不变的配置数据(包括您已配置的硬件寄存器),请在初始化时计算其校验和并定期进行验证。当您发现不匹配时,是时候重新初始化或重置。
存储冗余变量。如果您有一个重要变量x
,请在x1
,x2
和x3
中写下其值,并将其读作(x1 == x2) ? x2 : x3
。
实施程序流程监控。 XOR一个全局标志,在主循环调用的重要函数/分支中具有唯一值。在具有接近100%测试覆盖率的无辐射环境中运行程序应该在循环结束时为您提供标志的可接受值列表。如果发现偏差,请重置。
监控堆栈指针。在主循环的开头,将堆栈指针与其预期值进行比较。重置偏差。
答案 7 :(得分:27)
可以帮助你的是watchdog。看门狗在20世纪80年代被广泛用于工业计算领域。硬件故障比较常见 - 另一个答案也指那个时期。
看门狗是一种组合的硬件/软件功能。硬件是一个简单的计数器,从一个数字(比如1023)倒数到零。可以使用TTL或其他逻辑。
该软件的设计使得一个例程监控所有基本系统的正确操作。如果此例程正确完成=发现计算机正常运行,则会将计数器设置回1023。
总体设计是这样的,在正常情况下,软件可以防止硬件计数器达到零。在计数器达到零的情况下,计数器的硬件执行其唯一任务并重置整个系统。从计数器的角度来看,零等于1024,计数器再次继续倒计时。
此监视程序可确保在许多故障情况下重新启动连接的计算机。我必须承认,我不熟悉能够在今天的计算机上执行此类功能的硬件。与外部硬件的接口现在比以前复杂得多。
看门狗的一个固有缺点是,在看门狗计数器达到零+重启时间之前,系统从失效时就不可用。虽然该时间通常比任何外部或人工干预时间短得多,但支持的设备需要能够在该时间范围内无需计算机控制。
答案 8 :(得分:23)
这个答案假设您关注的是系统运行正常,而且系统成本最低或速度最快;大多数玩放射性物体的人都非常重视速度/成本的正确性/安全性
有些人建议您可以进行硬件更改(很好 - 这里已经有很多好东西在答案中我并不打算重复所有这些),而其他人则提出了冗余(很好用)原则),但我认为没有人建议冗余在实践中如何运作。你怎么样失败?你怎么知道什么事情出了问题'?许多技术在所有工作的基础上工作,因此失败是一个棘手的事情。然而,一些分布式计算技术设计用于规模期望失败(毕竟具有足够的规模,对于单个节点,任何MTBF都不可避免地发生许多节点的故障);你可以利用它来适应你的环境。
以下是一些想法:
确保整个硬件被复制n
次(其中n
大于2,最好是奇数),并且每个硬件元素可以与其他硬件元素进行通信。以太网是一种显而易见的方法,但还有许多其他更简单的路由可以提供更好的保护(例如CAN)。最大限度地减少通用组件(甚至是电源)。这可能意味着例如在多个地方对ADC输入进行采样。
确保您的申请状态位于一个地方,例如在有限状态机中。这可以完全基于RAM,但不排除稳定存储。因此它将存储在几个地方。
采用法定协议进行状态更改。例如,请参阅RAFT。当您使用C ++时,有一些众所周知的库。只有当大多数节点同意时才会对FSM进行更改。使用已知良好的库来进行协议栈和仲裁协议而不是自己滚动,或者当仲裁协议挂起时,所有关于冗余的好工作都将被浪费。
确保校验和(例如CRC / SHA)您的FSM,并将CRC / SHA存储在FSM本身(以及在消息中传输,并对消息本身进行校验和)。让节点根据这些校验和,校验和传入消息定期检查其FSM,并检查它们的校验和是否与仲裁的校验和匹配。
尽可能多地对系统进行内部检查,使节点检测到自己的故障重启(如果你有足够的节点,这比继续工作一半要好)。在重新启动期间尝试让他们干净地从法定人数中移除,以防他们再次出现。在重新启动时,让他们对软件映像(以及它们加载的任何其他内容)进行校验和,并在重新引入仲裁之前执行完整的RAM测试。
使用硬件为您提供支持,但要小心谨慎。例如,您可以获得ECC RAM,并定期读取/写入ECC错误以纠正ECC错误(如果错误无法纠正,则会出现紧急情况)。然而(从存储器中)静态RAM比DRAM首先更容忍电离辐射,因此可能更好地使用静态DRAM。请参阅“我不会做的事情”下的第一点'同样。
让我们假设您在一天内有任何一个节点失败的可能性为1%,并且让我们假装您可以让失败完全独立。使用5个节点,您需要在一天内完成三个故障,这是一个.00001%的机会。更多,嗯,你明白了。
我不的事情:
低估了没有问题的价值。除非重量是一个问题,否则设备周围的大块金属将会更便宜,更可靠解决方案比一个程序员团队能想出来的。同样,EMI输入的光耦合是一个问题,等等。无论如何,尝试采购组件以获得最佳电阻辐射。
滚动您自己的算法。人们以前做过这些事。使用他们的工作。容错和分布式算法很难。尽可能使用其他人的工作。
在天真的情况下使用复杂的编译器设置希望您检测到更多故障。如果幸运的话,您可能会发现更多故障。更有可能的是,您将在编译器中使用经过较少测试的代码路径,特别是如果您自己进行了编译。
使用在您的环境中未经测试的技术。大多数编写高可用性软件的人必须模拟故障模式以检查其HA是否正常工作,并因此错过许多故障模式。你是幸运的'根据需求频繁失败的立场。因此,测试每种技术,并确保其应用实际上将MTBF提高了超过引入它的复杂性(复杂性带来了错误)。特别是应用于我的建议re quorum算法等。
答案 9 :(得分:22)
由于您特别要求使用软件解决方案,并且您正在使用C ++,为什么不使用运算符重载来创建自己的安全数据类型?例如:
不要使用uint32_t
(以及double
,int64_t
等),而是创建自己的SAFE_uint32_t
,其中包含uint32_t的倍数(最少3个)。重载所需的所有操作(* + - /<<>> = ==!=等)以执行,并使重载操作在每个内部值上独立执行,即不执行一次,复制结果。在之前和之后,检查所有内部值是否匹配。如果值不匹配,则可以使用最常见的值更新错误的值。如果没有最常见的值,您可以安全地通知存在错误。
这样,如果在ALU,寄存器,RAM或总线上发生损坏并不重要,您仍然会有多次尝试并且很有可能发现错误。但请注意,虽然这只适用于您可以替换的变量 - 例如,您的堆栈指针仍然容易受到影响。
一个侧面故事:我遇到了类似的问题,也是在旧的ARM芯片上。事实证明它是一个工具链,它使用了旧版本的GCC,它与我们使用的特定芯片一起触发了某些边缘情况中的错误,这些错误会(有时)破坏传递给函数的值。确保您的设备在将其归咎于无线电活动之前没有任何问题,是的,有时它是编译器错误=)
答案 10 :(得分:15)
免责声明:我不是放射性专业人士,也不是这类应用的工作人员。但我在关键数据的长期存档方面致力于软错误和冗余,这在某种程度上是相互联系的(同样的问题,不同的目标)。
我认为放射性的主要问题是放射性可以切换位,因此放射性可以/将会篡改任何数字存储器。这些错误通常称为soft errors,有点腐烂等。
问题是:当你的记忆不可靠时如何可靠地计算?
要显着降低软错误率(以计算开销为代价,因为它主要是基于软件的解决方案),您可以:
依靠优秀的 redundancy scheme ,更具体地说,效率更高 error correcting codes (同样的目的,但更聪明的算法,所以你可以用更少的冗余恢复更多的比特)。这有时(错误地)也称为校验和。使用这种解决方案,您必须随时在主变量/类(或结构?)中存储程序的完整状态,计算ECC,并在执行任何操作之前检查ECC是否正确,如果不,修理田地。但是,此解决方案并不能保证您的软件可以正常工作(只是它可以正常工作,或者如果不能正常工作,因为ECC可以告诉您是否有问题),在这种情况下,您可以停止您的软件以便您不会得到假结果。
或者您可以使用弹性算法数据结构,这可以保证,即使存在软错误,您的程序仍能提供正确的结果。这些算法可以看作是常见的算法结构与本机混合的ECC方案的混合,但这比那更具弹性,因为弹性方案与结构紧密相关,因此您不需要编码检查ECC的其他程序,通常它们要快得多。这些结构提供了一种方法来确保您的程序在任何条件下都能工作,直到软错误的理论范围。您还可以将这些弹性结构与冗余/ ECC方案混合以获得额外的安全性(或将最重要的数据结构编码为弹性,其余的,可以从主数据结构中重新计算的可消耗数据,作为具有a的常规数据结构)一点ECC或奇偶校验,计算速度非常快。)
如果您对弹性数据结构感兴趣(这是算法和冗余工程中最近但令人兴奋的新领域),我建议您阅读以下文档:
Resilient algorithms data structures intro by Giuseppe F.Italiano, Universita di Roma "Tor Vergata"
Christiano,P.,Demaine,E。D.,& Kishore,S。(2011)。具有附加开销的无损容错数据结构。在算法和数据结构中(第243-254页)。施普林格柏林海德堡。
Ferraro-Petrillo,U.,Grandoni,F。,& Italiano,G。F.(2013)。适应内存故障的数据结构:对字典的实验研究。实验算法杂志(JEA),18,1-6。
Italiano,G。F.(2010)。弹性算法和数据结构。在算法和复杂性(第13-24页)。施普林格柏林海德堡。
如果您有兴趣了解有关弹性数据结构领域的更多信息,可以查看Giuseppe F. Italiano的工作(并按照自己的方式完成参考)和故障RAM模型(在Finocchi等人2005年引入; Finocchi和Italiano 2008年)。
/编辑:我主要针对RAM内存和数据存储说明了软错误的预防/恢复,但我没有谈及计算(CPU)错误。其他答案已经指出使用数据库中的原子事务,因此我将提出另一个更简单的方案:冗余和多数投票。
这个想法是你只需对你需要做的每个计算做x次相同的计算,并将结果存储在x个不同的变量中(x> = 3)。然后,您可以比较您的x变量:
与ECC(实际上是O(1))相比,此冗余方案非常快,当您需要故障保护<时,它会为您提供清除信号 / strong>即可。多数投票也(几乎)保证永远不会产生损坏的输出以及从较小的计算错误中恢复,因为x计算给出相同输出的概率是无穷小的(因为存在大量可能的输出,所以几乎不可能随机获得相同的3倍,如果x> 3则几乎不可能。
因此,通过多数投票,您可以安全地避免输出损坏,并且冗余x == 3,您可以恢复1个错误(x == 4,它将是2个可恢复的错误,等等 - 确切的等式为{{ 1}}其中x是计算重复次数,因为您需要至少2次同意计算才能使用多数投票来恢复。
缺点是您需要计算x次而不是一次,因此您需要额外的计算成本,但是线性复杂性,所以渐渐地,您不会因为获得的收益而损失太多。进行多数投票的快速方法是计算阵列上的模式,但您也可以使用中值过滤器。
此外,如果您想更加确定计算是否正确进行,如果您可以制作自己的硬件,则可以使用x CPU构建设备,并连接系统,以便在x CPU中自动复制计算最后以机械方式完成多数投票(例如使用AND / OR门)。这通常在飞机和任务关键型设备中实现(参见triple modular redundancy)。这样,您就不会有任何计算开销(因为额外的计算将并行完成),并且您还有另一层软错误保护(因为计算重复和多数投票将由硬件直接管理,而不是由软件 - 由于程序只是存储在内存中的位,因此更容易被破坏。)。
答案 11 :(得分:9)
您希望3台以上的奴隶机器在辐射环境之外拥有主机。所有I / O都通过包含投票和/或重试机制的主服务器。奴隶必须各有一个硬件监视器,并且应该用CRC等包围碰撞它们的呼叫,以减少无意识碰撞的可能性。 Bumping应由master控制,因此与master失去的连接等于几秒钟后重启。
此解决方案的一个优点是您可以使用与主服务器相同的API和从服务器,因此冗余成为透明功能。
编辑:从评论中我觉得有必要澄清“CRC的想法”。如果您使用CRC或对来自主设备的随机数据进行摘要检查来包围凸起,那么奴隶碰撞它自己的看门狗的可能性接近于零。当受到严格审查的奴隶与其他人对齐时,该随机数据仅从主人发送。每次碰撞后立即清除随机数据和CRC /摘要。主从凸点频率应大于看门狗超时的double。从主站发送的数据每次都是唯一生成的。
答案 12 :(得分:8)
似乎没有人提到过一点。你说你正在开发GCC并交叉编译到ARM上。你怎么知道你没有代码可以假设有关空闲RAM,整数大小,指针大小,进行某项操作需要多长时间,系统连续运行多长时间,或者各种类似的东西?这是一个非常普遍的问题。
答案通常是自动化单元测试。编写在开发系统上运行代码的测试工具,然后在目标系统上运行相同的测试工具。寻找差异!
同时检查嵌入式设备上的勘误表。您可能会发现有一些关于“不要这样做因为它会崩溃,所以启用该编译器选项并且编译器将解决它”。
简而言之,您最可能的崩溃源是您的代码中的错误。直到你完全确定情况并非如此,不要担心(还)关于更深奥的失败模式。
答案 13 :(得分:7)
如果您的硬件出现故障,那么您可以使用机械存储来恢复它。如果您的代码库很小并且有一些物理空间,那么您可以使用机械数据存储。
将会有一个不受辐射影响的材料表面。将有多个齿轮。机械读卡器将在所有齿轮上运行,并且可以灵活地上下移动。向下表示它为0,向上表示它为1.从0和1可以生成代码库。
答案 14 :(得分:7)
或许有必要知道硬件是否适合这种环境&#34;它如何纠正和/或表明存在SEU错误?
在一个太空探索相关项目中,我们有一个自定义MCU,它会引发SEU错误的异常/中断,但有一些延迟,即一些周期可能通过/指令在导致SEU异常的一个insn之后执行
特别容易受到数据缓存的影响,因此处理程序会使违规缓存行无效并重新启动程序。只有这一点,由于异常的不精确性,由异常提升insn领导的insn序列可能无法重新启动。
我们确定了危险(不可重启)序列(如lw $3, 0x0($2)
,后跟insn,修改$2
并且不依赖于数据$3
),我做了修改到GCC,所以这样的序列不会发生(例如,作为最后的手段,用nop
将两个insn分开。)
需要考虑的事情......
答案 15 :(得分:7)
有人提到使用较慢的芯片来防止离子轻易地翻转位。以类似的方式,也许使用专门的cpu / ram,它实际上使用多个位来存储单个位。因此提供硬件容错,因为所有位都不太可能被翻转。所以1 = 1111但需要被击中4次才能实际翻转。 (4可能是一个坏数字,因为如果2位被翻转它已经模棱两可)。因此,如果你选择8,你可以减少8倍的ram和一些较慢的访问时间,但是更可靠的数据表示。您可以在软件级别使用专门的编译器(为所有内容分配x更多空间)或语言实现(为这样分配事物的数据结构的写包装器)执行此操作。或具有相同逻辑结构但在固件中执行此操作的专用硬件。
答案 16 :(得分:7)
你问的是相当复杂的话题 - 不容易回答。其他答案还可以,但它们仅涵盖了您需要做的所有事情的一小部分。
As seen in comments,不可能100%修复硬件问题,但是可能有可能通过各种技术来减少或捕获它们。
如果我是你,我会创建最高Safety integrity level级别的软件(SIL-4)。获取IEC 61513文件(针对核工业)并遵循它。
答案 17 :(得分:7)
如何运行应用程序的许多实例。如果崩溃是由于随机内存位更改造成的,那么您的某些应用实例可能会通过并产生准确的结果。可能很容易(对于具有统计背景的人)计算你需要多少个实例给出的位翻转概率,以实现你想要的微小整体错误。
答案 18 :(得分:5)
使用cyclic scheduler。这使您能够添加定期维护时间以检查关键数据的正确性。最常遇到的问题是堆栈损坏。如果您的软件是循环的,您可以在循环之间重新初始化堆栈。不要重复使用堆栈进行中断调用,为每个重要的中断调用设置一个单独的堆栈。
类似于看门狗概念是截止日期计时器。在调用函数之前启动硬件计时器。如果在截止时间计时器中断之前函数未返回,则重新加载堆栈并再次尝试。如果在3/5尝试后仍然失败,则需要从ROM重新加载。
将软件拆分为零件并隔离这些零件以使用单独的存储区和执行时间(特别是在控制环境中)。示例:信号采集,预处理数据,主算法和结果实现/传输。这意味着一个部件的故障不会导致程序的其余部分出现故障。因此,当我们修复信号采集时,剩余的任务继续在陈旧数据上进行。
一切都需要CRC。如果执行RAM,即使你的.text需要CRC。如果您使用循环调度程序,请定期检查CRC。一些编译器(不是GCC)可以为每个部分生成CRC,并且一些处理器具有用于进行CRC计算的专用硬件,但我想这将不属于您的问题范围。检查CRC还会提示内存上的ECC控制器在出现问题之前修复单个位错误。
答案 19 :(得分:4)
首先,围绕失败设计您的应用。确保作为正常流量操作的一部分,它需要重置(取决于您的应用程序和软故障或硬故障的类型)。这很难完美:需要某种程度的事务性的关键操作可能需要在汇编级别进行检查和调整,以便关键点的中断不会导致外部命令不一致。 一旦检测到任何不可恢复的内存损坏或控制流偏差,快速失败。如果可能,记录失败。
其次,尽可能纠正损坏并继续。这意味着经常校验和修复常量表(如果可以的话,还有程序代码);也许在每次主要操作之前或在定时中断之前,将变量存储在自动更正的结构中(再次在每个主要操作或定时中断之前从3获得多数投票并且如果是单个偏差则更正)。如果可能,记录更正。
第三,测试失败。设置可重复的测试环境,伪随机地翻转内存中的位。这将允许您复制损坏情况并帮助围绕它们设计应用程序。
答案 20 :(得分:3)
鉴于supercat的评论,现代编译器的倾向以及其他事情,我很想回到古代,并在整个程序集和静态内存分配中编写整个代码。对于这种完全可靠性,我认为装配不再导致成本的很大一部分差异。
答案 21 :(得分:1)
以下是大量的回复,但我会尝试总结一下我的想法。
崩溃或无法正常工作可能是由于您自己的错误造成的 - 然后在找到问题时应该很容易解决。但也存在硬件故障的可能性 - 即使不是不可能整体修复也很困难。
我建议首先尝试通过记录(堆栈,寄存器,函数调用)来捕获有问题的情况 - 通过将它们记录到文件中,或者以某种方式直接传输它们(&#34;哦不 - 我&#39;砰的一声&#34;)。
从这种错误情况中恢复是重启(如果软件仍处于活动状态并且正在启动)或硬件重置(例如,hw看门狗)。从第一个开始更容易。
如果问题与硬件有关 - 那么日志记录应该可以帮助您确定发生哪个函数调用问题,并且可以让您了解哪些函数不起作用以及在哪里。
此外,如果代码相对复杂,那么分享和征服&#34;它 - 意味着你删除/禁用一些你怀疑问题的函数调用 - 通常禁用一半代码并启用另一半 - 你可以得到&#34;确实有效&#34; /&#34;不起作用&#34;一种决定,之后你可以专注于另一半的代码。 (问题出在哪里)
如果问题在一段时间后发生 - 那么可能会怀疑堆栈溢出 - 那么监控堆栈点寄存器会更好 - 如果它们不断增长。
如果您设法完全最小化您的代码,直到&#34; hello world&#34;应用程序 - 它仍然随机失败 - 然后会出现硬件问题 - 并且需要进行硬件升级&#34; - 意思是发明这样的cpu / ram / ... - 硬件组合,它可以更好地容忍辐射。
最重要的可能是,如果机器完全停止/重置/不起作用,你可以如何获取你的日志 - 可能是第一件事就是bootstap应该做的事情 - 如果有问题的情况被发现,那就回到家了。
如果您的环境中也可以传输信号并接收响应 - 您可以尝试构建某种在线远程调试环境,但是您必须至少使用通信媒体和某些处理器/某些公羊处于工作状态。通过远程调试我的意思是GDB / gdb存根方法或您自己实现从应用程序返回所需的内容(例如下载日志文件,下载调用堆栈,下载ram,重启)
答案 22 :(得分:0)
我真的读了很多很棒的答案!
这是我的2分:通过编写软件来检查内存或执行频繁的寄存器比较,构建内存/寄存器异常的统计模型。此外,创建一个虚拟机风格的模拟器,您可以在其中试验该问题。我想如果你改变结点大小,时钟频率,供应商,外壳等会发现不同的行为。
即使我们的台式电脑内存也有一定的失败率,但这并不会影响日常工作。