cortex-a8裸机系列:第十五章 NANDFLASH
0赞一、 NAND接口
九鼎开发板的NAND接口,虽然目前开发板没有NAND,但是硬件设计有留着接口,需要使用NAND,只需要焊上去就行了。
NAND的命名:如K9F2G08
K9F:三星的NANDFLASH。
2G: NAND的大小,2Gbit,除以8,也就是568MB
08: 表示NAND的数据位宽是8位
从NAND命名,可以看出: 厂家,大小,数据位宽
NAND的数据线
l 数据是并行接口
l 数据上也会传递命令
二、 NANDFLASH的功能框图
1. Nand芯片中主要包含2部分:Nand存储颗粒+Nand接口电路。存储颗粒就是纯粹的Nand芯片的存储单元;Nand接口电路时用来管理存储颗粒的,并且给外界提供一个统一的Nand接口规格的访问接口。
2. Nand中有多个存储单元,每个单元都有自己的地址(地址是精确到字节的,但是不能按照字节访问)。因此Nand是地址编排精确到自己,但是实际读写却只能精确到页(所以Nand的很多操作要求地址页对齐,如2K,4K等这样的地址)。
3. Nand的读写时序传递是通过IO先发送的,因为地址有30位而IO只有8位,所以需要多个cycle才能发送完毕。一般的Nand都是4cycle或者5cycle发送地址(所以这里把Nand分为了4cycle和5cycle)。
三、 NANDFLASH的组织结构
1. NAND的结构可以看成是一个矩阵式存储器,其中被分成一个一个的小块,每一个小块可以存储一个bit,然后彼此以一定单位组合成NAND。
2. NAND中可以被单次访问最小单元(对NAND进行一次读写,至少要读写这么多,或者是这么多的整数倍)叫做page。如K9F2G08,page的大小是2KB+64B。也就是对这么芯片进行读写,至少要读写2KB或者2KB*n,即使只需要读一个字节。这就是典型的块设备。
3. 页往上还有Block(块),1个块有若干个页。如K9F2G08,一个块等于64个页。
4. 块上面就是整个芯片了,也就是Device,一个Device有若干个块。如K9F2G08,一个Device有2048个块。所以该芯片大小为2048*64*2KB = 256MB
5. 块设备分page、block。由于物理限制,对于块设备不能完全按字节访问而必须块访问。在NAND中,page是读写Nand的最小单位,Block是擦除Nand的最小单位。
总结:
Nand芯片内部有存储空间,并且有电路来管理这些存储空间,向外部提供统一的Nand接口的访问规则,然后外部的Soc可以使用Nand接口时序来读写这个Nand存储芯片。Nand接口是一种公用标准,是一种标准。
理论上外部soc可以直接模拟Nand接口来读写Nand芯片,但是实际上因为Nand接口对时序要求比较严格,而且时序很复杂,所以一般的Soc都是通过专门的硬件的Nand控制器来操作Nand芯片。
NandFlash的结构
1. Nand的单元组织:block和page(大页Nand和小页Nand)
Nand的页和之前的块设备的扇区是类似的。不过扇区最早在磁盘中是512字节,后来也有些高级硬盘扇区不是512字节,而是其他字节。Nand也是一样,不同的Nand的页的大小是不同的,也有512字节/1024字节/2048字节/4096字节等。
一个块是多少页也是不定的。不同的Nand也不同。一个Nand芯片有多少个block也是不定的。不同的Nand芯片也不同。
因此Nand的组织结构不统一,接口时序也不同。造成不同厂家的Nand芯片,或者是同一个厂家的不同系列型号容量的Nand接口也不一样。所以Nand的很大问题是一旦升级容量或者换芯片系列,硬件需要更改,软件需要重新移植。
2. 页的带内数据和带外数据
Nand的每个页由2部分组成,这2部分各自都有一定的存储空间。如K9F2G08中位2K+64字节,其中的2K字节属于带内数据,是我们真正的存储空间,将来存储在Nand中的有效数据就是存在这2K范围内的(计算Nand的容量也只是考虑着2KB)。64字节的带外数据不能用来存储有效数据,是作为别的附加用途的(如用来存储ECC数据、用来存储坏块标志等)。
ECC(error correction code,错误校验码)。因为Nand存储本身出错(位反转)的概率高(Nand较Nor最大的缺点就是稳定性),所以当将有效信息存储到Nand中时都会按照一定算法计算一个ECC信息(如CRC16等校验算法),将ECC信息同时存储到Nand这个页的带外数据区。将来读取数据时,对数据使用同样的算法再计算一次ECC,并且和带外数据区读出来的ECC进行校验。如果校验通过则证明Nand的有效数据可信,如果校验不通过则证明这个数据已经被损坏(只能丢弃或者尝试修复)
坏块标志:Nand芯片用一段时间后,可能某些块会坏掉(这些块无法擦除了,或者无法读写),Nand的坏块非常类似于硬盘的坏道。坏块是不可避免的,而且随着Nand的使用坏块会越来越多。当坏块还不算太多时,Nand是还可以使用的。除非坏块太多,才会更新Nand。所以为了管理Nand发明了一种坏块标志机制。Nand的每个页的64字节的带外数据中,定义一个固定位置(如定位到24字节,这个是由文件系统来管理的)来标记这个块是是否为坏块。文件系统在发现这个块坏了就会将这个块标记为坏块,以后访问Nand时直接跳过这个块即可。
3. Nand的地址时序
Nand的地址有多位,分4\5周期通过IO管脚发送给Nand芯片,来对Nand进行寻址。寻址的最小单位是字节,但是读写的最小单位是页。
Nand的地址在编写程序时,要按照Nand的时序和顺序去依次写入。
4. Nand的命令码
外部Soc要想通过Nand控制器来访问Nand(实质就是通过Nand接口),就必须按照Nand接口给Nand发送命令、地址、数据等信息来读写Nand。
Nand芯片内部的管理电路本身可以接收外部发送的命令,然后根据这些命令来读写Nand内容与外部soc交互。所以对Nand进行的所有操作(擦除,读,写…)都要有命令、地址、数据的参与才能完成,而且必须按照Nand芯片规定的流程来做。
以上是Nand的命令码。不同的命令发送不同的内容。
5. NandFlash的常见操作及流程分析
1. 坏块检查
Flash使用之前要先统一擦除(擦除的单位就是块)。擦除后,数据为0XFF(8位),0XFFFF(16位),也就是擦除后,数据全为1。
检查坏块思路:先把块擦除,然后将块中的所有数据读出来,一次检测各字节是否为0xff,如果是则表明不是坏块,如果不是则表明是坏块。
2. 页写(program)操作
写之前确保这个页是被擦除过的。如果未被擦除,写进去的值不正确。
写操作在flash的操作中就是编程(program)。
SOC写Flash时通过命令线、IO线依次发送写命令、写页地址、写数据。
写的过程:soc通过Nand控制器和Nand芯片完成顺序对接,然后按照时序要求将一页数据发给Nand芯片内部的接口电路。接口电路先接收数据到自己的缓冲区,然后再集中写入Nand芯片的存储区域中。Nand接口电路将一页数据从缓冲区写入Nand存储系统中需要一定的时间,这段时间Nand芯片不能再响应soc发过来的其他命令,所以soc要等待Nand接口电路处理结束。等待方法是soc不断读取状态寄存器(这个状态寄存器有两种情况:一种是soc的Nand控制器自带的,另一种是soc通过发命令得到命令响应得到的),然后通过检查这个状态寄存器的状态位就能知道Nand接口电路刚才写的那一页数据是否写成功。直到soc收到正确的状态寄存器响应才能认为刚才写的那一页数据已经成功。(如果soc收到的状态一直不对,可以考虑重写或者认为这一页所在的块是坏块)。
因为Nand的读写有不靠谱情况,因此会为了安全会做ECC校验。ECC校验有硬件式校验和软件式检验两种。软件式检验可以采用的策略有很多,其中之一:将刚才写入的1页数据读出来,和写入的数据进行逐一对比。如果读出的和写入的完全一样,说明写入成功。
硬件式ECC:soc的Nand控制器可以提供硬件式ECC(目前普遍的情况)
通过会在数据写入后,再进行校验。将之前写的数据再读出来,判断是否成功。硬件式ECC就是在Nand的控制器中有个硬件模块专门做ECC操作。当操作Nand芯片时,只要按照Soc的要求打开ECC生成开关,则当写入Nand芯片时soc的Nand控制器的ECC模块会自动生成ECC数据,放在相应的寄存器。对于编程只需要将生成的ECC数据写入Nand芯片的带外数据区。在将来读取数据时,同样打开硬件ECC开关,然后开始读取数据,读的过程当中,硬件ECC会自动计算读进来的一页数据的ECC值并将之放到相应的寄存器中。然后再读取带外数据区中原来写入时存入的ECC值,和刚才得到的ECC值进行校验。检验通过则说明读写正确。校验不通过则说明不正确(放弃数据或者尝试修复)。
左边部分是将数据写入到Nand中,右边的部分是在效验,将刚刚写进去的数据读出来,和之前写的数据进行比较,然后写的数据是否成功,不成功的话,说明可能有坏块,就需要更换一个块进行重新写入数据。
如果使用ECC的话,就不需要这样检验了。
3. 擦除(erase)操作
擦除是以块为单位。即擦除时必须给块对齐的地址。如果给了不对齐的地址,结果不可知(有些Nand会自动将其对齐,而有些Nand会返回错误)
4. 页读(read)操作
四、 s5pv210的Nand代码解读
定义了一些寄存器。
1. NFCONF:
Nand控制器的参数(时序,页大小,地址周期)设置。
2. NFCONT:
Nand控制器的控制寄存器。控制Nand的操作。如使能/禁止片选,ECC校验,中断使能/禁止等。
3. NFCMMD:
Nand命令寄存器,需要发送的命令写入到这个寄存器,Nand控制器会自动按照时序发送该命令。
4. NFADDR:
Nand地址寄存器,需要发送的地址写入到这个寄存器,Nand控制器会自动按照时序发送该地址
5. NFDATA和NFDATA8:
这里两个寄存器的地址都是一样的,只是类型不一样。需要读取数据,直接读取该寄存器,对于NFDATA,一次性读取8位,对于NFDATA8,一次性读取32位。需要写入数据时,将数据写入到该寄存器中,对于NFDATA,一次性写8位,对于NFDATA8,一次性写32位。置于读取和写入数据的时序,Nand控制器会自动产生。
6. NFSTAT:
Nand状态寄存器。里面有对Nand状态进行记录。主要是使用第4位RnB_TransDetect ,能判断Nand操作是否结束。
7. MP0_1CON,MP0_2CON,MP0_3CON:
和Nand有关的管脚的功能设置寄存器。在GPIO说明中,有说与NF有关的GPIO有MP0_1,MP0_2,MP0_3。所以当要使用Nand控制器,需要将这些管脚设置为Nand的功能。
定义Nand的一些信息。最大块为8192,页大小是2KB,块大小64页。
Nand控制器需要的三个时序参数的值。这三个时序参数是很重要的,要根据具体使用的NAND的芯片进行设置。
Nand操作过程中的命令。
定义操作Nand的基本函数。
/* Nand 初始化
参数:无
返回值:无
功能: 对Nand控制器进行初始化
*/
void nand_init(void);
/* Nand 读取ID
参数:无
返回值:无
功能:对Nand芯片的ID号读取,保存到全局ID结构体nand_id_info变量中。
*/
void nand_read_id(void);
/* Nand 块擦除
参数:待擦除块的块数,有效范围 0 - MAX_NAND_BLOCK
返回值:0 成功
1 失败
功能:对Nand的指定块进行擦除
*/
int nand_block_erase(unsigned long block_num);
/* Nand 页数据读取
参数1: 页地址
参数2: 内存地址
参数3: 读取nand数据的长度
返回值:0 成功
-1 失败
功能:对Nand的指定页进行读取
*/
int nand_page_read(unsigned int pgaddr, unsigned char *buf, unsigned int length);
/* Nand 页数据编程
参数1: 页地址
参数2: 内存地址
参数3: 读取nand数据的长度
返回值:0 成功
-1 失败
功能:对Nand的指定页进行写入
*/
int nand_page_write(unsigned int pgaddr, const unsigned char *buf, unsigned int length);
/* 拷贝Nand数据到DRAM
参数1: 内存地址
参数2: nand的地址
参数3: 读取nand数据的长度
返回值:0 成功
-1 失败
功能:从Nand的指定地址(字节地址)读取length长度数据,并保存在起始地址为sdram_addr的内存中
*/
具体函数就不用再说明了,移植的时候,只需要修改Nand_init函数即可。其他基本上不用修改。
而这个nand_init函数,修改,只需要设置CONF寄存器,以及端口配置,即可。