マイクロコントローラーのEEPROMの摩耗の平準化


15

例:ATtiny2313のデータシート(ほとんどのAtmel AVRデータシートと同様)は次のように述べています:

128バイトのインシステムプログラマブルEEPROM耐久性:100,000回の書き込み/消去サイクル

プログラムがいくつかの構成を保存するのに2バイトしか必要としないと想像してください、他の126バイトは効果的に浪費されます。私が心配しているのは、2つの構成バイトを定期的に更新すると、デバイスのEEPROMが消耗し、使用できなくなる可能性があることです。EEPROMのどのバイトが信頼できないかを特定の瞬間に追跡できないため、デバイス全体が信頼できなくなります。

利用可能な128のうち1または2バイトのみを効果的に使用するときに、マイクロコントローラのEEPROMでウェアレベリングを行うスマートな方法はありますか?


1
10万回の書き込みサイクルが制約である場合、代わりに他のテクノロジーを使用することは理にかなっていますか?内部的にレベリングを組み込んだメカニズム、それとも一桁以上の耐久性を備えたものでしょうか?
アニンドゴーシュ

1
@AnindoGhosh概念実証のテストのためにEEPROMを使い果たしたという理由だけで、マイクロコントローラの小さな在庫を無駄にしたくありません。コントローラーを再利用するときに、以前のプロジェクトでどのバイトを使用していたかを心配したくありません。そして、私が利用可能なハードウェアを最適に活用していることを知ってとても嬉しいです。
ジッピー

3
これが役立つかもしれません:AVR101:高耐久EEPROMストレージ
m.Alin

1
たぶんstackoverflowで私の答えを見てください。
-JimmyB

TIのMSP430 FRAMシリーズをご覧ください... 10 ^ 13書き込み!!!
geometrikal

回答:


19

私が通常使用する手法は、データの先頭に4バイトのローリングシーケンス番号を付けることです。ここで、最大の番号は最新の値を表します。合計6バイトになる実際のデータの2バイトを保存する場合、128バイトのEEPROMに対して21エントリが含まれ、耐久性が21倍になるように、循環キュー構成になります。

次に、ブート時に最大のシーケンス番号を使用して、次に使用するシーケンス番号とキューの現在のテールの両方を決定で​​きます。次のC擬似コードは、初期プログラミング時にEEPROMエリアが0xFFの値に消去されたため、0xFFFFのシーケンス番号を無視することを前提としています。

struct
{
  uint32_t sequence_no;
  uint16_t my_data;
} QUEUE_ENTRY;

#define EEPROM_SIZE 128
#define QUEUE_ENTRIES (EEPROM_SIZE / sizeof(QUEUE_ENTRY))

uint32_t last_sequence_no;
uint8_t queue_tail;
uint16_t current_value;

// Called at startup
void load_queue()
{
  int i;

  last_sequence_no = 0;
  queue_tail = 0;
  current_value = 0;
  for (i=0; i < QUEUE_ENTRIES; i++)
  {
    // Following assumes you've written a function where the parameters
    // are address, pointer to data, bytes to read
    read_EEPROM(i * sizeof(QUEUE_ENTRY), &QUEUE_ENTRY, sizeof(QUEUE_ENTRY));
    if ((QUEUE_ENTRY.sequence_no > last_sequence_no) && (QUEUE_ENTRY.sequence_no != 0xFFFF))
    {
      queue_tail = i;
      last_sequence_no = QUEUE_ENTRY.sequence_no;
      current_value = QUEUE_ENTRY.my_data;
    }
  }
}

void write_value(uint16_t v)
{
  queue_tail++;
  if (queue_tail >= QUEUE_ENTRIES)
    queue_tail = 0;
  last_sequence_no++;
  QUEUE_ENTRY.sequence_no = last_sequence_no;
  QUEUE_ENTRY.my_data = v;
  // Following assumes you've written a function where the parameters
  // are address, pointer to data, bytes to write
  write_EEPROM(queue_tail * sizeof(QUEUE_ENTRY), &QUEUE_ENTRY, sizeof(QUEUE_ENTRY));
  current_value = v;
}

EEPROMが小さい場合、標準のデータ型を使用する代わりにビットを少しスライスする必要がありますが、3バイトシーケンスの方が効率的です。


+1、すてきなアプローチ。より少ない「タグ」バイトを使用することで、ストレージを少し最適化できます。おそらく、追加の分散を提供するために何らかの形のハッシュバケットメカニズムに依存しますか?レベリングなしとアプローチのハイブリッドですか?
アニンドゴーシュ

@AnindoGhosh、はい、そうだと思います。私は通常、このアプローチをコードの簡素化のために小さなマイクロで使用し、個人的には主にDataFLASHのような大きなデバイスで使用しました。頭に浮かぶもう1つの簡単なアイデアは、シーケンス番号を定期的に下げて、より小さい値に保つことです。
PeterJ

@ m.Alinによって言及されたAtmelアプリケーションノートには、スマートな簡略化があります。RESETの後、[...]バッファーを調べて、最後の[...]バッファー要素を見つけることができます。バッファー要素と次のバッファー要素の差が1より大きい
ジッピー

write_value()はqueue_tail * sizeof(QUEUE_ENTRY)にエントリを配置すべきではありませんか?初めて正解になりますが、複数の書き込みがある場合は続行しませんか?iは、load_queue()の外側ではインクリメントされません。
マーシャルユーバンクス

2
@ DWORD32:はい、それは技術的には正しいですが、実際には無関係です。発生するまでに、EEPROMの摩耗制限は2000倍になります!
デイブツイード

5

以下は、バケットとバケットごとに約1バイトのオーバーヘッドバイトを使用する方法です。バケットバイトとオーバーヘッドバイトは、ほぼ同じ量の摩耗になります。現在の例では、128個のEEPROMバイトが与えられると、このメソッドは42個の2バイトバケットと44個のステータスバイトを割り当て、摩耗能力を約42倍に増やします。

方法:

EEPROMアドレス空間を分割するk個のバケット、K =⌊ E /(N +1)⌋、N =セットアップデータアレイサイズ=バケットサイズ、及びE = EEPROMサイズ(または、より一般的には、EEPROMの数このデータ構造に専念するセル)。

ディレクトリを初期化します。m= En・kで、すべてがkに設定されたmバイトの配列です。デバイスが起動すると、現在のエントリ(kと等しくないバイト)が見つかるまでディレクトリを読み取ります。[すべてのディレクトリエントリがkに等しい場合、最初のディレクトリエントリを0に初期化し、そこから続行します。]

現在のディレクトリエントリにjが含まれる場合、バケットjには現在のデータが含まれます。新しいセットアップデータエントリを書き込む必要がある場合、現在のディレクトリエントリにj +1 を格納します。それがkに等しくなる場合、次のディレクトリエントリを0に初期化し、そこから先に進みます。

注バケットがあるため、2・バイトとしてディレクトリのバイトは摩耗のほぼ同じ量を得ることをK > Mkは

Arduino SEの質問34189EEPROMの寿命を延ばす方法への私の答えから上記を適合させましたか?


2

これにはローリングシーケンス番号を使用しました(ピーターの答えに似ています)。キュー内の要素の数が奇数であれば、シーケンス番号は実際には1ビットとすることができます。頭と尾は、2つの連続した1または0でマークされます。

たとえば、5つの要素をロールスルーする場合、シーケンス番号は次のようになります。

{01010}(0に書き込む){11010}(1に書き込む){10010}(2に書き込む){10110}(3に書き込む){10100}(4に書き込む){10101}(5に書き込む)


1

使用しているEEPROMの種類とデータのサイズに応じて、いくつかのオプションがあります。

  1. EEPROMに個別に消去可能なページがあり、1ページ(またはそれ以上)を使用している場合は、使用中のページを除くすべてのページを消去したままにし、ページを循環的に再利用します。

  2. 一度に消去する必要があるページの一部のみを使用する場合は、そのページをデータエントリに分割します。書き込み中は常にクリーンエントリを使用し、クリーンエントリがなくなったら消去します。

「ダーティ」ビットを使用して、必要に応じてクリーンエントリとダーティエントリを区別します(通常、ダーティエントリの追跡に使用できる0xFFとは異なることが保証されている少なくとも1バイトがあります)。

EEPROMライブラリが消去機能(Arduinoなど)を公開しない場合、アルゴリズム#2の巧妙なトリックがあります。最初のEEPROMエントリが常に使用されるため、「ダーティ」ビットの値を読み取ることで判断できます。その後、クリーンエントリがなくなると、最初のエントリから再び開始し、「ダーティ」ビットを反転させるだけで、残りのエントリは自動的に「クリーン」としてマークされます。

シーケンス番号とカタログは、不良ページを追跡したり、EEPROMデータのさまざまな部分を個別に更新したりしない限り、スペースの無駄です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.