对Nand flash读操作的分析

简介:     前几天大致分析了u-boot针对smdk2410的源码,了解了启动的流程,但是对板上许多硬件的驱动过程还不太清楚。smdk2410源码中有针对Nor Flash的初始化和读取,但源码中没有对Nand Flash的操作,虽然针对其他型号的板子应该有Nand的源码,但方便起见,我查阅了vivi的源码,它支持从Nand Flash启动,自然有我需要的东西。

    前几天大致分析了u-boot针对smdk2410的源码,了解了启动的流程,但是对板上许多硬件的驱动过程还不太清楚。smdk2410源码中有针对Nor Flash的初始化和读取,但源码中没有对Nand Flash的操作,虽然针对其他型号的板子应该有Nand的源码,但方便起见,我查阅了vivi的源码,它支持从Nand Flash启动,自然有我需要的东西。下面我就自己的分析和总结列出来,中间当然也从google上得到不少前人留下的宝贵资料:)。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

我先摘录一段对NorNand flash区别的几条总结:
    NOR flash采用位读写,因为它具有sram的接口,有足够的引脚来寻址,可以很容易的存取其内部的每一个字节
    NAND flash使用复杂的I/O口来串行地存取数据。8个引脚用来传送控制、地址和数据信息(复用)。NAND读和写单位为512Byte的页,擦写单位为32页的块
    ● NOR的读速度比NAND稍快一些。
    ● NAND的写入速度比NOR快很多。 
    ● NAND4ms擦除速度远比NOR5s快。
    ● 大多数写入操作需要先进行擦除操作。
    ● NAND的擦除单元更小,相应的擦除电路更少。
    NOR器件上运行代码不需要任何的软件支持,在NAND器件上进行同样操作时,通常需要驱动程序,也就是内存技术驱动程序(MTD)NANDNOR器件在进行写入和擦除操作时都需要MTD

再来看看Nand flash自身的特点(部分摘自August0703的文章):
     Nand Flash
的数据是以bit的方式保存在memory cell中,一般来说,一个cell 中只能存储一个bit。这些cell 8个或者16个为单位,连成bit line,形成所谓的byte(x8)/word(x16)这就是NAND Device的位宽
   
多个line(多个位宽大小的数据)会再组成Page。我使用的Nand flash是三星的K9F1208U0M,从datesheet上得知,此flash每页528Bytes512byteMain Area + 16byteSpare Area),每32page形成一个Block(32*528B)。具体一片flash上有多少个Block视需要所定。我所使用的k9f1208U0M具有4096block,故总容量为4096*32*528B=66MB,但是其中的2MBSpaer Area)是用来保存ECC校验码等额外数据的,故实际中可使用的为64MB
    Nand flash
以页(512Byte)为单位读写数据,而以块(16KB)为单位擦除数据。按照这样的组织方式可以形成所谓的三类地址: 
    ● Column Address
:列地址,地址的低8
    ● Page Address 
:页地址
    ● Block Address 
:块地址
   
对于NAND Flash来讲,地址命令只能在I/O[7:0]上传递,数据宽度也是8,这导致在读写指定地址的数据时,地址是分4次传递的(3次右移),见后文。
    s3c2410
这个处理器之所以可以直接从Nand flash启动,是因为CPU内置了4KB的片内SRAM,手册上称作“Steppingstone”。板子上电复位之后,CPU会自动将Nand flash的前4KB代码拷贝到片内SRAM中去执行(此过程是靠硬件实现的,见datasheetFigure 6-1. NAND Flash Controller Block Diagram),这也是导致从NorNand启动后的内存映射不同的原因。所以,vivistage1代码head.S必须要小于4KB,其中实现基本的CPU初始化等工作,并且要实现把自身拷贝到SDRAM中,之后的stage2就实现复杂功能。
   
关于SDRAM的初始化,之前分析u-boot时虽然涉及,但那篇没有仔细分析,我将另外归纳一篇。

下面具体看一下如何读写这块 Nand flash
   
s3c2410datasheet上得知,Nand flash的操作通过NFCONFNFCMDNFADDRNFDATANFSTATNFECC这六个寄存器来完成,并且列出操作Nand flash4个步骤:

NAND FLASH MODE CONFIGURATION
1. Set NAND flash configuration by NFCONF register.
2. Write NAND flash command onto NFCMD register.
3. Write NAND flash address onto NFADDR register.
4. Read/Write data while checking NAND flash status by NFSTAT register. R/nB signal should be checked before read operation or after program operation.

    下面结合vivi源码来详细的分析具体如何操作这6个寄存器来完成以上4个步骤来完成读过程:
先要初始化Nand flash,紧接着复位一下:

void reset_nand()
{
    int i=0;
    NFCONF &= ~0x800;    /* 现在真正使用Nand flash,bit[11]要置0,与初始化时相反 */
        for(; i10; i++);
    NFCMD = 0xff;    //reset command

/* 复位命令。NFCMD寄存器只用到低8位(bit[7:0])。
K9F1208U0M手册中列出了针对此块flash的各种命令,见Table 1. Command Sets。vivi/include/mtd/nand.h更直观的列出了各种命令 */

    wait_idle();
}

/* 初始化NAND Flash */
/* NFCONF设定为0xf830,作用是使能Nand flash控制器、初始化ECC、Nand flash片选信号nFCE=1(inactive,真正使用时再让它等于0)、设置TACLS、TWRPH0、TWRPH1。
TACLS、TWRPH0、TWRPH1这三个参数是控制Nand flash信号线CLE/ALE和写控制信号nWE的时序关系的,要参照具体的flash芯片手册来设置。我这个是K9F1208U0M ,在表“AC Timing Characteristics for Command / Address / Data Input”中可以看到:
CLE setup Time = 0 ns,CLE Hold Time = 10 ns,
ALE setup Time = 0 ns,ALE Hold Time = 10 ns,
WE Pulse Width = 25 ns
可以计算,即使在HCLK=100MHz的情况下,TACLS+TWRPH0+TWRPH1=6/100 uS=60 ns,也是可以满足NAND Flash K9F1208U0M的时序要求的。(此句摘自thisway.diy@163.com的文章。关于如何配置时序,以后我得好好学习一下,还自诩电子出身,丢人啊。。。)*/

void init_nand()
{
    NFCONF = 0xf830;
    reset_nand();
}

    初始化Nand flash之后,就可以把stage2main函数代码拷贝到SDRAM中去执行,当然之前已经配置好了SDRAM。以上工作都是在stage1阶段(片内SRAM中)完成的,之后就可以读写Nand flash了。
   
下面分析读操作的实现,贴上vivi/s3c2410/nand_read.c源码:

#include config.h>

#define __REGb(x)    (*(volatile unsigned char *)(x))
#define __REGi(x)    (*(volatile unsigned int *)(x))
#define NF_BASE        0x4e000000
#define NFCONF        __REGi(NF_BASE + 0x0)
#define NFCMD        __REGb(NF_BASE + 0x4)
#define NFADDR        __REGb(NF_BASE + 0x8)
#define NFDATA        __REGb(NF_BASE + 0xc)
#define NFSTAT        __REGb(NF_BASE + 0x10)

#define BUSY 1
inline void wait_idle(void) {
    int i;

/* NFSTAT:只用到位0,0-busy,1-ready */
    while(!(NFSTAT & BUSY))
      for(i=0; i10; i++);
}

#define NAND_SECTOR_SIZE    512            /* Nand flash是以512Byte为单位来读写的 */
#define NAND_BLOCK_MASK        (NAND_SECTOR_SIZE - 1)

/* low level nand read function */
/* 下面的读过程严格按照2410手册上的顺序 */
int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
    int i, j;

    if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {
        return -1;    /* invalid alignment */
    }

/* chip Enable */
/* 对应第一条:1. Set NAND flash configuration by NFCONF register. */
    NFCONF &= ~0x800;
    for(i=0; i10; i++);

    for(i=start_addr; i (start_addr + size);) {
      /* READ0 */
/* 对应第二条:2. Write NAND flash command onto NFCMD register. */
      NFCMD = 0;

      /* Write Address */
/* 对应第三条:3. Write NAND flash address onto NFADDR register.
*NFADDR寄存器也只用到低八位来传输,所以需要分4次来写入一个完整的32位地址,需要注意后3次的移位操作
*/

      NFADDR = i & 0xff;
      NFADDR = (i >> 9) & 0xff;
      NFADDR = (i >> 17) & 0xff;
      NFADDR = (i >> 25) & 0xff;

/* 对应第四条:4. Read/Write data while checking NAND flash status by NFSTAT register.
一个地址对应512个字节数据。所以,由于8bit位宽的限制,每次读取8位(1个字节),共读512次得到1页512Byte数据
*/

      wait_idle();
      for(j=0; j NAND_SECTOR_SIZE; j++, i++) {
        *buf = (NFDATA & 0xff);
        buf++;
      }
    }

/* chip Disable */
/* 读写完毕需要禁止Nand flash ,与开始相对应*/
    NFCONF |= 0x800;    /* chip disable */

    return 0;
}


    以上是对
Nand flash读操作的分析。总体来看,关键在于根据CPUFlashdatasheet配置各寄存器和按照规定顺序进行操作。具体的配置过程是比较繁杂的,可参照u-bootvivi中对各种硬件支持的源码来配置,可省不少事。以后若自己尝试写bootloader,再实践一下作为练习吧。

目录
相关文章
22、【收货地址管理模块】——收货地址增、删、改、查、分页列表、地址详情的功能开发
1、接口开发: 新建ShippingController类 image.png 在类上添加相关注解 @Controller @RequestMapping("/shipping/") public class ShippingContro...
3018 0
|
存储 SQL JSON
[SIGMOD 21 学习] 《JSON Tiles》解读: 半结构化 JSON 存算优化
本文是对 SIGMOD 2021 上《JSON Tiles: Fast Analytics on Semi-Structured Data》的学习总结,有错误之处欢迎交流。
914 2
|
数据中心
《阿里巴巴浸没液冷数据中心规范》电子版地址
阿里巴巴浸没液冷数据中心规范
468 0
《阿里巴巴浸没液冷数据中心规范》电子版地址
|
存储 弹性计算 安全
阿里云服务器ECS购买、部署Web站点流程详解
讲述阿里云服务器购买细节,以及如何使用ECS服务器部署Web站点
14398 2
阿里云服务器ECS购买、部署Web站点流程详解
|
存储 IDE 开发工具
数字信号处理-02- FPGA常用运算模块-加减法器和乘法器
数字信号处理-02- FPGA常用运算模块-加减法器和乘法器
1526 0
数字信号处理-02- FPGA常用运算模块-加减法器和乘法器
|
小程序 前端开发 JavaScript
微信小程序--》小程序—全局数据共享和分包
⚓经过web前端开发的学习,相信大家对于前端开发有了一定深入的了解,今天我开设了微信小程序,主要想从移动端开发方向进一步发展,而对于我来说写移动端博文的第一站就是小程序开发,希望看到我文章的朋友能对你有所帮助。
593 0
微信小程序--》小程序—全局数据共享和分包
|
存储 缓存 Java
关于缓存一致性协议、MESI、StoreBuffer、InvalidateQueue、内存屏障、Lock指令和JMM的那点事
关于缓存一致性协议、MESI、StoreBuffer、InvalidateQueue、内存屏障、Lock指令和JMM的那点事
492 0
|
SQL OLAP HIVE
Hive窗口函数保姆级教程 (一)
在SQL中有一类函数叫做聚合函数,例如sum()、avg()、max()等等,这类函数可以将多行数据按照规则聚集为一行,一般来讲聚集后的行数是要少于聚集前的行数的。但是有时我们想要既显示聚集前的数据,又要显示聚集后的数据,这时我们便引入了窗口函数。窗口函数又叫OLAP函数/分析函数,窗口函数兼具分组和排序功能。
1496 0
Hive窗口函数保姆级教程 (一)
|
SQL 数据可视化 Devops
研发效能数据平台 DevLake 正式开源,连接 DevOps 中的数据孤岛
研发效能数据平台 DevLake 正式开源,连接 DevOps 中的数据孤岛
1596 0
研发效能数据平台 DevLake 正式开源,连接 DevOps 中的数据孤岛
|
存储 运维 监控
SmartX:敏态、稳态兼顾,拓展超融合价值边界
SmartX:敏态、稳态兼顾,拓展超融合价值边界
781 0
SmartX:敏态、稳态兼顾,拓展超融合价值边界