开发环境开发环境宿主机: Windows7 64bits 系统
开发板:安米MDK972
软件环境: RealEvo-IDE3.0
NAND Flash: S34ML02G100TF100
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
NAND控制器结构NUC970的NAND控制器包含在FMI中。FMI分为DMA单元和FMI单元。对于NAND,支持单一DMA通道和硬件ECC,如图 2-1所示。
图 2-1 NUC970 NAND控制器
技术实现驱动框架SylixOS中NAND Flsh的驱动框架如图 3-1所示。NAND通用驱动主要在fs/mtd/nand/nand_base.c中,该文件包含了NAND的通用操作。驱动工程师需要在NAND通用驱动的基础上实现与硬件相关的驱动层的结构体(nand_chip),该结构体包含了对具体硬件相关的控制和操作函数,以及相关硬件参数和配置信息。MTD层与文件系统,SylixOS已经完全实现,不需要驱动工程师实现。
图 3-1 NAND驱动框架
框架实现NAND驱动需要完成NAND控制器、ECC的配置以及NAND的相关操作函数及文件系统挂载,如果使用硬件ECC一般自己定义OOB布局。NUC970驱动实现的操作如程序清单 3-1所示。
程序清单 3-1 NAND实现框架
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;
控制器初始化控制器初始化主要实现了模块时钟使能、管脚复用、时序设置、片选、解除写保护、页大小、软件复位等操作。
ECC配置ECC配置主要设置冗余区大小,保护前3字节,自动写校验值到NAND,设置算法等级,ECC使能等操作。
函数cmd_ctrl该函数主要实现对ALE/CLE/nCE的控制,同时用来写命令和地址。
程序清单3-2 命令控制函数
staticVOIDhwControl(structmtd_info*pMtd,INTiCmd,UINTuiCtrl){structnand_chip*pChip=pMtd->priv;if(uiCtrl&NAND_CTRL_CHANGE){ULONGIO_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);}}函数cmdfunc该函数主要实现向芯片中写命令的功能,在系统提供的默认函数中通过调用cmd_ctrl函数来实现具体写操作。由于NUC970的控制器需在最后一个地址周期手动设置EOA位,无法使用默认函数,差异代码如程序清单 3-3所示:
程序清单 3-3 命令功能函数差异代码
writel((iColumn>>BUS_WIDTH)|NANDADDR_EOA,REG_NANDADDR);
函数dev_ready该函数主要用来获得设备ready/busy引脚状态。如果该函数指针设置为NULL无法获得ready/busy引脚状态,则ready/busy信息需要通过读取NAND芯片的状态寄存器。代码实现如程序清单 3-4所示。
程序清单 3-4 获得NAND状态
return((readl(REG_NANDINTSTS)&NANDINTSTS_RB0_Status)?1:0);
函数read_byte该函数功能为从NAND芯片读取一个字节,代码如程序清单 3-5所示。
程序清单 3-5 从NAND读一个字节
return((UCHAR)readl(REG_NANDDATA));
函数write_buf该函数功能为从一个缓冲区写数数据到NAND芯片。代码实现如程序清单 3-6。
程序清单 3-6 写缓冲区数据到NAND
for(i=0;i<iLen;i++){writel(pucbuf[i],REG_NANDDATA);}
函数read_buf该函数功能为从NAND芯片读数据到一个缓冲区。代码实现如程序清单 3-7所示。
程序清单 3-7 读数据到缓冲区
for(i=0;i<iLen;i++){writel(pucbuf[i],REG_NANDDATA);}
函数ecc.hwctl该函数用于控制硬件ECC发生器,只有在使用硬件ECC时实现。本例的硬件校验在传输中实现,因此该函数为空实现。
函数ecc.calculate该函数用于ECC计算,或从ECC硬件中读回。本例的硬件校验在传输中实现,因此该函数为空实现。
函数ecc.correct该函数用于ECC校正。本例的硬件校验在传输中实现,因此该函数为空实现。
函数ecc.write_page该函数主要实现带ECC的写一页数据到NAND芯片。在传输的过程中,ECC电路会自动计算ECC校验值,并存储到控制器分配的寄存器组中。完成传输后寄存器组中的OOB数据会根据设置自动写进NAND芯片。实现流程如程序清单 3-8所示。
程序清单 3-8 带硬件ECC的写页
staticINTnandWritePageHwEcc(structmtd_info*pMtd,structnand_chip*pChip,constUCHAR*pucBuf,INTiOobRequired){UCHAR*pucEccCalc=pChip->buffers->ecccalc;UINTuiEccBytes=pChip->ecc.layout->eccbytes;registerCHAR*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);/**CopyparitycodeinSMRAtocalc*/memcpy((VOID*)pucEccCalc,(VOID*)(REG_NANDRA0+(pMtd->oobsize-pChip->ecc.total)),pChip->ecc.total);/**Copyparitycodeincalctooob_poi*/memcpy((VOID*)(pChip->oob_poi+uiEccBytes),(VOID*)pucEccCalc,pChip->ecc.total);return0;}
函数ecc.read_page该函数主要实现带ECC校验的从NAND芯片读出一页数据。本例为硬件ECC,需要先读出OOB区数据到控制器分配的寄存器组中。在数据传输的过程中,ECC电路会计算ECC校验值,并与寄存器组中的值比较,检查是否产生错误,以及定位和计算校错值。若产生错误,程序需要根据错误位置和错误值进行校错。具体流程如程序清单 3-9所示:
程序清单 3-9 带ECC的读页
staticINTnandReadPageHwEccOobFirst(structmtd_info*pMtd,structnand_chip*pChip,UCHAR*ucBuf,INTiOobRequired,INTiPage){INTiEccSize=pChip->ecc.size;CHAR*pcPtr=(CHAR*)REG_NANDRA0;/**Atfirst,readtheOOBarea*/nandCommand(pMtd,NAND_CMD_READOOB,0,iPage);nandReadBuf(pMtd,pChip->oob_poi,pMtd->oobsize);/**Second,copyOOBdatatoSMRAforpageread*/memcpy((VOID*)pcPtr,(VOID*)pChip->oob_poi,pMtd->oobsize);/**Third,readdatafromnand*/nandCommand(pMtd,NAND_CMD_READ0,0,iPage);nandDmaTransfer(pMtd,ucBuf,iEccSize,0x0);/**Fouth,restoreOOBdatafromSMRA*/memcpy((VOID*)pChip->oob_poi,(VOID*)pcPtr,pMtd->oobsize);return0;}
函数ecc.read_oob该函数主要实现从芯片中读取OOB数据。实现流程如程序清单 3-10所示。
程序清单 3-10 带硬件ECC的读OOB区数据
staticINTnandReadoobHwEcc(structmtd_info*pMtd,structnand_chip*pChip,INTiPage){CHAR*cPtr=(char*)REG_NANDRA0;/**Atfirst,readtheOOBarea*/nandCommand(pMtd,NAND_CMD_READOOB,0,iPage);nandReadBuf(pMtd,pChip->oob_poi,pMtd->oobsize);/**Second,copyOOBdatatoSMRAforpageread*/memcpy((VOID*)cPtr,(VOID*)pChip->oob_poi,pMtd->oobsize);return0;}
ecc.layoutnand_ecc为ECC布局控制结构体。通过该结构体配置OOB区中ECC的位数和位置,可用位数和空闲位数。本例通过调用程序清单 3-11代码实现OOB区的布局控制。
程序清单 3-11 OOB区布局
staticVOIDoobTableLayout(structnand_ecclayout*pNandOOBTbl,INTiOobSize,INTiEccBytes){pNandOOBTbl->eccbytes=iEccBytes;pNandOOBTbl->oobavail=iOobSize-DEF_RESERVER_OOB_SIZE_FOR_MARKER-iEccBytes;pNandOOBTbl->oobfree[0].offset=DEF_RESERVER_OOB_SIZE_FOR_MARKER;/*Badblockmarkersize*/pNandOOBTbl->oobfree[0].length=iOobSize-iEccBytes-pNandOOBTbl->oobfree[0].offset;}
文件系统挂载在SylixOS下NAND Flash通常挂载YAFFS文件系统,并分为n0和n1分区,其中n0分区用作启动分区,n1作为应用分区。挂载流程如程序清单 3-12所示。
程序清单 3-12 文件系统挂挂载
yaffs_mtd_drv_install(&__GyaffsdevBootDev);yaffs_mtd_drv_install(&__GyaffsdevCommDev);yaffs_add_device(&__GyaffsdevBootDev);/*addtoyaffsdevicetable*/yaffs_add_device(&__GyaffsdevCommDev);/*addtoyaffsdevicetable*/yaffs_mount(cBootDevName);yaffs_mount(cCommDevName);
参考资料无。 |