NAND FLASH

转载地址:http://blog.csdn.net/liangkaiming/article/details/6307910

 当OM1、OM0都是低电平——即开发板插上BOOT SEL跳线时,S3C2410从NAND Flash启动:NAND Flash的开始4k代码会被自动地复制到内部SRAM中。我们需要使用这4k代码来把更多的代码从NAND Flash中读到SDRAM中去。NAND Flash的操作通过NFCONFNFCMDNFADDRNFDATANFSTATNFECC六个寄存器来完成。在开始下面内容前,请打开S3C2410数据手册和NAND Flash K9F1208U0M的数据手册。 
 在S3C2410数据手册218页,我们可以看到读写NAND Flash的操作次序: 
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 from NFDATA while checking NAND flash status by NFSTAT 
register
. R/nB signal should be checked before read operation or 
after program operation.

 

下面依次介绍: 
 1、NFCONF:设为0xf830——使能NAND Flash控制器(15位)初始化ECC(12位)、NAND Flash片选信号nFCE=1(inactive也就是未激活,真正使用时再让它等于0,一般在自启动后这位都会被置为1,等到真正需要使用NAND FLASH时才激活这位)设置TACLSTWRPH0TWRPH1。需要指出的是TACLS、TWRPH0和TWRPH1,请打开S3C2410数据手册218页,
可以看到这三个参数控制的是NAND Flash信号线CLE/ALE与写控制信号nWE的时序关系。我们设的值为TACLS=0,TWRPH0=3,TWRPH1=0,其含义为: TACLS=1个HCLK时钟TWRPH0=4个HCLK时钟,TWRPH1=1个HCLK时钟。请打开K9F1208U0M数据手册第13页,在表“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的时序要求的。

 

2、NFCMD: 
 对于不同型号的Flash,操作命令一般不一样。对于本板使用的K9F1208U0M,请打开其数据手册第8页“Table 1. Command Sets”,上面列得一清二楚。下面在分析源代码是会涉及到Flash的命令,该寄存器一般也只用后八位也就是[7:0]。

 

3、NFADDR:同样也只用到该寄存器的后八位 
4、NFDATA:只用到低8位,这个寄存器中涉及到读和写两种状态,如果NFCMD中是读命令,则可以直接从这个寄存器中读出对应地址的值,如果是写命令则是Programming data。
5、NFSTAT:只用到位0,0-busy,1-ready 
6、NFECC:待补

 

 

现在来看一下如何从NAND Flash中读出数据,请打开K9F1208U0M数据手册第29页“PAGE READ”,跟本节的第2段是遥相呼应啊,提炼出来罗列如下(设读地址为addr)首先从汇编代码上来分析:

前面的对看门狗,SDRAM的初始化与上面都是一样的,主要来看看对NAND FLASH操作的这一块:

 

copy_myself:
 mov r10, lr  //前面代码中是通过bl copy_myself来进入到该程序段来执行,所以必须要保存lr,当使用bl或者blx跳转到子过程的时候,r14保存了返回地址,可以在调用过程结尾恢复

 @ inital
 mov r1, #NAND_CTL_BASE
 ldr r2, =0xf830  @ initial value
 str r2, [r1, #oNFCONF] //初始化NFCONF寄存器

 @ reset nand flash
 ldr r2, [r1, #oNFCONF]  //将NFCONF寄存器中的值赋给r2
 bic r2, r2, #0x800 @ nFCE active  在第一次操作NAND Flash前,通常复位一下,相当于NFCONF &= ~0x800 (使能NAND Flash) ,也就是将NFCONF寄存器的11位使能NAND FLASH的片选信号激活,这样后续才能对FLASH操作
 str r2, [r1, #oNFCONF]
 mov r2, #0xff  @ reset command
 strb r2, [r1, #oNFCMD]  //NFCMD = 0xff  (reset命令) 在这里就涉及到FLASH的命令行拉,这里0xff表示的是reset命令

 @ delay  
 mov r3, #0x0a  //一段延迟代码,用来等待一段时间然后再来检测NFSTAT是否准备好
1:
 subs r3, r3, #1
 bne 1b

 @ wait idle state 
2:
 ldr r2, [r1, #oNFSTAT]
 tst r2, #0x01  //tst指令:
位测试比较指令,主要来测试r1的第0位是否为1,将r2与0x01相与来查看r2的最后一位是否为1,不为1则表示NAND FLASH还没准备好,则循环等待

 beq 2b

 ldr r2, [r1, #oNFCONF]
 orr r2, r2, #0x800 @ nFCE inactive 如果FLASH已经准备好了,则将FLASH重新设置为未激活也就是NFCONF |=0x800,也就是相当于保存当前的状态,然后等到在C代码中要实际操作FLASH由重新激活
 str r2, [r1, #oNFCONF]

 ldr sp, =4096  @ nand_read.c needed 在上个例子中讲过因为涉及到C函数的调用所以需要进出栈,所以必须要设置堆栈
 ldr r0, =0x30000000 @ nand_read_ll argument 1 为C函数的调用准备好参数,r0:SDRAM的起始地址,为目的地址
 mov r1, #4096  @ nand_read_ll argument 2  因为NAND FLASH前4K的数据早就自动的读到steppingstone中,所以需要从4K之后开始读取,所以r1就是源地址
 mov r2, #1024  @ nand_read_ll argument 3  r3表示要读取数据的大小 在这里是要读取1K的数据
    @ 1024 enough for this example
 bl nand_read_ll //调用C函数nand_read_ll

 mov pc, r10  //调用C函数完毕后返回原调用处

 

继续来看看利用C语言来的FLASH进行操作:

 

#define NFCONF  (*(volatile unsigned int *)0x4e000000)
#define NFCMD  (*(volatile unsigned int *)0x4e000004)
#define NFADDR  (*(volatile unsigned char *)0x4e000008)
#define NFDATA  (*(volatile unsigned char *)0x4e00000c)
#define NFSTAT  (*(volatile unsigned char *)0x4e000010)

 

上面是定义NAND FLASH寄存器的地址。再来看看真正的FLASH 读函数:

#define NAND_SECTOR_SIZE 512  //nand flash 每个扇区的大小为512B
#define NAND_BLOCK_MASK  (NAND_SECTOR_SIZE - 1)

/* low level nand read function */
int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{    //可以看到从汇编语言中传递过来的参数buf为目的地址SDRAM的0x30000000,start_addr则是FLASH中的源地址
 int i, j;

 /*
  * K9F5608UOC asks for 512B per page, and read/write operation must
  * do with page. Therefore, first judge whether start_addr and size
  * are valid.
  */ 
 if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) { 检查FLASH中开始地址和药读取的大小是否是按页为单位的。
  return -1; /* invalid alignment */
 }

 /* chip Enable */因为要开始对FLASH进行操作了,所以需要将FLASH的片选激活
 NFCONF &= ~0x800;
 for (i=0; i<10; i++) {
  ;
 }

//正式开始从FLASH中读取数据到SDRAM中

 for (i=start_addr; i < (start_addr + size); i+=NAND_SECTOR_SIZE) {
  NFCMD = 0;//将FLASH命令寄存器设置为0x0表示是读命令

  /* Write Address */  这步得稍微注意一下,请打开K9F1208U0M数据手册第7页,那个表格列出了在地址操作的4个步骤对应的地址线,A8没用到:
  NFADDR = i & 0xff;
  NFADDR = (i >> 9) & 0xff;   (注意了,左移9位,不是8位) 
  NFADDR = (i >> 17) & 0xff;  (左移17位,不是16位)
  NFADDR = (i >> 25) & 0xff;  (左移25位,不是24位)

  wait_idle();在地址,命令都设置好后,我们就循环的等待NFSTAT的最后一位为1也就是准备好了,然后才开始从NFDATA读数据

   for(j=0; j < NAND_SECTOR_SIZE; j++) {   //连续读NFDATA寄存器512次,得到一页数据(512字节) 
   *buf++ = (NFDATA & 0xff);
  }
 }

 /* chip Disable */
 NFCONF |= 0x800; /* chip disable */

 return 0;
}

 

 

本实验代码在NAND目录下,源代码为head.s、nand_read.c和main.c。head.s中关WATCH DOG、初始化SDRAM、初始化NAND Flash,然后调用nand_read.c中的nand_read_ll函数来从NAND Flash地址4096开始处复制到SDRAM中,最后,跳到main.c中的
main函数继续执行。代码本身没什么难度,与前面程序最大的差别就是“链接脚本”的引入:在上个实验中最后一段提到过在arm-linux-ld命令中,选项“-Ttext”可以使用选项“-Tfilexxx”来代替。在本实验中,使用“-Tnand.lds”。nand.lds
内容如下: 
1 SECTIONS {  
2   firtst   0x00000000 : { head.o init.o } 也就是相当于FLASH的前4K用来存放head.o以及init.o,自启动时加载到steppingstone并且在该地址执行
3   second  0x30000000 : AT(4096) { main.o } 
4 }  
 
 完整的连接脚本文件形式如下: 
SECTIONS { 
... 
secname start BLOCK(align) (NOLOAD) : AT ( ldadr ) 
  { contents } >region :phdr =fill 
... 

并非每个选项都是必须的,仅介绍nand.lds用到的:

1、secname:段名,对于nand.lds,段名为first和second 
2、start:本段运行时的地址如果没有使用AT(xxx),则本段存储的地址也是start ,这就是我们所讲的运行时域和加载时域
3、AT( ldadr ):定义本段存储(加载)的地址 
4、{ contents }:决定哪些内容放在本段,可以是整个目标文件,也可以是目标文件中的某段(代码段、数据段等) 
 
nand.lds的含义是:head.o放在0x00000000地址开始处,init.o放在hean.o后面,它们的运行地址是0x00000000;main.o放在地址4096(0x1000)开始处,但是它的运行地址在0x30000000,在运行前需要从4096处复制到0x30000000处。


发布了0 篇原创文章 · 获赞 5 · 访问量 7万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章