JZ2440的NandFlash控制器

【点此回顾上课学习内容]

芯片的型号:K9F2G08U0C

摘要: 本文以S3C2440为例来讲解NAND FLASH控制器的使用方法. 例程中故意将一部分代码放置到 nand 的4k 字节之后, 因无法自动拷贝到steppingstone, 所以需要读取nand中的内容到sdram, 本例程重点关注nand的初始化和读取.

1. 硬件部分:


1.1 简介

  • 芯片大小:256M Byte
  • 记忆单元阵列:(256M + 8,192K)bit x 8bit
  • 擦写次数比较少:10 万次 
  • 数据保留时间:10 年
  • 8个IO 口进行数据和地址的复用,因此,读写的时候要用到多个周期
 引脚定义及接法



1.2 内部存储单元的组织结构:



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

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

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

对 nandflash 的结构的几点说明:

  1.  一页中 1k 表示的是main 区(用于存储用户数据)容量,32 表示的是 spare 区(用于在读写操作的时候存放校验码)容量
  2. 块的大小一般是 128kb、256kb、512kb,貌似这里更小,是64kb
  3. 每个块里边包含很多页,老的 nandflash ,页大小是 256 Bytes、512Bytes,这类被称作 small block,地址周期只有 4 个。常见的nandflash,页大小多数是 2k Bytes,被称作 big block,地址周期 5 个,更新的 nandflash 页大小是 4k Bytes,这里的这个芯片,页大小是 2k Bytes,属于 big block。
  4. 这个芯片的写操作是以页为单位的擦除是以块为单位的
  5. 在一个块中,对每一页的编程必须是顺序的,比如,一个块中有128个页,那么你只能先对 Page0 编程,再对 Page1 编程 ...
  6. 为了能让 nandflash 作为启动介质,s3c2440 内部集成了4k 的 sram ,当从 nand 启动的时候,nandflash 代码的前 4k 空间会被赋值到 s3c2440 内部,然后从内部的 sram 开始启动。
  7. s3c2440 硬件产生 ECC 校验码。

1.3寻址方式

   NAND的读写的最小单位为Page:
小页:


大页:(K9F2G08U0C共有2048个Block(块), 每个Block含有 64 Page(页),所以我们这款芯片为大页读写)



列地址: 进行 Block 和 Page 寻址

行地址: 进行 Page 内寻址

如何区分Nandflash读写是大页还是小页点我!


2. 软件部分:


由于 s3c2440 内部有nand 的控制器,去查看2440的数据手册,自己编写时序操作程序不是明智的选择,正确的方式是一步一步配置好 s3c2440 的 nandflash 控制器,然后对应查看K9F2G08U0C的芯片手册看是否符合要求!
在此以读Nandflash为例子来配置寄存器。

2.1 初始化部分:

设置好时序中的几个间隔时间:
从 s3c2440 芯片手册上可以知道:
    图1


对于 CLE/ALE 上的时序,我们需要设置 TACLS,TWRPH0,TWRPH1,这几个都在 NFCONF 寄存器里。

寄存器

地址

R/W

描述

复位值

NFCONF

0x4E000000

R/W

Nandflash 的配置寄存器

0x0000100X

 

NFCONF

Bit

描述

初始值

Reserved

[15:14]

保留

-

TACLS

[13:12]

CLE & ALE duration setting value (0~3)

Duration =  HCLK x TACLS

01

...

...

...

...

TWRPH0

[10:8]

TWRPH0 duration setting value (0~7)

Duration =  HCLK x ( TWRPH0 + 1 )

000

...

...

...

...

TWRPH1

[6:4]

TWRPH1 duration setting value (0~7)

Duration =  HCLK x ( TWRPH1 + 1 )

000

从这里之后的几个都是由硬件决定(就是上下拉)的不需要软件来管。


 原理图中的这个地方设置的是上表中 TWRPH1 之后的位:


CCON = 1; // 支持 1k 字节或 2k 字字节每页的NAND flash存储器
GPG13 = 1; // 每页 2k 字节
GPG14 = 1; // 5个地址周期
GPG15 = 0; // 8位总线

要 求上边的 TACLS、TWRPH0、TWRPH1 的值:
  1. 我们假设 HCLK 为 最大值 136MHz ,稍作分析可得,我们实际的HCLK肯定要小很多,但是如果最大值满足那么小一点的HCLK肯定满足条件!则 HCLK 的周期是 1/136MHz = 7.4ns<flash时钟来自于HCLK后面讲时钟会提到>
  2. 查看 nandflash 的数据手册,找到跟时间相关的时序图和时间:
        时间表

        时序:
找一个跟上边红色数字 图1 中都有的一张时序图:

我们把图一也拿过来
    图1

则 
TACLS 时间是 tCLS -tWP,查 时间表 得到 12ns -12ns = 0ns
    根据寄存器中描述的计算公式:Duration =  HCLK x TACLS =>  0ns = 7.4ns x TACLS  => TACLS = 0

TWRPH0 的时间是 tWP ,查 时间表 得到 12ns 
    根据寄存器中描述的计算公式:Duration =  HCLK x ( TWRPH0 + 1 )  =>  12ns = 7.4ns x (TWRPH0 + 1)  => TWRPH0 = 1.6 ,由于他的取值范围是 (0~7) ,并且,时间表中的时间是最小能识别的时间,那我们取TWRPH0 =3

TWRPH1 的时间是 tclh = 5ns
    根据寄存器中描述的计算公式:Duration =  HCLK x ( TWRPH1 + 1 ) => 5ns = 7.4ns x (TWRPH1 + 1) => TWRPH1 = 0 即定能满足
[cpp] view plain copy
 print?
  1. #define NFCONF  (*((volatile unsigned long *)0x4E000000))  
  2.   
  3. void nand_init(void )  
  4. {  
  5.     #define TACLS   0  
  6.     #define TWRPH0  3  
  7.     #define TWRPH1  0  
  8.   
  9.     /* 设置时序 */  
  10.     NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);  
  11.     /* 使能 nandflash 控制器,初始化ECC,关片选 */  
  12.     NFCONT = (1<<4)|(1<<1)|(1<<0);  
  13. }  

2.2 芯片的选择和禁止(让芯片操作 CE 引脚):

[cpp] view plain copy
 print?
  1. #define NFCONT  (*((volatile unsigned long *)0x4E000004))  
  2.   
  3. void nand_select(void )  
  4. {  
  5.     NFCONT &= ~(1<<1);  
  6. }  
  7.   
  8. void nand_deselect(void )  
  9. {  
  10.     NFCONT |= (1<<1);  
  11. }  

2.3 写命令和写地址

[cpp] view plain copy
 print?
  1. // 写命令 注意是八位的命令  
  2. #define NFCMMD (*((volatile unsigned char *)0x4E000008))  
  3.   
  4. void nand_cmd(unsigned char cmd)  
  5. {  
  6.  volatile int i;  
  7.  NFCMMD = cmd;  
  8.  for (i = 0; i<10; i++); // 延时一段时间  
  9. }  

  1. // 写地址   (大页)
  2. #define NFADDR (*((volatile unsigned char *)0x4E00000C))  
  3. #define NAND_SECTOR_SIZE    2048  
  4. #define NAND_BLOCK_MASK     (NAND_SECTOR_SIZE_LP - 1)  
  5.   
  6. void nand_addr(unsigned int addr)  
  7. {  
  8. // unsigned int col = addr % 2048;  
  9. // unsigned int page = addr / 2048;  
  10.  col = addr & NAND_BLOCK_MASK;  
  11.  page = addr / NAND_SECTOR_SIZE;  
  12.  volatile int i;  
  13.  NFADDR = col & 0xff;                /* Column Address A0~A7 */  
  14.  for (i = 0; i<10; i++);  
  15.  NFADDR = (col>>8) & 0x0f;       /* Column Address A8~A11 */  
  16.  for (i = 0; i<10; i++);  
  17.  NFADDR = page & 0xff;            /* Row Address A12~A19 */  
  18.  for (i = 0; i<10; i++);  
  19.  NFADDR = (page>>8) & 0xff;   /* Row Address A20~A27 */  
  20.  for (i = 0; i<10; i++);  
  21.  NFADDR = (page>>16) & 0x03;  /* Row Address A28~A29 */  
  22.  for (i = 0; i<10; i++);  
  23. }  



对程序的解释:
 flash芯片的手册上有对于大页 flash 的访问 各个周期传递的位:

因此上边的命令 NFADDR = ... 就不难理解了。
至于,各个周期间的延时:
从flash芯片的数据手册上知道:

对于 I/O 引脚上的数据进行采集是在 WE 的上升沿进行的,因此, 每两个周期的间隔至少应该大于一个 tDS = 12ns,而对 flash 他的时钟来自 HCLK =136MHz,即使是单周期指令,也要 1/136MHz=7.4ns,因此,要延时一段时间。

2.4 读数据:

flash 芯片上:

flash 芯片上:



[cpp] view plain copy
 print?
  1. #define NFSTAT (*((volatile unsigned char *)0x4E000020))  
  2. #define NFDATA (*((volatile unsigned char *)0x4E000010))  
  3. void nand_wait_teady(void)  
  4. {  
  5.     while(!(NFSTAT & 1))  
  6.          for(i = 0; i < 10; i++);  
  7. }  
  8.   
  9. unsigned char nand_data(void)  
  10. {  
  11.     return NFDATA;  
  12. }  
  13. /* 
  14. * 参数的含义: 从NAND Flash位置start_addr开始,将数据复制到SDRAM地址buf处,共复制size字节
  15. */  
  16. void nand_read(unsignedchar* buf, unsignedlong start_addr, int size)
  17. {  
  18.     int i,j;  
  19.   if((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)){
  20. return;
  21.    }
  22.     
  23.    nand_select_chip(); //选中芯片  
  24.    
  25.    for(i=start_addr; i < (start_addr + size);) 
  26.     {  
  27.         write_cmd(0x00); // 发出读命令 00h  
  28.         write_addr(i); // 发送读的地址  
  29.         write_cmd(0x30); // 发出读命令 30h  
  30.         nand_wait_ready(); // 等待不忙  
  31.   
  32.         for(j=0; j < NAND_SECTOR_SIZE; j++,i++)  
  33.         {  
  34.             *buf = read_data(); // 读数据    
  35.             buf++;  
  36.         }    
  37.     }  
  38.     nand_deselect_ship(); // 取消片选
  39.     return ;  
  40. }  

总结一下:

(1)选中芯片

(2)发送00h

(3)发出地址

(4)发30h

(5)等待就绪

(6)读一页数据

2.5 复位 flash 芯片:

知道了上边的命令的表格,那 复位的实现也就简单了:
[cpp] view plain copy
 print?
  1. void nand_reset(void)  
  2. {  
  3.     nand_select_chip(); // 选中芯片  
  4.     write_cmd(0xff);  
  5.     nand_wait_ready();  
  6.     nand_deselect_chip();  
  7. }  

3.NandFlash操作实例

 

篇幅有限,点击跳转NandFlash 控制器操作实例:读Flash



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
移动电话的功能日益丰富,其对系统中数据存储容量的需求正在快速增长。 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
发出的红包

打赏作者

种瓜大爷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值