要約:不揮発性メモリは、マイクロコントローラを組み込んだアプリケーションにとって不可欠です。このアプリケーションノートでは、トランザクションベースのコミット/ロールバック機構を使用して、外付けEEPROMメモリデバイスの内容を保護する方法について説明します。ここでは、外付けメモリデバイスを対象としていますが、このアプリケーションノートに記載した原理は、多くのMAXQマイクロコントローラに搭載された内蔵EEPROMにも等しく適用することができます。
はじめに
通常、マイクロコントローラを組み込んだアプリケーションにとって、不揮発性メモリは不可欠です。電源が失われても設定を維持し続ける場合や、会社の重要なトランザクションレコードを保存する場合など、現代のマイクロコントローラの状況において信頼性の高い不揮発性メモリは、非常に重要な要素となります。
不揮発性ストレージは、ほとんどの場合、外付けのシリアルメモリという形をとります。文字どおり、何十億ものメモリ部品が、長年にわたって、現場における高い信頼性を実証してきました。現在では、これらの小型で低価格のデバイスは数百バイト~1メガバイト以上の容量で販売されており、設定を維持する必要のあるほぼすべての機器に少なくとも1つは搭載されています。
EEPROMからフラッシュや回転記憶装置に至るまで、あらゆる種類の不揮発性ストレージに伴う問題は、書込みサイクルの中断によってデータ損失が生じることです。書込みサイクルの実行中に電源障害が発生した場合、電源の復旧後にデータを回復することができる簡単な手段がなければ、データが破損する可能性があります。
この記事では、外付けシリアルEEPROMメモリデバイスの内容を保護する、トランザクションベースのコミット/ロールバック機構について説明します。ここに記載する原理は、多くのMAXQ マイクロコントローラに搭載されている内蔵EEPROMにも等しく適用することができます。このアプリケーション用のファイルは、ダウンロード で入手可能です(ZIP、20.5kb)。
I2 C EEPROMの特性
シリアルメモリデバイスには、さまざまなインタフェースが付属しますが、最も一般的に使用されるインタフェースは、I2 Cです。このバスには多くの長所があります。すなわち、高度に標準化されていること、コントローラとメモリを接続するのに必要な線は2本のみであること、タイミング要件の柔軟性が非常に高くソフトウェアで駆動可能なことなどです。1つのI2 Cマスタで複数のI2 Cスレーブデバイスを駆動することができるため、マスタデバイスのピン数を最小限に抑えることができます。
EEPROMデバイスでは常に、書込みサイクルの方が読出しサイクルよりもはるかに長い時間を必要とします。この理由は、書込みサイクル時には、電荷がトンネリング機構を利用して絶縁バリアを越えて転送されるため、この動作に時間を要するからです。電圧を高くすることによってこの処理を速めることはできますが、電位があまりにも高くなると、バリアの誘電破壊を引き起こし、デバイスが損傷する恐れがあります。EEPROMデバイスの一般的な書込みサイクル時間が数十ミリ秒であるのに対して、読出しサイクル時間は通常、数百ナノ秒です。
多くのI2 Cデバイスは、ページモード を使用して、書込みサイクル時間の影響を軽減しようとしています。このモードでは、複数のバイトをバッファに転送することが可能で、その後、データは一度にアレイに書き込まれます。I2 Cメモリデバイスの一般的なページサイズは、32バイトです。したがって、単一の書込みサイクルで32バイトのEEPROMアレイを満たすことができます。
これは、非常に重要なことです。なぜなら、シリアルEEPROMデバイスには、特定の耐久性 、つまり、1ページ単位で許容することができる書込みサイクル数の上限が定められているためです。書込みサイクルの標準的な耐久性の範囲は、10,000~1,000,000回にも及びます。しかし、書込みサイクルの耐久性が百万回であったとしても、ソフトウェアによっては極めて短期間でメモリデバイスが消耗されることになります。1秒当りわずか100回の書込みサイクルを実行した場合でも、デバイスの書込みサイクル数は、3時間以内に上限に達してしまいます。
EEPROMのこれらの基本特性を考慮すると、組込み型プロセッサ向けの堅牢な不揮発性ストレージシステムの設計者は、以下の点に留意する必要があります。
いずれのページも繰り返して書き込まれる対象にならないようにします。特に、あるページを、別のページが書き込まれるたびに毎回更新しなければならない「ディレクトリ」として使用することは許されません。
書込みサイクル中に電源障害が発生した場合、(1)中断された書込みを検出し、(2)トランザクションを完了し、さらに(3)トランザクションを書込み前の状態にロールバックするための機構が必要となります。
何らかのデータチェック機構(チェックサム、CRC、メッセージダイジェスト)を使用して、データ完全性を保証する必要があります。
設計目標
EEPROMに関する上記の検討事項は、多くの不揮発性ファイルシステムを使用することによって解決されますが、このようなファイル機構は、小型の組込み式マイクロコントローラには大きな負担となります。ファイルシステムの多くは、小型マイクロコントローラで利用可能な容量以上のRAMを必要とし、また大部分のアプリケーションでは、完全なファイルシステムが強く要求されています。
このことを念頭に置いて、EEPROMのデータ保護機構の目標を以下にまとめます。
軽負荷 :保護機構において、チェックデータ用に確保するスペースは、EEPROMスペースの10%以内に抑えるようにします。必要な計算オーバヘッドが少量になるようにします。
ブロックサイズ :保護されるブロックのブロックサイズが、EEPROM内の本来の書込みページと等しくなるようにします。EEPROMデバイスのページサイズは常に2の偶数乗であるため、各ブロックから1または2バイトを用意する場合よりもソフトウェアのコーディングは簡単です。
耐久性 :いずれの単一ページも保護サイクルごとに書き込まれることのないようにします。
堅牢性 :どのようなケースの電源障害であっても、そのほとんど で回復可能なようにします。
ここに記載する保護機構は、read、write、commit、rollback、check、およびcleanup という6つのインタフェース関数を備えています。
read 関数は、ブロック数と、32バイトバッファへのポインタを受け取ります。バッファアドレスとブロック数が有効な範囲内にあれば、このルーチンは指定のブロックをバッファ内に読み出し、その妥当性を確認します。このルーチンは、valid read (有効な読出し)、invalid read (無効な読出し)、invalid buffer address (無効なバッファアドレス)、invalid page number (無効なページ番号)、またはprotection failure (保護の障害)という状態を返します。
write 関数は、ブロック数と、32バイトバッファ(事前に書込み対象のデータが格納されている)へのポインタを受け取ります。バッファアドレスとブロック数が有効な範囲内にあれば、このルーチンは不揮発性の保持バッファにデータをコピーし、コミット準備が整ったものとしてバッファにマークします。
commit およびrollback 関数は、write の後に実行することのできる補足的な動作です。commit 関数は、直前に書き込まれたバッファをメモリアレイ内の最終位置にコピーし、次に書込み予定のデータセットのためにバッファの構造を整えます。rollback 関数は、基本的に「アンドゥ(取り消し)」です。この関数は、直前のwrite 動作の結果を無効にし、バッファのサブシステムを次のwrite 用に整えます。
check 関数は、メモリデバイスのあらゆるブロックを読み出して、保存データの妥当性を検証します。また、この関数には、バッファのサブシステムを検査して、保留状態の書込みがないことを確認する働きもあります。無効なブロックや保留状態の書込みが見つかると、check 関数はエラー状態を返します。
cleanup 関数は、破損したEEPROMを修復します。具体的には、この関数は、発生した不良の内容を判断し、問題解決の方法の決定を試みます。
上記のすべての関数の詳細については、後述の動作の詳細 に記載しています。
図1. EEPROMアレイの構造。EEPROMアレイは、3つの領域に分かれています。この3つとは、実際のユーザデータを格納するメインアレイ、メインアレイのそれぞれの行のCRCを格納するチェックアレイ、および一時的な書込みデータを保存する4つのバッファを格納したバッファアレイです。
EEPROMの構造
EEPROMの構造については、上記の図1 を参照してください。EEPROMには、以下に示す3つの主要な領域があります。
メインアレイ :EEPROMの最大部分であり、データの保存用に使用されます。16kBのデバイスには、合計で512のページ(ページ当り32バイト)があります。このようなデバイスの場合、最初の473ページが実際のデータ保存に使用されます。
チェックアレイ :EEPROMの2つ目の区分は、メインアレイ内の行ごとにワードをチェックします。チェックアレイの各ページには、15の16ビットCRC値のセットが格納されています。各ページの最後のCRC値によって、そのページをチェックします。チェックアレイは、11ページを占有します(473~503ページ)。
バッファアレイ :EEPROMの最後の区分には、4つの書込みバッファで構成された8ページが含まれます。それぞれのバッファは、4つのフィールドから構成されています。その4つとは、次のcommit 命令でメインアレイに書き込まれる32バイトのデータを含んだdata (データ)フィールド、バッファが参照するページのアドレスを確定するaddress (アドレス)フィールド、バッファの状態(利用可能、使用中、有効期限切れ)を確定するstate (状態)フィールド、および書込みバッファ全体をチェックする16ビットのCRCフィールドです。バッファの構造については、上記の図1を参照してください。
このEEPROM構造によって、主要な設計目標のほとんどが実現されます。まず、メインアレイの各ページが第2の領域でチェックされるため、ページのすべてのビットが、ユーザデータとして利用可能です。次に、メインアレイのすべてのページが、チェックアレイ内の一意のワードによってチェックされるため、チェックアレイ内には「単一障害点」が存在せず、また、アレイ全体の中でいずれのページも、書込みサイクルごとに更新する必要はありません。最後に、4つの書込みバッファを使用することによって、書込みサイクルの消耗が分散されます。
動作の詳細
保護のないEEPROMの動作は単純です。読出しサイクルは、選択されたアドレスからホストにバイトを転送するだけです。また、書込みサイクルは、ホストからEEPROMにバイトを転送し、動作が完了するのを待ちます(大部分のデバイスにおいて、数ミリ秒)。一方、保護されたEEPROMの環境では、読出しと書込みの動作はより複雑になります。以下の項では、各動作を分析し、関数が呼び出されたときに行われる内容を具体的に説明します。
読出し
図2. READ動作のフローチャート
読出し動作は、インタフェース関数の中では最も単純ですが、それでもかなり複雑です。図2 は、動作のフローを図示しています。
ページアドレスとバッファアドレスが有効であるかどうかを検査します。無効であれば、ここで動作は終了し、invalid buffer address (無効なバッファアドレス)またはinvalid page number (無効なページ番号)のエラーを返します。
選択されたページをバッファに読み出します。
チェックページのアドレスを計算し、チェックページをスクラッチバッファに読み出します。
チェックページのCRCを計算します。これが無効であれば、protection failure (保護の障害)エラーを返します。
データバッファ全体のCRCを計算し、読出しページに対応するスクラッチバッファ内に保存されたCRCと比較します。CRCが一致すると、ルーチンはvalid read (有効な読出し)を返し、一致しない場合は、invalid read (無効な読出し)を返します。いずれにせよ、実際に読み出されたデータがリターンバッファ内に残されているため、必要に応じて呼出しルーチンがこれを使用することができます。
書込み
図3. WRITE動作のフローチャート
上述のとおり、write 動作は、実際にはメインアレイにwrite しません。その代わりに、データを4つのバッファの1つに保存します。この方法では、メインアレイ内の元のデータは、書込み処理の妥当性が確認されるまで、保存されたままとなります。図3 のフローチャートの内容を以下に示します。
ページアドレスとバッファアドレスが有効であるかどうかを検査します。無効であれば、ここで動作は終了し、invalid buffer address (無効なバッファアドレス)またはinvalid page number (無効なページ番号)のエラーを返します。
各書込みバッファのstate (状態)フィールドを読み出します。どのバッファの状態もoccupied (使用中)であれば、write sequence (書込みシーケンス)エラーとなり動作は失敗します。
4つの書込みバッファの1つが、expired (有効期限切れ)状態になっているはずです。この場合、シーケンス内の次の バッファが有効になります。
書込みバッファのdata (データ)フィールドにデータをコピーします。
address (アドレス)フィールドにページアドレスを書き込みます。CRCを計算し、CRC フィールドに書き込みます。状態はoccupied (使用中)に更新されます。以前の バッファが、expired (有効期限切れ)からavailable (利用可能)状態に設定されます。
ここで注意すべきことは、read 動作では、新たに書き込まれるページに古い値 を返すということです。新しい値は、commit 動作が完了するまで、返されません。
コミット
図4. COMMIT動作のフローチャート
commit 関数にはパラメータは必要ありません。コミット関数の唯一の役割は、書込みバッファからデータをメインアレイに確実に転送してから、書込みバッファを有効期限切れとしてマークすることです。 commit 関数は、図4 に示すとおりに動作します。
各書込みバッファのstate (状態)フィールドを読み出します。1つのバッファのみが「使用中」とマークされているはずです。そうでなければ、write sequence (書込みシーケンス)エラーとなり関数は終了します。
使用中のバッファのCRCをチェックします。一致しない場合は、データ破損エラーとなります。
アドレスを取り出して、メインアレイ内の指定ページにデータを書き込みます。
バッファのデータ部分の全体についてCRCを計算します。この値は、一時レジスタに保持されます。
チェックページの位置を見つけて、選択されたメインページを読み出します。
以前に計算したCRCでチェックページを更新し、チェックページ用に新しいCRCを計算します。
チェックページをチェックアレイに書き戻します。
書込みバッファをexpired (有効期限切れ)状態に更新します。
ロールバック
図5. ROLLBACK動作のフローチャート
rollback 関数は、図5 に示すように、最も単純な関数の1つです。メインアレイは、write 動作の後ではなく、 commit 動作の完了時にのみ更新されるため、rollback は、書込みバッファを無効にするだけです。
各書込みバッファのstate (状態)フィールドを読み出します。1つのバッファのみがoccupied (使用中)とマークされているはずです。そうでなければ、write sequence (書込みシーケンス)エラーとなり関数は終了します。
選択された書込みバッファのstate (状態)フィールドには、expired (有効期限切れ)の値が与えられます。
チェック
図6. CHECK動作のフローチャート
check 関数は、いずれの電源投入時にも呼び出されて、EEPROMがデータを受け取る準備ができているかどうかを確認します。check 関数は、ストレージシステムの健全性を検証し、エラーが発生していれば、それを報告します。この関数は、図6 に示すチェック動作を実行します。
各書込みバッファを読み出します。1つのバッファのみがavailable (利用可能)状態でないことを確認します。1つのバッファにのみ未定義の状態コードが含まれている場合は、interrupted write (書込みの中断)エラーを返します。すべてのバッファに未定義の状態コードが含まれている場合は、uninitialized EEPROM (EEPROMの未初期化)エラーを返します。
1つのバッファにのみoccupied (使用中)状態コードが含まれている場合は、そのバッファのCRCを実行します。CRCが失敗すると、interrupted write (書込みの中断)エラーを返します。
チェックアレイの各ページをチェックします。CRCチェックが1行でも失敗した場合は、protection failure (保護の障害)エラーを返します。
最後に、メインアレイの各ページを、保存されたCRCと照合してチェックします。CRCチェックの失敗したページが1ページでもある場合は、interrupted commit (コミットの中断)エラーのフラグをセットします。
クリーンアップ
図7. CLEANUP動作のフローチャート
cleanup 関数は、EEPROMシステムに伴うあらゆる問題を解決します。cleanup が終了すると、EEPROMのサブシステムは、どのような状態にあったにせよ、使用準備が整ったことになります。コミットされていない書込みはすべてロールバックされ、失敗したcommit の動作は完了することになります。
図7 は、cleanup の仕組みを示しています。
check がuninitialized EEPROM (EEPROMの未初期化)エラーを返した場合に、EEPROMが初期化されます。すべてのデータページがクリアされ、すべてのチェックページが初期化されます。すべての書込みバッファがクリアされ、最後の書込みバッファを除き、available (利用可能)状態が書き込まれます(最後の書込みバッファは、expired (有効期限切れ)状態が書き込まれます)。
check がinterrupted write (書込みの中断)エラーを返した場合、状態がavailable (利用可能)以外の状態である1つの書込みバッファを見つけます。その状態をexpired (有効期限切れ)に変更します。
check がinterrupted commit (コミットの中断)エラーを返した場合、CRCが一致しないメインページを見つけます。そのCRCを計算し、関連するチェックページを更新します。
check がprotection failure (保護の障害)エラーを返した場合、commit 動作に続くチェックページの更新が中断されたということです。不具合のあるチェックページに関連するメインページをすべて読み出して、そのチェックページをリフレッシュします。
セキュリティの証明
システムのセキュリティの証明は、書込みトランザクション中の脆弱な時点を特定することに重点を置いています(読出しトランザクションは、本質的に安全です。読出し動作中は、EEPROMページの書込みは行われないため、データが破損することはありません)。これらの脆弱な時点を特定した後は、回復処理を特定するだけです。特定されたすべての脆弱性が、回復機能の対象範囲内である場合、また、チェック/クリーンアップサイクルが、EEPROMの書込みサイクルを破損する可能性のある動作(電源投入など)に続く最初のイベントであると想定した場合、システムはおそらくセキュアであることが証明されます。
大部分のシリアルEEPROMデバイスでは通常、書込み動作は、該当するページ内のすべてのビットをまず既知の値に設定してから、変更する必要のあるビットを変更して所望の値を書き込みます。このため、電源障害が発生すると、「書込みの中断」動作時にページのすべて のバイトが破損する可能性が高くなります。一般的に、破損したページに新しいデータを書き込むことによって、この失敗イベントからの回復が可能です。とはいえ、以前のデータは失われます。
write 動作中の脆弱な時点を時系列で以下にまとめます。
data (データ)フィールドへの書込み動作中 :この期間に電源障害が発生した場合、チェック動作はエラーを検出しません。書き込まれている途中の書込みバッファはavailable (利用可能)状態のままですが、利用可能なバッファには有効なCRC値が含まれません。
現在の書込みバッファの状態を書込み中 :この動作は、status (状態)フィールドをoccupied (使用中)に変更し、CRCを設定し、書込み動作用のページアドレスを埋めます。この処理が中断されると、次のいずれかの状態が当てはまります。すなわち、(1) status (状態)が無効になり、interrupted write (書込みの中断)エラーが発生する、(2) status (状態)は有効だが、CRCが失敗し、やはりinterrupted write (書込みの中断)エラーが発生する、(3) status (状態)フィールドとCRCフィールドが有効になる。この最後のケースでは、システムは、コミットされていない書込みが保留された状態になります。この状況は、1つのバッファがoccupied (使用中)状態で、もう1つのバッファがexpired (有効期限切れ)状態となるため、検出することが可能です。サスブシテムの残りがチェックアウトされた場合、commit またはrollback を発行することによって、ユーザコードを続行することができます。いずれの場合でも、メインアレイとチェックアレイは、安全です。
以前のバッファの状態をクリアしてavailable (利用可能)にする途中 :バッファは、状態またはCRCが破損され、次の バッファがoccupied (使用中)状態になります。これは、そのバッファの状態をクリアするための動作が中断されたこと、またcommit またはrollback のいずれかが可能であることを意味します。
write動作とcommit動作の間 :1つの書込みバッファのみがoccupied (使用中)状態となり、そのCRCが検証を行います。ユーザコードは、commit またはrollback を要求することができます。書込みバッファアレイ、チェックアレイ、およびメインアレイは安全です。
commit 動作における脆弱な時点は、以下のとおりです。
data (データ)フィールドをメインアレイにコピーする間 :書込み動作が中断された場合、メインアレイのいずれかのページが破損している可能性があります。check 関数は、(1)有効なoccupied (使用中)状態の書込みバッファと(2)破損しているメインアレイページの状態をinterrupted commit (コミットの中断)として確定します。書込みバッファアレイとチェックアレイは、安全です。この場合、cleanup がコミットを完了し、クリーン済みのシステムを返します。書込みが完了している場合であっても、check 動作は失敗することに注意してください。チェックアレイ内のCRCは、計算済みのCRCと異なるからです。
チェックアレイ内のCRCの更新中 :チェックアレイ内のページへの書込み動作が中断された場合、おそらくページ全体が破損します。これは、メインアレイ内の15ページのCRC値が無効であることを意味します。しかし、チェックアレイ内の各ページが専用のチェックサムを持つため、check はこれを発見可能であり、書込みの中断の後にこれは失敗することになります。この場合、check はprotection failure (保護の障害)エラーを返します。修復方法として、まず、該当する15のすべてのページについてのCRC値を再計算します。次に、これらの値を、ページそのものの有効なCRC値とともに、チェックアレイ内のページに書き込みます。
書込みバッファアレイ内のstate (状態)の更新中 :変数state がoccupied (使用中)からexpired (有効期限切れ)に変更されるときに書込みサイクルが中断された場合、おそらく行全体が破損しています。ただし、チェックアレイとメインアレイは、安全なままです。check は、いずれかのページの破損を見つけ、interrupted write (書込みの中断)エラーを発行します。cleanup が動作すると、書込みバッファのサブシステムをリセットし、commit 動作を完了します。
最後に、rollback 中の脆弱な時点を以下にまとめます。
書込みバッファアレイ内でのstate (状態)の更新中 :commit サイクルの最後の状態と同様、単純に、書込みバッファ内のoccupied (使用中)状態をexpired (有効期限切れ)状態にリセットします。これが中断された場合、check ルーチンが、interrupted write (書込みの中断)を返し、cleanup が書込みバッファアレイ全体を再初期化します。この場合も、チェックアレイとメインアレイは安全です。
このように、電源障害やプロセッサのリセットがいつ発生しても、ストレージサブシステムの完全性は保たれること がわかります。電源障害の後、ストレージサブシステムは、書込みまたは読出し準備が整った状態に戻ります。commit 動作が中断された場合、サブシステムは、commit またはrollback が成功した状態に戻ります。
以降の設計
MAXQマイクロコントローラ用のEEPROMストレージシステムはこれで完成です。このシステムを改良するかどうかは、個々のシステムインテグレータの自由裁量に任されますが、思い付くいくつかのアイデアを以下に紹介します。
結論
外付けのシリアルEEPROMによって、マイクロコントローラ環境で不揮発性データを保存するための確実な手段を実現しています。このアプリケーションノートに記載した手法を使用すれば、書込みサイクルが中断された場合でも、シリアルEEPROMは高い信頼性を確保することができます。データの完全性がアプリケーションにとって極めて重要であるときには、設計者は必ずこれらの手法を考慮することをお勧めします。
自動アップデート
お客様が関心のある分野でアプリケーションノートが新規に掲載された際に自動通知Eメールの受信を希望する場合は、EE-Mail™にご登録ください。
フィードバックをお寄せください。 内容に満足されましたか、あるいは満足されていませんか?もっと良いページにできると思いますか?あるいは、単なるコメントでも結構です。フィードバックをお待ちしています。 —マキシムはお客様からいただく訂正、提案を元に改善していきます。
このページを評価し、フィードバックを送信する。