由于flash从K9F1G08U0D升级到k9F1G08U0E,导致格式为yaffs2的根文件系统在烧写成功后,第一次启动正常,但若启动后对根文件系统中的文件有修改或创建新文件,会出现再次启动后修改或创建的文件被破坏,在较大数据量的操作后甚至直接导致根文件系统破坏,无法挂载以至启动失败。旧flash K9F1G08U0D不会出现此问题。
1.问题调试:通过对写入page数据和读取page数据,当不包含OOB数据时,发现读写数据一致;但若读写的数据包含OOB数据时,读写数据的page页内数据和OOB数据均不一致;通过对yaffs2代码的阅读(yaffs2管理代码的入口处主要在linux/fs/yaffs/yaffs_guts.c当中,并最终调用到linux/drivers/mtd/nand/下的具体flash操作),发现yaffs2的管理中会在坏块标识(起始2字节)和ECC校验码(位置是分散的)空间之外,使用OOB上剩余空间来存放这个chunk(yaffs2中的最小管理单元,基本是对应flash中的页大小内容)对应的tag信息,这个tag信息主要是包含了chunkId、serialnumber等重要参数(见yaffs_guts.h中的yaffs_Tags数据结构),所以OOB数据对于yaffs2而言很重要。ps: yaffs2也可以配置将tag信息移到页内,但没做过这个测试;ubifs不会在OOB中存放文件系统管理相关的信息。
2.通过比较U0D和U0E的数据手册,发现它们的工作时序完全相同,主要的区别在于两点:U0E的块擦除和页编程的耗时都比U0D更长,以及NOP参数不同。
对于block擦除和page编程的耗时问题,驱动代码中已经对flash上的操作是否完成的标识进行判断,只有在操作完成后才会进行一步操作,且通过在flash驱动中擦除和编程后强制sleep 20ms,都未能解决问题;
对于NOP(Number of Partial program cycles)参数的意思是,对于flash上的一个page可进行多次编程的次数,比如U0D的这个参数为4,则说明U0D的同一个页在块擦除后可以分4次进行编程,U0E的这个参数为1,说明U0E的同一个页在块擦除后只能进行1次编程,如果多次进行编程可能会导致数据出错(具体出什么错,未知)。在网络上查询,有较多的反映,因为这个参数的不同导致ubifs文件系统工作异常的,因为ubifs本身有子页的概念,如果格式化时指定子页的个数与NOP参数大,就会出现问题。但yaffs2文件系统并没有子页的概念,并且通过flash接口驱动处的代码确认以及用示波器对flash页编程过程的波形进行监控,均显示我们的产品中对flash的页编程都是对整个页进行的。而且,有验证过flash的页在写之前先读取出来验证是否全为0xff,是正常的(这个验证,只需开启mtd驱动中的宏CONFIG_MTD_NAND_VERIY_WRITE即可,只是这个验证较耗时,平常都处于关闭状态)。
3.以上这些验证,似乎排除了NOP不同引起的问题??非也,问题可能出在uboot中。在uboot中,手动使用命令对rootfs镜像进行烧写,发现问题不会重现,但如果使用uboot中解析升级控制脚本文件对rootfs镜像进行自动烧写时就会重现问题。那么,就去分析镜像自动烧写过程吧。通过代码检查,发现在自动烧写rootfs镜像时会自动将镜像长度补齐到512KBytes对齐(具体原因未知,可能只是从jffs2镜像烧写过程移植过来吧),也就是说自动烧写时会对根文件系统分区中部分未被占用的页进行写操作,将这个长度补齐操作去除,重新编译和使用uboot进行rootfs镜像自动烧写,启动系统验证问题解决!
至此,可以证明正是uboot里对未占用的页进行编程导致的后面正常使用中的flash问题。但是有个疑问,补齐的这些页,写入的都0xff数据,难道这不是相当于没操作过吗,难道也算是进行编程了1次?因为块擦除后的数据就都是0xff呀。不解!
以下附上本产品中的yaffs2的chunk数据写过程跟踪:
写1个chunk,在这里,1个chunk为flash里的1个page(含OOB),其中yaffs2的tags信息记录在这个page的OOB里(OOB里还记录了ECC码):
yaffs_WriteChunkWithTagsToNAND (yaff_nand.c)
dev->writeChunkWithTagsToNAND = nandmtd2_WriteChunkWithTagsToNAND (yaffs_mtdif2.c)
mtd->write_oob = part_write_oob (mtdpart.c)
part->master->write_oob = nand_write_oob (nand_base.c)
nand_do_write_ops (nand_base.c)
chip->write_page = nand_write_page (nand_base.c)
chip->ecc.write_page = nand_write_page_raw (nand_base.c)
chip->write_buf = hinand_write_buf (hinand.c)
本文乃zfd原创文章,请勿转载。
阅读(4543) | 评论(0) | 转发(0) |