NAND FLASH控制器

  1. 存储组织形式

    111326_JcrI_1256737.png

K9F2G08X0A共有2048个Block(块), 每个Block含有 64 Page(页), 每个Page含有2k byte的正常存储空间以及64 byte的校验空间 .

总空间 = 2048 * 64 * (2 * 1024 + 64)  byte

实际存储空间 = 2048 * 64 * 2 * 1024 byte

2. 引脚定义及接法

162932_f0qD_1256737.png

134355_TV7G_1256737.png

3. 寻址方式

133226_tLTx_1256737.png

列地址: 进行 Block 和 Page 寻址

行地址: 进行 Page 内寻址

4. 命令

以下为几个命令操作示意, 更多命令请参靠芯片手册.

(1) Read ID

134801_NzTD_1256737.png

说明: 先发送  0x90  然后再发送 0x00, 然后 nand flash 会返回5个数据, 依据芯片型号的不同数据内容也不尽相同, 具体值见手册

(2) Page Read

141137_bofN_1256737.png

说明: 先发送 0x00 然后发送要读取数据的地址, 之后发送 0x30, 根据 R/B 可以判断是否发送完成. 命令发送完成之后 从RE 的第一个下降沿开始, 芯片将从该地址开始到当页结束的所有数据依次输出

(3) Page Program

145445_qows_1256737.png

说明: 先发送 0x80 然后发送 地址 和数据, 之后发送 0x10, 读取 R/B , 命令写入完成之后 发送 0x70 再依据 I/O0来判断写入是否成功, 0 : 成功 1 : 失败

(4) Block Erase 

135151_BDFh_1256737.png

说明: 先发送 0x90  然后再发送 行地址 1 2 3, 然后再发送 擦除命令 0xD0, 根据 R/B 引脚判断擦除操作是否完成, 完成之后发送 0x70 根据 I/O0的状态来判断擦除是否成功, 0 : 成功  1: 失败

注释1:

/* 初始化NAND Flash */
void nand_init(void)
{
#define TACLS   0
#define TWRPH0  3
#define TWRPH1  0

    /* 判断是S3C2410还是S3C2440 */
    if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))    //2410
    {    
        nand_chip.nand_reset         = s3c2410_nand_reset;
        nand_chip.wait_idle          = s3c2410_wait_idle;
        nand_chip.nand_select_chip   = s3c2410_nand_select_chip;
        nand_chip.nand_deselect_chip = s3c2410_nand_deselect_chip;
        nand_chip.write_cmd          = s3c2410_write_cmd;
        nand_chip.write_addr         = s3c2410_write_addr;
        nand_chip.read_data          = s3c2410_read_data;

	/* 使能NAND Flash控制器, 初始化ECC, 禁止片选, 设置时序 */
        s3c2410nand->NFCONF = (1<<15)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);
    }
    else
    {
        nand_chip.nand_reset         = s3c2440_nand_reset;
        nand_chip.wait_idle          = s3c2440_wait_idle;
        nand_chip.nand_select_chip   = s3c2440_nand_select_chip;
        nand_chip.nand_deselect_chip = s3c2440_nand_deselect_chip;
        nand_chip.write_cmd          = s3c2440_write_cmd;
#ifdef LARGER_NAND_PAGE
        nand_chip.write_addr         = s3c2440_write_addr_lp;
#else
	nand_chip.write_addr         = s3c2440_write_addr;
#endif
        nand_chip.read_data          = s3c2440_read_data;

	/* 设置时序 */
        s3c2440nand->NFCONF = (TACLS << 12)|(TWRPH0 << 8)|(TWRPH1 << 4);
       
         /* 使能NAND控制器, 初始化ECC, 禁止片选 */
        s3c2440nand->NFCONT = (1 << 4)|(1 << 1)|(1 << 0);
    }
    
    /* 复位NAND Flash */
    nand_reset();
}

这里需要注意的是 大页 nand , 地址发送五次, 前两次是 colum addr 后三次是 row addr,  colum addr负责页内寻址, 我们实际寻址的空间为2k = 2^11, 所以只需要11位即可, 所以程序中只取了低11位

A0~A10用来页内寻址. 另外64byte的OOB空间可以使用A11来寻址. 

A12~A17用来在块内寻址页, 共64页

A18~A28用来寻址块, 共2048块

nand flash 底层操作函数:

/* 复位 */
static void s3c2410_nand_reset(void)
{
    s3c2410_nand_select_chip();   // 选中芯片 
    s3c2410_write_cmd(0xff);      // 复位命令
    s3c2410_wait_idle();          // 等待nand就绪
    s3c2410_nand_deselect_chip(); // 取消选中   
}

/* 等待NAND Flash就绪 */
static void s3c2410_wait_idle(void)
{
    int i;
    volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFSTAT;
    while(!(*p & BUSY))
        for(i=0; i<10; i++);
}

/* 发出片选信号 */
static void s3c2410_nand_select_chip(void)
{
    int i;
    s3c2410nand->NFCONF &= ~(1<<11);
    for(i=0; i<10; i++);    
}

/* 取消片选信号 */
static void s3c2410_nand_deselect_chip(void)
{
    s3c2410nand->NFCONF |= (1<<11);
}

/* 发出命令 */
static void s3c2410_write_cmd(int cmd)
{
    volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFCMD;
    *p = cmd;
}

/* 发出地址 */
static void s3c2410_write_addr(unsigned int addr)
{
    int i;
    volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFADDR;
    
    *p = addr & 0xff;
    for(i=0; i<10; i++);
    *p = (addr >> 9) & 0xff;
    for(i=0; i<10; i++);
    *p = (addr >> 17) & 0xff;
    for(i=0; i<10; i++);
    *p = (addr >> 25) & 0xff;
    for(i=0; i<10; i++);
}

/* 读取数据 */
static unsigned char s3c2410_read_data(void)
{
    volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFDATA;
    return *p;
}

/* S3C2440的NAND Flash操作函数 */

/* 复位 */
static void s3c2440_nand_reset(void)
{
    s3c2440_nand_select_chip();
    s3c2440_write_cmd(0xff);  // 复位命令
    s3c2440_wait_idle();
    s3c2440_nand_deselect_chip();
}

/* 等待NAND Flash就绪 */
static void s3c2440_wait_idle(void)
{
    int i;
    volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT;//状态寄存器, 只用到位0. 0 : busy   1 : ready
    while(!(*p & BUSY))
        for(i=0; i<10; i++);
}

/* 发出片选信号 */
static void s3c2440_nand_select_chip(void)
{
    int i;
    s3c2440nand->NFCONT &= ~(1<<1);
    for(i=0; i<10; i++);    
}

/* 取消片选信号 */
static void s3c2440_nand_deselect_chip(void)
{
    s3c2440nand->NFCONT |= (1<<1);
}

/* 发出命令 */
static void s3c2440_write_cmd(int cmd)
{
    volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMD;//NFCMD 不同的flash命令不一样, 发送命令信号
    *p = cmd;
}

/* 发出地址(小页 4周期) */
static void s3c2440_write_addr(unsigned int addr)
{
    int i;
    volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;
    
    *p = addr & 0xff;
    for(i=0; i<10; i++);
    *p = (addr >> 9) & 0xff;
    for(i=0; i<10; i++);
    *p = (addr >> 17) & 0xff;
    for(i=0; i<10; i++);
    *p = (addr >> 25) & 0xff;
    for(i=0; i<10; i++);
}
/* 发出地址(大页 5周期) */
static void s3c2440_write_addr_lp(unsigned int addr)
{
	int i;
	volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;//NFADDR当向该寄存器写入数据时, 芯片向nand发送地址信号
	int col, page;
             //#define NAND_SECTOR_SIZE_LP    2048
             //#define NAND_BLOCK_MASK_LP     (NAND_SECTOR_SIZE_LP - 1)
	col = addr & NAND_BLOCK_MASK_LP;        //2048 -1 = 11111111111b, 这里是屏蔽高位, 取低11位数据, 因为寻址空间只到 2k = 2^11 , 所以最多用11位, 这里直接不考虑第12位
	                                        //参考链接:http://bbs.csdn.net/topics/360034390
	page = addr / NAND_SECTOR_SIZE_LP;      //2048 = 2^11, 这里将数据右移11位, 获取高位数据
	
	*p = col & 0xff;			/* Column Address A0~A7 */
	for(i=0; i<10; i++);		
	*p = (col >> 8) & 0x0f; 	/* Column Address A8~A11 */
	for(i=0; i<10; i++);
	*p = page & 0xff;			/* Row Address A12~A19 */
	for(i=0; i<10; i++);
	*p = (page >> 8) & 0xff;	/* Row Address A20~A27 */
	for(i=0; i<10; i++);
	*p = (page >> 16) & 0x03;	/* Row Address A28~A29 */
	for(i=0; i<10; i++);
}

/* 读取数据 */
static unsigned char s3c2440_read_data(void)
{
    volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA;//数据寄存器, 读写都是这个寄存器. 只用到它的低 8 位
    return *p;
}

/* 在第一次使用NAND Flash前,复位一下NAND Flash */
static void nand_reset(void)
{
    nand_chip.nand_reset();
}

发送地址: NFADDR      发送命令: NFCMD        发送/读取数据: NFDATA          读取状态: NFSTAT          初始化控制器: NFCONT

由以上代码可以看出: 初始化完毕之后就不必关系总线上的时序只需要把 地址/命令/数据放到对应的寄存器, 芯片就能在总线上发出对应的时序

而初始化需要配置的是: 时序的参数/数据位宽/只读位/页的大小

注释2:

/* 读函数 */
void nand_read(unsigned char *buf, unsigned long start_addr, int size)//在head.S中设置的r0 r1 r2 分别为该函数的三个参数
{
    int i, j;

#ifdef LARGER_NAND_PAGE
    if ((start_addr & NAND_BLOCK_MASK_LP) || (size & NAND_BLOCK_MASK_LP)) {
        return ;    /* 地址或长度不对齐 */
    }
#else
    if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {
        return ;    /* 地址或长度不对齐 */
    }
#endif

    /* 选中芯片 */
    nand_select_chip();

    for(i=start_addr; i < (start_addr + size);){
      /* 发出READ0命令 */
      write_cmd(0);

      /* Write Address */
      write_addr(i);
#ifdef LARGER_NAND_PAGE
      write_cmd(0x30);
#endif
      wait_idle();

#ifdef LARGER_NAND_PAGE
      for(j=0; j < NAND_SECTOR_SIZE_LP; j++, i++) {
#else
	  for(j=0; j < NAND_SECTOR_SIZE; j++, i++) {
#endif
          *buf = read_data();
          buf++;
      }
    }

    /* 取消片选信号 */
    nand_deselect_chip();

    return ;
}

总结一下:

(1)选中芯片

(2)发送00h

(3)发出地址

(4)发30h

(5)等待就绪

(6)读一页数据

链接文件:

SECTIONS { 
  firtst  	0x00000000 : { head.o init.o nand.o}
  second 	0x30000000 : AT(4096) { main.o }
}

main.c存放到了nand的4096地址处.

入口文件:

@******************************************************************************
@ File:head.s
@ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行
@******************************************************************************       
  
.text
.global _start
_start:
                                            @函数disable_watch_dog, memsetup, init_nand, nand_read_ll在init.c中定义
            ldr     sp, =4096               @设置堆栈 
            bl      disable_watch_dog       @关WATCH DOG
            bl      memsetup                @初始化SDRAM
            bl      nand_init               @初始化NAND Flash                                                                 注释1

                                            @将NAND Flash中地址4096开始的1024字节代码(main.c编译得到)复制到SDRAM中
                                            @nand_read_ll函数需要3个参数:
            ldr     r0,     =0x30000000     @1. 目标地址=0x30000000,这是SDRAM的起始地址
            mov     r1,     #4096           @2.  源地址   = 4096,连接的时候,main.c中的代码都存在NAND Flash地址4096开始处
            mov     r2,     #2048           @3.  复制长度= 2048(bytes),对于本实验的main.c,这是足够了
            bl      nand_read               @调用C函数nand_read                                                               注释2

            ldr     sp, =0x34000000         @设置栈
            ldr     lr, =halt_loop          @设置返回地址
            ldr     pc, =main               @b指令和bl指令只能前后跳转32M的范围,所以这里使用向pc赋值的方法进行跳转
halt_loop:
            b       halt_loop

这里将nand flash 从4096地址开始的2048字节复制到sdram的0x3000 0000地址处

main.c中是led闪烁程序. 

转载于:https://my.oschina.net/cxh1024/blog/260787

移动电话的功能日益丰富,其对系统中数据存储容量的需求正在快速增长。 NAND Flash具有速度快、密度大、成本低等特点,在各种数码产品中得到了广泛 应用,在各种片上系统芯片中(SOC)集成NAND Flash控制器正成为一种趋势。 本文讨论了Flash Memory的两种主流实现技术即NAND Flash和NOR Flash 的特点和区别,分析了市场上存在的NAND Flash的典型规格及其存储结构特点, 阐述了不同NAND Flash器件一些通用的存取操作方式,近一步分析了进行这些 存取操作所必须满足的时序规范,在此基础上,结合某公司手机SOC芯片的设计 需求,提出了一种基于AMBA总线的NAND Flash控制器实现方案,对该实现方 案进行了充分的验证工作。 本文所提出的控制器的实现方式,可以支持市场上存在的两种典型规格的 NAND Flash器件,可同时外接1至4个Flash芯片,通过可配置的控制方式可灵 活的对不同存取速度的器件予以支持,具备良好的可扩展性。在控制器的主控逻 辑设计中,采取了“块读’’和“块写”方式实现对大页器件的读页和写页操作, 这种方式有效减小了控制器中用做数据缓存的buffer大小,降低了芯片面积。针 对NAND Flash在使用过程中可能出现的位反转现象,在控制器的设计中加入了错 误检测和纠错功能。论文深入分析了ECC(Error Checking and Correcting)算法,讨 论了ECC算法的硬件实现和优化方法。在不影响对存储器读写效率的前提下,实 现对数据的存取进行实时的高速检错和纠错,为提高NAND Flash的可靠性提供了 硬件上的支持。 对控制器的验证采用了模拟验证和FPGA验证两种方式。在模拟验证阶段对 控制器的所有功能点进行全面验证,结果正确后,在Xilinx公司的Vertex4开发板 上对控制器进行了FPGA验证。结果表明控制器能正确控制对于NAND Flash的各 种存取操作,工作完全正常。 本文设计验证的NAND Flash控制器即将应用于某公司的SOC手机芯片,提 出的控制器实现方案对NAND Flash控制系统的设计优化具有普遍适用性,论文研 究的工程实用价值大。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值