嵌入式Linux——nand flash 驱动(4):AT91驱动分析以及写自己的nand驱动

本文分析了AT91的NAND驱动程序,并基于此指导读者编写自己的NAND驱动。文章详细介绍了NAND驱动的编写步骤,包括设置NAND芯片结构体、寄存器映射及初始化等。

        就像题目所说,本文有两个内容要讲,第一是分析AT91的nand驱动程序,而第二个就是写自己的驱动程序。可能有人会问我们前面不是已经分析过s3c2440的nand驱动了吗?那为啥还要分析AT91 的那?因为AT91的程序简单,他的驱动程序更直接的去设置相应的函数或者寄存器,所以更利于我们去读懂同时也更利于我们去模仿写自己的驱动程序。

下面我们言归正传在分析我们的drivers/mtd/nand/at91_nand.c

同样我们先从入口函数开始分析:

static int __init at91_nand_init(void)
{
	return platform_driver_register(&at91_nand_driver);
} 

同样是注册一个平台驱动结构体,用于和平台设备的名字进行比较,而他的名字为:.name = "at91_nand"。这里我们认为名字匹配,来进入probe函数来分析其驱动程序的编写,因为分析需要,我将部分不重要的判断给删了


static int __init at91_nand_probe(struct platform_device *pdev)
{
	struct at91_nand_host *host;
	struct mtd_info *mtd;           /* 定义mtd_info结构体 */
	struct nand_chip *nand_chip;    /* 定义nand_chip结构体,这里将mtd_info和nand_chip分开写其实是为了更方便的设置 */
	int res;

#ifdef CONFIG_MTD_PARTITIONS
	struct mtd_partition *partitions = NULL;   /* 如果定义了分区,对分区进行设置 */
	int num_partitions = 0;
#endif

	/* Allocate memory for the device structure (and zero it) */
	host = kzalloc(sizeof(struct at91_nand_host), GFP_KERNEL);  /* 为host分配内存空间 */

	host->io_base = ioremap(pdev->resource[0].start,
				pdev->resource[0].end - pdev->resource[0].start + 1);  /* 对nand的寄存器进行重映射 */

	mtd = &host->mtd;
	nand_chip = &host->nand_chip;
	host->board = pdev->dev.platform_data;

	nand_chip->priv = host;		/* link the private data structures */
	mtd->priv = nand_chip;         /* 将nand_chip结构体放到mtd_info的私有数据中,以实现mtd_info对nand_chip的调用 */
	mtd->owner = THIS_MODULE;     

	/* Set address of NAND IO lines */
	nand_chip->IO_ADDR_R = host->io_base;                        /* 设置读缓存地址 */
	nand_chip->IO_ADDR_W = host->io_base;                        /* 设置写缓存地址 */
	nand_chip->cmd_ctrl = at91_nand_cmd_ctrl;                    /* 设置写地址/命令函数 */
	nand_chip->dev_ready = at91_nand_device_ready;               /* 就绪函数 */
	nand_chip->ecc.mode = NAND_ECC_SOFT;	/* enable ECC */     /* 设置ECC为软件检测 */    
	nand_chip->chip_delay = 20;		/* 20us command delay time */ 

	platform_set_drvdata(pdev, host);                            
	at91_nand_enable(host);                                       /* 设置nand片选信号 */                                  

	/* Scan to find existance of the device */
	if (nand_scan(mtd, 1)) {                                     /* 扫描nand */
		res = -ENXIO;
		goto out;
	}

#ifdef CONFIG_MTD_PARTITIONS
	if (host->board->partition_info)
		partitions = host->board->partition_info(mtd->size, &num_partitions);
#ifdef CONFIG_MTD_CMDLINE_PARTS
	else {
		mtd->name = "at91_nand";
		num_partitions = parse_mtd_partitions(mtd, part_probes, &partitions, 0);
	}
#endif

	if ((!partitions) || (num_partitions == 0)) {
		printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n");
		res = ENXIO;
		goto release;
	}

	res = add_mtd_partitions(mtd, partitions, num_partitions);            /* 添加分区 */
#else
	res = add_mtd_device(mtd);
#endif

	if (!res)
		return res;

release:
	nand_release(mtd);
out:
	at91_nand_disable(host);
	platform_set_drvdata(pdev, NULL);
	iounmap(host->io_base);
	kfree(host);
	return res;
}

从上面我们可以很清楚的看出一个驱动程序的大体步骤:

1.分配一个nand_chip结构体

2.设置nand_chip结构体,对nand_chip中的选项进行设置,其实就是对各个寄存器进行操作。

3.硬件相关的设置:如对IO口进行重映射。

4.nand_scan调用mtd_info结构体

5.添加分区:add_mtd_partitions

 

有了上面的步骤我们开始写自己的nand驱动程序.

首先我们要做的第一件事就是开nand的总开关

/* 0.设置nand的总开关 */
	nand_clk = clk_get(NULL,"nand");
	clk_enable(nand_clk);           /* 这个设置相当于将CLKCON的bit[4]=1 */

只有开启nand在CLKCON的总开关才可以对nand进行后续的设置。

接着我们将分配一个nand_chip结构体的空间:

	/* 1.分配一个nand_chip结构体 */
	s3c_nand = kzalloc(sizeof(struct nand_chip),GFP_KERNEL);

然后就是nand_chip结构体,因为在设置nand_chip中会用到nand寄存器虚拟地址的设置,所以先对nand寄存器进行地址重映射然后设置:

	s3c_nand_regs = ioremap(0x4e000000,sizeof(struct s3c_nand_reg));  /* nand寄存器地址重映射 */
	
	/* 2.设置nand_chip结构体 */
	/* 设置nand_chip是给nand_scan函数使用的,如果不知道怎么设置,
	*  先看nand_scan函数怎么使用,他应该提供:
	*  选中,发命令,发地址,发数据判断状态的功能。
	*/
	s3c_nand->select_chip = s3c2440_select_chip;   /* 片选,默认函数不能提供次功能,要自己写 */
	s3c_nand->cmd_ctrl    = s3c2440_cmd_ctrl ;     /* 写命令/地址,此函数可以参考AT91 */
	s3c_nand->IO_ADDR_R   = &s3c_nand_regs->nfdata;/* 读数据的地址,NFDATA 的虚拟地址 */
	s3c_nand->IO_ADDR_W   = &s3c_nand_regs->nfdata;/* 写数据的地址,NFDATA 的虚拟地址 */	
	s3c_nand->dev_ready   = s3c2440_dev_ready;     /* 就绪函数 */  
	s3c_nand->ecc.mode    = NAND_ECC_SOFT;         /* 设置nand的ecc检测模式为软件设置 */

而nand的寄存器地址放在结构体s3c_nand_reg中:

struct s3c_nand_reg{
	unsigned long nfconf;      /* nand配置寄存器 */
	unsigned long nfcont;      /* nand控制寄存器 */
	unsigned long nfcmd;       /* nand命令寄存器 */
	unsigned long nfaddr;      /* nand地址寄存器 */
	unsigned long nfdata;      /* nand数据寄存器 */
	unsigned long nfmeccd0; 
	unsigned long nfmeccd1;
	unsigned long nfseccd;
	unsigned long nfstat;      /* nand状态寄存器 */
	unsigned long nfestat0;
	unsigned long nfestat1;
	unsigned long nfmecc0;
	unsigned long nfmecc1;
	unsigned long nfsecc;
	unsigned long nfsblk;
	unsigned long nfeblk;
};

而片选函数:s3c_nand->select_chip = s3c2440_select_chip为:

/* 片选控制函数 */
static void s3c2440_select_chip(struct mtd_info *mtd, int chip)
{
	if(chip == -1)
	{
		/* 取消片选:NFCONT[1]=1 */
		s3c_nand_regs->nfcont |= (1<<1);
	}else{
		/* 选中  :NFCONT[1]=0  */
		s3c_nand_regs->nfcont &= ~(1<<1);
	}

}

而写命令/地址函数:s3c2440_cmd_ctrl

/* 写命令/地址函数 */
static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat,unsigned int ctrl)
{
	if(ctrl & NAND_CLE){
		/* 发命令:NFCMMD = dat */
		s3c_nand_regs->nfcmd = dat;
	}else{
		/* 发地址:NFADDR    = dat */
		s3c_nand_regs->nfaddr = dat;
	}
}

等待就绪函数:s3c2440_dev_ready

/* 等待就绪函数 */
int s3c2440_dev_ready(struct mtd_info *mtd)
{
	/* 返回NFSTAT的bit[0] */
	return s3c_nand_regs->nfstat & (1<<0);
}

写完上面的函数,接下来就是与硬件相关的操作了,其实就是设置TACLS,TWRPH0 ,TWRPH1 的值,以及使能nand

	/* 3.硬件相关的设置 */
	/* 
	*HCLK = 100MHz;
	*TACLS:发出ALE/CLE之后多长时间才发出nWE信号,从nand手册可知ALE/CLE与nWE可以同时发出所以
	*      TACLS=0
	*TWRPH0:nWE的脉冲宽度,HCLK*(TWRPH0+1),从nand的手册可知脉冲宽度要大于12ns,所以
	*      TWRPH0>=1;
	*TWRPH1:nWE变为高电平后多长时间ALE/CLE能变为低电平,HCLK*(TWRPH1+1),
	*		 从nand的手册可知这段时间要大于5ns,所以
	*		TWRPH1 >= 0;
	*/
#define TACLS   0
#define TWRPH0  1
#define TWRPH1  0

	s3c_nand_regs->nfconf |= (TACLS << 12) | (TWRPH0 << 8) | (TWRPH1 << 4);

	/*
	*	NFCONT的bit[1]=1 : 取消片选
	*           bit[0]=1 : 使能nand flash控制器
	*/
	s3c_nand_regs->nfcont = (1<<1) | (1<<0);

而最后要写的就是nand_scan函数和add_mtd_partitions函数

	/* 4.使用:nand_scan */
	s3c_mtd = kzalloc(sizeof(struct mtd_info),GFP_KERNEL);
	s3c_mtd->owner = THIS_MODULE;
	s3c_mtd->priv  = s3c_nand;          //私有数据
	
	nand_scan(s3c_mtd, 1);              //参数二为最大芯片数
	
	/* 5.分区:add_mtd_partitions */
	/* 如果不想分区用add_mtd_device(s3c_mtd)就可以了 */
	add_mtd_partitions(s3c_mtd,s3c_nand_parts, 4);

而要添加的分区信息为:

static struct mtd_partition s3c_nand_parts[] = {
	[0] = {
		.name   = "bootloader",           /* 分区名 */
		.size   = 0x00040000,             /* 分区大小 */
		.offset = 0,                      /* 偏移值 */
	},
	[1] = {
		.name   = "params",
		.size   = 0x00020000,
		.offset = MTDPART_OFS_APPEND,
	},
	[2] = {
		.name   = "kernel",
		.size   = 0x00020000,
		.offset = MTDPART_OFS_APPEND,
	},
	[3] = {
		.name   = "root",
		.size   = MTDPART_SIZ_FULL,
		.offset = MTDPART_OFS_APPEND,
	}
};

 

上面就是一个nand驱动程序了。下面我会将一些我认为比较好的文章的连接放到这里,希望对你有用:

 

 

【详解】如何编写Linux下Nand Flash驱动超级推荐这个,他写的特详细。

    24.Linux-Nand Flash驱动(分析MTD层并制作NAND驱动)

 

基于MTD的NANDFLASH设备驱动底层实现原理分析(一)

Linux NAND FLASH驱动程序框架分析

 

而在文章的最后我将我的驱动程序全部贴上,希望对大家有用:

 

/*
*可参考 drivers\mtd\nand\s3c2410.c
*    和 drivers\mtd\nand\at91_nand.c
*/

#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk.h>

#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>

#include <asm/io.h>

#include <asm/arch/regs-nand.h>
#include <asm/arch/nand.h>

struct s3c_nand_reg{
	unsigned long nfconf;
	unsigned long nfcont;
	unsigned long nfcmd;
	unsigned long nfaddr;
	unsigned long nfdata;
	unsigned long nfmeccd0;
	unsigned long nfmeccd1;
	unsigned long nfseccd;
	unsigned long nfstat;
	unsigned long nfestat0;
	unsigned long nfestat1;
	unsigned long nfmecc0;
	unsigned long nfmecc1;
	unsigned long nfsecc;
	unsigned long nfsblk;
	unsigned long nfeblk;
};

static struct s3c_nand_reg *s3c_nand_regs;
static struct nand_chip *s3c_nand;
static struct mtd_info  *s3c_mtd;
static struct clk *nand_clk;

static struct mtd_partition s3c_nand_parts[] = {
	[0] = {
		.name   = "bootloader",
		.size   = 0x00040000,
		.offset = 0,
	},
	[1] = {
		.name   = "params",
		.size   = 0x00020000,
		.offset = MTDPART_OFS_APPEND,
	},
	[2] = {
		.name   = "kernel",
		.size   = 0x00020000,
		.offset = MTDPART_OFS_APPEND,
	},
	[3] = {
		.name   = "root",
		.size   = MTDPART_SIZ_FULL,
		.offset = MTDPART_OFS_APPEND,
	}
};

/* 片选控制函数 */
static void s3c2440_select_chip(struct mtd_info *mtd, int chip)
{
	if(chip == -1)
	{
		/* 取消片选:NFCONT[1]=1 */
		s3c_nand_regs->nfcont |= (1<<1);
	}else{
		/* 选中  :NFCONT[1]=0  */
		s3c_nand_regs->nfcont &= ~(1<<1);
	}

}

/* 写命令/地址函数 */
static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat,unsigned int ctrl)
{
	if(ctrl & NAND_CLE){
		/* 发命令:NFCMMD = dat */
		s3c_nand_regs->nfcmd = dat;
	}else{
		/* 发地址:NFADDR    = dat */
		s3c_nand_regs->nfaddr = dat;
	}
}

/* 等待就绪函数 */
int s3c2440_dev_ready(struct mtd_info *mtd)
{
	/* 返回NFSTAT的bit[0] */
	return s3c_nand_regs->nfstat & (1<<0);
}


static int s3c_nand_init(void)
{
	/* 0.设置nand的总开关 */
	nand_clk = clk_get(NULL,"nand");
	clk_enable(nand_clk);           /* 这个设置相当于将CLKCON的bit[4]=1 */

	/* 1.分配一个nand_chip结构体 */
	s3c_nand = kzalloc(sizeof(struct nand_chip),GFP_KERNEL);

	s3c_nand_regs = ioremap(0x4e000000,sizeof(struct s3c_nand_reg));
	
	/* 2.设置nand_chip结构体 */
	/* 设置nand_chip是给nand_scan函数使用的,如果不知道怎么设置,
	*  先看nand_scan函数怎么使用,他应该提供:
	*  选中,发命令,发地址,发数据判断状态的功能。
	*/
	s3c_nand->select_chip = s3c2440_select_chip;   /* 片选,默认函数不能提供次功能,要自己写 */
	s3c_nand->cmd_ctrl    = s3c2440_cmd_ctrl ;     /* 写命令/地址,此函数可以参考AT91 */
	s3c_nand->IO_ADDR_R   = &s3c_nand_regs->nfdata;/* 读数据的地址,NFDATA 的虚拟地址 */
	s3c_nand->IO_ADDR_W   = &s3c_nand_regs->nfdata;/* 写数据的地址,NFDATA 的虚拟地址 */	
	s3c_nand->dev_ready   = s3c2440_dev_ready;     /* 就绪函数 */  
	s3c_nand->ecc.mode    = NAND_ECC_SOFT;         /* 设置nand的ecc检测模式为软件设置 */

	/* 3.硬件相关的设置 */
	/* 
	*HCLK = 100MHz;
	*TACLS:发出ALE/CLE之后多长时间才发出nWE信号,从nand手册可知ALE/CLE与nWE可以同时发出所以
	*      TACLS=0
	*TWRPH0:nWE的脉冲宽度,HCLK*(TWRPH0+1),从nand的手册可知脉冲宽度要大于12ns,所以
	*      TWRPH0>=1;
	*TWRPH1:nWE变为高电平后多长时间ALE/CLE能变为低电平,HCLK*(TWRPH1+1),
	*		 从nand的手册可知这段时间要大于5ns,所以
	*		TWRPH1 >= 0;
	*/
#define TACLS   0
#define TWRPH0  1
#define TWRPH1  0

	s3c_nand_regs->nfconf |= (TACLS << 12) | (TWRPH0 << 8) | (TWRPH1 << 4);

	/*
	*	NFCONT的bit[1]=1 : 取消片选
	*           bit[0]=1 : 使能nand flash控制器
	*/
	s3c_nand_regs->nfcont = (1<<1) | (1<<0);

	/* 4.使用:nand_scan */
	s3c_mtd = kzalloc(sizeof(struct mtd_info),GFP_KERNEL);
	s3c_mtd->owner = THIS_MODULE;
	s3c_mtd->priv  = s3c_nand;          //私有数据
	
	nand_scan(s3c_mtd, 1);              //参数二为最大芯片数
	
	/* 5.分区:add_mtd_partitions */
	/* 如果不想分区用add_mtd_device(s3c_mtd)就可以了 */
	add_mtd_partitions(s3c_mtd,s3c_nand_parts, 4);
	
	return 0;
}
static void s3c_nand_exit(void)
{
	kfree(s3c_mtd);
	iounmap(s3c_nand_regs);
	kfree(s3c_nand);
	del_mtd_partitions(s3c_mtd);
}

module_init(s3c_nand_init);
module_exit(s3c_nand_exit);
MODULE_LICENSE("GPL");



 

 

 

 

 

 

 

 

 

2.软件分析 2.1.问题描述 2025年3月期间,在中电星河本部通过反复上下电数台A2主板成功复现C6678 Linux系统版本接收机FPGA启动失败遗留问题,接收机指示灯与上位机工作状态灯均出现红色告警,接收机FPGA未正常启动。连接telnet后,输入dmsg指令查看Linux系统日志,发现接收机上电时Mount挂载NandFlash文件系统过程中报Check Error告警,文件系统在接收机上电启动时未能正常mount挂载,导致NandFlash中的FPGA1.bin、FPGA2.bin、FPGA3.bin程序文件无法加载成功。 待Linux系统正常启动后,手动输入start.sh脚本中的"time mount -t jffs2 -0 sync/dev/mtdblock5/nand_disk/ "指令,NandFlash文件系统正常挂载,通过FTP获取该故障NandFLash中的FPGA1.bin、FPGA2.bin、FPGA3.bin镜像文件至PC本地,并对比其正常程序文件,发现部分镜像文件的二进制数据在不同地址段出现损坏。 一开始以为上述故障现象与接收机下电/上电存在强相关关系,且偶发出现,综合该类型接收机NandFlash历史故障信息及公司的复现情况,得出如下初步结论:接收机硬件上下电过程与该故障复现存在相关性;软件上可针对Linux系统启动过程进行优化。 采取的解决措施包括:a)在C6678 EMIF初始化与NandFlash 驱动加载命令行前增加3s延时,规避上电过程中的不稳定状态阶段。b)将与NandFlash相关命令行间增加少量,在保护操作前、jffs2文件系统挂载前分别增加3s延时,在拷贝FPGA镜像文件前、卸载jffs2前增加1s延时,避免Linux非实时性操作问题。 在上述优化后,降低了出错概率但是多台设备长时间上下电仍然还会存在偶发文件损坏现象,且最新测试发现在系统软复位(不限于上下电)过程中也出现该问题。出现该问题的过程Linux端会稳定的伴随报错:JFFS2 notice:(123)check_node_data:wrong data CRC in data node at 0x05be2e1c:read 0x3005c91d,calculate 0x9ceec087。因此本次软件排查主要从内核层面进行分析,主要是从以下2个方向入手:a)Nand Flash挂载的根文件系统格式;b)Nand Flash驱动; 2.2.问题定位 2.2.1.故障树 接收机上电或软复位后,FPGA镜像自Nand Flash加载失败。故障树顶层为“Flash内文件损坏”。向下分三支:一、EMIF接口时序参数与硬件不匹配,读采样窗口偏移,引入位翻转;二、ECC策略强度或布局与实际Flash位错率不符,未能纠正错误;三、保护机制缺失,复位过程中总线乱,将垃圾数据入镜像块。系统层JFFS2在EMIF尚未稳定、电源噪声尚存时过早挂载,进一步放大了上述底层错误,最终表现为CRC校验失败、镜像失效。若改用UBIFS,其自带更强CRC与日志恢复,可在同等硬件缺陷下仍保持挂载成功。因此,应从同步修正EMIF时序、提升ECC等级、启用 early-write-protection、延后文件系统挂载四点着手,切断故障链。 图 4 软件故障树 2.2.2.实验定位 根据故障树描述,顶层问题有两个定位方向。为进一步对定位方向进行判断,设计以下实验。 1.实验一 实验目的 当程序异常时,手动将Nand Flash挂载成jffs2格式,看是否能挂载成功,验证JFFS2映像是否损坏? 实验操作 每次出现异常时,手动挂载一次Nand Flash,连接telnet df -h查看挂载情况。 测试结果 每次df -h查看是仍能挂载分区,且还能正常往Nand Flash里读数据,则JFFS2映像未损坏 2.实验二 实验目的 将与NandFlash相关命令行间增加延时、jffs2文件系统挂载前分别增加延时,在拷贝FPGA镜像文件前、卸载jffs2前增加延时,保证emif稳定后进行Nand Flash挂载,然后进行压力测试,验证是否emif初始化未稳定挂载导致的CRC错误? 实验操作 进行长时间压测后查看Nand Flash文件是否出现损坏。 测试结果 长时间压测后,7台设备中仍偶发文件损坏现象,则不是该原因导致。 3.实验三 实验目的 尝试将Nand Flash不进行文件系统的挂载,直接对Nand Flash进行递增数数据流的读压测,验证是否由挂载格式导致的数据损坏还是纯Nand Flash驱动导致的数据损坏? 实验操作 1)修改自启动脚本,屏蔽jffs2格式的Nand Flash挂载,增加nandtest指令进行全盘范围的读测试; 2)测试结束后每1分钟进行一次系统软复位:reboot; 3)每次将读测试结果进行计数记录,正确或异常分别存储到相应的技术文本中。 4)7台设备同时开始压测 修改的脚本内容如下: #Nand Flash测试 ERR_FILE="/nor_disk/err_nct.txt" [ -f "$ERR_FILE" ] && ERR_CNT=$(cat "$ERR_FILE") || ERR_CNT=0 RIGHT_FILE="/nor_disk/right_nct.txt" [ -f "$RIGHT_FILE" ] && RIGHT_CNT=$(cat "$RIGHT_FILE") || RIGHT_CNT=0 NANDTEST_LOG="/tmp/nandtest.log" MOUNTNAND_LOG="/tmp/mounttest.log" nandtest -p 1 -k -l 0x8000000 /dev/mtd5 >"$NANDTEST_LOG" 2>&1 # 判断本次是否出现 compare failed if grep -q "compare failed" "$NANDTEST_LOG"; then ERR_CNT=$((ERR_CNT + 1)) echo "$ERR_CNT" > "$ERR_FILE" sync # 立即落盘,防掉电 fi if grep -q "Finished pass 1 successfully" "$NANDTEST_LOG"; then RIGHT_CNT=$((RIGHT_CNT + 1)) echo "$RIGHT_CNT" > "$RIGHT_FILE" sync # 立即落盘,防掉电 fi 测试结果 7台设备中由3台设备出现,读不一致的测试结果,且每次异常的时候查看日志打印,发现读的数据会出现1位翻转的错误。截图如下: 通过以上3个实验可初步定位,Nand Flash的文件损坏基本与jffs2格式文件系统挂载无关,与Nand Flash驱动强相关。 2.2.3.驱动Nand Flash的影响 从故障树得知:Nand Flash驱动对文件读的影响主要包括几部分: EMIF 初始化:把 C6678 的 External Memory Interface 寄存器(A1CR、A2CR、时钟分频、TA、hold/setup 等)配成与 NAND 颗粒 tRC/tWC/tWP/tREH 完全匹配的时序,同时打开 WAIT 握手或关闭延长,保证总线采样窗口落在数据有效区。 ECC 策略:在页时实时计算校验码,页读时纠错并上报 bit-flip 数量;同时把 syndrome 放到约定 OOB 偏移,避免与坏块标记、文件系统内部标记冲突。 保护:通过 GPIO/WP# 引脚或 on-die 保护寄存器,在复位、电源抖动、软件跑飞期间把 PROGRAM/ERASE 命令屏蔽掉,确保代码区、BBT、环境变量等关键块不被误改。 为进一步对定位方向进行更精准判断,尝试以下实验。 1.实验一 实验目的 走查C6678手册和现有驱动代码的emif初始化部分代码,针对时序优化后,进行压力测试,验证emif初始化是否异常? 实验操作 每台设备加载新的驱动,然后进行上下电压力测试,查看Nand Flash文件是否出现损坏。 测试结果 长时间压测后,7台设备中仍偶发文件损坏现象,则不是该原因导致。 2.实验二 实验目的 走查c6678的手册、Nand Flash的手册MT29F1G16ABBDAH4 128MB NandFLash手册,验证ECC策略是否正确? 实验操作 走查手册与NandFlash驱动ECC部分代码,发现NAND 的 ECC 需求(4-bit)≫EMIF16 硬件能力(1-bit),现有驱动中用的4-bit的硬件ECC,但是C6678的EMIF16并不支持该硬件ECC策略。 测试结果 比对手册和代码发现ECC策略不匹配。 针对保护异常问题,硬件上通过IO控制了Nand Flash,仍会出现该问题,基本可排除保护的相关影响。通过以上两个实验及分析可知,目前问题可基本定位为Nand Flash驱动ECC策略存在问题。 2.2.4.总结 通过系统化的实验验证与故障树分析,已将 FPGA 镜像加载失败的根本原因锁定在 NAND Flash 驱动层 ECC 策略配置错误。实验表明,文件损坏与 JFFS2 文件系统挂载无关,而是纯 NAND 驱动在读过程中引入的位翻转未被有效纠正所致。进一步比对 C6678 EMIF16 控制器手册与 MT29F1G16ABBDAH4 NAND Flash 规格确认,EMIF16 硬件仅支持 1-bit ECC,而当前驱动启用了硬件4-bit ECC 策略,存在能力失配,导致纠错失败、数据静默损坏。EMIF 时序优化与保护机制排查未显著改善问题,进一步佐证 ECC 策略为关键缺陷点。后续应将 ECC 策略降级为 1-bit,并启用软件 ECC 补充或多页冗余校验机制,最终需从驱动层彻底修正 ECC 实现,切断故障链,确保镜像加载可靠性。根据上述文字定位出的问题,给出一段机理分析
最新发布
11-05
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值