たまりば

パソコン・インターネット パソコン・インターネット三鷹市 三鷹市

EEPROM書換え上限テスト
2014年11月17日 08:06

PICマイコンに内蔵されていたりするEEPROMには書換え回数に限界がある。
PIC16F628A内蔵のものでは、書換え耐性は最小10万回、標準100万回となっている。
これをテストしてみよう。

なおこれは既にやっている方がいる。
参考: 自己満足系 「PIC耐久試験」

同じことをやっても芸が無いので、書込みデータを変えてみた。
EEPROMの書き込み手順はまず0xFFにクリアしたのち、必要なbitのみ0にするという手順を踏むそうだ。
であれば「0」のみを書き込み続けた場合と「0」「1」を交互に書き込んだ場合では「0」のみの方が早く限界に達するはずだ。
これはMicrochip社のアプリケーションノート「AN537」を見てもそう書いてある。(figure4)
これを確かめるため、書込みデータは「0x5C」と「0xAC」の交互にした。上位(7bit目~4bit目)が0/1交互、3,2bit目が1固定、下位(1,0bit目)が0固定だ。
これが意外な結果になるのだが…。

プログラムは最後に載せるが、内容を説明すると以下のとおり。
・電源ONでまずEEPROMの内容を全て出力し、その後待機状態に入る
・スイッチを押すとLEDを点灯させて開始を示し、1秒後に消灯して処理を開始
・書込み後に読み出してベリファイし、値が異なっていればエラー表示をして終了
・0x5Cと0xACの2回の書き込みを1セットとし、256セットごとにEEPROMに書込み回数を保存
・256セットごとにスイッチを監視して、一時中断が可能

装置はこんな感じ。
EEPROM破壊装置
下の3つは置き場が無くて挿してあるPICで、回路に無関係である。上のコンデンサについては後述する。
橙の線はスイッチ代わり。LEDは開始・終了のインジケーター。もう1個エラー表示もつけようと思ってプログラムもそう書いたのだが、よく考えてみれば終了LEDだけでことが済んだのでつけなかった。
余談だが、LEDが青なのはこの試験中に青色LED開発者がノーベル賞を取ったのでなんとなく元は赤だったのを変えてみたためだ。

試験を開始する。
電源は仕様上3V以上必要なのだが、いつもEneloop2本で動かしているので今回もそれでやってしまった。どうせ厳密な試験をするつもりもなし、動いてるので問題ないだろう。
まず少々処理が進んだところで一時中断し、書き込み速度をチェックする。
どうやら1回あたり5msほど掛かっている。時間からして書き込みは行われているようだ。
(プログラムの製作中は書き込み操作をコメントアウトしていたが、この時はマイクロ秒単位でループが回っていた)
ここから破壊予定を計算してみよう。
100000*5/1000/60=8.33
1000000*5/1000/60/60=1.39
より、最小の10万回ならわずか8分強、標準の100万回でも1時間半ももたない計算になる。
上記の先人の使った16F84は標準1000万回のところ2300万回で破壊に至ったということなので、同じ割合なら3時間ほど。
何日も点けっぱなしにするのは面倒なのでこれは楽でいい。
と思ったのだが…。

試験はTwitterにつぶやきながら行っていたので、ログを見ながら進行状況を書いていく。
10/05 16:55 試験開始
10/05 17:13 開始20分で2*0x1A400回 (※書き込み2回ごとに1カウントしている)
10/05 18:43 標準値を突破、1.3M回超
10/05 20:04 標準寿命の倍を超えたがまだ動く
10/05 21:12 3.1M回を超える
10/05 22:15 2*1D6600回
10/05 23:56 5M回
10/06 01:51 6.5M回
 標準寿命の2,3倍程度だろうと思っていたため、寝る前に終わる予定でいたので困る。
10/06 06:04 結局点けっぱなしで朝、9.5M回
 標準寿命の10倍近くでまだ正常と主張しているのでそろそろプログラムのミスが疑われ始める。
 とりあえず中断し、電池を充電する。
(10/06 21:38 再開時刻記録忘れ。遅くともこの時刻)
10/07 00:22 11,661,312回
 本格的にプログラムのミスが心配になる。
10/07 01:44 ついに終了

以上、延べ17.25時間掛けて、12,663,170回目でエラー発生。なんと標準値の12倍ももつという記録が出た。
書込み回数の記録をグラフにするとこんな感じ。
EEPROM書込み回数グラフ
記録がいい加減(「標準の倍を超えた」とか)なわりには意外と綺麗なグラフになっている。温度で書込み時間に違いが出たりするかと期待していたが、少なくともこのレベルの記録には残らないようだ。

エラーの内容だが、8Cが読めているので、ACを書き込んで8Cが読めた、つまり5bit目が異常になったエラーのようだ。これは不思議なことで、前述のとおり常に0を書いている下位2bitが真っ先にやられるものと予想していた。
それはそうとここから先は未知の領域なのでとりあえず試験を続ける。
まず意外だったのが、一度8Cが読めた箇所を20回ほど読んでみても正しくACが読めることだ。どうやらEEPROMのエラーというものは、一度エラーが出たらそれっきりというものではなく、「書いた値が稀に正常に読めない」という症状を示すようだ。
その後再度同じプログラムで走らせても、しばらくの間は正常にベリファイが走る。やはりエラーは確率的なもののようだ。
これを考えると、テストのやり方は正しくなかったと言える。1千万回で初めてエラーが出たとして、それ以前から確率的に読み取れない状態になっていたのだろうから、ある程度の回数書き込むたびに読み出しが正しくできるかのテストを数百回なり数千回なり読み出してみて調べるべきだろう。まあこれは今後の研究課題ということで。

さてその後も同じプログラムで何度も走らせてみた。結果がこれだ。
EEPROM書込みエラー記録
プログラムを走らせ、エラーが発生して停止するまでに書込みが行われた回数を毎回記録している(最後除く)。
3色の◆の部分が最初と同じプログラムで走らせたものである。
傾向として最初の1266万回から回を追うごとに減っていき、15回目あたりからエラーまでの書き込み回数がほぼ一定になっているのが見て取れる。これは512回である。
最初に書いたとおり、このプログラムでは2回×256セットごと、すなわち512回ごとにEEPROMに書込み回数を保存している。つまりこの書込み直後の読み込みが失敗しやすいようである。
この原因として書込みによって電圧が下がっている可能性を考え、コンデンサを付けてみた(赤部分)。するとしばらくの間書き込みエラーが起こりにくくなったような気もするが、またコンデンサを外しても14万回とかなりの回数が出たのでよく分からない。
もう少し確かめてみたかったが、そうこうしている間に別のエラーが発生した。
部分が、読めた値が2Cであった部分である。AC→2Cになったものであろうから、7bit目が異常になったようだ。またも上位bitからエラーが発生した。このとき累計書込み回数1663万回。
その後数回2Cが続き、再び8Cでのエラーが発生。
ここで、プログラムを変更する。異常になった2つのbitをマスクして、他のbitのみを見るようにした。これが紫部分。
変更後最初のエラーまでに64万回、累計1765万回。1Cが読めたので、5C→1Cで6bit目が異常。
その後2回ほど試したのち、またマスクを変更。3つのbitをマスクする。これが水色部分。(なぜか凡例だけマークが「*」になっているが、Excelが異常で凡例のマークをどうやっても変更できなかった)
変更後最初のエラーまでに209万回、累計1974万回。4Cが読めたので、5C→4Cで4bit目が異常。
これで上位4bitが全て異常になり、下位4bitには異常が見られないという状況になった。
不可解だがとりあえず続けよう。マスクを変更。上位4bitをマスクするようにした。
すると、いつまで走らせても一向にエラーが起こらない。累計1億回、すなわち標準寿命の100倍に達してなおエラーが出なかったので、あきらめて試験を終了することにした。この時点が橙である。

以上まとめると、0/1を交互に書き込んだ場合は標準寿命の10~20倍で最初のエラーが発生し、0のみまたは1のみを書き込んだ場合は標準寿命の100倍を超えてもエラーが発生しないという結果になった。
資料から予想した結果と違っており不可解だが、この結果から考えられることとして、PICのEEPROMは前回と同じ値を書き込むときは消去・書込み動作を行わないのだろうか。
EEPROMはバイトごとに消去が行われるものと思っているのだが、bitごとの消去ができるものもあるのだろうか。この辺も今後の研究課題である。

また、エラー発生時の累計回数の生データを見ると1つ不思議なことが分かる。以下がそのデータだ。
609CC0, 6545D4, 675110, 67C8C0, 6A3690, 6A3700, 6AB598, 6AB600, 6AB710, 6AB800, 6AC59C, 6B08C0, 6B0D58, 6B14D0, 6B1500, 6B1600, 6B1700, 6B1800, 6B1900, 6B1A00, 6B1B00, 6B1C00, 6B1D00, 6B1E00, 6B1F84, 6B2048, 6B2100, 6B2200, 6B2304, 6B2400, 6B2500, 6B2600, 6B2700, 6B3380, 6CE7D8, 6CE800, 6D0904, 6D2590, 6D2654, 6D2710, 6D2800, 6D4184, 6D4200, 6EB540, 702D10, 702E10, 702F00, 703000, 745114, 7A5C14, 7A5D10, 7A5E04, 7D1404, 7D1504, 7E2B0C, 7E2C00, 7E2D00, 7EE104, 7EE200, 7EE30C, 7EED00, 81C300, 86A514, 86A600, 86A70C, 969F00
1の位に0,4,8,Cと4の倍数しか現れていない。00になるのはEEPROM書込み直後なので分かるとして、4の倍数、すなわち8回ごとにエラーが起こる理由が分からない。内部構造に原因があるのだろうか。今後の研究課題だ。研究課題多いなあ。

最後にコード。
    list p=16f628a
    #include p16f628a.inc
    radix dec
    __CONFIG _INTOSC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_ON & _CP_OFF & _LVP_OFF & _MCLRE_ON & _BODEN_OFF

    radix dec


;     RA2 +-v-+ RA1
;     RA3 |   | RA0
;     RA4 |   | RA7
; Vpp RA5 |   | RA6
;     GND |   | Vdd
;     RB0 |   | RB7 PGD
;     RB1 |   | RB6 PGC
;     RB2 |   | RB5
;     RB3 +---+ RB4

; EEPROM制御がしやすいよう、変数はbank1にとる
    cblock 0xA0
        targetaddr
        dataaddr
        cnt0,cnt1,cnt2
        cnt232
        dat,datbuf
        waitcnt0,waitcnt1
    endc
; 結局使わなかった割り込み退避用変数
    cblock 0x70
        wbuf
        sbuf
    endc


#define TESTPTR 0x00
#define MASK 0xFF ;結果をマスクするときここを変える

;pin
#define TXPIN232 0
#define BUTTON 1
#define ERRLED 2
#define STOPLED 3

    org 0
    goto init

    org 4
    retfie

init:

    bsf STATUS,RP0 ;BANK1
    movlw b'00000011'
    ;       ^/pull up enable
    ;        ^int edge
    ;         ^tmr0 clock source 0:internal
    ;          ^tmr0 source edge
    ;           ^prescaler assignment 0:tmr
    ;            ^^^prescaler rate
    movwf OPTION_REG
    movlw 1<<BUTTON
; 間違ってPORTBの値を設定する前にTRISBを設定しているのでたまにシリアル出力の先頭が化けることに後で気づいたが直し忘れた
    movwf TRISB
    bcf STATUS,RP0
    movlw 0x07
    movwf CMCON
; EEPROMの制御のためコードはすべて全てbank1で走らせる
; そのため変数はすべてbank1にとった
; bank0のレジスタでPORTBだけ必要なのでINDFをPORTBにしておく
    banksel 0x80 ;bank1
    movlw PORTB
    movwf FSR ;INDF=PORTB
    clrf INDF
    clrf cnt232
; EEPROMに保存してある書込み回数カウントを取得
    movlw (~TESTPTR)&0x7F
    movwf targetaddr
    movlw TESTPTR*4
    movwf dataaddr
    
    movf dataaddr,W
    movwf EEADR
    bsf EECON1,RD
    movf EEDATA,W
    movwf cnt2
    incf dataaddr,W
    movwf EEADR
    bsf EECON1,RD
    movf EEDATA,W
    movwf cnt1
    movf dataaddr,W
    addlw .2
    movwf EEADR
    bsf EECON1,RD
    movf EEDATA,W
    movwf cnt0
; 書込み回数カウントを…と思ったが面倒なのでEEPROM全体をシリアルに出力
    movlw '/'
    call tx232
showeep:
    clrf EEADR
showeeploop:
    bsf EECON1,RD
    movf EEDATA,W
    call tx232hex
    incf EEADR,F
    btfss EEADR,7
    goto showeeploop
; スタート待ち
    btfsc INDF,BUTTON
    goto $-1
; LEDを1秒点灯させスタート表示 (停止表示用だった名残が名前に見える)
    bsf INDF,STOPLED
    
    call wait1s
    
    bcf INDF,STOPLED

main:
    movf targetaddr,W
    movwf EEADR

mainloop:
; データAを書込み、読み取り、比較
    movlw 0x5C ;データA
    call writeeep
    clrf EEDATA
    bsf EECON1,RD
    movlw 0x5C
    xorwf EEDATA,W
    andlw MASK
    btfss STATUS,Z
    goto err

    movlw 0xAC ;データB
    call writeeep
    clrf EEDATA
    bsf EECON1,RD
    movlw 0xAC
    xorwf EEDATA,W
    andlw MASK
    btfss STATUS,Z
    goto err

    incfsz cnt0
    goto f
; 256カウントごとの操作
; 上位カウント
    incf cnt1,F
    btfsc STATUS,Z
    incf cnt2,F
    bcf STATUS,C

; カウント値を保存
    movf dataaddr,W
    movwf EEADR
    movf cnt2,W
    call writeeep
    
    incf dataaddr,W
    movwf EEADR
    movf cnt1,W
    call writeeep
    
    movlw 0x02
    addwf dataaddr,W
    movwf EEADR
    movf cnt0,W
    call writeeep

; ボタンを確認し、押されていれば中断
    btfss INDF,BUTTON
    goto stop

f:
    goto mainloop

; 書込みエラーが発生したら、カウント下位および読み取った値を書き込んで、LEDを点灯させ停止
err:
    bsf dataaddr,1
    incf dataaddr,W
    movwf EEADR
    movf EEDATA,W
    call writeeep
    movf dataaddr,W
    movwf EEADR
    movf cnt0,W
    call writeeep
    
    bsf INDF,ERRLED

stop:
    bsf INDF,STOPLED
    goto $

; EEPROM書込みサブルーチン
; WをEEADRで示すアドレスに書き込む
writeeep:
    movwf EEDATA
    bsf EECON1,WREN
    movlw 0x55
    movwf EECON2
    movlw 0xAA
    movwf EECON2
    bsf EECON1,WR
    btfsc EECON1,WR
    goto $-1
    return

; Wを16進表記でシリアル出力 (IOピン直結)
tx232hex:
    movwf datbuf
    swapf datbuf,W
    call sendnibble
    movf datbuf,W
    call sendnibble
    return

sendnibble:
    andlw 0x0F
    movwf dat
    movlw .6
    addwf dat,F
    movlw '0'-.6
    btfsc STATUS,DC
    movlw 'A'-.10-.6
    addwf dat,W
; Wをシリアル出力
tx232:
    movwf dat
    bsf INDF,TXPIN232
    bsf cnt232,3
tx232loop:
    rrf dat,F
    movf INDF,W
    andlw ~(1<<TXPIN232)
    btfss STATUS,C
    iorlw 1<<TXPIN232
    movwf INDF
    decfsz cnt232,F
    goto tx232loop
    goto $+1
    goto $+1
    nop
    bcf INDF,TXPIN232
    
    retlw 0

; 1秒くらい待つ
wait1s:
    clrf waitcnt0
    clrf waitcnt1
waitloop0:
waitloop1:
    goto $+1
    goto $+1
    goto $+1
    goto $+1
    goto $+1
    goto $+1
    decfsz waitcnt1
    goto waitloop1
    decfsz waitcnt0
    goto waitloop0
    
    return

; 初回のみここのコメントを外してビルドしEEPROMのデータ保存領域を0で初期化する
;    org 0x2100
;    dt 0,0,0,0,0,0
    
    end

  • 同じカテゴリー(プログラム・アルゴリズム)の記事画像
    6段のカレンダーが好きだ
    ファミコンで9×9ドット文字表示(ほか)
    JPEG圧縮を繰り返しても際限なく劣化するわけではない
    ゲームボーイの吸い出し機を作った (後編)
    ファミコンで全画面に任意の画像(ただしモノクロ)を表示
    最近のWindowsのビットマップフォントの太字
    同じカテゴリー(プログラム・アルゴリズム)の記事
     CDのピットの数 (2021-03-05 23:32)
     6段のカレンダーが好きだ (2020-11-15 04:06)
     PIC16のDhrystone MIPSを測ろうとしてみた (2020-04-29 02:23)
     Twitterの画像の扱いがやっとまともになって嬉しい (2019-06-23 00:23)
     ファミコンで9×9ドット文字表示(ほか) (2019-04-08 02:02)
     謎の色名「honeydewtab」とlegacy color valueパース手順 (2018-03-24 12:45)
    URL欄を実験的に消してる間に廃止されてしまいました。まあいいか。
     
    <ご注意>
    書き込まれた内容は公開され、ブログの持ち主だけが削除できます。
    削除
    EEPROM書換え上限テスト
      コメント(0)