对Nand flash读操作的分析

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

我先摘录一段对 Nor Nand flash 区别的几条总结:
    NOR   flash 采用 位读写 ,因为它具有sram 的接口,有 足够的引脚 来寻址,可以很容易的存取其内部的 每一个字节
    NAND   flash 使用复杂的I/O 口来 串行 地存取数据。 8 个引脚 用来传送控制、地址和数据信息(复用)。NAND 读和写单位为512Byte 的页,擦写单位为32 页的块
    ●  NOR 的读速度比NAND 稍快一些。
    ●  NAND 的写入速度比NOR 快很多。  
    ●  NAND 4ms 擦除速度远比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 每页 528Bytes 512byte Main Area + 16byte Spare Area) ,每 32 page 形成一个 Block(32*528B) 。具体一片 flash 上有多少个 Block 视需要所定。我所使用的 k9f1208U0M 具有 4096 block ,故总容量为 4096* 32*528B =66MB ,但是其中的 2MB Spaer 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 中去执行(此过程是靠硬件实现的,见 datasheet Figure 6-1. NAND Flash Controller Block Diagram ),这也是导致从 Nor Nand 启动后的内存映射不同的原因。所以, vivi stage1 代码 head.S 必须要小于 4KB ,其中实现基本的 CPU 初始化等工作,并且要实现把自身拷贝到 SDRAM 中,之后的 stage2 就实现复杂功能。
   
关于 SDRAM 的初始化,之前分析 u-boot 时虽然涉及,但那篇没有仔细分析,我将另外归纳一篇。

下面具体看一下如何读写这块 Nand flash
   
s3c2410 datasheet 上得知, Nand flash 的操作通过 NFCONF NFCMD NFADDR NFDATA NFSTAT NFECC 这六个寄存器来完成,并且列出操作 Nand flash 4 个步骤:

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 ( ; i< 10; 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 之后,就可以把 stage2 main 函数代码拷贝到 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; i< 10; 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; i< 10; 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 读操作的分析。总体来看,关键在于根据 CPU Flash datasheet 配置各寄存器和按照规定顺序进行操作。具体的配置过程是比较繁杂的,可参照 u-boot vivi 中对各种硬件支持的源码来配置,可省不少事。以后若自己尝试写 bootloader ,再实践一下作为练习吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值