シリアルフラッシュメモリのデータロギング

GPSからシリアルフラッシュW25Q64FVに位置を書き込むデータロガーに取り組んでいます。私は毎秒GPSデータを書くつもりです。いくつか質問があります。

  1. このフラッシュメモリでは、1から256バイトまでの任意の位置に同時に書き込むことができます(ページ)。仕様によると、このメモリには100,000回の書き込み/消去サイクルがあります。それはページごとかバイトごとか?したがって、アドレスXに1バイトを書き込み、その後アドレスX + 1に別のバイト(ただし同じページ)を書き込んだ場合、そのページには99.998書き込みしか残っていないことになりますか?私はいいえと言うでしょう、しかし確かに私は尋ねています。 1バイトを書き込むことでページ全体が内部的に書き換えられる場合はどうなりますか。
  2. 最後に書き込んだバイトのアドレスを永続的に保持する方法毎回更新するたびにArduinoのEEPROMに書き込むことができましたが、書き込みサイクルは "わずか" 10万サイクルしかないので、これはもっと早く達成されるでしょう。他の可能性はすべての書かれたメモリをスキャンして空のバイトを探すことかもしれませんがこれはおそらく時間がかかることがあります。あるいは、フラッシュヘッダーのビットマップに全ページをマークするようにして、最後のページだけをスキャンすることもできます。他に何か提案はありますか?
  3. ブロックを消去したり、完全なフラッシュメモリを消去するという特別な目的はありますか。ラウンドロビン方式でメモリに書き込むことはできませんか。 HDD上のファイルを削除しても、すべてのバイトにゼロが書き込まれないため、フラッシュと同じにできませんでしたか。
1

4 答え

まず第一に、フラッシュの動作方法は、一般的に、書き込みコマンドは1から0にビットを変更することしかできないということです。ビットを1に戻すには、消去ブロック全体を消去する必要があります。したがって、「ハードドライブのように書き込む」ことはできません。ほとんどのフラッシュデバイスでは、同じブロックまたは同じバイトに複数の書き込みを行うことができます。これも、1をゼロに変更するだけの場合です。 (製造元が同じブロックへの複数の書き込みに対して推奨するフラッシュデバイスがいくつかありますが、これはまれです。)

あなたの具体的な質問に答えるには:

1)一般に、耐久性は各ブロックの消去サイクルに基づいています。個々のビットを好きなだけ(ゼロに)書き込むことができますが、ビットを1に戻すために消去を行うとフラッシュのそれらのビットが消耗します。

2)1つの方法は、各レコードの最初のバイトを状況標識として使用することです。消去後、0xFFで始まります。レコードの書き込みを開始すると、そのバイトに0x7Fを書き込みます(上位ビットにゼロ)。レコードの書き込みが完了したら、そのバイトに0x3Fを書き込みます(次に高いビットのゼロ)。レコードを削除済みとしてマークするには、そのバイトに0x00を書き込みます。これはすべて、あなたがいつでも1から0に少し変えることができるという事実を利用しています。

後で、レコードを読んでいるとき、あなたはそのバイトに0x3Fがあるレコードだけを見ます。 0x7Fは、レコードの書き込みが中断され(電源障害/再起動)、無効であることを意味します。消去ブロック全体に0x00、0x7F、および0xFFブロックしかない場合は、消去ブロックを消去できます。

ちなみに、起動時にスキャンするだけで済むように、使用済み/空きブロックのマップをRAMに保存することができます。

3)この投稿のトップを見てください。

3
追加された
@Martinあなたがシーケンシャルに書いていて、あなたが8Mバイトのデバイスを持っているなら、バイナリサーチで0xffではない最初を見つけるために23バイトを読む必要があります。
追加された 著者 Will Gorman,
@Martinは8MBが2 ^ 23なので、二分検索ではアドレスを見つけるのに多くのテストが必要になります。循環バッファを使用している場合は、上限と下限を見つけるために2つの検索を実行する必要があります。バッファがいっぱいになるたびに増加する開始バイトで各レコードをマークすると想定します
追加された 著者 Will Gorman,
@supercatどのメモリを参照していますか?私はそれを見たことがない。
追加された 著者 Calvin Allen,
ある種のメモリはバイトの個々のビットへの書き込みを許可しないかもしれないことに注意してください。したがって、1レコードにつき3バイトを予約し、いつ書き込まれるのか、いつ完了するのか、そしていつ消去されるのかを示す必要があります。
追加された 著者 firedfly,
@markrages:私が見た制限(シリアルフラッシュ、パラレルフラッシュ、NANDフラッシュ、内蔵CPUフラッシュなど)が正確にはわからないが、フラッシュデバイスの中にはセルごとに複数の充電レベルを格納するものもあり、論理。 FF以外のバイトへの書き込みが正当であることを明示的に指定しているデバイスが比較的少数であり、マルチレベルストレージへの推進がある場合、制限が一般的になる可能性があります。
追加された 著者 firedfly,
あなたが私の提案を使うならば、あなたはそれが空いているかどうか見るために各レコードから1バイトを読むだけでよいです。それはうまくいけばかなり速いはずです。フラッシュの先頭に「インデックス」を付けたい場合は、レコードごとに1ビットを使用し、レコードが書き込まれるときにそのビットにゼロを書き込むことをお勧めします。そうすれば、デバイス全体を消去するときにインデックスビットを消去するだけで済みます。
追加された 著者 DoxyLover,
可変長レコードはそれをはるかに不器用にします。もっと大きな固定長レコードを使用する余裕はありますか。
追加された 著者 DoxyLover,
完璧な説明をありがとう。第二に、私はレコードを削除するつもりはありません、そして、デバイスがいっぱいなら私はそれを消去します。ところで、すでに消去されたブロックを消去することは1消去サイクルかかりますか?次に、デバイスの電源を入れた後、レコードを1つずつスキャンして、書き込みを続けることができる空きの場所を見つける必要がありますか?メモリがいっぱいになると、しばらく時間がかかると思います。もっと一般的な方法はありませんか。例えば、私が提案したフルページのビットマップは、塗りつぶされたページごとに更新され、フラッシュの開始時に保存されます。
追加された 著者 Mike,
@DoxyLover、私は毎秒1レコードを持っているので本当にたくさんのレコードがあるでしょう:-)。また、レコードはおそらく可変長になります。
追加された 著者 Mike,
@ピート、なぜ23バイト?そしてどの23バイト?
追加された 著者 Mike,

フラッシュを使っていてさまざまな問題に悩まされてきたので、何かに混乱しないようにデータ形式を設計することをお勧めします。停電時に消去されたブロック。私が現在推奨している方法は、ローリングシーケンスのブロックを使用することです。各ブロックには、最新のブロックを除くすべてのブロックにプログラムされたバイトがあります。そのブロックに続くブロックはゴミを含んでいると見なされ、その内容は無視されるべきです。最新のブロックがいっぱいになったら、次のブロックを消去し、必要なヘッダー情報をすべて書きます。それが行われると、前の「最新の」ブロックはそれがもはや最新のブロックではないことを示すためにそのフラグバイトをプログラムされるべきです(新しく消去されたものはそうです)。

この方式では、一見有効なデータパターンが部分的に消去されたブロックに表示されたり、ソフトウェアが部分的に消去されたブロックを空白のブロックと間違えたりする危険性はありません。部分的に消去されたブロックの「ステータス」ビットをコードで読み取らないようにすることはできませんが、そのブロックは純粋に他のゴミ以外のブロックの内容に基づいてゴミとして認識できます。

1
追加された

これが私がEERPOM/FLASHを編集する方法です。

Split your memory into two arrays, once for data and one as warelevel (I assume 16bit number for warelevel here, which works ok so long as you have < 65535 records).

すべてのメモリから始めるには、0xFFバイトでなければなりません。したがって、最初の書き込みは、warelevel [0]とwarelevel [1]の両方が0xFFFFである特殊なケースです。レコードデータをdata [0x0000]に書き込み、warelevel [0x0000] = 0x0001を書き込みます。

それ以降のすべての書き込みでは、インクリメントされていない数を探しながら、ウェアレベル配列を横断します。 1つ見つけたら、データ配列にそのインデックスを使用し、順次配列になるようにウェアレベル配列を更新します。

2回目の書き込みでは、warelevel [0] = 0x0001になりますが、warelevel [1] = 0xFFFFになりますが、これは増分ではありません。したがって、次に書き込むインデックスは1であることがわかります。したがって、warelevel [1] = warelevel [0] + 1と書き込み、データをdata [1]に書き込みます。

次の書き込みでは、warelevel [1] = 0x0001、warelevel [2] = 0x0002、warelevel [3] = 0xFFFFなので、アドレス3に書き込むことがわかったので、warelevel [3]を0x0003に設定します。

これは永遠に続きます。基本的にはいつでも次の書き込み位置を見つけることができます。単純に、ウェアレベルの配列で増加しない番号の変更を探すだけです。

それからあなたがしなければならないのは「ラップアラウンド」だけです。例えば、0xFFFFを過ぎて0までカウントし続けます。標準の整数オーバーフローでは、すべてがうまくいきます。 0xFFFFレコードより少ない場合でも、カウンタは0〜0xFFFFになります(ただし、0xFFFFレコードより多い場合は、より大きなビットサイズのカウンタが必要です)。

これにより、すべてのバイトが同じ回数書き込まれるようになります。そのため、メモリの寿命が10k、100k、1Mのいずれであっても、コードは基本的に「最善の使用方法」です。

欠点は、もちろん、ウェアレベルアレイのスペースを失うことです。記録が小さければ小さいほど、より多くのスペースが失われます。

この方法の利点は、インデックスをどこにでも格納する必要がないことです。したがって、激しく破壊されるようなメモリロケーションはありません。

0
追加された
次のバイトに進む前にそのバイトを8回使用することはできないため、各バイトは実際のストレージデータの1バイトにつき1回だけ使用する必要があります。そうでなければ、インクリメント/トラッキングセクションが最初に死にます。また、00とFFだけを使用することはできません。最後に到達するとどうなりますか? 1バイトから他のバイトにすべてのバイトをリセットする必要があります。そのため、データセクションの1回の書き込みにつき、2回の書き込みが行われます。
追加された 著者 natsirt,
レコードごとにウェアレベルが上がる理由は何ですか?すべての書き込まれたレコードのウェアレベルがちょうど0x0000になる場合は、最初の0xFFFFを探すだけで次の空きブロックを見つけることができます。そしてこれはさらにビットレベルに縮小することができます - 1つのレコードのための1ビット(従ってシーケンスは1バイトのための0xFF、0x7F、0x3F、0x1F、0x0F、0x07、0x03、0x01、0x00であり、それから次のバイトに続く... )
追加された 著者 Mike,

1)セクターのP/Eサイクルは、そのセクターに複数の値を書き込み、ある時点でセクター全体を消去することです。そのため、1つのセクタにすべてのアドレスを書き込むことができ、そのセクタがいっぱいになった後にそのセクタを消去する場合に限り、1 p/eサイクルとしてカウントされます。

2)最後のメモリ位置を永続的に保存する必要がありますか?おそらく、デバイスの電源投入時に最後に使用されたFlashの場所を確認してから、アドレスカウンタをRAMに保存します。

3)あなたのアプリケーションによっては、デバイスを一杯にして、一度消去するだけです。あるいは、開始アドレスを移動させる、つまりデータが外部デバイスに送信されたら次のブロックにスキップする、ある種の管理を実装することもできます。それは、ウェアレベリングの簡単な形式としても使用できます。

0
追加された