WINCE6.0+S3C2451K9K8G08U0D升级为U0E遇到的问题及解决
之前在U0D上正常工作,现在换为U0E后在烧录系统的时候,系统加载时无法正常启动,停在下面的串口信息处:
WindowsCE Kernel for ARM (Thumb Enabled) Built on Oct 20 2009 at 18:39:19
INFO:OALLogSetZones:dpCurSettings.ulZoneMask: 0xb
DCache:128 sets, 4 ways, 32 line size, 16384 size
ICache:128 sets, 4 ways, 32 line size, 16384 size
FCLK:400000000,HCLK:133333333, PCLK:66666666
+OALArgsInit()
Argumentsarea has some values
-OALArgsInit()
OEM:Force clean boot.
-OEMInit
+OEMSetRealTime(2008/6/112:0:0.000)
####FMD_DRIVER:::FMD_OEMIoControl IOCTL (0x71f8c).
FMD_OEMIoControl:IOCTL_FMD_GET_INTERFACE
####FMD_DRIVER:::FMD_INIT
(NAND ID:0xecd3) --> OK.
NUM_OF_BLOCKS = 8192
PAGES_PER_BLOCK = 64
SECTORS_PER_PAGE = 4
NUMBLOCKS: 8192(0x2000), SECTORSPERBLOCK = 64(0x40), BYTESPERSECTOR = 2048(0x800)
####FMD_DRIVER:::FMD_OEMIoControl IOCTL (0x71c24).
FMD_OEMIoControl:unrecognized IOCTL (0x71c24).
MECCUncorrectable error(0x4246)
FMD_LB_ReadSector()startSectorAddr=0x4246
MECCUncorrectable error(0x4246)
FMD_LB_ReadSector()startSectorAddr=0x4246
OEM:Clearing storage registry hive
MECCUncorrectable error(0x1fc)
FMD_LB_ReadSector()startSectorAddr=0x1fc
MECCUncorrectable error(0x1fc)
FMD_LB_ReadSector()startSectorAddr=0x1fc
MECCcorrectable error(0x44da). Byte:117, bit:7
MECCcorrectable error(0x44da). Byte:327, bit:6
MECCcorrectable error(0x44da). Byte:161, bit:2
MECCcorrectable error(0x44da). Byte:219, bit:0
MECCcorrectable error(0x44da). Byte:117, bit:7
MECC correctableerror(0x44da). Byte:327, bit:6
MECCcorrectable error(0x44da). Byte:161, bit:2
MECCcorrectable error(0x44da). Byte:219, bit:0
MECCcorrectable error(0x4398). Byte:384, bit:1
MECCUncorrectable error(0x4626)
FMD_LB_ReadSector()startSectorAddr=0x4626
MECCUncorrectable error(0x4626)
FMD_LB_ReadSector()startSectorAddr=0x4626
OEM: Notclearing storage
MECCUncorrectable error(0x4246)
FMD_LB_ReadSector()startSectorAddr=0x4246
MECCUncorrectable error(0x4246)
FMD_LB_ReadSector()startSectorAddr=0x4246
其中最后的信息是在FMD_LB_ReadSector函数读取数据后调用ECC_CorrectData进行ECC校验出错时输出的。
1. U0D和U0E的异同
对比了U0D和U0E的功能方块图和阵列组织图是一样的,如下图:
图1
K9K8G08U0D和U0E一片是8192blocks。
经过仔细对比,还发现下面的一些差别:
(1) 页编程、块擦除等参数的差别
图2
(2) NOP由最大有4个周期变为1个周期
图3
(3) 交互操作(Interleave Operation)
U0E支持交互操作,相关的其中一部分如下图:
图4
这里可以看出U0E和U0D都支持interleave operation,但为什么U0D数据手册中没有说明了,我怀疑是U0E这里描述有问题,也就是说U0D并不支持Interleave Operation的。
还可以看出Interleave page proam设计目的是为了系统吞吐量的,而且是非Interleave page program的两倍。
(4)
2. 解决此问题的过程
下面是解决问题的确认
2.1 TACLS、TWRPH0和TWRPH1参数的确定
先来看S3C2451的相关时序部分:
图5
由图7可知TACLS、TWRPH0和TWRPH1是对COMMAND/ADDRESS操作时序的描述,再来看K9K8G08U0E此部分的时序图:
图6
结合图7和图8可知,TACLS:表示CLT和ALE的建立时间(setup time) =tCLS/tALS-tWP;TWRPH0表示nWE的脉冲宽度=tWP;TWRPH0表示CLE和ALE维持时间,我们来看K9K8G08U0E数据手册这几个参数的具体要求:
图7
我们HCLK=400M/3=133MHZ,相当于一个HCLK时钟需要7.5ns,在这里我们的取值是:
#defineTACLS 7 //7 //1
#defineTWRPH0 7 //7 //6
#defineTWRPH1 7 //7 //2
这三个参数取值都为7,都是能够设置的最大值,比如对于TACLS=7来说,标明NAND控制TACLS=7*7.5=52.5n,是满足图6给出的参数的。而且对比了K9K8G08U0D这部分时序参数,没有差别。
2.2 降低HCLK
因为NAND控制器是由HCLK提供时钟的,由于U0E速度下来了,想通过降低HCLK来看能否解决此问题,寄存器定义了HCLK时钟的控制,如下图:
图8
目前我们CLKDIV0寄存器相关配置在SMDK2450\SRC\INC\S3c2450.inc中,如下图:
图9
由上图可知目前的HCLK=PREDIV_CLK/2=(MPLL/3)/2=(800MHZ/3)/2=133MHZ,目标是直接降为67MHZ,需要修改的地方如下:
(1) SMDK2450\SRC\INC\S3c2450.inc
如图8所示需要把Startup_HCLKdiv的值改为3,也就是HCLK=PREDIV_CLK/4,但由于PCLK是由HCLK分频的,这样改之后PCLK=HCLK/2也相应降低了,从而导致调试串口输出乱码,为确保调试串口输出正常,我们同时把Startup_PCLKdiv改为0,也就PCLK=HCLK,这样就不会输出乱码了。
(2) SRC\OAL\OALLIB\startup.s
SMDK2450\SRC\COMMON\TIMER\timer_fixedtick.c,此文件的ChangeSystemStateDVS函数下面通过调用HCLK_DOWNTO_PCLK函数也修改到HCLK和PCLK,HCLK_DOWNTO_PCLK函数在SMDK2450\SRC\OAL\OALLIB\startup.s文件中实现,如下:
LEAF_ENTRY HCLK_DOWNTO_PCLK
ldr r0, =vCLKDIV0 ; Set Clock Divider
ldr r1, [r0]
bic r1, r1, #0x7 ; clear PCLKDIV, HCLKDIV
ldr r2, =((0<<PCLKDIV_bit)+(3<<HCLKDIV_bit));
orr r1, r1, r2
str r1, [r0]
mov pc, lr
可以看出来,这里并没有采用S3c2450.inc中定义的Startup_HCLKdiv和Startup_PCLKdiv,这样的风格很不好,如果不仔细查找,不容易找到这里的,但经过确认ChangeSystemStateDVS函数暂时没有用到,所以可以不管这里。
2.3 U0E支持的interleave operation
根据图4中U0E支持的interleave operation修改了驱动代码,但还是不行。
2.4 NOP
Number ofPartial Program Cycles,见图3,可知U0D支持的NOP最大是4个周期,而U0E支持的NOP最大是1个周期,后来也看到老外在I.MX35+Linux平台上也提到NOP
https://community.freescale.com/thread/319855
回到FMD_LB_WriteSector函数中,我们增加下面的调试信息:
RETAILMSG(1,(TEXT("FMD_LB_WriteSector: %08x, %08x, %08x\r\n"),
startSectorAddr,(DWORD)pSectorBuff, (DWORD)pSectorInfoBuff));
见相关代码:
图10
依次输出页地址,保存页数据buffer的地址,保存SectorInfo结构体数据的buffer地址,相关的输出信息如下:
Windows CE Kernel for ARM(Thumb Enabled) Built on Oct 20 2009 at 18:39:19
INFO:OALLogSetZones:dpCurSettings.ulZoneMask: 0xb
DCache: 128 sets, 4 ways,32 line size, 16384 size
ICache: 128 sets, 4 ways,32 line size, 16384 size
FCLK:400000000,HCLK:133333333, PCLK:66666666
+OALArgsInit()
Arguments area has somevalues
-OALArgsInit()
-OEMInit
+OEMSetRealTime(2008/6/112:0:0.000)
####FMD_DRIVER:::FMD_OEMIoControl IOCTL (0x71f8c).
FMD_OEMIoControl:IOCTL_FMD_GET_INTERFACE
#### FMD_DRIVER:::FMD_INIT
(NAND ID:0xecdc) --> OK.
NUM_OF_BLOCKS = 4096
PAGES_PER_BLOCK = 64
SECTORS_PER_PAGE = 4
NUMBLOCKS : 4096(0x1000),SECTORSPERBLOCK = 64(0x40), BYTESPERSECTOR = 2048(0x800)
####FMD_DRIVER:::FMD_OEMIoControl IOCTL (0x71c24).
FMD_OEMIoControl:unrecognized IOCTL (0x71c24).
OEM: Not clearing storage
FMD_LB_WriteSector:000001c0, 00000000, d023eb2c
FMD_LB_WriteSector:000001c0, d0370000, d023eb2c
FMD_LB_WriteSector:000001c1, 00000000, d023eb2c
FMD_LB_WriteSector:000001c1, d0370800, d023eb2c
FMD_LB_WriteSector:000001c2, 00000000, d023eb2c
FMD_LB_WriteSector:000001c2, d0371000, d023eb2c
FMD_LB_WriteSector:000001c3, 00000000, d023eb2c
FMD_LB_WriteSector:000001c3, d0371800, d023eb2c
…………………………
可以看出来,相同的startSectorAddr地址,发现调用FMD_LB_WriteSector函数两次,第1次的pSectorBuff的值是00000000,这次直接调用NAND_LB_WriteSectorInfo函数后就返回了,第2次的值比如是d0370000,才是执行FMD_LB_WriteSector后面的部分。
为了验证是否是NOP的问题,这里我们注释掉对NAND_LB_WriteSectorInfo函数的调用,这样系统就可以正常跑起来了,这样就验证了是U0ENOP最大支持1个周期,所以对于支持最大为4个NOP的U0D就没有问题。
但是我发现有个问题,先从SD卡启动,在eboot中依次操作a---f---9---s后,然后改为从NAND启动,就看不到任何串口输出信息,如果改为依次操作f---9---s(少了a操作)后,从NAND可以正常启动,奇怪啊。为了搞清楚这几个问题,先来看看a和f对应的操作
A对应的操作:
图11
很简单,就是发送擦除命令擦除所有的块。
F对应的操作:
图12
下面来介绍为什么执行了a操作后,重启无法完成启动的:
(1) A:把所有的block重置,包括spare区的数据.
(2) F:调用FMD_WriteSector(i, NULL, &si,1)把SectorInfo数据写入到spare区,但因为注释掉NAND_LB_WriteSectorInfo函数,所以无法把SectorInfo数据写入到spare区
(3) 系统重启,stepldr\nand.c的NF_ReadPage函数在读取数据时,这样在重启的时候,根据下面的判断
图13
因为第(2)没有把有效的SectorInfo数据写入到spare区,所以这里直接返回return nRet,也就是返回FALSE,所以nboot无法读取eboot,所以没有看到串口输入任何串口信息
要解决此问题,就是bootloader阶段调用NAND_LB_WriteSectorInfo,系统阶段,不调用NAND_LB_WriteSectorInfo,但这样做还不知道是否有哪些风险,麻烦知道的朋友告知,谢谢了,接下来测试,也希望此总结对大家有帮忙
int NF_ReadPage(UINT32 block,UINT32 sector,UINT8 *buffer) { register UINT8 * bufPt=buffer; unsigned int RowAddr, ColAddr; DWORD MECCBuf, rddata1, rddata2; UINT32 nRetEcc; int nRet = FALSE; int nSectorLoop; NF_nFCE_L(); for (nSectorLoop = 0; nSectorLoop < (bLARGEBLOCK==TRUE?4:1); nSectorLoop++) { ColAddr = nSectorLoop * 512; NF_CMD(CMD_READ); // Read command if (bLARGEBLOCK == TRUE) { RowAddr = (block<<6) + sector; NF_ADDR((ColAddr) &0xff); // 1st cycle NF_ADDR((ColAddr>>8)&0xff); // 2nd cycle NF_ADDR((RowAddr) &0xff); // 3rd cycle NF_ADDR((RowAddr>>8)&0xff); // 4th cycle if (LB_NEED_EXT_ADDR) NF_ADDR((RowAddr>>16)&0xff); // 5th cycle } else { RowAddr = (block<<5) + sector; NF_ADDR((ColAddr) &0xff); // 1st cycle NF_ADDR((RowAddr) &0xff); // 2nd cycle NF_ADDR((RowAddr>>8)&0xff); // 3rd cycle if (SB_NEED_EXT_ADDR) NF_ADDR((RowAddr>>16)&0xff); // 4th cycle } NF_CLEAR_RB(); NF_CMD(CMD_READ3); // Read command NF_DETECT_RB(); //NF_WAITRB(); NF_RSTECC(); // Initialize ECC NF_MECC_UnLock(); __RdPage512(bufPt+nSectorLoop*512); // Read 512 bytes. NF_MECC_Lock(); if (bLARGEBLOCK == TRUE) { ColAddr = 2048; NF_CMD(CMD_RDO); // Random Data Output In a Page, 1st Cycle NF_ADDR((ColAddr) &0xff); // 1st cycle NF_ADDR((ColAddr>>8)&0xff); // 2nd cycle NF_CMD(CMD_RDO2); // Random Data Output In a Page. 2nd Cycle } rddata1 = NF_RDDATA32(); // check bad block rddata2 = NF_RDDATA32(); if (bLARGEBLOCK == TRUE) { if (((rddata1 & 0xff) != 0xff) && (((rddata2>>8) & 0xff) != 0x03)) return nRet; // bad block && !(OEM_BLOCK_RESERVED | OEM_BLOCK_READONLY) } else if ((((rddata2>>8) & 0xff) != 0xff) && ((rddata2 & 0xff) != 0x03)) return nRet; // bad block && !(OEM_BLOCK_RESERVED | OEM_BLOCK_READONLY) if (bLARGEBLOCK == TRUE) { ColAddr = 2048 + 8 + nSectorLoop*4; NF_CMD(CMD_RDO); // Random Data Output In a Page, 1st Cycle NF_ADDR((ColAddr) &0xff); // 1st cycle NF_ADDR((ColAddr>>8)&0xff); // 2nd cycle NF_CMD(CMD_RDO2); // Random Data Output In a Page. 2nd Cycle } MECCBuf = NF_RDDATA32(); NF_WRMECCD0( ((MECCBuf&0x0000ff00)<<8) | ((MECCBuf&0x000000ff) ) ); NF_WRMECCD1( ((MECCBuf&0xff000000)>>8) | ((MECCBuf&0x00ff0000)>>16) ); nRetEcc = rNFECCERR0; nRet = ECC_CorrectData(RowAddr, bufPt+nSectorLoop*512, nRetEcc, ECC_CORRECT_MAIN); if (!nRet) return nRet; } NF_nFCE_H(); return nRet; }
相关链接:
此问题csdn帖子:
K9K8G08U0D-SCB0升级为K9K8G08U0E时系统起不来
http://bbs.csdn.net/topics/390762117