宿主機(jī): Windows7 64bits 系統(tǒng)
創(chuàng)新互聯(lián)是專業(yè)的大余網(wǎng)站建設(shè)公司,大余接單;提供網(wǎng)站制作、網(wǎng)站設(shè)計(jì),網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行大余網(wǎng)站開(kāi)發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛(ài)的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來(lái)合作!
開(kāi)發(fā)板: 安米MDK972
軟件環(huán)境: RealEvo-IDE3.0
NAND Flash: S34ML02G100TF100
Density:2 Gbit
Input / Output Bus Width: 8-bits
Page Size:2112 (2048 + 64) bytes; 64 bytes is spare area
Block Size: 64 Pages;128k + 4k bytes
Plane Size: 1024 Blocks per Plane;128M + 4M bytes
Device Size: 2 Planes per Device or 256 Mbyte
NUC970的NAND控制器包含在FMI中。FMI分為DMA單元和FMI單元。對(duì)于NAND,支持單一DMA通道和硬件ECC,如圖 2-1所示。
圖 2-1 NUC970 NAND控制器
SylixOS中NAND Flsh的驅(qū)動(dòng)框架如圖 3-1所示。NAND通用驅(qū)動(dòng)主要在fs/mtd/nand/nand_base.c中,該文件包含了NAND的通用操作。驅(qū)動(dòng)工程師需要在NAND通用驅(qū)動(dòng)的基礎(chǔ)上實(shí)現(xiàn)與硬件相關(guān)的驅(qū)動(dòng)層的結(jié)構(gòu)體(nand_chip),該結(jié)構(gòu)體包含了對(duì)具體硬件相關(guān)的控制和操作函數(shù),以及相關(guān)硬件參數(shù)和配置信息。MTD層與文件系統(tǒng),SylixOS已經(jīng)完全實(shí)現(xiàn),不需要驅(qū)動(dòng)工程師實(shí)現(xiàn)。
圖 3-1 NAND驅(qū)動(dòng)框架
NAND驅(qū)動(dòng)需要完成NAND控制器、ECC的配置以及NAND的相關(guān)操作函數(shù)及文件系統(tǒng)掛載,如果使用硬件ECC一般自己定義OOB布局。NUC970驅(qū)動(dòng)實(shí)現(xiàn)的操作如程序清單 3-1所示。
程序清單 3-1 NAND實(shí)現(xiàn)框架
nandchipNand->cmd_ctrl = hwControl; nandchipNand->cmdfunc = nandCommand; nandchipNand->dev_ready = devReady; nandchipNand->select_chip = chipSelect; nandchipNand->read_byte = nandReadByte; nandchipNand->write_buf = nandWriteBuf; nandchipNand->read_buf = nandReadBuf; nandchipNand->chip_delay = 50; nandchipNand->ecc.mode = NAND_ECC_HW_OOB_FIRST; nandchipNand->ecc.hwctl = nandEnableHwEcc; nandchipNand->ecc.calculate = nandCalculateEcc; nandchipNand->ecc.correct = nandCorrectData; nandchipNand->ecc.write_page = nandWritePageHwEcc; nandchipNand->ecc.read_page = nandReadPageHwEccOobFirst; nandchipNand->ecc.read_oob = nandReadoobHwEcc; nandchipNand->ecc.layout = &__Gpnuc970nandoob;
控制器初始化主要實(shí)現(xiàn)了模塊時(shí)鐘使能、管腳復(fù)用、時(shí)序設(shè)置、片選、解除寫(xiě)保護(hù)、頁(yè)大小、軟件復(fù)位等操作。
ECC配置主要設(shè)置冗余區(qū)大小,保護(hù)前3字節(jié),自動(dòng)寫(xiě)校驗(yàn)值到NAND,設(shè)置算法等級(jí),ECC使能等操作。
該函數(shù)主要實(shí)現(xiàn)對(duì)ALE/CLE/nCE的控制,同時(shí)用來(lái)寫(xiě)命令和地址。
程序清單 3-2 命令控制函數(shù)
static VOID hwControl (struct mtd_info *pMtd, INT iCmd, UINT uiCtrl) { struct nand_chip *pChip = pMtd->priv; if (uiCtrl & NAND_CTRL_CHANGE) { ULONG IO_ADDR_W = (ULONG)REG_NANDDATA; if ((uiCtrl & NAND_CLE)) { IO_ADDR_W = REG_NANDCMD; } if ((uiCtrl & NAND_ALE)) { IO_ADDR_W = REG_NANDADDR; } pChip->IO_ADDR_W = (VOID *)IO_ADDR_W; } if (iCmd != NAND_CMD_NONE) { writeb(iCmd, pChip->IO_ADDR_W); } }
該函數(shù)主要實(shí)現(xiàn)向芯片中寫(xiě)命令的功能,在系統(tǒng)提供的默認(rèn)函數(shù)中通過(guò)調(diào)用cmd_ctrl函數(shù)來(lái)實(shí)現(xiàn)具體寫(xiě)操作。由于NUC970的控制器需在最后一個(gè)地址周期手動(dòng)設(shè)置EOA位,無(wú)法使用默認(rèn)函數(shù),差異代碼如程序清單 3-3所示:
程序清單 3-3 命令功能函數(shù)差異代碼
writel((iColumn >> BUS_WIDTH) | NANDADDR_EOA, REG_NANDADDR);
該函數(shù)主要用來(lái)獲得設(shè)備ready/busy引腳狀態(tài)。如果該函數(shù)指針設(shè)置為NULL無(wú)法獲得ready/busy引腳狀態(tài),則ready/busy信息需要通過(guò)讀取NAND芯片的狀態(tài)寄存器。代碼實(shí)現(xiàn)如程序清單 3-4所示。
程序清單 3-4 獲得NAND狀態(tài)
return ((readl(REG_NANDINTSTS) & NANDINTSTS_RB0_Status) ? 1 : 0);
該函數(shù)功能為從NAND芯片讀取一個(gè)字節(jié),代碼如程序清單 3-5所示。
程序清單 3-5 從NAND讀一個(gè)字節(jié)
return ((UCHAR)readl(REG_NANDDATA));
該函數(shù)功能為從一個(gè)緩沖區(qū)寫(xiě)數(shù)數(shù)據(jù)到NAND芯片。代碼實(shí)現(xiàn)如程序清單 3-6。
程序清單 3-6 寫(xiě)緩沖區(qū)數(shù)據(jù)到NAND
for (i = 0; i < iLen; i++) { writel(pucbuf[i], REG_NANDDATA); }
該函數(shù)功能為從NAND芯片讀數(shù)據(jù)到一個(gè)緩沖區(qū)。代碼實(shí)現(xiàn)如程序清單 3-7所示。
程序清單 3-7 讀數(shù)據(jù)到緩沖區(qū)
for (i = 0; i < iLen; i++) { writel(pucbuf[i], REG_NANDDATA); }
該函數(shù)用于控制硬件ECC發(fā)生器,只有在使用硬件ECC時(shí)實(shí)現(xiàn)。本例的硬件校驗(yàn)在傳輸中實(shí)現(xiàn),因此該函數(shù)為空實(shí)現(xiàn)。
該函數(shù)用于ECC計(jì)算,或從ECC硬件中讀回。本例的硬件校驗(yàn)在傳輸中實(shí)現(xiàn),因此該函數(shù)為空實(shí)現(xiàn)。
該函數(shù)用于ECC校正。本例的硬件校驗(yàn)在傳輸中實(shí)現(xiàn),因此該函數(shù)為空實(shí)現(xiàn)。
該函數(shù)主要實(shí)現(xiàn)帶ECC的寫(xiě)一頁(yè)數(shù)據(jù)到NAND芯片。在傳輸?shù)倪^(guò)程中,ECC電路會(huì)自動(dòng)計(jì)算ECC校驗(yàn)值,并存儲(chǔ)到控制器分配的寄存器組中。完成傳輸后寄存器組中的OOB數(shù)據(jù)會(huì)根據(jù)設(shè)置自動(dòng)寫(xiě)進(jìn)NAND芯片。實(shí)現(xiàn)流程如程序清單 3-8所示。
程序清單 3-8 帶硬件ECC的寫(xiě)頁(yè)
static INT nandWritePageHwEcc (struct mtd_info *pMtd, struct nand_chip *pChip, const UCHAR *pucBuf, INT iOobRequired) { UCHAR *pucEccCalc = pChip->buffers->ecccalc; UINT uiEccBytes = pChip->ecc.layout->eccbytes; register CHAR *pcPtr = (CHAR *)REG_NANDRA0; memset((VOID *)pcPtr, 0xFF, pMtd->oobsize); memcpy((VOID *)pcPtr, (VOID *)pChip->oob_poi, pMtd->oobsize - pChip->ecc.total); nandDmaTransfer(pMtd, pucBuf, pMtd->writesize , 0x1); /* * Copy parity code in SMRA to calc */ memcpy((VOID *)pucEccCalc, (VOID *)(REG_NANDRA0 + (pMtd->oobsize - pChip->ecc.total)), pChip->ecc.total); /* * Copy parity code in calc to oob_poi */ memcpy((VOID *)(pChip->oob_poi + uiEccBytes), (VOID *)pucEccCalc, pChip->ecc.total); return 0; }
該函數(shù)主要實(shí)現(xiàn)帶ECC校驗(yàn)的從NAND芯片讀出一頁(yè)數(shù)據(jù)。本例為硬件ECC,需要先讀出OOB區(qū)數(shù)據(jù)到控制器分配的寄存器組中。在數(shù)據(jù)傳輸?shù)倪^(guò)程中,ECC電路會(huì)計(jì)算ECC校驗(yàn)值,并與寄存器組中的值比較,檢查是否產(chǎn)生錯(cuò)誤,以及定位和計(jì)算校錯(cuò)值。若產(chǎn)生錯(cuò)誤,程序需要根據(jù)錯(cuò)誤位置和錯(cuò)誤值進(jìn)行校錯(cuò)。具體流程如程序清單 3-9所示:
程序清單 3-9 帶ECC的讀頁(yè)
static INT nandReadPageHwEccOobFirst (struct mtd_info *pMtd, struct nand_chip *pChip, UCHAR *ucBuf, INT iOobRequired, INT iPage) { INT iEccSize = pChip->ecc.size; CHAR *pcPtr = (CHAR *)REG_NANDRA0; /* * At first, read the OOB area */ nandCommand(pMtd, NAND_CMD_READOOB, 0, iPage); nandReadBuf(pMtd, pChip->oob_poi, pMtd->oobsize); /* * Second, copy OOB data to SMRA for page read */ memcpy((VOID *)pcPtr, (VOID *)pChip->oob_poi, pMtd->oobsize); /* * Third, read data from nand */ nandCommand(pMtd, NAND_CMD_READ0, 0, iPage); nandDmaTransfer(pMtd, ucBuf, iEccSize, 0x0); /* * Fouth, restore OOB data from SMRA */ memcpy((VOID *)pChip->oob_poi, (VOID *)pcPtr, pMtd->oobsize); return 0; }
該函數(shù)主要實(shí)現(xiàn)從芯片中讀取OOB數(shù)據(jù)。實(shí)現(xiàn)流程如程序清單 3-10所示。
程序清單 3-10 帶硬件ECC的讀OOB區(qū)數(shù)據(jù)
static INT nandReadoobHwEcc(struct mtd_info *pMtd, struct nand_chip *pChip, INT iPage) { CHAR *cPtr = (char *)REG_NANDRA0; /* * At first, read the OOB area */ nandCommand(pMtd, NAND_CMD_READOOB, 0, iPage); nandReadBuf(pMtd, pChip->oob_poi, pMtd->oobsize); /* * Second, copy OOB data to SMRA for page read */ memcpy ((VOID *)cPtr, (VOID *)pChip->oob_poi, pMtd->oobsize); return 0; }
nand_ecc為ECC布局控制結(jié)構(gòu)體。通過(guò)該結(jié)構(gòu)體配置OOB區(qū)中ECC的位數(shù)和位置,可用位數(shù)和空閑位數(shù)。本例通過(guò)調(diào)用程序清單 3-11代碼實(shí)現(xiàn)OOB區(qū)的布局控制。
程序清單 3-11 OOB區(qū)布局
static VOID oobTableLayout ( struct nand_ecclayout *pNandOOBTbl, INT iOobSize , INT iEccBytes) { pNandOOBTbl->eccbytes = iEccBytes; pNandOOBTbl->oobavail = iOobSize - DEF_RESERVER_OOB_SIZE_FOR_MARKER - iEccBytes ; pNandOOBTbl->oobfree[0].offset = DEF_RESERVER_OOB_SIZE_FOR_MARKER; /* Bad block marker size */ pNandOOBTbl->oobfree[0].length = iOobSize - iEccBytes - pNandOOBTbl->oobfree[0].offset ; }
在SylixOS下NAND Flash通常掛載YAFFS文件系統(tǒng),并分為n0和n1分區(qū),其中n0分區(qū)用作啟動(dòng)分區(qū),n1作為應(yīng)用分區(qū)。掛載流程如程序清單 3-12所示。
程序清單 3-12 文件系統(tǒng)掛掛載
yaffs_mtd_drv_install(&__GyaffsdevBootDev); yaffs_mtd_drv_install(&__GyaffsdevCommDev); yaffs_add_device(&__GyaffsdevBootDev); /* add to yaffs device table */ yaffs_add_device(&__GyaffsdevCommDev); /* add to yaffs device table */ yaffs_mount(cBootDevName); yaffs_mount(cCommDevName);
無(wú)。