WINCE6.0+SLC ECC产生和校验


 

备注:处理器为S3C2451,flash为K9K8G08U0D

 

1.     ECC分配标准

 

先来看三星NAND FLASH spare区域的分配标准:

WINCE6.0+SLC ECC产生和校验_第1张图片 

图1

2.     StepldrECC的处理

 

Stepldr的写采用自己的函数FMD_LB_WriteSector_Steploader,stepldr采用8bit的ECC纠错,为什么呢?S3C2451 FLASH控制器部分给出说明:

1. Auto boot by: The boot code istransferred into 8-KB Steppingstone after reset. After the boot code is

transfered, boot code will beexecuted on the Steppingstone.

Note: IROMboot support 8Bit ECC correction on Nand device booting

此函数通过设置NFCONF[24:23]=1来支持8bit ECC。还有需要注意的是,这里采用另一组参数:

#define  DEFAULT_TACLS               (1)          // 1 HCLK (7.5ns)
#define  DEFAULT_TWRPH0          (4)          // 5 HCLK(37.5ns)
#define  DEFAULT_TWRPH1          (1)          // 2 HCLK(15ns)

2.1   写stepldr镜像文件时ECC的操作

 

当写入512字节(不包括spare数据)的数据后, 8bit ECC模块内部产生ECC奇偶校验码,这些校验码自动更新到NF8MECC0、NF8MECC1、NF8MECC2、NF8MECC3寄存器中,然后可以把校验码写入到spare区域,下面来看代码:

typedef struct
{
    UINT32         n8MECC0;        // 8MECC0
    UINT32         n8MECC1;        // 8MECC1
    UINT32         n8MECC2;        // 8MECC2
    UINT8           n8MECC3;        // 8MECC3
} MECC8;

此结构体的定位,为什么n8MECC3需要定义为8位的,见寄存器的说明:

WINCE6.0+SLC ECC产生和校验_第2张图片

图2


MECC8 t8MECC[4];
 
// initializevariable.
    for(i = 0; i < 4; i++) {
        t8MECC[i].n8MECC0 = 0x0;
        t8MECC[i].n8MECC1 = 0x0;
        t8MECC[i].n8MECC2 = 0x0;
        t8MECC[i].n8MECC3 = 0x0;
    }

每往main区域写入512字节的数据,都产生ECC校验码并保存在t8MECC[nSectorLoop]中。

    // Write each Sector in the Page. (4 Sectorper Page, Loop 4 times.)
    for (nSectorLoop = 0; nSectorLoop <SECTORS_PER_PAGE; nSectorLoop++)
    {
        // Initialize ECC register
        NF_MECC_UnLock();
        NF_RSTECC();
 
        // Special case to handle un-alignedbuffer pointer.
        if( ((DWORD)(pSectorBuff+nSectorLoop*SECTOR_SIZE)) & 0x3)
        {
            // Write the data
            for(i=0; i<SECTOR_SIZE/sizeof(DWORD);i++)
            {
                wrdata =(pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+0];
                wrdata |=(pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+1]<<8;
                wrdata |=(pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+2]<<16;
                wrdata |=(pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+3]<<24;
                NF_WRDATA_WORD(wrdata);
            }
        }
        else
        {
           WrPage512(pSectorBuff+nSectorLoop*SECTOR_SIZE);
        }
 
        NF_MECC_Lock();
 
       while(!(s2450NAND->NFSTAT&(1<<7))) ;
        s2450NAND->NFSTAT|=(1<<7);
 
        // Read out the ECC value generated by HW
        t8MECC[nSectorLoop].n8MECC0 =NF_8MECC0();
        t8MECC[nSectorLoop].n8MECC1 =NF_8MECC1();
        t8MECC[nSectorLoop].n8MECC2 =NF_8MECC2();
        t8MECC[nSectorLoop].n8MECC3 =(NF_8MECC3() & 0xff);
    }

这里要重点注意了,先来看数据手册的说明:

WINCE6.0+SLC ECC产生和校验_第3张图片

图3

也就是说,如果使用512字节的NAND FLASH memory,可以直接把校验码写入到spare区域,但如果使用多于512字节的NAND FLASH memory(上面的代码是写入4*512字节的数据),就需要先拷贝校验码到其他的存储空间(比如DRAM,这里对应局部变量数据t8MECC),等写完所有的main数据后,再把拷贝后的校验码写到spare区域。

 

//写完所有的main数据,现在写入到spare区域

    for(nSectorLoop = 0; nSectorLoop < 4;nSectorLoop++)
    {
       NF_WRDATA_WORD(t8MECC[nSectorLoop].n8MECC0); // 4 byte n8MECC0
       NF_WRDATA_WORD(t8MECC[nSectorLoop].n8MECC1); // 4 byte n8MECC1
       NF_WRDATA_WORD(t8MECC[nSectorLoop].n8MECC2); // 4 byte n8MECC2
       NF_WRDATA_BYTE((t8MECC[nSectorLoop].n8MECC3) & 0xff); // 1 byten8MECC3
    }

由此可知stepldr镜像文件对应的每页的16 字节spare区域安排如下:


图4

2.2   读stepldr镜像文件时ECC的操作

 

Sepldr内容是由S3C2451的IROM代码来读取的,读取时校验码的比较也是由IROM来完成的,为什么eboot在写入stepldr时采用8bit ECC,是因为:

Note: IROMboot support 8Bit ECC correction on Nand device booting

 

 

3.     EbootECC的处理

 

Eboot中写eboot和NK镜像文件采用1bit ECC

 

3.1   写Eboot和NK镜像文件时ECC的操作

由FMD_LB_WriteSector函数来实现,首先是写入main区域,然后重生ECC码,再写入spare区域。

 

// Write eachSector in the Page. (4 Sector per Page, Loop 4 times.)
              for (nSectorLoop = 0; nSectorLoop< SECTORS_PER_PAGE; nSectorLoop++)
              {
                             //  Initialize ECC register
                             NF_RSTECC();
                             NF_MECC_UnLock();
             
                             // Special case tohandle un-aligned buffer pointer.
                             if( ((DWORD)(pSectorBuff+nSectorLoop*SECTOR_SIZE)) & 0x3)
                             {
                                           //  Write the data
                                           for(i=0;i<SECTOR_SIZE/sizeof(DWORD); i++)
                                           {
                                                         wrdata= (pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+0];
                                                         wrdata|= (pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+1]<<8;
                                                         wrdata|= (pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+2]<<16;
                                                         wrdata|= (pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+3]<<24;
                                                         NF_WRDATA_WORD(wrdata);
                                           }
                             }
                             else
                             {
                                           WrPage512(pSectorBuff+nSectorLoop*SECTOR_SIZE);
                             }
             
                             NF_MECC_Lock();
                            
                             //  Read out the ECC value generated by HW
                             MECCBuf[nSectorLoop]= NF_RDMECC0();  
              }

当读或是写main数据(不包括spare数据)完成时,自动把ECC模块产生的校验码更新到NFMECC0、NFMECC1寄存器中,并且ECC状态寄存器的值不会改变。这时可以从这些寄存器中读取其值。

 

// Write theSectorInfo data to the media
              // NOTE: This hardware is odd:only a byte can be written at a time and it must reside in the
              //       upper byte of a USHORT.
              if(pSectorInfoBuff)             // SectorInfoBuff啊 瞒 乐促搁, 角力 Sector Info甫持绰促.
              {
#if CHECK_SPAREECC
                             NF_RSTECC();
                             NF_SECC_UnLock();
#endif
 
        // Write the first reserved field (DWORD)
                             NF_WRDATA_BYTE(pSectorInfoBuff->bBadBlock);
       NF_WRDATA_WORD(pSectorInfoBuff->dwReserved1);
                             NF_WRDATA_BYTE(pSectorInfoBuff->bOEMReserved);
#if CHECK_SPAREECC
                             NF_SECC_Lock();
#endif
                             NF_WRDATA_BYTE(pSectorInfoBuff->wReserved2&0xff);
                             NF_WRDATA_BYTE((pSectorInfoBuff->wReserved2>>8)&0xff);
              }
              else                                    // SectorInfoBuff啊 厚绢乐促搁, 歹固 data甫 持绰促. (0xffffffff ffffffff)
              {
                             // Make sure weadvance the Flash's write pointer (even though we aren't writing the SectorInfodata)
                             for(i=0;i<sizeof(SectorInfo)/sizeof(DWORD); i++)
                             {
                                           NF_WRDATA_WORD(0xffffffff);
                             }
              }

上面的内容是把SectorInfo结构体的数据写入到spare中,并且为这些数据产生ECC校验码,下面接着把main数据的ECC校验码接着写入spare中

    // Write the ECC value to the flash
              NF_WRDATA_WORD(MECCBuf[0]);
              NF_WRDATA_WORD(MECCBuf[1]);
              NF_WRDATA_WORD(MECCBuf[2]);
              NF_WRDATA_WORD(MECCBuf[3]);

上面在写入SectorInfo结构体bBadBlock、dwReserved1和bOEMReserved数据到spare的时候,为这三个数据产生ECC校验码,下面从NFSECC寄存器中读取出ECC校验码并写入到spare中

#if CHECK_SPAREECC
              if(pSectorInfoBuff)
              {
                             SECCBuf =NF_RDSECC();
                             NF_WRDATA_WORD(SECCBuf);
              }
#endif

根据上面的代码,可以每页(2048字节=4*512)对应的spare区域安排如下:

WINCE6.0+SLC ECC产生和校验_第4张图片

图5

其中bBadBlock表示坏块信息,如果其值为0xFF表示是好块,如果不是则为坏块;dwReserved1用来保存逻辑页(2048字节)的数量,比如,如果是第1block的第1页,那么它的值=64页,因为我们的flash一个block=64页。bOEMReserved,我们可以用来设置block的属性,比如设置此block为预留block,或是只读block等。wReserved2用来为FAL只是数据是否有效(Indicates data is valid for the FAL( flash abstraction layer))。

 

3.2   读Eboot和NK镜像文件时ECC的操作

由FMD_LB_ReadSector函数来实现,先读取spare区域的数据

 

(1)  读取spare区域数据,并进行ECC校验

//读取图5中的前8字节的数据

              if (pSectorInfoBuff)
              {
#if CHECK_SPAREECC
                             NF_RSTECC();
                             NF_SECC_UnLock();
#endif
 
                             pSectorInfoBuff->bBadBlock= NF_RDDATA_BYTE();
                             pSectorInfoBuff->dwReserved1= NF_RDDATA_WORD();
                             pSectorInfoBuff->bOEMReserved= NF_RDDATA_BYTE();
#if CHECK_SPAREECC
                             NF_SECC_Lock();
#endif
                             pSectorInfoBuff->wReserved2= NF_RDDATA_BYTE();
                             pSectorInfoBuff->wReserved2|= (NF_RDDATA_BYTE()<<8);
              }
              // If there is noSectorInfoBuffer, Just read and waste.
              else       
              {
                             for(i=0;i<sizeof(SectorInfo)/sizeof(DWORD); i++)
                             rddata = (DWORD)NF_RDDATA_WORD();                  // readand trash the data
              }

//接着读取保存MECC0校验数据(这是写入main数据时重生的ECC校验码)

 for (nSectorLoop = 0; nSectorLoop< SECTORS_PER_PAGE; nSectorLoop++)
              {
                             MECCBuf[nSectorLoop]= NF_RDDATA_WORD();
              }

//读取出spare的ECC,然后写入到NFSECCD中,并校验

#if CHECK_SPAREECC
              if (pSectorInfoBuff)
              {
                             SECCBuf =NF_RDDATA_WORD();
                             NF_WRSECCD((SECCBuf&0xff)|((SECCBuf<<8)&0xff0000));
 
                             nRetEcc =NF_ECC_ERR0;
 
                             if(!ECC_CorrectData(startSectorAddr, (LPBYTE)pSectorInfoBuff, nRetEcc,ECC_CORRECT_SPARE))
                                           returnFALSE;
              }
#endif

上面代码依据于数据手册下面的部分:

WINCE6.0+SLC ECC产生和校验_第5张图片

图6

当把数据写入到NFSECCD后,通过NFECCERR0来定为ECC出错的具体信息,如下图:

WINCE6.0+SLC ECC产生和校验_第6张图片

图7

 

下面重点来看ECC_CorrectData函数

BOOLECC_CorrectData(SECTOR_ADDR sectoraddr, LPBYTE pData, UINT32 nRetEcc,ECC_CORRECT_TYPE nType)
{
              DWORD  nErrStatus;
              DWORD  nErrDataNo;
              DWORD  nErrBitNo;
              UINT32 nErrDataMask;
              UINT32 nErrBitMask = 0x7;
              BOOL bRet = TRUE;
 
              if (nType == ECC_CORRECT_MAIN)
              {
                             //RETAILMSG(1,(TEXT("ECC_CorrectData()--->ECC_CORRECT_MAIN\r\n")));
                             nErrStatus   = 0;
                             nErrDataNo   = 7;
                             nErrBitNo    = 4;
                             nErrDataMask =0x7ff;
              }
              else if (nType ==ECC_CORRECT_SPARE)
              {
                             //RETAILMSG(1,(TEXT("ECC_CorrectData()--->ECC_CORRECT_SPARE\r\n")));
                 nErrStatus   = 2;
                 nErrDataNo   = 21;
                 nErrBitNo    = 18;
                 nErrDataMask = 0xf;
              }
              else
              {
                             //RETAILMSG(1,(TEXT("ECC_CorrectData()--->value=%d\r\n"),nType));
                 return FALSE;
              }
             
              switch((nRetEcc>>nErrStatus)& 0x3)
              {
                             case 0:   // No Error
                             //RETAILMSG(1,(TEXT("ECC_CorrectData()--->noerror\r\n")));
                                           bRet= TRUE;
                                           break;
                             case 1:   // 1-bit Error(Correctable)
                                           RETAILMSG(1,(TEXT("%cECCcorrectable error(0x%x). Byte:%d, bit:%d\r\n"),((nType==ECC_CORRECT_MAIN)?'M':'S'), sectoraddr,(nRetEcc>>nErrDataNo)&nErrDataMask,(nRetEcc>>nErrBitNo)&nErrBitMask));
                                           (pData)[(nRetEcc>>nErrDataNo)&nErrDataMask]^= (1<<((nRetEcc>>nErrBitNo)&nErrBitMask));
                                           bRet= TRUE;                     
                                           break;
                             case 2:   // Multiple Error
                                           RETAILMSG(1,(TEXT("%cECCUncorrectable error(0x%x)\r\n"), ((nType==ECC_CORRECT_MAIN)?'M':'S'),sectoraddr));
                                           bRet= FALSE;                                  
                                           break;
                             case 3:   // ECC area Error
                                           RETAILMSG(1,(TEXT("%cECCarea error\r\n"), ((nType==ECC_CORRECT_MAIN)?'M':'S')));
                            default:
                                           bRet= FALSE;                                  
                                           break;
              }
 
              return bRet;
}

此函数需要密切结合NFECCERR0寄存器来理解,见下图:

WINCE6.0+SLC ECC产生和校验_第7张图片

图8

(2)  读取main区域数据,并进行ECC校验

这部分代码原理和上面的基本一样

// Read eachSector in the Page. (4 Sector per Page, Loop 4 times.)
              for (nSectorLoop = 0; nSectorLoop< SECTORS_PER_PAGE; nSectorLoop++)
              {
                             // calculate startaddress of each Sector
                             NewDataAddr =nSectorLoop * SECTOR_SIZE;
 
                             // Set address forrandom access
                             NF_CMD(CMD_RDO);
                             NF_ADDR((NewDataAddr)&0xff);
                             NF_ADDR((NewDataAddr>>8)&0xff);
                             NF_CMD(CMD_RDO2);
 
                             // Initialize ECCmodule
                             NF_RSTECC();
                             NF_MECC_UnLock();
                            
                              // Special case to handle un-aligned bufferpointer.
                             if( ((DWORD)(pSectorBuff+nSectorLoop*SECTOR_SIZE)) & 0x3)
                             {
                                           for(i=0;i<SECTOR_SIZE/sizeof(DWORD); i++)
                                           {
                                                         rddata= (DWORD) NF_RDDATA_WORD();
                                                         (pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+0]= (BYTE)(rddata & 0xff);
                                                         (pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+1]= (BYTE)(rddata>>8 & 0xff);
                                                         (pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+2]= (BYTE)(rddata>>16 & 0xff);
                                                         (pSectorBuff+nSectorLoop*SECTOR_SIZE)[i*4+3]= (BYTE)(rddata>>24 & 0xff);
                                           }
                             }
                             // usual case tohandle 4byte aligned buffer pointer
                             else
                             {
                                           RdPage512(pSectorBuff+nSectorLoop*SECTOR_SIZE);                                                                      // Read page/sector data.
                             }
             
                             NF_MECC_Lock();
                            
                             // Check ECC aboutMain Data with MECC parity codel
                             NF_WRMECCD0(((MECCBuf[nSectorLoop]&0xff00)<<8)|(MECCBuf[nSectorLoop]&0xff) );
                            NF_WRMECCD1(((MECCBuf[nSectorLoop]&0xff000000)>>8)|((MECCBuf[nSectorLoop]&0xff0000)>>16));
 
                             nRetEcc =NF_ECC_ERR0;
             
                             if(!ECC_CorrectData(startSectorAddr, pSectorBuff+nSectorLoop*SECTOR_SIZE,nRetEcc, ECC_CORRECT_MAIN))
                             {
                                           RETAILMSG(1,(TEXT("#### FMD_DRIVER:::FMD_LB_READSECTOR 7\r\n")));
                                           returnFALSE;
                             }
              }


你可能感兴趣的:(WINCE6.0+SLC ECC产生和校验)