所谓Flash,是内存(Memory)的一种,但兼有RAM和ROM 的优点,是一种可在系统(In-System)进行电擦写,掉电后信息不丢失的存储器,同时它的高集成度和低成本使它成为市场主流。
    Flash 芯片是由内部成千上万个存储单元组成的,每个单元存储一个bit。具有低功耗、大容量、擦写速度快、可整片或分扇区在系统编程(烧写)、擦除等特点,并且可由内部嵌入的算法完成对芯片的操作,因而在各种嵌入式系统中得到了广泛的应用。
作为一种非易失性存储器,Flash在系统中通常用于存放 程序 代码、常量表以及一些在系统掉电后需要保存的用户数据等。
常用的Flash为8位或16位的数据宽度,编程电压为单3.3V。主要的生产厂商为INTEL、ATMEL、AMD、HYUNDAI等。Flash 技术根据不同的应用场合也分为不同的发展方向,有擅长存储代码的NOR Flash和擅长存储数据的NAND Flash。一下对NOR Flash和NAND Flash的技术分别作了相应的介绍。
一.NOR Flash
1.      市场介绍
随着技术的发展,愈来愈多的电子产品需要更多的智能化,这也对这些产品的程序存储提出了更高的要求。Flash 作为一种低成本、高集成度的存储技术在电子产品领域的应用非常广泛。今天90%的PC、超过90%的手机、超过50%的Modem,都是用了Flash,如今Flash市场规模已经超过了100亿美元。
如此巨大的市场规模,也导致市场上的Flash 品牌层出不穷。在NOR Flash市场中,Intel公司是非常重要的一家生产厂商。Intel公司生产的Flash芯片多年来占据着市场的很大份额,而它的芯片封装形式和接口也成为业界标准,从而为不同品牌的Flash带来了兼容的方便。
2.      NOR Flash 的硬件设计和调试
首先,Flash 要通过系统总线接在处理器上,即保持一个高速的数据交换的通道。那么就必须了解一下Flash在系统总线上的基本操作。
1) 先了解一下处理器存储空间BANK的概念。以32位处理器S3C2410为例,理论上可以寻址的空间为4GB,但其中有3GB的空间都预留给处理器内部的寄存器和其他设备了,留给外部可寻址的空间只有1GB,也就是0X00000000~0X3fffffff,总共应该有30根地址线。这1GB的空间,2410处理器又根据所支持的设备的特点将它分为了8份,每份空间有128MB,这每一份的空间又称为一个BANK。为方便操作,2410独立地给了每个BANK一个片选信号(nGCS7~nGCS0)。其实这8个片选信号可以看作是2410处理器内部30根地址线的最高三位所做的地址译码的结果。正因为这3根地址线所代表的地址信息已经由8个片选信号来传递了,因此2410处理器最后输出的实际地址线就只有A26~A0(如下图1)

图1 2410内存BANK示意图
    2)以图2(带nWAIT信号)为例,描述一下处理器的总线的读操作过程,来说明Flash整体读、写的流程。第一个时钟周期开始,系统地址总线给出需要访问的存储空间地址,经过Tacs时间后,片选信号也相应给出(锁存当前地址线上地址信息),再经过Tcso时间后,处理器给出当前操作是读(nOE为低)还是写(new为低),并在Tacc时间内将数据数据准备好放之总线上,Tacc时间后(并查看nWAIT信号,为低则延长本次总线操作),nOE 拉高,锁存数据线数据。这样一个总线操作就基本完成

图2 带nWAIT 信号的总线读操作
3 ) NOR Flash的接口设计(现代的29LV160芯片)
29LV160存储容量为8M字节,工作电压为3.3V,采用56脚TSOP封装或48脚FBGA封装,16位数据宽度。29LV160仅需单3.3V电压即可完成在系统的编程与擦除操作,通过对其内部的命令寄存器写入标准的命令序列,可对Flash进行编程(烧写)、整片擦除、按扇区擦除以及其他操作。引脚信号描述和接口电路分别如图3和图4所示。

图3 29LV160引脚信号描述

图4 FLASH(29LV160)接口电路
可以从信号引脚图3和总线操作图2看出,NOR Flash的接口和系统总线接口完全匹配,可以很容易地接到系统总线上。
3.      NOR Flash的软件设计
Flash 的命令很多,但常用到的命令就3种:识别、擦除、编程命令。以下就对3种命令作分别的简要介绍:
1) NOR Flash的识别
29lv160_CheckId()
{
    U8 tmp;
    U16 manId,devId;
    int i;
    _RESET();
    
    _WR(0x555,0xaa);
    _WR(0x2aa,0x55);
    _WR(0x555,0x90);
    manId=_RD(0x0);
    devId=_RD(0x1);
    _RESET();   
    printf("Manufacture ID(0x22C4)=%4x, Device ID(0x2249)=%4x\n",manId,devId);
    if(manId == 0x22C4 && devId == 0x2249)
        return 1;
    else 
        return 0;
}
NOR Flash 的识别程序由四个读写周期就可以完成,在Flash的相关命令表中可以查到相应ID识别的命令。
2) NOR Flash的擦除
要对NOR Flash进行写操作,就一定要先进性擦除操作。NOR Flash 的擦除都是以块(sector)为单位进行的,但是每一种型号的Flash的sector的大小不同,即使在同一片的Flash内,,不同sector的大小也是不完全一样的。
void 29lv160db_EraseSector(int targetAddr)
{
        printf("Sector Erase is started!\n");
    _RESET();
    _WR(0x555,0xaa);
    _WR(0x2aa,0x55);
    _WR(0x555,0x80);
    _WR(0x555,0xaa);
_WR(0x2aa,0x55);
_WR(BADDR2WADDR(targetAddr),0x30); 
return _WAIT(BADDR2WADDR(targetAddr);    
}
                  

                          
                       图5  Erase Operation     
/**************
如上图5所示,擦除操作时还要有一个关键的操作擦除查询算法,即等待Flash擦除的过程,并返回擦除是否成功的结果。算法如右图6所示
****************/
Int _WAIT(void)
{
unsigned int state,flashStatus,old;
old=_RD(BADDR2WADDR(0x0));
while(1)
    {
        flashStatus=_RD(BADDR2WADDR(0x0));
        if( (old&0x40) == (flashStatus&0x40) )
            break;
        if( flashStatus&0x20 )
        {
        //printf("[DQ5=1:%x]\n",flashStatus);
        old=_RD(BADDR2WADDR(0x0));
        flashStatus=_RD(BADDR2WADDR(0x0));
        if( (old&0x40) == (flashStatus&0x40) )
            return 0;
        else return 1;
        }
        //printf(".");
        old=flashStatus;
    }                                            //printf("!\n");
    return 1; 
}

图6  Toggle Bit Algorithm
        
以上的方法为查询数据线上的一个特定位Toggle位。此外还有2种检测方法,一种为提供额外的Busy信号,处理器通过不断查询Busy信号来得知Flash的擦除操作是否完成,一般较少应用;一种为查询Polling位。
3) NOR Flash 的编程操作
int 29lv160db_ProgFlash(U32 realAddr,U16 data)
{
        _WR(0x555,0xaa);
        _WR(0x2aa,0x55);
        _WR(0x555,0xa0);
        _WR(BADDR2WADDR(realAddr),data);
           return _WAIT(BADDR2WADDR(realAddr); 
          
}
对擦除过的Flash进行编程比较简单,但仍然用到以上提到的查询算法,速度比较慢,一般为20uS,最长的达到500uS 。
二.NAND FLASH
NAND FLASH 在对大容量的数据存储需要中日益发展,到现今,所有的数码相机、多数MP3播放器、各种类型的U盘、很多PDA里面都有NAND FLASH的身影。
1.      Flash的简介
NOR Flash :
u       程序和数据可存放在同一片芯片上,拥有独立的数据总线和地址总线,能快速随机地读取,允许系统直接从Flash中读取代码执行,而无需先将代码下载至RAM中再执行
u       可以单字节或单字编程,但不能单字节擦除,必须以块为单位或对整片执行擦除操作,在对存储器进行编程之前需要对块或整片进行预编程和擦除操作。
NAND FLASH 
u       以页为单位进行读写操作,1页为256B或512B;以块为单位进行擦除操作,1块为4KB、8KB或16KB。具有快编程和快擦除的功能
u       数据、地址采用同一总线,实现串行读取。随机读取速度慢且不能按字节随机编程 
u       芯片尺寸小,引脚少,是位成本(bit cost)最低的固态存储器
u       芯片存储位错误率较高,推荐使用 ECC校验,并包含有冗余块,其数目大概占1%,当某个存储块发生错误后可以进行标注,并以冗余块代替
u       Samsung、TOSHIBA和Fujistu三家公司支持采用NAND技术NAND Flash。目前,Samsung公司推出的最大存储容量可达8Gbit。NAND 主要作为SmartMedia卡、Compact Flash卡、PCMCIA ATA卡、固态盘的存储介质,并正成为Flash磁盘技术的核心。
2.      NAND FLASH 和NOR FLASH 的比较
1)       性能比较
    flash闪存是非易失存储器,可以对称为块的存储器单元块进行擦写和再编程。任何flash器件的写入操作只能在空或已擦除的单元内进行,所以大多数情况下,在进行写入操作之前必须先执行擦除。NAND器件执行擦除操作是十分简单的,而NOR则要求在进行擦除前先要将目标块内所有的位都写为0。
    由于擦除NOR器件时是以64~128KB的块进行的,执行一个写入/擦除操作的时间为5s,与此相反,擦除NAND器件是以8~32KB的块进行的,执行相同的操作最多只需要4ms。
    执行擦除时块尺寸的不同进一步拉大了NOR和NADN之间的性能差距,统计表明,对于给定的一套写入操作(尤其是更新小文件时),更多的擦除操作必须在基于NOR的单元中进行。这样,当选择存储解决方案时,设计师必须权衡以下的各项因素。
        ● NOR的读速度比NAND稍快一些。
        ● NAND的写入速度比NOR快很多。
        ● NAND的4ms擦除速度远比NOR的5s快。
        ● 大多数写入操作需要先进行擦除操作。
        ● NAND的擦除单元更小,相应的擦除电路更少。
2)       接口差别
    NOR flash带有SRAM接口,有足够的地址引脚来寻址,可以很容易地存取其内部的每一个字节。
NAND器件使用复杂的I/O口来串行地存取数据,共用8位总线(各个产品或厂商的方法可能各不相同)。8个引脚用来传送控制、地址和数据信息。NAND读和写操作采用512字节的页和32KB的块为单位,这一点有点像硬盘管理此类操作,很自然地,基于NAND的存储器就可以取代硬盘或其他块设备。
3)       容量和成本
NAND flash的单元尺寸几乎是NOR器件的一半,由于生产过程更为简单,NAND结构可以在给定的模具尺寸内提供更高的容量,也就相应地降低了价格,大概只有NOR的十分之一。
NOR flash占据了容量为1~16MB闪存市场的大部分,而NAND flash只是用在8~128MB的产品当中,这也说明NOR主要应用在代码存储介质中,NAND适合于数据存储,NAND在CompactFlash、Secure Digital、PC Cards和MMC存储卡市场上所占份额最大。
4)       可靠性和耐用性
采用flahs介质时一个需要重点考虑的问题是可靠性。对于需要扩展MTBF的系统来说,Flash是非常合适的存储方案。可以从寿命(耐用性)、位交换和坏块处理三个方面来比较NOR和NAND的可靠性。
在NAND闪存中每个块的最大擦写次数是一百万次,而NOR的擦写次数是十万次。NAND存储器除了具有10比1的块擦除周期优势,典型的NAND块尺寸要比NOR器件小8倍,每个NAND存储器块在给定的时间内的删除次数要少一些。
5)       位交换(错误率)
所有flash器件都受位交换现象的困扰。在某些情况下(很少见,NAND发生的次数要比NOR多),一个比特位会发生反转或被报告反转了。一位的变化可能不很明显,但是如果发生在一个关键文件上,这个小小的故障可能导致系统停机。如果只是报告有问题,多读几次就可能解决了。当然,如果这个位真的改变了,就必须采用错误探测/错误更正(EDC/ECC)算法。位反转的问题更多见于NAND闪存,NAND的供应商建议使用NAND闪存的时候,同时使用EDC/ECC算法。
这个问题对于用NAND存储多媒体信息时倒不是致命的。当然,如果用本地存储设备来存储操作系统、配置文件或其他敏感信息时,必须使用EDC/ECC系统以确保可靠性。
6)       坏块处理
NAND器件中的坏块是随机分布的。以前也曾有过消除坏块的努力,但发现成品率太低,代价太高,根本不划算。NAND器件需要对介质进行初始化扫描以发现坏块,并将坏块标记为不可用。现在的FLSAH一般都提供冗余块来代替坏块如发现某个块的数据发生错误(ECC校验),则将该块标注成坏块,并以冗余块代替。这导致了在NAND Flash 中,一般都需要对坏块进行编号管理,让每一个块都有自己的逻辑地址。
7)       易于使用
可以非常直接地使用基于NOR的闪存,可以像其他存储器那样连接,并可以在上面直接运行代码。由于需要I/O接口,NAND要复杂得多。各种NAND器件的存取方法因厂家而异。在使用NAND器件时,必须先写入驱动程序,才能继续执行其他操作。向NAND器件写入信息需要相当的技巧,因为设计师绝不能向坏块写入,这就意味着在NAND器件上自始至终都必须进行虚拟映射。
8)       软件支持
当讨论软件支持的时候,应该区别基本的读/写/擦操作和高一级的用于磁盘仿真和闪存管理算法的软件,包括性能优化。在NOR器件上运行代码不需要任何的软件支持,在NAND器件上进行同样操作时,通常需要驱动程序,也就是内存技术驱动程序(MTD),NAND和NOR器件在进行写入和擦除操作时都需要MTD。使用NOR器件时所需要的MTD要相对少一些,许多厂商都提供用于NOR器件的更高级软件,这其中包括M-System的TrueFFS驱动,该驱动被Wind River System、Microsoft、QNX Software System、Symbian和Intel等厂商所采用。驱动还用于对DiskOnChip产品进行仿真和NAND闪存的管理,包括纠错、坏块处理和损耗平衡。
在掌上电脑里要使用NAND FLASH 存储数据和程序,但是必须有NOR FLASH来启动。除了SAMSUNG处理器,其他用在掌上电脑的主流处理器还不支持直接由NAND FLASH 启动程序。因此,必须先用一片小的NOR FLASH 启动机器,在把OS等软件从NAND FLASH 载入SDRAM中运行才行。
9)       主要供应商
NOR FLASH的主要供应商是INTEL ,MICRO等厂商,曾经是FLASH的主流产品,但现在被NANDFLASH挤的比较难受。它的优点是可以直接从FLASH中运行程序,但是工艺复杂,价格比较贵。
NAND FLASH的主要供应商是SAMSUNG和东芝,在U盘、各种存储卡、MP3播放器里面的都是这种FLASH,由于工艺上的不同,它比NORFLASH拥有更大存储容量,而且便宜。但也有缺点,就是无法寻址直接运行程序,只能存储数据。另外NAND FLASH非常容易出现坏区,所以需要有校验的算法。
3.NAND Flash的硬件设计
NAND FLASH是采用与非门结构技术的非易失存储器,有8位和16位两种组织形式,下面以8位的NAND FLASH进行讨论。
1)      接口信号
与NOR Flash相比较,其数据线宽度只有8bit,没有地址总线,I/O接口可用于控制命令和地址的输入,也可用于数据的输入和输出,多了CLE和ALE来区分总线上的数据类别。
信号
类型
描述
CLE
O
命令锁存使能
ALE
O
地址锁存使能
nFCE
O
NAND Flash片选
NFRE
O
NAND Flash读使能
nFWE
O
NAND Flash写使能
NCON
I
NAND Flash配置
R/nB
I
NAND Flash Ready/Busy

2)      地址结构
NAND FLASH主要以页(page)为单位进行读写,以块(block)为单位进行擦除。FLASH页的大小和块的大小因不同类型块结构而不同,块结构有两种:小块(图7)和大块(图8),小块NAND FLASH包含32个页,每页512+16字节;大块NAND FLASH包含64页,每页2048+64字节。

图7 小块类型NAND FLASH


图8 大块类型NAND FLASH
其中,512B(或1024B)用于存放数据,16B(64B)用于存放其他信息(包括:块好坏的标记、块的逻辑地址、页内数据的ECC校验和等)。NAND设备的随机读取得效率很低,一般以页为单位进行读操作。系统在每次读一页后会计算其校验和,并和存储在页内的冗余的16B内的校验和做比较,以此来判断读出的数据是否正确。
大块和小块NAND FLASH都有与页大小相同的页寄存器,用于数据缓存。当读数据时,先从NAND FLASH内存单元把数据读到页寄存器,外部通过访问NAND FLASH I/O端口获得页寄存器中数据(地址自动累加);当写数据时,外部通过NAND FLASH I/O端口输入的数据首先缓存在页寄存器,写命令发出后才写入到内存单元中。
3)      接口电路设计(以下以2410和K9F1208U为例)
2410处理器拥有专门针对 NAND设备的接口,可以很方便地和NAND设备对接,如图9所示。虽然NAND设备的接口比较简单,容易接到系统总线上,但2410处理器针对NAND设备还集成了硬件ECC校验,这将大大提高NAND设备的读写效率。当没有处理器的ECC支持时,就需要由软件来完成ECC校验,这将消耗大量的CPU资源,使读写速度下降。

图9  S3C2410与NAND FLASH接口电路示意图
3.NAND FLASH 的软件编写和调试
     NAND设备的软件调试一般分为以下几个步骤:设置相关寄存器、NAND 设备的初始化、NAND设备的识别、NAND设备的读擦写(带ECC校验 )
    NAND设备的操作都是需要通过命令来完成,不同厂家的命令稍有不同,以下一Samsung公司的K9F1208U0M命令表为例介绍NAND设备的软件编写。

表2 K9F1208U0M Command Sets

1)      根据2410寄存器定义如下的命令宏
#define NF_CMD(cmd) {rNFCMD=cmd;}
#define NF_ADDR(addr)   {rNFADDR=addr;} 
#define NF_nFCE_L() {rNFCONF&=~(1
#define NF_nFCE_H() {rNFCONF|=(1
#define NF_RSTECC() {rNFCONF|=(1
#define NF_RDDATA()     (rNFDATA)
#define NF_WRDATA(data) {rNFDATA=data;}
#define NF_WAITRB()    {while(!(rNFSTAT&(1
        //wait tWB and check F_RNB pin.

2)      NAND 设备的初始化
static void NF_Init(void)                          //Flash 初始化
{
rNFCONF=(1                                            //设置NAND设备的相关寄存器
    // 1 1    1     1,   1      xxx, r xxx,   r xxx        
    // En 512B 4step ECCR nFCE="H" tACLS   tWRPH0   tWRPH1
    
    NF_Reset();
}
static void NF_Reset(void)                           //Flash重置 
{
    int i;
    NF_nFCE_L();
    NF_CMD(0xFF);                      //reset command
    for(i=0;i               //tWB = 100ns
    NF_WAITRB();                        //wait 200~500us;
    NF_nFCE_H();
}
3)      NAND设备的识别                    //#define ID_K9F1208U0M 0xec76
static U16 NF_CheckId(void)                           //Id 辨别
{
    int i;
    U16 id;
    
    NF_nFCE_L();
    NF_CMD(0x90);
    NF_ADDR(0x0);
    
    for(i=0;i                 //wait tWB(100ns)
    
    id=NF_RDDATA()                 // Maker code(K9F1208U:0xec)
    id|=NF_RDDATA();                    // Devide code(K9F1208U:0x76)
    
    NF_nFCE_H();
    return id;
}
4)      NAND 的擦操作
static int NF_EraseBlock(U32 block)
{
    U32 blockPage=(block
    int i;
    NF_nFCE_L();
    
    NF_CMD(0x60
[q1]
);                          // Erase one block 1st command
    NF_ADDR(blockPage&0xff);                // Page number="0"
    NF_ADDR((blockPage>>8)&0xff);   
    NF_ADDR((blockPage>>16)&0xff);
    NF_CMD(0xd0
[q2]
);                           // Erase one blcok 2nd command
    
   for(i=0;i                       //wait tWB(100ns)//??????
    NF_WAITRB();                            // Wait tBERS max 3ms.
    NF_CMD(0x70);                           // Read status command
    if (NF_RDDATA()&0x1)                    // Erase error
    {   
        NF_nFCE_H();
    Uart_Printf("[ERASE_ERROR:block#=%d]\n",block);
        return 0;
    }
    else 
    {
        NF_nFCE_H();
        return 1;
    }
}

5)      NAND 的读操作
static int NF_ReadPage(U32 block,U32 page,U8 *buffer)       //读Flash
{
    int i;
    unsigned int blockPage;
    U8 ecc0,ecc1,ecc2;
    U8 *bufPt=buffer;
    U8 se[16];    
    
    page=page&0x1f;                                 //32页
    blockPage=(block                      //1Bolck包含32页
    NF_RSTECC();                                    // Initialize ECC
    
    NF_nFCE_L();    
    NF_CMD(0x00);                                   // Read command
    NF_ADDR(0);                                     // Column = 0
    NF_ADDR(blockPage&0xff);                        //
    NF_ADDR((blockPage>>8)&0xff);                   // Block & Page num.
  NF_ADDR((blockPage>>16)&0xff);                 //
    for(i=0;i                             //wait tWB(100ns)
    
    NF_WAITRB();                                    // Wait tR(max 12us)
    for(i=0;i
    {
        *bufPt++=NF_RDDATA();                       // Read one page
}
/************************ECC校验***************************/
   ecc0=rNFECC0;                              //利用2410自带的硬件ECC校验
    ecc1=rNFECC1;
    ecc2=rNFECC2;
[q3]
     for(i=0;i
    {
        se =NF_RDDATA();                         // Read spare array
                                                    //读页内冗余的16B
    } 
       NF_nFCE_H();    
    if(ecc0==se[0] && ecc1==se[1] && ecc2==se[2])   //未知使用哪一种软件规范?
    {                                               //比较数据结果是否正确
    Uart_Printf("[ECC OK:%x,%x,%x]\n",se[0],se[1],se[2]);
        return 1;
    }
    else
    {
    Uart_Printf("[ECC ERROR(RD):read:%x,%x,%x, reg:%x,%x,%x]\n",
        se[0],se[1],se[2],ecc0,ecc1,ecc2);
        return 0;
    }       
}
6)      NAND 的写操作
static int NF_WritePage(U32 block,U32 page,U8 *buffer)             //写Flash 
{
    int i;
    U32 blockPage=(block
    U8 *bufPt=buffer;
    NF_RSTECC();                                // Initialize ECC
    
    NF_nFCE_L(); 
    NF_CMD(0x0
[q4]
);                                //?????\\Read Mode 1
    NF_CMD(0x80);                               // Write 1st command,数据输入
    NF_ADDR(0);                                 // Column 0
    NF_ADDR(blockPage&0xff);        
    NF_ADDR((blockPage>>8)&0xff);               // Block & page num.
    NF_ADDR((blockPage>>16)&0xff); 
    for(i=0;i
    {
    NF_WRDATA(*bufPt++);                    // Write one page to NFM from buffer
    } 
    
    seBuf[0]=rNFECC0;
    seBuf[1]=rNFECC1;
    seBuf[2]=rNFECC2;
    seBuf[5]=0xff;                          // Marking good block
    
    for(i=0;i
    {
    NF_WRDATA(seBuf);                    // Write spare array(ECC and Mark)
    } 
[q5]

NF_CMD(0x10);                           // Write 2nd command
    
    for(i=0;i                     //tWB = 100ns. ??????
    NF_WAITRB();                            //wait tPROG 200~500us;
    NF_CMD(0x70);                           // Read status command   
    
    for(i=0;i                      //twhr=60ns
    
    if (NF_RDDATA()&0x1)                    // Page write error
    {   
        NF_nFCE_H();
    Uart_Printf("[PROGRAM_ERROR:block#=%d]\n",block);
    return 0;
    }
    else 
    {
        NF_nFCE_H();
    #if (WRITEVERIFY==1)
    //return NF_VerifyPage(block,page,pPage);   
    #else
    return 1;
    #endif
    }
}
以下讨论一下NAND 设备上所支持的文件系统,大概现在有以下几种:
A.       JFFS2(没有坏块处理,支持大容量存储的时候需要消耗大量的内存,大量的随机访问降低了NAND设备的读取效率)和YAFFS(速度快,但不支持文件的压缩和解压)
B.       支持DiskOnChip设备的TRUEFFS(True Flash File System). TRUEFFS是M-Systems公司为其产品DiskOnChip开发的文件系统,其规范并不开放。
C.       由SSFDC(Solid State Floppy Disk Card)论坛定义的支持SM卡的DOS-FAT。SM卡的DOS-FAT文件系统是由SSFDC论坛定义的,但它必须用在标准的块设备上。
对于大量用在各类存储卡上的NAND 设备而言,他们几乎都采用FAT文件系统,而在嵌入式操作系统下,还没有驱动程序可以直接让NAND设备采用文件系统,就技术角度来说,FAT文件系统不是很适合NAND设备,因为FAT文件系统的文件分区表需要不断地擦写,而NAND设备的只能有限次的擦写。
    在上面已经很明显的提到,NAND设备存在坏块,为和上层文件系统接口,NAND设备的驱动程序必须给文件系统提供一个可靠的存储空间,这就需要ECC(Error Corection Code)校验,坏块标注、地址映射等一系列的技术手段来达到可靠存储目的。
    SSFDC软件规范中,详细定义了如何利用NAND设备每个页中的冗余信息来实现上述功能。这个软件规范中,很重要的一个概念就是块的逻辑地址,它将在物理上可能不连续、不可靠的空间分配编号,为他们在逻辑空间上给系统文件提供一个连续可靠的存储空间。
表3给出了SSFDC规范中逻辑地址的标注方法。在系统初始化的时候,驱动程序先将所有的块扫描一遍,读出他们所对应的逻辑地址,并把逻辑地址和虚拟地址的映射表建好。系统运行时,驱动程序通过查询映射表,找到需要访问的逻辑地址所对应的物理地址然后进行数据读写。      
               表3 冗余字节定义
字节序号
内容
字节序号
内容
512
用户定义数据
520
后256BECC校验和
513
521
514
522
515
523
块逻辑地址
516
数据状态
524
517
块状态
525
前256BECC校验和
518
块逻辑地址1
526
519
527
表4给出了块逻辑地址的存放格式,LA表示逻辑地址,P代表偶校验位。逻辑地址只有10bit,代表只有1024bit的寻址空间。而SSFDC规范将NAND设备分成了多个zone,每个zone 内有1024块,但这物理上的1024块映射到逻辑空间只有1000块,其他的24块就作为备份使用,当有坏块存在时,就可以以备份块将其替换。
表4 逻辑地址格式
D7
D6
D5
D4
D3
D2
D1
D0
0
0
0
1
0
LA9
LA8
LA7
第518   523字节
LA6
LA5
LA4
LA3
LA2
LA1
LA0
P
第519   524字节
有了以上的软件规范,就可以对NAND设备写出较标准的ECC校验,并可以编写检测坏块、标记坏块、建立物理地址和逻辑地址的映射表的程序了。
static int NF_IsBadBlock(U32 block)             //检测坏块
{
    int i;
    unsigned int blockPage;
    U8 data;
    
    blockPage=(block       // For 2'nd cycle I/O[7:5] 
    
    NF_nFCE_L();    
    NF_CMD(0x50);       // Spare array read command
    NF_ADDR(517&0xf);   // Read the mark of bad block in spare array(M addr="5") 
[q6]

NF_ADDR(blockPage&0xff);    // The mark of bad block is in 0 page
    NF_ADDR((blockPage>>8)&0xff);   // For block number A[24:17]
    NF_ADDR((blockPage>>16)&0xff); // For block number A[25]
   for(i=0;i               // wait tWB(100ns) //?????
    
    NF_WAITRB();                    // Wait tR(max 12us)
    
    data=NF_RDDATA();
    NF_nFCE_H();    
    if(data!=0xff)
    {
        Uart_Printf("[block %d has been marked as a bad block(%x)]\n",block,data);
        return 1;
    }
    else
    {
        return 0;
    }
}
static int NF_MarkBadBlock(U32 block)                       //标记坏块
{
    int i;
    U32 blockPage=(block
    seBuf[0]=0xff;
    seBuf[1]=0xff;    
    seBuf[2]=0xff;    
   seBuf[5]=0x44;                              // Bad blcok mark="0"
[q7]

NF_nFCE_L(); 
    NF_CMD(0x50);                               //????
    NF_CMD(0x80);                               // Write 1st command
    
    NF_ADDR(0x0);                               // The mark of bad block is 
    NF_ADDR(blockPage&0xff);                    // marked 5th spare array 
    NF_ADDR((blockPage>>8)&0xff);               // in the 1st page.
    NF_ADDR((blockPage>>16)&0xff);             
    
    for(i=0;i
    {
    NF_WRDATA(seBuf);                        // Write spare array
    }
    NF_CMD(0x10);                               // Write 2nd command
    
    for(i=0;i                         //tWB = 100ns. ///???????
    NF_WAITRB();                                // Wait tPROG(200~500us)
    NF_CMD(0x70);
    
    for(i=0;i                          //twhr=60ns??????
    
    if (NF_RDDATA()&0x1)                        // Spare arrray write error
    {   
        NF_nFCE_H();
        Uart_Printf("[Program error is occurred but ignored]\n");
    }
    else 
    {
        NF_nFCE_H();
    }
    Uart_Printf("[block #%d is marked as a bad block]\n",block);
    return 1;
}
int search_logic_block(void)                    //建立物理地址到逻
//辑地址的映射表
{
    unsigned int block,i,blockPage,logic_no,zone,zone_i;
    U8 SE[16];
    for(i=0;i                         //初始化全局变量
        lg2ph=space_block=0xffff;
    logic_number=0;
    space_nr=0;
    NF_nFCE_L();
    zone=BLOCK_NR/1024;                             //确定NAND设备中zone
//的个数
    for(zone_i=0;zone_i
    {
        //搜索每个zone 内逻辑地址和物理地址的映射关系
        for(block=0;block
        {
            blockPage=((block+zone_i*1024)
        NF_WATIRB();                                //等待R/B#信号有效
        
        NF_CMD(0x50);                               // 读取每个block内部第
//0个Page内冗余的16个字节
        NF_ADDR(0);                                 // Column 0
        NF_ADDR(blockPage&0xff);        
        NF_ADDR((blockPage>>8)&0xff);               // Block & page num.
        NF_ADDR((blockPage>>16)&0xff); 
        NF_WATIRB();                                //等待R/B#信号有效
        for(i=0;i se=NF_RDDATA();       // Write spare array
        NF_WATIRB();
        if(se[5]!=0xff)
[q8]
                              //检测是否存在坏块
            printk("\n\rphysic block %d is bad block\n\r",block);
        else if(se[7]!=se[12]
[q9]
)
            printk("block address1:%d!=block address2 %d\n\r",se[7],se[12]);
        else if((se[6]
[q10]
& 0xf8)==0x10)
        {
            //计算该block对应的逻辑地址
            logic_no=((0x7&se[6])>1)+zone_i*1000;
            if(lg2ph[logic_no]!=0xffff)             //说明有2个block拥有相
//同的逻辑地址
                printk("physical block %d and block %d have the same logic number %d\n",lg2ph[logic_no],block,logic_no);
            else lg2ph[logic_no]=block;             //将该block的逻辑地址
//关系记入lg2ph表
            logic_number++;                         
        }
        else if(se[7]==0xff)                        //说明该block尚未编号
        {space_block[space_nr]=block;
        space_nr++;
        }
        }
    }
    printk("there are totally %d logic blocks\n\r",logic_number);
    NF_nFCE_H();
    return logic_number;
}
这段代码的主要作用就是产生数组lg2ph[],这个数组的含义就是“块物理地址=lg2ph[逻辑地址]”。

嵌入式系统中的FLASH相关推荐

  1. 嵌入式系统中时间的应用以及rtc的验证过程

    在嵌入式系统中时间分为3种,分别为当前时间,系统时间,硬件时间,三种之间有一定的关联关系,如果开发板没有电池,用于保持开发板的时钟,那么当开发板断电后,时钟恢复为默认时间,一般为1970年1月1日,0 ...

  2. 嵌入式系统中进程间通信的监视方法

    概述 复杂的嵌入式系统中,常常同时运行着相当多的进程.这些进程之间频繁的进行着大量的通信动作.进程的运行状态与这些不断发生的通信有着直接和紧密的联系.通过对进程间通信的监视,开发人员可以掌控系统内部运 ...

  3. python好还是c+-嵌入式系统中,Python与C/C++哪方更为适用?

    [51CTO.com快译]长久以来,C/C++一直编译着嵌入式系统编程领域,但二者亦拥有自己的缺陷.相比之下,Python则成为嵌入式系统中的另一大***语言选项.在今天的文章中,我们将共同探讨双方的 ...

  4. 【原创】QT在嵌入式系统中显示中文的方法

    [原创]QT在嵌入式系统中显示中文的方法 此篇文章主要借鉴:http://zzqh007.blog.163.com/blog/static/44434847201011312168296/ 移植QT4 ...

  5. 嵌入式系统中对汉字的处理

    现在要解决的问题是,嵌入式系统中经常要使用的并非是完整的汉字库,往往只是需要提供数量有限的汉字供必要的显示功能.例如,一个微波炉的LCD上没有必要提供显示"电子邮件"的功能: 一个 ...

  6. python嵌入式系统开发_Python在开发实时嵌入式系统中的作用

    原标题:Python在开发实时嵌入式系统中的作用 在过去的几年里,Python已经成为一种热门的程序语言.以著名的Monty Python喜剧小组命名,该语言是面向对象和解释.该属性导致Python在 ...

  7. 看门狗(Watch Dog)是嵌入式系统中一种常用的保证系统可靠性的技术,()会产生看门狗中断。【详细!小白也能看懂!】

    看门狗(Watch Dog)是嵌入式系统中一种常用的保证系统可靠性的技术,()会产生看门狗中断. A. 软件喂狗 B. 处理器温度过高 C. 外部中断 D. 看门狗定时器超时 解析: 思考方式: 每个 ...

  8. 嵌入式系统中看门狗概述。。。

    一直以来对于嵌入式中的watch dog(看门狗)都比较陌生,一直都不知道它到底是做什么的,单从名字上看也不知其所以然,然后就在网上找到了一篇blog,就是再说看门狗的作用和概述,原文如下: 1.概述 ...

  9. 单片机c语言位运算写法,单片机与嵌入式系统中C语言的位运算小记

    原标题:单片机与嵌入式系统中C语言的位运算小记 编了个PIC的项目,对里的还是蛮有感悟的,特此记录一下. 譬如说,在程序中定义了一个char类型的变量, purge_short_enable_flag ...

  10. 用crontab、crond在嵌入式系统中添加定时任务

    在嵌入式系统中,定时任务通过crond和cronttab两个系统命令来联合执行. 其中crond是定时任务的守护进程,系统开始时是没有开启的.crontab主要作用是管理用户的crontab file ...

最新文章

  1. 基于jQuery+ashx+.net实现三级栏目联动操作
  2. 调试技巧之 找准调试点
  3. 微软算法100题58 从尾到头输出链表(java)
  4. DES和RSA算法的java实现
  5. Linux中断 - tasklet
  6. 360 php offer,审批终于通过了,从面试到拿到奇虎360的offer已经失…
  7. 计算机网络云南大学实验四,云南大学软件学计算机网络原理实验四.doc
  8. mysql三高教程(二):2.7 如何约束数据
  9. 通用性好的win2003序列号: (推荐先用这个里面的)
  10. 【分段哈希】H. Paint the Wall
  11. idea整个项目乱码解决办法
  12. tgp dnf服务器文件在哪,DNFTGP补丁使用说明及问题解决方案
  13. GridView日期列使用DataFormatString格式化技巧
  14. jsp文件上传图片到服务器
  15. 养老院智能健康手环开发/功能/特点/结构/原理
  16. 陈经纶2021年高考成绩查询时间,北京2021高考成绩排名榜单,北京各高中高考成绩喜报...
  17. windows 无法完成安装, 若要在此计算机上安装Windows, 请重新启动安装
  18. java基于springboot在线小说阅读网站
  19. java类图与代码实例
  20. linux - 异常:安装包冲突 conflicts with

热门文章

  1. PLECS专家的教诲
  2. Fragstats官方入门教程4 移动窗口分析
  3. 解决Tomcat乱码
  4. 斐讯k2刷无线打印服务器,斐讯K2无线路由器刷华硕固件教程和工具
  5. 将k8容器中文件下载到本地
  6. 网站上部署Live2D模型(moc3格式)
  7. M2Det: A Single-Shot Object Detector based on Multi-Level Feature Pyramid
  8. BCH编译码的matlab性能分析
  9. 西门子基于RS485通讯恒压供水一拖二S7-200SMART_PLC
  10. linux的桌面分辨率设置,linux xorg屏幕分辨率设置