轉載地址: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的操作通過NFCONF、NFCMD、NFADDR、NFDATA、NFSTAT和NFECC六個寄存器來完成。在開始下面內容前,請打開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時才激活這位)、設置TACLS、TWRPH0、TWRPH1。需要指出的是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處。