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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章