NAND Flash--嵌入式NAND Flash讀寫技術

原文地址  http://blogold.chinaunix.net/u2/72003/showart_1931513.html

NAND Flash控制器

 S3C2410板的Nand Flash支持由兩部分組成:Nand Flash控制器(集成在S3C2410 CPU)和Nand Flash存儲芯片(K9F1208U0B)兩部分組成。當要訪問Nand Flash中的數據時,必須通過Nand Flash控制器發送命令才能完成。所以Nand Flash相當於S3C2410的一個外設,而不位於它的內存地址區.

 

   爲了支持NAND Flash的啓動裝載,S3C2410A配置了一個叫Steppingstone的內部SRAM緩衝器。當系統啓動時,NAND Flash存儲器的前4KB將被自動加載到Steppingstone中,然後系統自動執行這些載入的啓動代碼。

   一般情況下,這4KB的啓動代碼需要將NAND Flash中的內容複製到SDRAM中。使用S3C2410A內部硬件ECC功能可以對NAND Flash的數據進行有效性的檢查。複製完成後,將在SDRAM中執行主程序。

NAND Flash控制其具有以下特性:

 

    * NAND Flash模式:支持讀/擦除/編程NAND Flash存儲器。

    * 自動啓動模式:復位後,啓動代碼被傳送到Steppingstone中。傳送完畢後,啓動代碼在Steppingstone中執行。

    * 具備硬件ECC(校驗碼:Error Correction Code)生成模塊(硬件生成校驗碼,通過軟件校驗)

    * NAND Flash啓動以後,4KB的內部SRAM緩衝器Steppingstone可以作爲其他用途使用。

    * NAND Flash控制器不能通過DMA訪問,可以使用LDM/STM指令來代替DMA操作。

 

自啓動模式的執行步驟如下:

 

(1)完成復位

 

(2)如果自動啓動模式使能,NAND Flash存儲器的前4KB自動複製到Steppingstone內部緩衝器;

 

(3)Steppingstone映射到nGCS0;

 

(4)CPU在Steppingstone的4KB內部緩衝器中開始執行啓動代碼。

 

注意:在自動啓動模式下,不進行ECC檢測。因此,應確保NAND Flash的前4KB不能有位錯誤(一般NAND Flash廠家都能確保)。

 

 

 

NAND Flash模式需要進行以下配置:

 

(1)通過NFCONF寄存器設置NAND Flash配置;

 

(2)將NAND Flash命令寫入NFCONF寄存器;

 

(3)將NAND Flash地址寫入NFADDR寄存器;

 

(4)通過NFSTAT寄存器檢查NAND Flash狀態,並讀/寫數據。在讀操作之前或者編程操作之後應該檢查R/nB信號。

 

引腳配置

D[7:0]  數據/命令/地址的輸入/輸出口(與數據總線共享)

CLE      命令鎖存使能(輸出)

ALE      地址鎖存使能(輸出)

nFCE     NAND Flash片選使能(輸出)

nFRE     NAND Flash讀使能(輸出)

nFWE     NAND Flash寫使能(輸出)

R/nB     NAND Flash就緒/忙(輸入)

 

系統啓動和NAND Flash所需的配置如下:

(1)OM[1:0]=00b:使能NAND Flash控制器爲自動啓動模式;

(2)NAND Flash存儲器的頁面大小應該爲512字節;

(3)NCON:NAND Flash存儲器尋址步數選擇。0爲3步;1爲4步尋址。

 

相關寄存器

NAND Flash配置寄存器

NFCONF    地址0x4E000000

 

NAND Flash命令設置寄存器

NFCMD      地址0x4E000004

 

NAND Flash地址設置寄存器

NFADDR     地址0x4E000008

 

NAND Flash數據寄存器

NFDATA     地址0x4E00000C

 

NAND Flash操作狀態寄存器

NFSTAT     地址0x4E000010

 

NAND Flash ECC寄存器

NFECC       地址0x4E000014

 

 

 

下面針對三星的K9F1208U0M爲例說明nand flash的讀寫。

 

NAND Flash物理組成

正如硬盤的盤片被分爲磁道,每個磁道又分爲若干扇區,一塊nand flash也分爲若干block,每個block分爲如干page。一般而言,block、page之間的關係隨着芯片的不同而不同,典型的分配是這樣的:

1block = 32page

1page = 512bytes(datafield) + 16bytes(oob)

 

 

需要注意的是,對於flash的讀寫都是以一個page開始的,但是在讀寫之前必須進行flash的擦寫,而擦寫則是以一個block爲單位的。按照這種組織方式形成三類地址

Column Address:列地址,地址的低8位

Page Address:頁地址

Block Address:塊地址

8個I/O引腳充當地址、數據、命令的複用端口,所以每次傳地址只能傳8位,而nand falsh的地址位位26位,因此讀寫一次nand flash需要傳送4次(A[7:0] A[16:9] A[24:17]

A[25]

 

一頁有528B,在每一頁中,最後16個字節(OOB)用於nand flash執行完命令後設置狀態用的,剩餘512B又分爲前半部(1st half Page Register)和後半部(2nd half Page Register)。可以通過nand flash命令對1st half和2nd half

以及OOB進行定位通過nand flash內置的指針指向各自的首地址

 

存儲操作特點:

1.擦除操作的最小單位是塊

2.Nand Flash芯片每一位只能從1變爲0,而不能從0變爲1,所以在對其進行寫入操作之前一定要將相應塊擦除(擦除就是將相應塊的位全部變爲1

3 OOB部分的第六字節(即517字節)標誌是否壞塊,如果不是壞塊該值爲FF,否則爲壞塊

4 除OOB第六字節外,通常至少把OOB前3字節存放Nand Flash硬件ECC碼

 

NAND Flash尋址方式

512byte需要9bit來表示,對於528byte系列的NAND,這512byte被分成1st half Page Register和2nd half Page Register,各自的訪問由地址指針命令來選擇,A[7:0]就是所謂的column address(列地址),在進行擦除操作時不需要列地址,爲什麼?因爲以塊爲單位擦除。32個page需要5bit來表示,佔用A[13:9],即該page在塊內的相對地址。A8這一位地址被用來設置512byte的1st half page還是2nd half page,0表示1st,1表示2nd。Block的地址是由A14以上的bit來表示。

例如64MB(512Mb)的NAND flash(實際中由於存在spare area,故都大於這個值),共4096block,因此,需要12個bit來表示,即A[25:14],如果是128MB(1Gbit) 的528byte/page的NAND Flash,則block address用A[26:14]表示。由於地址只能在I/O[7:0]上傳遞,因此,必須採用移位的方式進行。以NAND_ADDR 爲例:

第1 步是傳遞column address,就是NAND_ADDR[7:0],不需移位即可傳遞到I/O[7:0]上,而halfpage pointer即A8 是由操作指令決定的,即指令決定在哪個halfpage 上進行讀寫,而真正的A8 的值是不需程序員關心的。

第2 步就是將NAND_ADDR 右移9位,將NAND_ADDR[16:9]傳到I/O[7:0]上;

第3 步將NAND_ADDR[24:17]放到I/O上;

第4步需要將NAND_ADDR[25]放到I/O上;

因此,整個地址傳遞過程需要4 步才能完成,即4-step addressing。 如果NAND Flash 的容量是32MB(256Mbit)以下,那麼,block adress最高位只到bit24,因此尋址只需要3步。

 

 

Nand flash主要的內設命令

Nand flash命令執行是通過將命令字送到Nand flash控制寄存器的命令寄存器中來執行的,其命令是分週期執行的,每條命令都有一個或多個執行週期,每個執行週期都有相應的代碼表示將要執行的動作。

 

功能

第一時鐘週期

第二時鐘週期

讀取數據寄存器

Read1

00h/01h

 

讀取數據寄存器下半區(OOB)

Read2

50h

 

讀取芯片ID

90h

 

RESET

FFh

 

寫頁面(page program)

(首先寫入00h(A區)/01h(B區)/05h(C區)表示寫入區;再寫入80h開始編程模式(寫入模式),接下來寫入地址和數據,最後寫入10h表示編程結束。

 

80h

10h

塊擦除(block erase)

60h

D0h

讀取狀態(read status)

70h

 

 

 

 

 

Nand Flash地址的計算
Column Address: 列地址。Column Address其實就是指定Page上的某個Byte,指定這個Byte其實也就是指定此頁的讀寫起始地址。

Paage Address:頁地址。由於頁地址總是以512Bytes對齊的,所以它的低9位總是0。確定讀寫操作是在Flash上的哪個頁進行的。

當我們得到一個Nand Flash地址srcaddr時候,我們可以這樣分解出Column Address和Page Address

columnaddr=srcaddr%512  //column address

pageaddr=srcaddr>>9     //page address

 

也可以這麼認爲,一個Nand Flash地址的A0~A7是它的column_addr,A9~A25是它的Page Address。(注意地址位A8並沒有出現,也就是A8被忽略,在下面你將瞭解到這是什麼原因)

以read1命令爲例:

Read1 命令的操作分爲4個Cycle,發送完讀命令00h或01h(00h與01h的區別請見下文描述)之後將分4個Cycle發送參數,1st.Cycle是發送Column Address。2nd.Cycle ,3rd.Cycle和4th.Cycle則是指定Page Address(每次向地址寄存器發送的數據只能是8位,所以17位的Page Address必須分成3次進行發送

Read1的命令裏面出現了兩個命令選項,分別是00h和01h。這裏出現了兩個讀命是否令你意識到什麼呢?是的,00h是用於讀寫1st half的命令,而01h是用於讀取2nd half的命令。現在我可以結合上圖給你說明爲什麼K9F1208U0B的DataField被分爲2個half了。

如上文所提及的,Read1的1st.Cycle是發送Column Address,假設我現在指定的Column Address是0,那麼讀操作將從此頁的第0號Byte開始一直讀取到此頁的最後一個Byte(包括Spare Field),如果我指定的Column Address是127,情況也與前面一樣,但不知道你發現沒有,用於傳遞Column Address的數據線有8條(I/O0~I/O7,對應A0~A7,這也是A8爲什麼不出現在我們傳遞的地址位中),也就是說我們能夠指定的 Column Address範圍爲0~255,但不要忘了,1個Page的DataField是由512個Byte組成的,假設現在我要指定讀命令從第256個字節處開始讀取此頁,那將會發生什麼情景?我必須把Column Address設置爲256,但Column Address最大隻能是255,這就造成數據溢出。。。正是因爲這個原因我們才把Data Field分爲兩個半區,當要讀取的起始地址(Column Address)在0~255內時我們用00h命令,當讀取的起始地址是在256~511時,則使用01h命令.假設現在我要指定從第256個byte開始讀取此頁,那麼我將這樣發送命令串

column_addr=256;

NF_CMD=0x01; ß                                       從2nd half開始讀取

NF_ADDR=column_addr&0xff;                       1st Cycle

NF_ADDR=page_address&0xff;                      2nd.Cycle

NF_ADDR=(page_address>>8)&0xff;             3rd.Cycle

NF_ADDR=(page_address>>16)&0xff;           4th.Cycle

其中NF_CMD和NF_ADDR分別是NandFlash的命令寄存器和地址寄存器的地址解引用,我一般這樣定義它們,

#define rNFCMD        (*(volatile unsigned char *)0x4e000004)        //NADD Flash command

#define rNFADDR        (*(volatile unsigned char *)0x4e000008)        //NAND Flash address

事實上,當NF_CMD=0x01時,地址寄存器中的第8位(A8)將被設置爲1(如上文分析,A8位不在我們傳遞的地址中,這個位其實就是硬件電路根據 01h或是00h這兩個命令來置高位或是置低位),這樣我們傳遞column_addr的值256隨然由於數據溢出變爲1,但A8位已經由於NF_CMD =0x01的關係被置爲1了,所以我們傳到地址寄存器裏的值變成了

 

A0  A1  A2  A3  A4  A5  A6  A7  A8

0     0    0    0    0     0    0    0    1   & 0xff = 0000 0000

 

這8個位所表示的正好是256,這樣讀操作將從此頁的第256號byte(2nd half的第0號byte)開始讀取數據。

現在舉一個例子,假設我要從Nand Flash中的第5000字節處開始讀取1024個字節到內存的0x30000000處,我們這樣調用read函數

nf_read(5000, 0x30000000,1024);

我們來分析5000這個src_addr.

根據   

column_addr=src_addr%512;       

page_address=(src_addr>>9);     

我們可得出column_addr=5000%512=392

page_address=(5000>>9)=9

於是我們可以知道5000這個地址是在第9頁的第392個字節處,於是我們的nf_read函數將這樣發送命令和參數

column_addr=5000%512;

>page_address=(5000>>9);

NF_CMD=0x01;                                           從2nd half開始讀取

NF_ADDR= column_addr &0xff;                     1st Cycle A[7:0]

NF_ADDR=page_address&0xff;                      2nd.Cycle A[16:9]

NF_ADDR=(page_address>>8)&0xff;             3rd.Cycle   A[24:17]

NF_ADDR=(page_address>>16)&0xff;           4th.Cycle   A[25]

向NandFlash的命令寄存器和地址寄存器發送完以上命令和參數之後,我們就可以從rNFDATA寄存器(NandFlash數據寄存器)讀取數據了.

我用下面的代碼進行數據的讀取.

for(i=column_addr;i<512;i++)

{

        *buf++=NF_RDDATA();

}

每當讀取完一個Page之後,數據指針會落在下一個Page的0號Column(0號Byte).

 

例如實現一個從某字節處開始讀取size大小的數據

static int NF_read(unsigned int src_addr,unsigned char *desc_addr,int size)

{

    int i;

    unsigned int column_addr = src_addr % 512;

    unsigned int page_address =(src_addr >> 9);

    unsigned char * buf = desc_addr;

   

    while((unsigned int)buf < (unsigned int)(desc_addr)+size)

    {

        NF_nFCE_L();    //enable chip

       

        if(column_addr > 255)

            NF_CMD(0x01);

        else

            NF_CMD(0x00);

 

       

        NF_ADDR(cloumn_addr & 0xff);    //column address A[7:0];

        NF_ADDR(page_address & 0xff);   //page address A[16:9]

        NF_ADDR((page_address >> 8) & 0xff);  //A[24:17]

        NF_ADDR((page_address >>16) & 0xff);  //A[25];

 

        for(i=0;i<10;i++);

        NF_WAITRB();

 

        for(i=column_addr;i<512;i++)

        {

            *buf++=NF_RDDATA();

        }

        NF_nFCE_H();

        column_addr = 0;

        page_address ++;

    }

    return ;

}

 

 

打開s3c2410 的datasheet page 230:我們定義如下寄存器

 

#define rNFCONF     (*(volatile unsigned *)0x4e000000)   //nand flash configuration

#define rNFCMD      (*(volatile char *)0x4e000004      //nand flash command

#define rNFADDR (*(volatile char *)0x4e000008         //nand flash address

#define rNFDATA     (*(volatile char *)0x4e00000c         //nand flash data

#define rNFSTAT     (*(volatile unsigned *)0x4e000010  //nand flash opreation status

#define rNFECC      (*(volatile int *)0x4e000014         //nand flash ecc

#define rNFECC0     (*(volatile char *)0x4e000014

#define rNFECC1     (*(volatile char *)0x4e000015

#define rNFECC2     (*volatile char *)0x4e000016

 

#define NF_CMD(cmd) {rNFCMD=cmd;}

#define NF_ADDR(addr)   {rNFADDR=addr;}

#define NF_nFCEL_L()    {rNFCONF &= ~(1<<11);}

#define NF_nFCLE_H()    {rNFCONF |= (1<<11);}

#define NF_RSTECC()      {rNFCONF |= (1<<12);}                          //Initialize ECC

#define NF_RDDATA() (rNFDATA)

#define NF_WRDATA(data) {rNFDATA=data;}

 

#define NF_WATRB()  {while(!(rNFSTAT&(1<<0)));}

 

//讀一頁數據的程序。            

static int NF_RreadPage(int block,int page,char *buffer)

{

    unsigned int blockpage;

    char *pbuf=buffer;

    char *oob[16];

    unsigned char ecc[3];

 

    page=page&0x1f;

    blockpage=(block << 5)+page;

 

    NF_RSTECC();    //Initialize ECC;

   

    NF_nFCE_L();

    NF_CMD(0x00);   //read command;

   

    NF_ADDR(0);     //A[7:0]  column=0 從第0字節開始讀一直讀完512B

    NF_ADDR(blockpage&0xff);   //A[16:9];

    NF_ADDR((blockpage>>8)&0xff);   //A[24:17]

    NF_ADDR((blockpage>>16)&0xff); //A[25];

   

    for(i=0;i<10;i++);  //wait tWB(100ns)

   

    NF_WAITRB();        //wait tR(max 12us)

   

    for(i=0;i<512;i++)

    {

        *pbuf++=NF_RDDATA();

    }

   

    ecc[0]=rNFECC0;

    ecc[1]=rNFECC1;

    ecc[2]=rNFECC2;

    for(i=0;i<16;i++)

    {

        oob[i]=NF_RDDATA(); //read oob;

    }

    NF_nFCE_H();

   

    if(ecc[0]==oob[0] && ecc[1] == oob[1] && ecc[2] == oob[2])  //Ecc校驗;

    {

        print("ECC OK:%x,%x,%x/n",oob[0],oob[1],oob[2]);

        return 1;

    }else{

        printf("ECC ERROR: read:%x,%x,%x, ECC reg:%x,%x,%x/n",oob[0],oob[1],oob[2],ecc[0],ecc[1],ecc[2]);

        return 0;

    }

}

 

static int NF_WritePage(unsigned int block,unsigned int page,char *buffer)

{

    int i;

    unsigned int blockpage=(block<<5)+page;

    char *pbuf=buffer;

    oobbuf[16]={0xff};

 

    NF_RSRECC();

   

    NF_nFCE_L();

    NF_CMD(0x00);

    NF_ADDR(blockpage&0xff);

    NF_ADDR((blockpage>>8)&0xff);

    NF_ADDR((blockpage>>16)&0xff);

 

    for(i=0;i<512;i++)

    {

        NF_WRDATA(*pbuf++);

    }

   

    oobbuf[0]=rNFECC0;

    oobbuf[1]=rNFECC1;

    oobbuf[2]=rNFECC2;

    oobbuf[5]=0xff;

 

    for(i=0;i<16;i++)

    {

        NF_WRDATA(oobbuf[i]);

    }

 

    NF_CMD(0x10);       //Write 2nd command;

 

    for(i=0;i<10;i++);  //tWB=100ns;

 

    NF_WAITRB();

 

    NF_CMD(0x70);    //read status command;

 

    for(i=0;i<3;i++);

 

    if(NF_RDDATA()&0x1)     //write error

    {

        NF_nFCE_H();

        NF_MarkBadBlock(block);

        return 0;

    }else{

        NF_nFCE_H();

        return 1;

    }

 

}

 

 

 

static int NF_EraseBlock(U32 block)

{

    U32 blockPage=(block<<5);

    int i;

 

#if BAD_CHECK//壞塊校驗

    if(NF_IsBadBlock(block))

    return 0;

#endif

 

    NF_nFCE_L();//NF的CE(片選)拉低

   

    NF_CMD(0x60);   // Erase one block 1st command

 

    NF_ADDR(blockPage&0xff);        // 塊擦除只針對頁

    NF_ADDR((blockPage>>8)&0xff);  

    NF_ADDR((blockPage>>16)&0xff);

 

    NF_CMD(0xd0);   // Erase one blcok 2nd command

   

    for(i=0;i<10;i++); //wait tWB(100ns)//??????

 

    NF_WAITRB();    // Wait tBERS max 3ms.

    NF_CMD(0x70);   // Read status command

 

    if (NF_RDDATA()&0x1) // Erase error

    {  

        NF_nFCE_H();

    Uart_Printf("[ERASE_ERROR:block#=%d]/n",block);

    NF_MarkBadBlock(block);

    return 0;

    }

    else

    {

        NF_nFCE_H();////NF的CE(片選)拉高

        return 1;

    }

}

 

 

NAND設備存在壞塊,爲和上層文件系統接口,NAND設備的驅動程序必須給文件系統提供一個可靠的存儲空間,這就需要ECC(Error Corection Code)校驗,壞塊標註、地址映射等一系列的技術手段來達到可靠存儲目的。

    SSFDC軟件規範中,詳細定義瞭如何利用NAND設備每個頁中的冗餘信息來實現上述功能。這個軟件規範中,很重要的一個概念就是塊的邏輯地址,它將在物理上可能不連續、不可靠的空間分配編號,爲他們在邏輯空間上給系統文件提供一個連續可靠的存儲空間。

表3給出了SSFDC規範中邏輯地址的標註方法。在系統初始化的時候,驅動程序先將所有的塊掃描一遍,讀出他們所對應的邏輯地址,並把邏輯地址和虛擬地址的映射表建好。系統運行時,驅動程序通過查詢映射表,找到需要訪問的邏輯地址所對應的物理地址然後進行數據讀寫。     

 

                表3 冗餘字節定義

字節序號

內容

字節序號

內容

512

用戶定義數據

520

後256BECC校驗和

513

521

514

522

515

523

塊邏輯地址

516

數據狀態

524

517

塊狀態

525

前256BECC校驗和

518

塊邏輯地址1

526

519

527

 

表4給出了塊邏輯地址的存放格式,LA表示邏輯地址,P代表偶校驗位。邏輯地址只有10bit,代表只有1024bit的尋址空間。而SSFDC規範將NAND設備分成了多個zone,每個zone 內有1024塊,但這物理上的1024塊映射到邏輯空間只有1000塊,其他的24塊就作爲備份使用,當有壞塊存在時,就可以以備份塊將其替換。

表4  邏輯地址格式

<!--[if !supportMisalignedColumns]--> <!--[endif]-->

D7

D6

D5

D4

D3

D2

D1

D0

 

 

0

0

0

1

0

LA9

LA8

LA7

第518   523字節

 

 

LA6

LA5

LA4

LA3

LA2

LA1

LA0

P

第519   524字節

                                     

 

有了以上的軟件規範,就可以對NAND設備寫出較標準的ECC校驗,並可以編寫檢測壞塊、標記壞塊、建立物理地址和邏輯地址的映射表的程序了。

 

 

static int NF_IsBadBlock(unsigned int  block)

{

    int i;

    unsigned int blockPage;

    unsigned char data;

   

    blockPage=(block<<5);   // For 2'nd cycle I/O[7:5]

   

    NF_nFCE_L();   

    NF_CMD(0x50);       // Spare array read command

    NF_ADDR(517&0xf);       // Read the mark of bad block in spare array(M addr=5)

    NF_ADDR(blockPage&0xff);    // The mark of bad block is in 0 page

    NF_ADDR((blockPage>>8)&0xff);   // For block number A[24:17]

    NF_ADDR((blockPage>>16)&0xff);  // For block number A[25]

 

   for(i=0;i<10;i++);   // wait tWB(100ns) //?????

   

    NF_WAITRB();    // Wait tR(max 12us)

   

    data=NF_RDDATA();

 

    NF_nFCE_H();   

 

    if(data!=0xff)

    {

        printf("[block %d has been marked as a bad block(%x)]/n",block,data);

        return 1;

    }

    else

    {

        return 0;

    }

}

 

static int NF_MarkBadBlock(U32 block)

{

    int i;

    unsigned int blockPage=(block<<5);

 

    seBuf[0]=0xff;

    seBuf[1]=0xff;   

    seBuf[2]=0xff;   

    seBuf[5]=0x44;   // Bad blcok mark=0

   

    NF_nFCE_L();

    NF_CMD(0x50);   //read OOB

    NF_CMD(0x80);   // Write 1st command

   

    NF_ADDR(0x0);           // The mark of bad block is

    NF_ADDR(blockPage&0xff);        // marked 5th spare array

    NF_ADDR((blockPage>>8)&0xff);   // in the 1st page.

    NF_ADDR((blockPage>>16)&0xff);  //

   

    for(i=0;i<16;i++)

    {

    NF_WRDATA(seBuf[i]);    // Write spare array

    }

 

    NF_CMD(0x10);   // Write 2nd command

   

    for(i=0;i<10;i++);  //tWB = 100ns.

 

    NF_WAITRB();      // Wait tPROG(200~500us)

 

    NF_CMD(0x70);

   

    for(i=0;i<3;i++);  //twhr=60ns

   

    if (NF_RDDATA()&0x1) // Spare arrray write error

    {  

        NF_nFCE_H();

        printf("[Program error is occurred but ignored]/n");

    }

    else

    {

        NF_nFCE_H();

    }

 

    printf("[block #%d is marked as a bad block]/n",block);

    return 1;

}

 

int search_logic_block(void)                    //建立物理地址到邏輯地址的映射表

{

    unsigned int block,i,blockPage,logic_no,zone,zone_i;

    unsigned char oob[16];

    for(i=0;i<BLOCK_NR;i++)                         //初始化全局變量

        lg2ph[i]=space_block[i]=0xffff;

    logic_number=0;

    space_nr=0;

 

    NF_nFCE_L();

    zone=BLOCK_NR/1024;                             //確定NAND設備中zone的個數

    for(zone_i=0;zone_i<zone;zone_i++)

    {

        //搜索每個zone 內邏輯地址和物理地址的映射關係

        for(block=0;block<1024;block++)

        {

            blockPage=((block+zone_i*1024)<<BLOCK_ADDRERSS_SHIFT);

        NF_WATIRB();                                //等待R/B#信號有效

        NF_CMD(0x50);                               // 讀取每個block內部第0個Page內冗餘的16個字節

 

        NF_ADDR(0);                                 // Column 0

        NF_ADDR(blockPage&0xff);      

        NF_ADDR((blockPage>>8)&0xff);               // Block & page num.

        NF_ADDR((blockPage>>16)&0xff);

 

        NF_WATIRB();                                //等待R/B#信號有效

        for(i=0;i<16;i++)  se[i]=NF_RDDATA();       // Write spare array

        NF_WATIRB();

        if(oob[5]!=0xff)[q8]                              //檢測是否存在壞塊

            printk("/n/rphysic block %d is bad block/n/r",block);

        else if(oob[7]!=se[12][q9] )

            printk("block address1:%d!=block address2 %d/n/r",oob[7],oob[12]);

        else if((oob[6][q10] &0xf8)==0x10)

        {

            //計算該block對應的邏輯地址

            logic_no=((0x7&oob[6])<<7)+(se[7]>>1)+zone_i*1000;

            if(lg2ph[logic_no]!=0xffff)             //說明有2個block擁有相同的邏輯地址

               printk("physical block %d and block %d have the same logic number %d/n",lg2ph[logic_no],block,logic_no);

            else lg2ph[logic_no]=block;             //將該block的邏輯地址關係記入lg2ph表

            logic_number++;                       

        }

        else if(oob[7]==0xff)                        //說明該block尚未編號

        {space_block[space_nr]=block;

        space_nr++;

        }

        }

    }

    printk("there are totally %d logic blocks/n/r",logic_number);

   NF_nFCE_H();

    return logic_number;

}

這段代碼的主要作用就是產生數組lg2ph[],這個數組的含義就是塊物理地址=lg2ph[邏輯地址]

 

 

static unsigned short NF_CheckId(void)

{

    int i;

    unsigned short id;

   

    NF_nFCE_L();

   

    NF_CMD(0x90);

    NF_ADDR(0x0);

   

    for(i=0;i<10;i++); //wait tWB(100ns)////?????

   

    id=NF_RDDATA()<<8;  // Maker code(K9S1208V:0xec)

    id|=NF_RDDATA();    // Devide code(K9S1208V:0x76)

   

    NF_nFCE_H();

   

    return id;

}

 

static void NF_Reset(void)

{

    int i;

    unsigned short id;

   

   

    NF_nFCE_L();

 

    NF_CMD(0xFF);   //reset command

 

    for(i=0;i<10;i++);  //tWB = 100ns.

 

    NF_WAITRB();      //wait 200~500us;

    

    NF_nFCE_H();

}

 

 

static void NF_Init(void)

{

    rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);

    // 1  1    1     1,   1      xxx,  r xxx,   r xxx       

    // En 512B 4step ECCR nFCE=H tACLS   tWRPH0   tWRPH1

   

    NF_Reset();

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章