代码改变世界

延长EEPROM寿命的算法整理

2011-11-30 19:22  上帝之骰  阅读(4305)  评论(0编辑  收藏  举报

     这是个很老的话题,看到过匠人和其他牛人们2002年左右就开始讨论,深感差距,整理只是想梳理下思路。感谢各位大牛的贡献~~

  本文内容主要参考http://yby696.blog.163.com/blog/static/16700151020115333658670/

  以及匠人原文: http://blog.21ic.com/user1/349/archives/2005/670.html

     开发产品时,常常需要掉电保存一些数据,以防止电源不稳定或发生系统异常,这就需要使用FLASH或EEPROM芯片。这两种芯片,可擦除的次数是有限制的,Nor Flash可以擦除重写10w次以上,Nand Flash类似,而EEPROM的标称寿命一般是100w写入。当然也有寿命很高的非易失性存储器,比如最近宣传很多的FRAM,号称寿命在1000万亿次以上,而且可以以总线速度写入,耗电量还大大降低,价格贵的也不太多,在成本不太敏感的地方可以采用。

  对于Flash,写操作只能将数据位从1写成0,如果要将数据位从0变为1,则必须要进行擦除操作。但是擦除是以块为单位的,通常是256Byte或者更多,因此如果要改写块中的某一字节,往往要伴随着整块内容的备份-擦除-重写。关于Flash的使用寿命,http://topic.csdn.net/u/20090605/07/55a53b1b-6e2e-46f8-8aa6-34b0b2ddf740.html这篇文章给了比较详细的测试结果。

  EEPROM要灵活的多,既可以对数据位进行读操作,也可以进行写操作,不需要单独的擦除操作,一般少量又经常读写的数据都存放在EEPROM中。本文也主要分析EEPROM的寿命延长算法。

 

    其实所有延长EERPOM和Flash寿命的方法都是基于以下条件:读操作对寿命影响不大,写操作(对Nand Flash则为块擦除)次数决定存储器寿命;写入寿命在每个位之间是独立的。当然,假如一个字节中的某一位已经不可靠,估计很少有人会继续坚持使用该字节中剩下的“有效位”吧。因此,一般还是用字节或者字(依存储器组织而定)作为衡量寿命的基本单元。

    

    延长EEPROM寿命的基本思路是:不要只用一个篮子装鸡蛋。对于源源不断的鸡蛋(数据),要么轮流使用所有的篮子,要么在一个篮子破掉的时候立即换用另一个篮子。这样,每个篮子的寿命有限,但是N多篮子一起用,寿命就能大大延长。由此提出的方法很多,列举一些如下:

    

    1、分段循环写入法

     该方法是将存储器等分为多块,每块又分为flag+data+check sum,以256字节存储器为例,按16字节大小分成16等份,按以下格式存储参数

        地址:0x10*n +0x00 +0x01  +0x02 ... +0x09  +0x0A  +0x0B +0x0C  +0x0D +0x0E +0x0F

        内容:               flag   data1   data2  ...  data9   data10   保留1   保留2     保留3   保留4  check_sum

         check_sum=(flag+data1+data2+...+data10+保留1+...+保留4 )&0xFF
       

  flag为0xA5表示当前16个字节为正在使用的记录,为其它值表示当前16字节已经丢弃

  读数据时,寻找flag为0xA5的块,如果读出数据校验失败,则表明数据失效,读取默认数据,改写flag,并将默认数据写入到下一块。如果没有找到flag有效的块,则从第一块开始处理。

      写数据时,从当前有效块的下一块开始写起,并改写flag,可能还要考虑数据写入的安全性,确定正确的写入顺序。

   

  //这种方法可以提高数据安全性,但其实寿命并不能以16倍提升,原因是flag每个写周期要改写两次:使用前写入0xA5,使用后写入不同值,这样flag本身的寿命就要减半

  //考虑的改进方法是:写操作从第一个块开始,直到第一个块数据失效才转为下一个块,这样一来反而使用变址寻址法空间效率更高一些。

     //另一种改进是:用两个flag字节,如果两个字节不同则表示当前块正在使用。初始化时先将所有flag字节写为相同值,如0xaa,如果对某个块写入,则将其flag1+1,在切换至下一块是,再将flag2+1,如此,可保证flag寿命,但是空间利用率进一步降低。

  

  2、变址寻址法

  这种方法将存储器开始字节(地址0x00)定义为变址地址,保存当前所使用的存储单元地址;

  单片机读写EEPROM时,首先读取0x00所保存的变址地址,再对变址地址指向的存储单元进行处理;

    检查当前存储单元数据的可靠性,在写入后立即读取,如果两者不同则认为存储器失效,将变址地址+1.

  //以下是网友soho对此的评论

  由于EEPROM单元坏与不坏界线很是模糊. EEPROM单元能写入信息是因为它的浮栅能俘获电子并将其困在其中. 但随着时间的推移电子由于热运动或外界给予能量会逐渐逃逸, 所以说EEPROM保持信息是有一定年限的(比如100年). 写入与擦除信息即是向浮栅注入和释放电子,电子能量比较高,可能改变周围的晶格结构,导致浮栅俘获电子能力的下降,也就是表现为保存信息的时间变短, 所以才会有一个保守的写入次数限制(这里说保守是因为半导体的离散性大,实际的次数大得多). 到了规定写入次数并不是说该单元就坏了, 而是说该单元保持信息的时间已不可信赖(而实际上它可能还能保存相当长时间甚至几十上百年),所以实际上短时间很难判定某个单元是否可用(坏了). 如匠人的方法检测, 写入时测试好好的, 可能几秒钟之后该单元的数就逃了.

  

  3、冗余表决法

  这是匠人讨论后又提出改进方法,即每次额外写入2个冗余数据,对三个数据进行表决选择,如果其中一个不同则认为此单元失效,需要更换单元。

  

  //空间利用率为33%,但可靠性大大增强

 

  4、保存写入次数

  数据以块为单位存放, 每块可取4个字节, 前三个字节记录写入次数,第四个字节为要保存的数. 每次作写入操作时, 把次数+1连同数据一超写入EEPROM.

EEPROM前面开辟一个索引指针区(可供N个数据保存用), 存放数据块的地址由指针计算所得, 计算方法是:基址+指针值*数据块长度. 当某块区域的写入次数到达时, 调整指针指向下一个可用的数据区域.

  这样,EEPROM的空间利用率为25%, 也是以空间换取寿命的方式

  //优点是可以控制写入次数,从而可靠性可控。在写入多个字节数据时,空间利用率还可以进一步提高。

 

  5、循环计数法(@ymc8)

  将改写频繁的数据有2字节,保存在20H~1FFH中,以4字节页为单位使用,每页中前2字节作为计数器,后2字节为要保存的数据。
  所有计数器第一次使用前先初始化为0FFH。读出数据时先读第一块的计数器,=0,则读下一块的计数器,非0,则直接读出。写入数据时,先读第一块的计数器,=0,则读下一块的计数器,非0,则直接写入,写入后计数器减1,若计数器=0,则同时写入下一块。最后一块写满65535次后,将所有计数器再初始化为0FFH,然后进入下一循环……

  分析: 

   每页中的四个字节写入次数相同,应该具有接近的寿命;
  共使用120页,理论寿命应是只用固定2个字节的120倍;
  大部分操作为读计数器,速度快,并且程序判定=0比较容易;
  空间利用率一般:50%

  //其实既然是循环计数,完全可以用一个字节作为计数器,也就是每个单元每次循环中只写入255次,这样只是循环快一点,配合ram中的指针,对程序效率影响不大

 

总结:正如文前所说,所有延长EEPROM寿命的方法都是用空间换寿命,将整个存储空间分为很多单元,每个单元轮流使用,存储器最终的寿命就是所有单元寿命之和。

细分下来又有两种:一种是对某个单元一直写,直到写坏掉或者次数达到某个阈值才换用下一个,变址循环法、冗余表决法、保存写入次数法都属于此类。这种方法的难点在于单元失效的判断,写入后直接读取固然不可靠,使用冗余表决法和保存写入次数判断可靠性高,但是其空间效率又较低。好处则是:能够充分发挥每个单元的最大寿命,像老板一样榨干每个存储单元的最后一滴血!而且冗余表决法中数据的可靠性也不是一般的高。

另一种是对每个单元写一次或者固定次数,即切换到下一单元。分段循环写入法和循环计数法都是如此。优点是不用判断单元失效,各个单元的任务比较平均,数据可靠性是一个均匀下降的曲线。缺点:如果某个单元的数据变得不可靠,则整个过程就无法正常进行。当然也可以在这个过程中加入对单元失效的判断,不过会造成额外的时间代价。