私たちは 電離放射線 で砲撃された環境の中で遮蔽された装置に配備された組み込みC/C++アプリケーションをコンパイルしています。 ARMではGCCとクロスコンパイルを使用しています。デプロイされると、アプリケーションは誤ったデータを生成し、思ったよりも頻繁にクラッシュします。ハードウェアはこの環境用に設計されており、私たちのアプリケーションはこのプラットフォーム上で数年間実行されています。
コードに変更を加えることはできますか、それとも ソフトエラー および シングルイベントアップセット によって引き起こされるメモリ破損を特定/修正するために行うことができるコンパイル時の改善はありますか?長期実行アプリケーションに対するソフトエラーの悪影響を減らすことに成功した開発者はいますか?
NASAは 放射線耐性に関する論文 ソフトウェアを持っています。 3つの主なタスクについて説明します。
ほとんどの _ ecc _ メモリはマルチビットエラーではなくシングルビットエラーから回復できるため、メモリスキャンレートはマルチビットエラーが発生しにくい程度に十分に頻繁に設定する必要があります。
堅牢なエラー回復には、制御フローの転送(通常、エラーの前の時点でプロセスを再開する)、リソースの解放、およびデータの復元が含まれます。
データの復元に関する彼らの主な推奨事項は、エラーが発生する前に再起動してもデータを信頼できる状態にロールバックするように、中間データを一時的なものとして扱うことで、その必要性を避けることです。これはデータベースの「トランザクション」の概念に似ています。
彼らはC++のようなオブジェクト指向言語に特に適したテクニックを議論します。例えば
そして、まさにそうです、NASAは Mars Rover のような主要なプロジェクトのためにC++を使いました。
C++クラスの抽象化とカプセル化により、複数のプロジェクトや開発者間での迅速な開発とテストが可能になりました。
彼らは、問題を引き起こす可能性のある特定のC++機能を避けました。
new
およびdelete
以外)new
を使用しました)。ここにいくつかの考えやアイデアがあります:
ROMをよりクリエイティブに使用します。
ROMにできる限りのものを保管してください。計算する代わりに、ルックアップテーブルをROMに保存します。 (コンパイラが参照テーブルを読み取り専用セクションに出力していることを確認してください。実行時にメモリアドレスを印刷して確認してください。)割り込みベクタテーブルをROMに格納します。もちろん、あなたのROMがあなたのRAMとどれほど信頼できるかを確かめるためにいくつかのテストを実行してください。
スタックに最適なRAMを使用してください。
スタック内のSEUはおそらくインデックス変数、ステータス変数、リターンアドレス、さまざまな種類のポインタなどが存在する場所であるため、おそらく最も可能性の高いクラッシュの原因です。
timer-tickおよびwatchdogタイマールーチンを実装します。
システムのロックアップを処理するためのウォッチドッグルーチンと同様に、タイマーティックごとに "健全性チェック"ルーチンを実行することができます。メインコードは進捗を示すために定期的にカウンタをインクリメントすることもでき、健全性チェックルーチンはこれが発生したことを確認することができます。
実装 エラー訂正コード ソフトウェアで。
データを冗長化してエラーを検出または修正することができます。これにより処理時間が長くなり、プロセッサが長時間放射線にさらされる可能性があるため、エラーの可能性が高まるため、トレードオフを検討する必要があります。
キャッシュを覚えておいてください。
CPUキャッシュのサイズを確認してください。最近アクセスまたは変更したデータは、おそらくキャッシュ内にあります。少なくともいくつかのキャッシュを無効にすることができます(パフォーマンスが大幅に低下します)。これは、キャッシュがSEUにどの程度影響を受けやすいかを確認するために試してください。キャッシュがRAMよりも堅い場合は、重要なデータを定期的に読み書きして、キャッシュに残っていることを確認してRAMを元の状態に戻します。
ページフォルトハンドラを賢く使う。
メモリページを存在しないとマークした場合、アクセスしようとするとCPUはページフォルトを発行します。読み取り要求を処理する前に何らかのチェックを行うページフォルトハンドラを作成できます。 (PCオペレーティングシステムはこれを使用して、ディスクに交換されたページを透過的にロードします。)
重要なことにはアセンブリ言語を使用します(これがすべての可能性があります)。
アセンブリ言語では、knowレジスタの内容とRAMの内容を知っています。 you know CPUが使用している特別なRAMテーブルは何ですか?あなたは自分のリスクを抑えるためにラウンドアバウト方法でものを設計することができます。
objdump
を使用して、生成されたアセンブリ言語を実際に調べ、各ルーチンがどれだけのコードを占有するかを調べます。
あなたがLinuxのような大きいOSを使っているなら、あなたはトラブルを求めています。複雑になりすぎて、うまくいかないことがたくさんあります。
それは確率のゲームだということを忘れないでください。
コメンターは言った
エラーをキャッチするために作成したすべてのルーチンは、同じ原因から失敗する可能性があります。
これは事実ですが、チェックルーチンが正しく機能するために必要な(たとえば)100バイトのコードとデータにエラーが発生する可能性は、他の場所でエラーが発生する可能性よりもはるかに少なくなります。あなたのROMがかなり信頼でき、ほとんどすべてのコード/データが実際にROMに入っている場合、あなたのオッズはさらに優れています。
冗長ハードウェアを使用してください。
同一のコードで2つ以上の同一のハードウェア設定を使用してください。結果が異なる場合は、リセットをかける必要があります。 3台以上のデバイスでは、どのシステムが侵害されたのかを特定するために「投票」システムを使用できます。
また、アルゴリズムによるフォールトトレランスに関する豊富な文献に興味があるかもしれません。これには、古い代入が含まれます。一定数の比較が失敗するときに入力を正しくソートするソートを作成します(または、n
比較で漸近的な失敗比較数がlog(n)
に比例するとき、もう少し悪いバージョン)。
読み始める場所は、HuangとAbrahamの1984年の論文 " 行列演算のためのアルゴリズムベースのフォールトトレランス "です。彼らの考えは準同型暗号化計算と漠然と似ています(しかし、それらはオペレーションレベルでエラー検出/訂正を試みているので、実際には同じではありません)。
その論文の最近の子孫は、Bosilca、Delmas、Dongarra、およびLangouの「 高性能コンピューティングに適用されるアルゴリズムベースのフォールトトレランス 」です。
放射性環境のためのコードを書くことは、ミッションクリティカルなアプリケーションのためのコードを書くことと全く違いはありません。
すでに述べたことに加えて、ここにいくつかの雑多なヒントがあります:
重要:内部MCUレジスタの整合性を保証する必要があります。書き込み可能なハードウェア周辺機器の制御およびステータスレジスタはすべてRAMメモリに配置されている可能性があるため、脆弱です。
自分自身をレジスタの破損から保護するためには、レジスタの「ライトワンス」機能を内蔵したマイクロコントローラを選ぶのが望ましいです。さらに、すべてのハードウェアレジスタのデフォルト値をNVMに保存し、それらの値を定期的にレジスタにコピーする必要があります。あなたは同じ方法で重要な変数の完全性を保証することができます。
注:常に防御的プログラミングを使用してください。アプリケーションで使用されるものだけでなく、MCUに all レジスタを設定する必要があることを意味します。ランダムなハードウェア周辺機器が突然目覚めないようにする必要はありません。
チェックサム、 "ウォーキングパターン"、ソフトウェアECCなど、RAMまたはNVMのエラーをチェックする方法はすべてあります。現在最良の解決策は、これらのいずれも使用せず、MCUを使用することです。 ECCと同様のチェックを内蔵しています。ソフトウェアでこれを行うことは複雑で、エラーチェック自体がバグや予期しない問題を引き起こす可能性があるためです。
防御的プログラミングの概念を理解し、受け入れます。これはあなたのプログラムがすべての可能なケースを扱う必要があることを意味します。 例 。
高品質のミッションクリティカルなファームウェアは、できるだけ多くのエラーを検出し、それらを安全な方法で無視します。
重要:静的記憶期間変数のデフォルト値に依存しないでください。つまり、.data
や.bss
のデフォルトの内容を信頼しないでください。初期化の時点から実際に変数が使用される時点までの間には、いくらかの時間があるかもしれません、RAMが破損するのに十分な時間があったかもしれません。代わりに、そのような変数が初めて使用される直前に、実行時にそのような変数がすべてNVMから設定されるようにプログラムを作成してください。
実際には、変数がファイルスコープまたはstatic
として宣言されている場合、それを初期化するために=
を使用してはいけません(または値に頼ることができないので無意味です)。使用直前に必ず実行時に設定してください。 NVMからそのような変数を繰り返し更新することが可能であるならば、そうしてください。
C++でも同様に、静的記憶期間変数をコンストラクタに頼らないでください。コンストラクタからパブリックな "セットアップ"ルーチンを呼び出すようにします。このルーチンは、呼び出し側のアプリケーションから直接実行時に呼び出すこともできます。
可能であれば、.data
と.bss
を初期化する(そしてC++コンストラクタを呼び出す) "コピーダウン"スタートアップコードを削除してください。そうしなければリンカエラーが発生します。多くのコンパイラは、これをスキップすることができます。通常は「最小/高速起動」などと呼ばれます。
これは、外部ライブラリがそのような信頼を含まないようにチェックする必要があることを意味します。
重大なエラーが発生した場合に元に戻すプログラムの安全な状態を実装して定義します。
そのような環境で堅牢に動作するプログラムを書くためにCを使用することは可能かもしれませんが、それはほとんどの形式のコンパイラ最適化が無効になっている場合に限られます。最適化コンパイラは、多くの一見冗長なコーディングパターンを「より効率的な」パターンに置き換えるように設計されており、プログラマがx
に何か他のものを保持する方法がないとわかっているときにプログラマがx==42
をテストする理由はわからないかもしれません。システムが何らかの電気的グリッチを受け取った場合にその値を保持できる唯一の方法がある場合であっても、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()
の呼び出しが発生した場合。
そのようなコードは伝統的なCでは安全かもしれませんが、現代のCコンパイラではそうではありません。そのようなコンパイラは、そのような環境では非常に危険になる可能性があります。なぜなら、明確に定義されたメカニズムによって生じる可能性があり、結果として生じる結果も明確に定義される状況に関連するコードのみを含めるように積極的に試みるためです。場合によっては、障害を検出してクリーンアップすることを目的とするコードでは、状況が悪化することがあります。ある場合には回復の試みが未定義の動作を引き起こすとコンパイラが判断した場合、そのような場合に回復を必要とする条件は起こり得ないため、それらをチェックするコードが排除される可能性があります。
あなたを助けることができるのは watchdog です。ウォッチドッグは、1980年代に産業用コンピュータで広く使用されました。ハードウェア障害はそれよりはるかに一般的でした - 別の答えはまたその期間を示します。
ウォッチドッグは、ハードウェアとソフトウェアを組み合わせた機能です。ハードウェアは、数値(たとえば1023)からゼロまでカウントダウンする単純なカウンターです。 _ ttl _ または他のロジックを使用できます。
ソフトウェアは、1つのルーチンがすべての重要なシステムの正しい動作を監視するように設計されています。このルーチンが正しく完了した場合=コンピュータが正常に動作していることがわかった場合は、カウンタを1023に戻します。
全体的な設計は、通常の状況下ではソフトウェアがハードウェアカウンタがゼロになるのを防ぐようになっています。カウンタがゼロになると、カウンタのハードウェアがその唯一のタスクを実行し、システム全体をリセットします。カウンタの観点からは、ゼロは1024に等しく、カウンタは再びカウントダウンを続けます。
このウォッチドッグは、接続されたコンピュータが多くの場合に失敗した場合に確実に再起動されるようにします。私は、今日のコンピュータでそのような機能を実行できるハードウェアに精通していないことを認めなければなりません。外部ハードウェアへのインタフェースは、以前よりもはるかに複雑になりました。
ウォッチドッグの固有の不利な点は、システムが失敗してからウォッチドッグカウンタがゼロ+再起動時間に達するまでシステムが利用できないことです。その時間は一般的にいかなる外部または人間の介入よりもはるかに短いですが、サポートされている機器はその時間枠のためのコンピュータ制御なしで進むことができる必要があります。
これは非常に広い主題です。基本的に、あなたは本当にメモリ破損から回復することはできませんが、あなたは少なくとも 即座に失敗することを試みることができます 。これがあなたが使うことができるいくつかのテクニックです:
チェックサム定数データ /。長期間設定したままの設定データ(設定したハードウェアレジスタを含む)がある場合は、初期化時にチェックサムを計算し、定期的に検証します。不一致が見つかった場合は、再初期化またはリセットする必要があります。
冗長性を持って変数を保存する 。重要な変数x
がある場合は、その値をx1
、x2
、およびx3
に書き込み、それを(x1 == x2) ? x2 : x3
として読み取ります。
プログラムフローモニタリングを実装する 。 XORメインループから呼び出される重要な関数/ブランチ内で一意の値を持つグローバルフラグ。ほぼ100%のテストカバレッジで放射線のない環境でプログラムを実行すると、サイクルの終わりにフラグの許容値のリストが表示されます。偏差が見られたらリセットしてください。
スタックポインタを監視する 。メインループの始めに、スタックポインタをその期待値と比較します。偏差をリセットします。
この回答は、最小コストまたは高速のシステムを持つ以上に、正常に動作するシステムを持つことに関心があることを前提としています。放射性物質で遊ぶほとんどの人は、正確さ/速度よりも安全性/コストを重視しています
何人かの人々はあなたができるハードウェアの変更を提案しました(罰金-回答にはすでに多くの良いものがあり、私はそれをすべて繰り返すつもりはありません)、そして他の人は冗長性(原則的には素晴らしい)を提案しましたが、私は思いません誰もが、その冗長性が実際にどのように機能するかを示唆しています。どのようにフェイルオーバーしますか?何かが「間違っている」ことをどのようにして知るのですか?多くのテクノロジーは、すべてが機能することに基づいて機能するため、障害は対処が難しいものです。ただし、スケールexpect障害(結局、十分なスケールがあるため、多くのノードのうち1つが単一のMTBFノード);これを環境に利用できます。
ここにいくつかのアイデアがあります:
ハードウェア全体がn
回(n
は2より大きく、できれば奇数)複製され、各ハードウェア要素が他のハードウェア要素と通信できることを確認してください。イーサネットはそれを行う1つの明白な方法ですが、保護を強化するはるかに単純なルートが他にもたくさんあります(例:CAN)。一般的なコンポーネント(電源も含む)を最小限に抑えます。これは、たとえば複数の場所でADC入力をサンプリングすることを意味する場合があります。
アプリケーションの状態が単一の場所にあることを確認してください。有限状態マシンで。安定したストレージを妨げるものではありませんが、これは完全にRAMベースにすることができます。したがって、複数の場所に保存されます。
状態の変化に定足数プロトコルを採用します。 RAFT を参照してください。 C++で作業しているとき、このための有名なライブラリがあります。 FSMへの変更は、大多数のノードが同意した場合にのみ行われます。プロトコルスタックとクォーラムプロトコルに既知の適切なライブラリを使用するのは、1つを自分でロールバックするのではなく、そうしないと、クォーラムプロトコルがハングアップしたときに冗長性に関する適切な作業がすべて無駄になります。
FSMのチェックサム(CRC/SHAなど)を確認し、CRC/SHAをFSM自体に保存します(メッセージで送信し、メッセージ自体をチェックサムします)。これらのチェックサムに対して定期的にFSMをチェックし、受信メッセージをチェックサムし、チェックサムがクォーラムのチェックサムと一致することをノードに確認します。
システムに可能な限り多くの他の内部チェックを構築して、独自の障害再起動を検出するノードを作成します(十分なノードがある場合、これは半分の作業を実行するよりも優れています)。再起動しない場合に備えて、再起動中にクォーラムから自分自身をきれいに削除できるようにします。リブート時に、ソフトウェアイメージ(およびロードする他のもの)のチェックサムを作成し、クォーラムに再度導入する前にRAMテストをすべて実行します。
ハードウェアを使用してサポートしますが、慎重に行ってください。たとえば、ECC RAMを取得し、定期的に読み書きしてECCエラーを修正できます(エラーが修正不可能な場合はパニックになります)。ただし、(メモリから)static RAMは、DRAMよりも電離放射線に対する耐性がはるかに高いため、代わりにstatic DRAMを使用することをお勧めします。 「私がしないこと」の下の最初のポイントも参照してください。
1日以内に任意のノードで1%の障害が発生する可能性があるとし、障害を完全に独立させることができると仮定します。 5つのノードでは、1日以内に3つの障害が発生します。これは、.00001%の確率です。より多く、まあ、あなたはアイデアを得る。
私がしないすること:
最初から問題を抱えていないことの価値を過小評価してください。重量が問題でない限り、デバイスの周りの大きな金属ブロックは、はるかに安価で信頼性の高いソリューションになりますプログラマーのチームが思いつくよりも。 EMIの入力の同相光結合は問題などです。どのような場合でも、電離放射線に対して最高の定格のものを供給するためにコンポーネントを調達するときに試みてください。
独自のアルゴリズムをロールします。人々は以前にこのようなことをしたことがあります。彼らの仕事を使う。耐障害性と分散アルゴリズムは困難です。可能な場合は他の人の作品を使用します。
単純な複雑なコンパイラ設定を使用して、より多くの障害を検出することを望みます。運がよければ、より多くの障害を検出できます。より可能性が高いのは、特に自分でロールバックした場合は、あまりテストされていないコンパイラ内のコードパスを使用することです。
環境でテストされていない手法を使用します。高可用性ソフトウェアを作成するほとんどの人は、HAモードが正しく動作することを確認するために障害モードをシミュレートする必要があります。あなたは、オンデマンドで頻繁に失敗する「幸運な」立場にいます。そのため、各手法をテストし、そのアプリケーションが実際にMTBFを改善することを確認してください。特に、これを定足数アルゴリズムなどのアドバイスに適用してください。
ソフトウェアソリューションを特に求め、C++を使用しているため、独自の安全なデータ型を作成するために演算子のオーバーロードを使用しないでください。例えば:
uint32_t
(およびdouble
、int64_t
など)を使用する代わりに、倍数(最小3)のuint32_tを含む独自のSAFE_uint32_t
を作成してください。実行したいすべての操作(* + - /<< >> = ==!= etc)をオーバーロードし、オーバーロードした操作を各内部値で独立して実行するようにします。つまり、一度実行しないで結果をコピーします。前後の両方で、すべての内部値が一致することを確認してください。値が一致しない場合は、間違ったものを最も一般的なものに更新できます。最も一般的な値がない場合は、エラーがあることを安全に通知できます。
この方法では、ALU、レジスタ、RAM、またはバス上で破損が発生しても問題ありませんが、それでも複数の試行が行われ、エラーを検出する可能性が非常に高くなります。ただし、これは置き換え可能な変数に対してのみ機能することに注意してください。たとえば、スタックポインタは影響を受けやすいでしょう。
副次的な話:私は同様の問題に遭遇しました。古いARMチップについても同様です。 GCCの古いバージョンを使用したツールチェーンであることが判明しました。これは、使用していた特定のチップと共に、(場合によっては)関数に渡される値を破損するバグを引き起こすことがありました。お使いのデバイスがラジオの動作を非難する前に問題がないことを確認してください。そう、時々それはコンパイラのバグです=)
免責事項:私は放射能の専門家ではなく、この種のアプリケーションで働いていません。しかし、重要なデータの長期アーカイブのソフトエラーと冗長性に取り組みました。これは、ある程度リンクされています(同じ問題、異なる目標)。
私の意見では、放射能の主な問題は、放射能がビットを切り替えることができるということです。したがって、放射能はデジタルメモリを改ざんできる/改ざんするです。これらのエラーは通常 soft errors 、bit rotなどと呼ばれます。
問題は次のとおりです。メモリが信頼できない場合の信頼性の高い計算方法
ソフトエラーの割合を大幅に削減するには(ほとんどがソフトウェアベースのソリューションであるため、計算オーバーヘッドを犠牲にします)、次のいずれかを実行できます。
古き良き冗長性スキーム、より具体的にはより効率的なエラー修正コード(同じ目的ですが、より少ないアルゴリズムでより多くのビットを回復できるようにするための賢いアルゴリズム)。これは、時々(間違って)チェックサムとも呼ばれます。この種のソリューションでは、いつでもプログラムの完全な状態をマスター変数/クラス(または構造体?)に保存し、ECCを計算し、何かを行う前にECCが正しいことを確認する必要があります。ではなく、フィールドを修復します。ただし、このソリューションは、ソフトウェアが機能することを保証しません(ECCが何か問題があるかどうかを通知できるため、可能な場合は正常に機能することを保証しません。偽の結果を取得しないでください)。
または復元アルゴリズムデータ構造を使用できます。これにより、ソフトエラーが存在する場合でもプログラムが正しい結果を提供することを、ある程度まで保証します。これらのアルゴリズムは、一般的なアルゴリズム構造とECCスキームがネイティブに混在したものと見なすことができますが、復元スキームは構造に緊密にバインドされているため、追加のプロシージャをエンコードする必要がないため、これよりもはるかに回復力がありますECCをチェックします。通常ははるかに高速です。これらの構造は、ソフトエラーの理論的限界まで、プログラムがあらゆる条件下で動作することを保証する方法を提供します。これらの復元力のある構造を冗長性/ ECCスキームと組み合わせてセキュリティを強化することもできます(または、最も重要なデータ構造を復元力のあるものとしてエンコードし、残りはメインデータ構造から再計算できる消費可能なデータを通常のデータ構造としてエンコードしますECCのビット、または計算が非常に高速なパリティチェック)。
復元力のあるデータ構造(最近の、しかしエキサイティングな、アルゴリズムと冗長性エンジニアリングの新しい分野)に興味がある場合は、次のドキュメントを読むことをお勧めします。
Christiano、P.、Demaine、E. D.&Kishore、S.(2011)。付加的なオーバーヘッドを伴うロスレスのフォールトトレラントなデータ構造。アルゴリズムとデータ構造(pp。243-254)。スプリンガーベルリンハイデルベルク。
Ferraro-Petrillo、U.、Grandoni、F。、およびItaliano、G。F.(2013)。メモリ障害に強いデータ構造:辞書の実験的研究。 Journal of Experimental Algorithmics(JEA)、18、1-6。
Italiano、G. F.(2010)。弾力性のあるアルゴリズムとデータ構造。アルゴリズムと複雑さ(pp。13-24)。スプリンガーベルリンハイデルベルク。
復元力のあるデータ構造の分野について詳しく知りたい場合は、 Giuseppe F. Italiano (および参照の処理)とFaulty-RAM model(Finocchi et al。2005、Finocchi and Italiano 2008で紹介されています)。
/ EDIT:主にRAMメモリとデータストレージのソフトエラーの防止/回復を説明しましたが、計算(CPU)エラーについては話しませんでした。データベースのようにアトミックトランザクションを使用することをすでに指摘している他の回答があるため、別のよりシンプルなスキームを提案します。冗長性と多数決。
アイデアは、単純に同じ計算をx回実行する必要な各計算に対して、結果をx個の異なる変数に格納することです(x> = 3)。それからx変数を比較:
この冗長性スキームは非常に高速 ECCと比較して(実際にはO(1))であり、必要なときにclear signalを提供します- フェイルセーフ。多数決も(ほぼ)破損した出力が生成されないことを保証および軽度の計算エラーからの回復です。膨大な量の出力が存在する可能性があるため、同じ3倍をランダムに取得することはほぼ不可能であり、x> 3の場合はチャンスが少なくなります。
したがって、多数決では出力の破損から安全であり、冗長性x == 3を使用すると、1つのエラーを回復できます(x == 4では2つのエラーが回復可能になります。正確な方程式はnb_error_recoverable == (x-2)
です。ここで、xは、多数決を使用して回復するために少なくとも2つの同意する計算が必要であるため、計算の繰り返し数です)。
欠点は、1回ではなくx回計算する必要があるため、追加の計算コストがかかりますが、線形の複雑さなので、漸近的に得られる利益のために多くを失うことはありません。多数決を迅速に行うには、配列のモードを計算しますが、中央値フィルターを使用することもできます。
また、計算が正しく行われていることをさらに確認したい場合、独自のハードウェアを作成できる場合は、x CPUを使用してデバイスを構築し、システムを配線して、多数の投票を行ってx CPUに計算が自動的に複製されるようにします最後に機械的に(たとえばAND/ORゲートを使用)。多くの場合、これは飛行機やミッションクリティカルなデバイスに実装されています( トリプルモジュラー冗長性 を参照)。この方法では、計算のオーバーヘッドがなくなり(追加の計算が並行して行われるため)、ソフトエラーからの保護の別の層があります(計算の重複と多数決はハードウェアによってではなく、ハードウェアによって直接管理されるため)ソフトウェア-プログラムは単にメモリにビットが格納されているだけなので、より簡単に破損する可能性があります...)。
あなたは放射線環境の外にマスターを持つ3台以上のスレーブマシンが欲しいです。すべてのI/Oは投票および/または再試行メカニズムを含むマスターを通過します。スレーブはそれぞれハードウェアウォッチドッグを持っている必要があり、それらをぶつけるための呼び出しはCRCなどで囲まれる必要があります。バンピングはマスターが制御する必要があるため、マスターとの接続が失われると、数秒以内に再起動します。
このソリューションの利点の1つは、スレーブと同じAPIをマスターに使用できるため、冗長性が透過的な機能になることです。
編集: コメントから、「CRCの考え方」を明確にする必要があると感じています。あなた自身のウォッチドッグをぶつけているスレーブの可能性は、あなたがCRCでバンプを囲むか、マスターからのランダムデータのダイジェストチェックをするならば、ゼロに近いです。このランダムデータは、精査中のスレーブが他のスレーブと整列している場合にのみマスターから送信されます。ランダムデータとCRC /ダイジェストは各バンプの後すぐにクリアされます。マスター/スレーブバンプ周波数は、 double / watchdogのタイムアウトを超えている必要があります。マスタから送信されたデータは毎回一意に生成されます。
ワンポイント誰もが言及していないようです。あなたはGCCで開発していてARMにクロスコンパイルしていると言っています空きRAM、整数サイズ、ポインタサイズ、特定の操作にかかる時間、システムの継続時間、その他さまざまなことを想定したコードがないことをどのようにして知っていますか。これは非常に一般的な問題です。
答えは通常自動化された単体テストです。開発システムでコードを実行するテストハーネスを作成してから、ターゲットシステムで同じテストハーネスを実行します。違いを探す!
組み込み機器の正誤表も確認してください。 「クラッシュするのでこれを行わないでください。そのため、そのコンパイラオプションを有効にすると、コンパイラはそれを回避します」ということについて何かがあるかもしれません。
つまり、クラッシュの最も可能性の高い原因は、コード内のバグです。これが事実ではないことをかなり気にするまで、もっと難解な失敗モードについて(まだ)心配しないでください。
アプリケーションの多数のインスタンスを実行してはどうですか。クラッシュがランダムなメモリビットの変更によるものである場合、アプリインスタンスのいくつかがそれを通過させて正確な結果を生み出す可能性があります。 (統計的背景を持っている人にとっては)おそらく、ごくわずかな全体的なエラーを達成するために必要なビットフロップ確率を計算するのは非常に簡単です。
おそらくそれは、ハードウェアが「この環境用に設計されている」ことを意味するのかどうかを知るのに役立つでしょう。それはどのようにしてSEUエラーの存在を修正および/または表示しますか?
ある宇宙探査関連プロジェクトでは、SEUエラーで例外/割り込みを発生させるカスタムMCUがありましたが、若干の遅延がありました。
特に脆弱なのはデータキャッシュでしたので、ハンドラは問題のあるキャッシュラインを無効にしてプログラムを再起動します。例外の不正確な性質のために、例外を発生させるinsnが率いる一連のinsnを再起動できない場合があります。
危険な(再起動不可能な)シーケンス(lw $3, 0x0($2)
の後に$2
を変更し、$3
にはデータに依存しない)を指定し、GCCを変更したので、そのようなシーケンスは発生しません2つのinsnをnop
で区切ります。
考慮すべき点があります...
イオンがビットを弾くのを防ぐためにより遅いチップを使うことを誰かが言った。同様の方法で、実際には単一のビットを格納するために複数のビットを使用する特殊なcpu/ramを使用します。したがって、すべてのビットが反転する可能性は非常に低いので、ハードウェアのフォールトトレランスを提供します。 1 = 1111ですが、実際に弾くには4回打つ必要があります。 (2ビットが反転するとすでにあいまいなので4は悪い数かもしれません)。したがって、8を指定した場合、RAMの8分の1になり、アクセス時間がわずかに遅くなりますが、信頼性の高いデータ表現になります。あなたはたぶんソフトウェアレベルでこれをすることができるでしょう(全てのためにより多くのスペースを割り当てる)又は言語実装(このように物事を割り当てるデータ構造のためのラッパーを書く)。または同じ論理構造を持つがファームウェアでこれを行う特殊なハードウェア。
周期的スケジューラを使う 。これにより、定期的なメンテナンス時間を追加して、重要なデータの正確性を確認することができます。最も頻繁に発生する問題はスタックの破損です。あなたのソフトウェアが周期的であるならば、あなたはサイクルの間にスタックを再初期化することができます。スタックを割り込み呼び出しに再利用しないでください。重要な割り込み呼び出しごとに別々のスタックを設定してください。
ウォッチドッグの概念と同様に、期限タイマーがあります。関数を呼び出す前にハードウェアタイマーを起動してください。期限タイマーが割り込まれる前に関数が戻らない場合は、スタックを再ロードしてやり直してください。 3/5を試してもまだ失敗する場合は、ROMからリロードする必要があります。
ソフトウェアをいくつかの部分に分割し、これらの部分を分離して別々のメモリ領域と実行時間を使用します(特に制御環境で)。例:信号取得、データの事前解析、メインアルゴリズム、結果の実装/送信。これは、ある部分で障害が発生しても、プログラムの残りの部分で障害が発生しないことを意味します。そのため、信号取得を修復している間、残りのタスクは古いデータで続行されます。
すべてCRCが必要です。 RAMから実行する場合は、.textでもCRCが必要です。巡回スケジューラを使用している場合は、CRCを定期的に確認してください。セクションごとにCRCを生成できるコンパイラ(GCC以外)や、CRC計算を実行するための専用ハードウェアを備えているプロセッサもありますが、それは問題の範囲外です。 CRCをチェックすると、メモリ上のECCコントローラがシングルビットエラーを修復するように促します。
まず、アプリケーションを障害に合わせて設計する。通常のフロー操作の一環として、リセットが予想されることを確認します(アプリケーションおよびソフトまたはハードの障害のタイプに応じて)。これを完璧にすることは困難です。ある程度のトランザクション性を必要とする重要な操作は、アセンブリレベルでチェックおよび微調整する必要があるため、キーポイントでの中断によって一貫性のない外部コマンドが発生しません。 フェイルファーストいずれかの回復不能なメモリ破損または制御フローの逸脱が検出されるとすぐに。可能であれば失敗をログに記録します。
次に、可能であれば、破損を修正して続行。これは、定数テーブル(および可能な場合はプログラムコード)を頻繁にチェックサムおよび修正することを意味します。おそらく、各主要操作の前または時限割り込みの前に、自動修正する構造体に変数を保存します(繰り返しますが、各主要操作または時限割り込みの前に、3から多数決を取得し、単一の偏差であれば修正します)。可能であれば修正を記録します。
第三に、テストの失敗。メモリ内のビットを擬似ランダムに反転するrepeatableテスト環境をセットアップします。これにより、破損状況を再現し、その状況に合わせてアプリケーションを設計できるようになります。
Supercatのコメント、現代のコンパイラの傾向などを考えると、私は昔に戻って、アセンブリ全体のコードと静的なメモリ割り当てを至る所に書いてみたくなります。この種の全くの信頼性のために、私はAssemblyがもはやコストの大きな割合の差を被らないと思います。
ここに膨大な量の返信がありますが、私はこれについての私の考えを要約しようとします。
何かがクラッシュしたり、正しく動作しなかったりすると、あなた自身の間違いの結果である可能性があります - その場合、問題を見つけたときそれは簡単に解決できるはずです。しかし、ハードウェア障害の可能性もあります - そして全体的に修正することは不可能ではないにしても難しいです。
私は最初に問題の状況を記録すること(スタック、レジスタ、関数呼び出し)によってそれらをどこかにファイルに記録することによって、または何らかの方法で直接送信することによって試みることを試みることをお勧めします。
このようなエラー状態からの回復は、再起動(ソフトウェアがまだ動作している場合)またはハードウェアリセット(例:hw watchdogs)のいずれかです。最初のものから始めるのがより簡単です。
問題がハードウェアに関連している場合 - ロギングはどの関数呼び出しの問題が発生しているのかを識別するのに役立ちます。
また、コードが比較的複雑な場合 - 「分割して征服する」のが理にかなっています - 問題があると思われる箇所で関数呼び出しを削除/無効にする - 通常はコードの半分を無効にし、もう半分を有効にするには他のコードの半分に集中できるようになった後は、「機能しません」という種類の決定をします。 (問題があるところ)
しばらくしても問題が発生した場合(スタックオーバーフローが疑われる場合)、スタックポイントレジスタを監視することをお勧めします(それらが常に増加している場合)。
そして、「こんにちは世界」のようなアプリケーションになるまでコードを完全に最小化することができて、それでもまだランダムに失敗するのであれば、ハードウェアの問題が予想されます。放射線をよりよく許容するハードウェアとハードウェアの組み合わせ。
最も重要なことはおそらく、マシンが完全に停止/リセット/動作しない場合にどうやってログを取り戻すかです - おそらく最初にbootstapがすべきことは - 問題のある状況が発見された場合家に戻ることです。
あなたの環境で信号を送信して応答を受信することも可能であれば - ある種のオンラインリモートデバッグ環境を構築することを試みることができますが、それからあなたは少なくとも通信メディアが動作し、プロセッサ/ RAMが動作状態にある必要があります。そしてリモートデバッグとは、GDB/gdbスタブアプローチ、あるいはあなたのアプリケーションから取り戻す必要があるもののあなた自身のインプリメンテーションのどちらかを意味します(例:ログファイルのダウンロード、コールスタックのダウンロード、RAMのダウンロード、再起動)。
私は本当にたくさんの素晴らしい答えを読んでいます!
ここで私の2セントです:メモリをチェックするか、頻繁なレジスタ比較を実行するソフトウェアを書くことによって、メモリ/レジスタ異常の統計モデルを構築します。さらに、問題を試すことができる仮想マシンのスタイルでエミュレータを作成します。ジャンクションサイズ、クロック周波数、ベンダー、ケースなどを変えると、異なる動作が観察されると思います。
デスクトップPCのメモリでもある程度の失敗率がありますが、日常業務に支障はありません。