SPIフラッシュメモリチップは、dsPICの内部EEPROMと同じアトミックでない書き込み操作で問題がありますか?

A while back I had some intermittent trouble with the internal EEPROM of a dsPIC. Every so often, some value in the EEPROM would be found zeroed out on power-on. I tracked the problem down to when the chip lost power after the erase step of the write cycle, but before the write had completed. It was all about the timing of the power-down relative to the firmware execution, which was (in normal operation) random. I solved this by adding a buffer section to my EEPROM, to ensure that an incomplete write-cycle could be completed on restoration of power. I had to turn EEPROM writes into an atomic operation.

現在、内部EEPROMなしで別のdsPICを使用しています。外部フラッシュメモリチップに永続データを保存します。私も同じような懸念があるのではないかと思います。外部のフラッシュチップが書き​​込み中に電源を落としてデータを失い、内部EEPROMの場合のようにファームウェアに修正を書き込むのではないかと心配する必要がありますか?それとも、チップ自体がアトミックな書き込み操作を保証するのでしょうか。

より詳細には、私のバッファリング技術は3つのフィールドからなる永続的メモリの領域を定義します:書き込むアドレス、書き込むデータ、そしてREADYフラグ。 「書き込み」は4つのステップから成ります:バッファへの書き込み、READYフラグの設定、バッファからの書き込み、READYフラグのクリア。電源投入時に、READYフラグを確認します。設定されている場合は、バッファ内のものをすべて実行します。これはEEPROMではうまくいきましたが、フラッシュでうまくいくかどうかはわかりません。

6
フラッシュメモリは実際にはEEPROMよりも少し劣っています。何を使用するかによって(ベアフラッシュまたはコントローラ付きフラッシュ - 内部コントローラがあります)、EEPROMよりも大きいブロックを消去 - 書き込みする必要があるからです(通常ワード単位)これは通常はるかに時間がかかり、あなたに破損した書き込みの可能性を与えます。あなたがデータシートを読むことができるように、セクタ消去は最大で1秒(!)かかることがありますが、新鮮な書き込みは15ms(あなたのチップで)かかります。だから私は以前にアドバイスされてきたものを使用すると思います:あなたがフラッシュに書き込む前に電圧低下チェックをする。
追加された 著者 DougC,
FRAMは、コスト/可用性を犠牲にして、この問題を回避します。
追加された 著者 jns,
残念ながら、コストと可用性が私の最大の関心事です!
追加された 著者 user81219,

4 答え

外部電源を切る必要がある場合に書き込み(または消去)サイクルを完了するのに十分なエネルギー貯蔵を内部に持っているフラッシュメモリチップ(または内蔵フラッシュを備えたプロセッサ)について聞いたことがありません。つまり、システムの電源をいつ切るかを制御できない場合は、中断された可能性のある個々のフラッシュ更新操作を検出して処理できるプロトコルを常に作成する必要があります。

これを回避する1つの方法は、外部電源障害を検出しながら、まだ開始されている可能性のある書き込み/消去操作を完了できるように、必要なエネルギー貯蔵(たとえば電解コンデンサ)をボードに設けることです。

EDIT: Your write buffer concept could be used with the external flash, but it needs to be modified to take into account the larger erase granularity. According to the datasheet, the minimum erase size is one "sector" (4K bytes).

書き込みバッファ用に3つのセクタを予約する必要があります。これらのうちの1つはあなたのREADYフラグを保持します(これをWB_R wectorと呼びます)。 2番目は更新されるセクターのセクターアドレスを保持します(これをWB_Aセクターと呼びます)。 3番目はそのセクターの更新データを保持します(これをWB_Dセクターと呼びます)。

特定のバイト(または単一セクタ内のバイトのグループ)を更新するには、次の手順に従います。 WB_Rはすでに消去されていると仮定します。

  1. WB_Aを消去します。
  2. 変更したいバイトを含むフラッシュセクタを探します(これをDESTセクタと呼びます)。
  3. DESTのセクタアドレスをWB_Aに書き込みます。
  4. WB_Dを消去します。
  5. DESTの内容をWB_Dにコピーしますが、変更するバイトに到達したら、古い値ではなく新しい値をWB_Dに書き込みます。
  6. WB_RのREADYフラグを設定します。これはあなたがそれを消去されていない状態に変更することを意味することに注意してください。消去状態は0xFFなので、0x00と書くことになります。
  7. DESTを消去します(WB_Aからセクタアドレスを取得します)。
  8. WB_Dの内容をDESTにコピーします。
  9. WB_Rを消去します。

On power up, check the READY flag, and if it's set (anything other than 0xFF — it may have been only partially written or partially erased), jump directly to step 7.

このアルゴリズムでは、各書き込みバッファセクタは、書き込み操作ごとに少なくとも1回書き込まれて消去されます。製品の有効期間中に大量(10万件以上)の書き込みを行うと、これが問題になる可能性があります。その場合は、より高度なウェアレベリングアルゴリズムが必要になります。

5
追加された
よくわかりませんが、もっと詳しく説明する必要があります。あなたのボードがちょうどある種の書き込みサイクルを完了するのに十分なエネルギー貯蔵を持っているという事実に頼っているかもしれないようですね。
追加された 著者 GSerg,
1バイトの書き込みは冪等であるため(つまり、複数回行われても問題ありません)、書き込みバッファが機能するように見えます。私が見る唯一の失敗モードは、書き込みバッファへのアドレスやデータの書き込み中、あるいはREADYフラグの設定中に電源が落ちた場合です。この手法は外部フラッシュでも機能する可能性がありますが、書き込みバッファ、READYフラグ、およびターゲットの場所が個別に消去可能であることに依存しているため、より大きな消去粒度を考慮する必要があります。
追加された 著者 GSerg,
いいえ、ほとんどの外付けフラッシュデバイス(例外があります)では、消去と書き込みは2つの別々のステップとして明示的に行わなければなりません。通常、書き込みは一度に1バイトずつ実行できますが、消去はより大きな「ページ」または一度に1バイトのブロックに対して機能します。上記の私の編集を参照してください。
追加された 著者 GSerg,
私のバッファードライトテクニックでもうまくいくはずですね。
追加された 著者 user81219,
私は質問に私のバッファリング手法の説明を追加しました。エネルギー貯蔵が私のしていることに影響を与えるとは思わないが、私は間違っているかもしれない。
追加された 著者 user81219,
消去粒度については100%はっきりしていません。フラッシュは本質的に大きなブロックを消去し、変更されていないすべての部分を書き換えることを正しく理解していますか?もしそうなら、私のバッファのアイデアは絶対にうまくいきません...
追加された 著者 user81219,
私は明らかに間違ったメモリを使っています。私のアプリケーションには明らかにEEPROMが良い選択です。私はこの質問から始めるべきでした: electronics.stackexchange.com/questions/65503/… ヘルプを評価してください!
追加された 著者 user81219,
@StephenCollings:または、nvSRAM。1次供給が落ちたときに専用キャップから自動的に電荷を使って保存します。
追加された 著者 EmmyS,

バッファ書き込みは十分ではありません。ここではファイルシステムとデータベースからリーフを取得する必要があります。1ブロックが破損したときに「良い」状態に回復できるフラッシュのデータ構造が必要です。

これを行う一般的な方法は、2つのブロック間でピンポンをすることです。ブロックの最後の2バイトまたは4バイトをブロックの「シリアル番号」にし、残りのデータを残りのブロックにします。新しいブロックを書き込むときは、前のブロックのシリアル番号を1増やして、消去の「0」値(フラッシュの種類によっては0xff)をスキップし、新しいブロックにそのシリアル番号を書き込みます。

On power-up, read both blocks, and see which one has the later serial number (taking into account the wrap from 0xffff->0 and ignoring the skipped erase values.) Use that block. You may also want to add a CRC of your data to verify it wasn't corrupted in the middle (although if you put the serial at the end, that "shouldn't" be a problem.)

複雑なデータがある場合は、データベースやファイルシステムがディスク上のツリーを更新したり、書き込みロギングを実装したりする方法でこれを拡張できます。

3
追加された
私は過去にそのアプローチを使用しましたが、それ以来、(おそらく)部分的に消去された後に非常に悪く振る舞うブロックを持つフラッシュデバイスに遭遇しました。それ以来、私は少なくとも3つのブロックを使い始めました。いつでも他の2つのブロックの情報だけを使って、消去された最後のブロックをいつでも識別できるようにします。消去されているブロックが他のブロックが最後に消去されたものであると言うのに必要なビットパターンを任意に獲得するかもしれないので、2つのブロックは十分ではありません。 3つのブロックを使うことはその問題を避けます...
追加された 著者 firedfly,
... 2つのブロックがお互いを「非難する」場合、それらのうちの1つは最後に消去されたものでなければならず、残りのブロックはそれがどれであるかを知るでしょう。
追加された 著者 firedfly,

これは、座って慎重に物事を戦略化する必要がある分野です。データシートの詳細は以下のとおりです。

  1. 4kセクタ消去:\ $ 400ms \ $最悪の場合
  2. 32kブロックの消去:\ $ 800ms \ $最悪の場合
  3. 64kブロック消去:\ $ 1000ms \ $最悪の場合
  4. ページ書き込み:\ $ 3ms \ $最悪の場合
  5. 1バイト目の書き込み:\ $ 40 \ mu s \ $最悪の場合
  6. 次のバイトの書き込み:\ $ 12 \ mu s \ $最悪の場合

この詳細を制御する場合は、重要な書き込みが行われている間に決定した「ドループマージン」内に維持されるローカル電力を(コンデンサの蓄積電荷によって)調整することをお勧めします。あなたが賢明に最初のバイト書き込みタイミングを使用するならば、これは完全な秒である必要はありません(それに追加の通信/セットアップ時間を含めることを忘れないでください)。たとえば、ブロックまたはセクタの消去が開始されていること。これにより、電圧低下やリセットが発生したかどうかを判断できます。最後にどこにいたかを確認して、プロセスを終了できます。複数の「特別ページ」も必要になるかもしれません。しかし、いずれにせよ、あなたは徹底的にすべてのケースを考慮する必要があります!

2
追加された

フラッシュチップがブロックを消去している間に電力が失われた場合、ブロックが再消去されて消去サイクルが実行されるまで、または消去されるまで、ロバストソフトウェアはブロックの内容をいつでも任意に変更できる完了まで。ブロックがまだ古いデータを保持しているように見えても、それがいつまでも保持されるという保証はありません。ブロックが消去されたように見えても、プログラムされたビットが自発的に「出現」しないという保証はありません。私は、ビットが「真に」空白になっているのか、または「徹底的に」プログラムされているのかをチェックする機能を備えた内蔵フラッシュ付きのプロセッサをいくつか見たことがあります。

定期的にデータをフラッシュに保存し、停電の場合にすべての更新が完全に成功するか、またはまったく失敗しないことを保証するには、最低3つのフラッシュブロックが必要です。消去されている場合、他の2つのブロックの内容のみに基づいてそれを決定することができる。これを実装するためのさまざまなプロトコルがあります。ここでは、保存する情報量がフルブロックから1つの最小サイズのプログラマブルユニットを引いたもので、3つのブロックが利用可能であると仮定して、簡単なものを提案します。

各ブロックは、有効性/消去状態を追跡するために予約されている「制御」ビットをその中に有する。これらのビットをx、y、zと呼びます。動作中、システムは正しいデータを保持するブロックがその制御ビットを空白にするという不変条件を維持する。 「先行」ブロック(Xの前にZが付きます)では、その制御ビットがプログラムされます。残りのブロック(有効なデータを持つブロックの「後続」のブロック)の制御ビットは関係ありません。すべての制御ビットが空白の場合、何も正しく書き込まれていません。すべての制御ビットがプログラムされている場合、何かがひどく破損しています。

新しいデータを書き込むには、正しいデータを保持しているブロックの次のブロックを消去してから、そのブロックに新しいデータを保存します。最後に、最後のステップとして、現在のブロックであったものの制御ビットをプログラムします。その制御ビットがプログラムされるまでは、プログラムされたばかりのブロックの内容には関係ありません。一度そのビットがプログラムされると、新しいブロックに続くブロックの内容については何も気にしなくなります。その1ビットのプログラミングが成功または失敗することを保証するのに十分なエネルギーがシステムに利用可能であると仮定すると、すべての電力損失シナリオで信頼性のある動作が保証されます。

xがプログラムされていて、yが空白で、zが何かであるとします。有効データブロックはそれ自身のフラグが空白で前のブロックのフラグがプログラムされなければならないので、Xは有効ブロックになることができず(フラグxはプログラムされる)、そしてZは有効ブロックになることができない(フラグyがプログラムされるため)。したがって、Yが有効なデータを保持できる唯一のブロックです。ブロックXは前のバージョンのデータを保持しており、Zは何かを保持することに頼ることはできません。新しいデータを保存する必要があるとき、コードはZを消去して(すでに空白になっているかどうかにかかわらず)、含まれるべきすべてのデータをプログラミングすることから始めます。このプロセス中に電力が失われた場合、システム状態は開始前と同じになります(フラグに基づいて、Zの内容は意味がないと推定されるため、その内容はシステム状態にまったく影響しません)。

Zへのすべての書き込みが完了して有効なデータを保持した後で初めて、フラグyをプログラムする必要があります。そのフラグが書き込まれると、先行するブロックのflag(y)がプログラムされている間はZは自身のフラグが空白になるため、Zは有効なデータを保持しているブロックとして認識できます。 yがプログラムされたという事実は、Yが無効になったことを意味します。

次に新しいデータを保存する必要があるときには、ブロックXを消去してそこにデータを保存する必要があります。完了はプログラミングフラグzで示されるべきです。その後の時間で、Yは消去され、そこにデータが格納されます。完了はプログラミングフラグxで示されます。フラグx、y、およびzをプログラムしようとすると、実行が完了するか、まったく効果がないことが重要ですが、ハードウェアレベルで "アトミック"を保証する必要があるのはこれらの操作だけです。メモリへの他のすべての書き込みは、それらが完了するまで実行されない限り、内容が見られない(*)ことさえないブロックに対して行われます。

(*)通常、システムは無効なブロックへのアクセスを回避できませんが、システムの動作は読み込まれた値の影響を受けません。

ところで、フラグの書き込みを確実に完了させることができると確信が持てない場合は、冗長フラグビットを使用したアプローチがいくつか考えられますが、信頼性は保証されなくなります。例えば、ビットyが部分的にプログラムされている間にシステムが電力を失って、プログラムされていると読みとられるがブランクと読まれると仮定します。最初の電源投入時にyがブランクとして読み取られると、次の更新でZが消去されます。その消去中にシステムが電源を失うと、次の電源投入時にyがプログラムされたとおりに読み取られます。有効なブロックyがプログラムされたとおりに読み取られた場合、Zは 有効ブロックになり、次に消去されたブロックはXになります。両方とも空白として読み取られた場合、Zは正しく認識されたはずです。 2回目は無効ブロックです。冗長なフラグビットを追加することによってこれらの危険から保護しようと試みるかもしれませんが、そのようなアプローチはあまり役に立ちません。部分的にプログラムされたフラグが面倒に振る舞うことは「ありそうもない」ように物事を設計するかもしれませんが、それはフラグ書き込みが原子的に機能するならばチップが他の部分的に書き込まれたデータに関して何も報告できないという保証と基本的に異なりますトラブルを起こすでしょう。

1
追加された