フラッシュ/EEPROMと摩耗管理
組込みの不揮発メモリを「あと何回書けるか」で語れるようになる。消去・書込み単位の非対称、書換え寿命、ウェアレベリング、EEPROMエミュレーション、電源断でも壊れない書込み順序を原理から押さえ、フラッシュを狙って延命できる。
- 1.フラッシュは書込みが「1へ消去してから0へ書く」の非対称操作で、消去はページ/ブロック単位と粗く時間もかかる。この消去単位の粗さがウェアレベリングとEEPROMエミュレーションを生む根本原因。
- 2.書換え寿命はブロックあたりの消去回数(P/Eサイクル)で決まる。同じ番地を上書きし続けると局所摩耗で先に死ぬため、書込みを全ブロックへ均す(ウェアレベリング)ことで実効寿命をブロック数倍に伸ばす。
- 3.電源断耐性は「新データを別領域へ書き切ってから古い方を無効化する」順序で担保する。書込み途中に落ちても旧データが残り、世代番号や状態フラグで最新の完全なレコードを選び直せるよう設計する。
フラッシュはなぜ「そのまま上書き」できないのか
マイコンのプログラムを保持するフラッシュも、設定値を貯める EEPROM も、電源を切っても内容が消えない 不揮発メモリ です。しかし RAM のように「好きな番地へ好きな値を即座に書く」ことはできません。フローティングゲートに電荷を出し入れして 1 ビットを表すという物理のため、書換えには次の非対称な制約が付きます。この制約こそが、以降で扱うウェアレベリングや EEPROM エミュレーションのすべての出発点です。フラッシュや SRAM が 1 チップに載る全体像は /embedded/microcontroller-architecture/ を参照してください。
- 消去は 1、書込みは 0 方向の一方通行:消去するとビットは 1(未書込み)に戻り、書込み(プログラム)は 1 から 0 へしか落とせない。0 になったビットを 1 に戻すには、再び消去するしかありません。
- 消去は粗く、書込みは細かい:書込みはワード〜ページ単位で行える一方、消去はそれよりずっと大きい ブロック(セクタ)単位 でしかできません。1 バイトを書き換えたいだけでも、その1バイトを含むブロック全体を一度消さねばならないのです。
- 時間が桁違い:読出しは数十 ns、書込みは数十〜数百 µs、消去はミリ秒オーダー。消去が最も遅く、消費電流も大きい。
| 操作 | 単位 | 方向 | おおよその時間 |
|---|---|---|---|
| 読出し (Read) | バイト/ワード(ランダムアクセス可) | — | 数十 ns |
| 書込み (Program) | ワード〜ページ(数百バイト) | 1 → 0 のみ | 数十〜数百 µs |
| 消去 (Erase) | ブロック/セクタ(数 KB〜数十 KB) | 全ビット 1 に戻す | 数 ms〜数百 ms |
フラッシュの厄介さは、読み・書き・消しの3操作で単位と方向が食い違う点に集約されます。番地単位で読めるのに、書けるのはページ単位、消せるのはさらに粗いブロック単位で、しかも消去なしには 0 を 1 に戻せません。「1 バイトだけ更新する」という RAM では自明な操作が、フラッシュでは「ブロックを退避 → 消去 → 書き直し」という重い手順になります。ウェアレベリングも EEPROM エミュレーションも、この非対称を隠して上位に楽をさせるための工夫です。
書換え寿命:P/E サイクルという有限資源
フラッシュ/EEPROM のセルは、消去と書込みを繰り返すたびに絶縁膜(トンネル酸化膜)が少しずつ劣化します。電荷の出し入れで膜にストレスが蓄積し、やがて電荷を正しく保持できなくなる。この摩耗の単位が P/E サイクル(Program/Erase cycle、消去・書込み回数) です。寿命はブロックあたりの P/E サイクル数で規定され、代表値は次のとおりです。
| 種別 | 1ブロックあたりの書換え耐性 (P/E) | 特徴 |
|---|---|---|
| EEPROM(バイト単位書換え) | 約 100万 回 | 細かく書けるが容量小・単価高 |
| NOR フラッシュ | 約 10万 回 | ランダム読出しが速く、コード実行(XIP)向き |
| SLC NAND(1セル1ビット) | 約 10万 回 | 大容量・高耐久だがランダム読出しは苦手 |
| MLC/TLC NAND(多値) | 約 3千〜1万 回、さらに低下も | 大容量・低単価だが耐久と信頼性は下がる |
重要なのは、これが ブロック単位の消耗 だという点です。もし設定値をいつも同じ 1 ブロックに上書きし続ければ、そのブロックだけが集中的に P/E を消費し、他のブロックが新品同然でも先に寿命を迎えます。これが 局所摩耗(localized wear) です。たとえば 10 万回耐性のブロックへ 1 秒ごとに書けば、単純計算で 100000 / 86400 ≒ 約 1.16 日で摩耗限界に達してしまいます。個々のセルは丈夫でも、書込みの偏りが機器全体の寿命を決めるのです。
「10 万回も書けるなら十分」という直感は、書込みが一点に集中する現実で簡単に崩れます。寿命を食うのは総書込み回数そのものより、それが特定ブロックへ偏っているか否かです。ログや積算値のように高頻度で更新される変数を素朴に固定番地へ書くと、そのブロックだけが突出して摩耗し、フラッシュ全体としてはガラ空きなのに機器が壊れます。対策の核心は、書込みを空間的に散らすことにあります。
ウェアレベリング:摩耗を全ブロックへ均す
ウェアレベリング(wear leveling) は、書込みを特定ブロックに集中させず全ブロックへ均等に散らすことで、フラッシュ全体の実効寿命を最大化する手法です。原理は単純で、「同じ論理データでも、書くたびに物理ブロックを変える」——論理アドレスと物理ブロックの対応を固定せず、書込みごとに摩耗の少ない物理ブロックへ振り直します。この論理と物理の対応表が 論理・物理変換(L2P マッピング) です。方式は大きく2つに分かれます。
| 方式 | 対象ブロック | 考え方 | 得失 |
|---|---|---|---|
| ダイナミック | 書換えが起きるブロックのみ | 更新のたびに空きブロックへ書き、旧ブロックを回収して再利用 | 実装が軽い。ただし『書き換えられない静的データ』が居座るブロックは摩耗が進まず、可動域が狭まる |
| スタティック | 静的データも含む全ブロック | めったに書き換えない静的データも、時々あえて移動させて古いブロックを解放 | 全ブロックを平準化でき寿命が最大。移動コストと制御の複雑さが増す |
理想的にウェアレベリングが効けば、実効寿命はおおよそ「1ブロックの P/E 耐性 × ブロック数 ÷ 書込み量」まで伸びます。1 ブロックしか使わなければ 10 万回で終わる書込みが、1000 ブロックへ均等に散れば原理的には 1 億回まで耐えられる計算です。
局所摩耗 vs ウェアレベリング(10万回耐性・書込み総数が同じ場合)
[固定番地へ上書き]
ブロック0: ■■■■■■■■■■ 100000回 → 摩耗死 ← ここだけ集中
ブロック1: ................ 0回(新品のまま無駄に)
...
→ 全体としては未使用だらけなのに機器は寿命
[ウェアレベリングで分散]
ブロック0: ■......... 10000回
ブロック1: ■......... 10000回
...(全10ブロックへ均等)...
→ どのブロックも余裕。実効寿命がブロック数倍に
ウェアレベリングは書込みを空間的に散らしますが、そもそもの書込み回数を減らす工夫と併用するのが定石です。(1) 値が変わったときだけ書く(差分検出)、(2) 揮発する RAM 上に貯めてまとめて書く(バッファリング)、(3) 更新頻度の高い変数は EEPROM やバックアップ RAM など耐久の高い領域へ逃がす。散らして・減らして・逃がすの3点セットで、フラッシュは実務的な年数を持ちこたえます。
書込みを別ブロックへ振り直すと、古い無効データの入ったブロックが虫食い状に増えます。これを消去して空きへ戻す回収処理が ガベージコレクション で、有効データを別ブロックへ退避してから元ブロックを消去します。この「書込みを分散した結果、実際のフラッシュ書込み量が上位から要求された量より増える」度合いを ライトアンプリフィケーション(書込み増幅) と呼び、これが大きいほど寿命を余計に食う点に注意が要ります。
EEPROM エミュレーション:フラッシュを EEPROM のように使う
多くのマイコンは専用 EEPROM を持たず、設定値の保存にもプログラム用フラッシュを流用します。ところがフラッシュは前述のとおり「1 バイトだけ書き換える」のが苦手で、素朴にやるとブロック全消去が要ります。そこで、フラッシュ上に EEPROM 相当の「バイト単位で更新できる不揮発ストレージ」を仮想的に作る手法が EEPROM エミュレーション です。
鍵となる発想は 追記型ログ(ログ構造) です。値を更新するとき、古いレコードをその場で書き換えるのではなく、新しいレコードを空き領域へ追記 します。フラッシュは「消去済み(全ビット 1)の領域へ 0 を書き込む」ことなら消去なしにできるため、追記は消去を伴いません。読出し時は、同じキーを持つレコードのうち 最も新しいもの を最新値として採用します。
EEPROM エミュレーション:値の更新は「追記」で行う
2ページ(ブロック)を用意し、片方をアクティブに使う
ページA (active) 意味
[key=温度しきい値, val=25, ver=1] ← 最初の書込み
[key=カウンタ, val=100, ver=1]
[key=温度しきい値, val=30, ver=2] ← 上書きではなく追記
[key=カウンタ, val=101, ver=2] ← さらに追記
[ (空き: 全ビット1) ] ← 次の追記先
読出し(温度しきい値) → 同キーで最大 ver の val=30 を返す
ページが満杯 → ガベージコレクション:
1. 各キーの最新レコードだけをページB へコピー
2. ページA を一括消去(全ビット1に戻る)
3. B を active、A を予備に役割交代(ページ入替 / ページスワップ)
この構造には二重の利点があります。第一に、追記のたびに書く物理位置がページ内を前進していくので、自然に書込みが分散し、ページ単位でのウェアレベリングが働きます。第二に、後述する電源断耐性が得やすい——古いレコードを消さずに新レコードを足すだけなので、追記の途中で電源が落ちても、それ以前の完全なレコードはすべて無傷で残ります。ページが満杯になったら最新レコードだけを予備ページへ集約して古いページを消す ページスワップ で容量を再生します。フラッシュの物理レジスタやビット操作を直接叩く際の注意(volatile、読み書き順序)は /embedded/memory-mapped-io/ を参照してください。
追記型では「最新レコードの選び方」が正しさの生命線です。各レコードに単調増加する版数(バージョン)を刻み、読出しは必ず最大版のものを採る。ここでビットが 1 から 0 にしか落とせない性質を活かし、レコードの状態を「空き(11) → 書込み中(10) → 有効(00)」のように多段フラグで表すと、途中で電源が落ちた半端なレコードを『有効』と誤認せずに弾けます。版数をラップ(周回)させる実装では、周回時の大小比較を誤ると古いレコードを最新と誤採用するため、周回を考慮した比較が必須です。
電源断とデータ保全:書き切ってから捨てる
組込み機器はいつ電源が落ちるか分かりません。書込みや消去は前述のとおりミリ秒単位で時間がかかるため、その最中の電源断は現実的な脅威です。不揮発メモリの一貫性を守る大原則は一つ、「新しいデータを別領域へ完全に書き切ってから、初めて古いデータを無効化する」 ——更新をこの順序に固定することです。逆順(古い方を消してから新しい方を書く)にすると、その隙間で電源が落ちた瞬間に「新旧どちらのデータも存在しない」空白が生まれ、設定が失われます。
この順序制約は、/embedded/bootloader-ota-update/ が扱う A/B バンク更新と同じ思想です。あちらは実行イメージ全体、こちらは設定レコード1件と粒度は違えど、「現用面を壊さず別面へ書き、原子的な切替で確定する」構造は共通します。EEPROM エミュレーションの追記型がまさにこれで、旧レコードを残したまま新レコードを追記し、最後に世代番号で最新を指し示すことで切替を果たします。
電源断に安全な更新順序(追記型の場合)
① 新レコードを空き領域へ書き込む(版数 ver=n+1 付き)
└ ここで電源断 → 新レコードは半端。だが旧 ver=n は無傷
→ 起動時、半端レコードを弾き ver=n を最新として復旧
② 新レコードの「有効」フラグを立てる(1→0 の1ビット書換え、原子的)
└ ここで電源断 → ①まで完了。復旧処理で有効化をやり直せる
③ (任意)古いレコードに「無効」フラグを立てる or GC で回収
└ ここで電源断 → 新旧が両方見えるが、版数で新を選べば一貫
不変条件: どの時点で落ちても『完全で最新のレコード』が最低1つ残る
書込みが半端でも、追記型なら旧レコードが残るので復旧できます。より危険なのは 消去(ブロック全消去)の途中 での電源断です。消去中のブロックはビットが不定の中間状態になり、そこに有効データが残っていれば失われます。だからこそ「有効データを別ページへ退避し切ってからブロックを消す」順序が絶対で、消去対象ページには消去前後を示すフラグ(例:消去進行中マーク)を持たせ、起動時に『消去が中断されたページ』を検出して再消去できるようにします。突然の電源断が多い環境では、大容量コンデンサや専用の電源監視 IC で、書込み・消去を完了させるだけの猶予電力を確保する設計も併用します。
電源断からの復帰では、起動直後に不揮発ストレージ全体を走査し、半端なレコードや中断された消去を検出して安定状態へ戻す リカバリ処理 が要ります。この「起動時の自己修復」を、暴走検出でリセットをかける番犬タイマ(/embedded/watchdog-timer/)と組み合わせれば、書込み中ハングも電源断も同じ「起動時に整合性を取り直す」枠組みで扱え、機器は自力で健全な状態へ復帰できます。
「なぜフラッシュは1バイト更新が苦手か」には『書込みは 1→0 のみで、0 を戻すには粗いブロック単位の消去が要るから』。「ウェアレベリングの目的」には『書込みを全ブロックへ均して局所摩耗を防ぎ、実効寿命をブロック数倍に伸ばす』。「EEPROM エミュレーションの原理」には『上書きせず新レコードを追記し、最大版数を最新として読む追記型ログ』。「電源断でデータを守る鍵」には『新データを別領域へ書き切ってから旧データを無効化する順序と、版数・状態フラグによる最新レコードの一意な選択』と答えます。P/E サイクル、ライトアンプリフィケーション、ガベージコレクションの用語も頻出です。
まとめ
- フラッシュ/EEPROM は 書込みが 1→0 の一方通行 で、0 を 1 に戻すには粗い ブロック単位の消去 が要る。読み・書き・消しで単位も時間も食い違うこの非対称が、以降の全手法の出発点。
- 寿命は ブロックあたりの P/E サイクル で決まり、同じ番地への上書きは 局所摩耗 で機器を早死にさせる。寿命を左右するのはセルの耐性より 書込みの偏り。
- ウェアレベリング は書込みを全ブロックへ均し、実効寿命をおおよそブロック数倍まで伸ばす。書込みを散らすと同時に 差分検出・バッファリング で回数自体を減らし、ガベージコレクションと ライトアンプリフィケーション も見込む。
- EEPROM エミュレーション はフラッシュ上に追記型ログを作り、上書きせず新レコードを追記して最大版数を最新とする。追記は消去を伴わず、電源断耐性とページ単位のウェアレベリングを同時に得る。
- 電源断耐性の大原則は 「別領域へ書き切ってから古い方を無効化する」 順序。版数と状態フラグでどの時点の電源断でも完全・最新のレコードが1つ残るよう設計し、起動時のリカバリで整合性を取り直す。
組込み・IoT Article
フラッシュ/EEPROMと摩耗管理を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
組込み
比較で見る軸
難易度: advanced / カテゴリ: 組込み・IoT / タグ数: 5
導入後に効く点
書換え寿命はブロックあたりの消去回数(P/Eサイクル)で決まる。同じ番地を上書きし続けると局所摩耗で先に死ぬため、書込みを全ブロックへ均す(ウェアレベリング)ことで実効寿命をブロック数倍に伸ばす。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- 組込み・IoT
- タグ数
- 5
判断チェックリスト
- 自社の用途が「組込み / フラッシュ」に近いか確認する。
- 強みである「フラッシュは書込みが「1へ消去してから0へ書く」の非対称操作で、消去はページ/ブロック単位と粗く時間もかかる。この消去単位の粗さがウェアレベリングとEEPROMエミュレーションを生む根本原因。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。