ADS 下 flash 烧写程序原理及结构

时间:2023-03-09 19:59:41
ADS 下 flash 烧写程序原理及结构

本原理:在 windows 环境下借助 ADS 仿真器将在 SDRAM 中的一段存储区域中的数据写到 Nand flash 存 储空间中。烧写程序在纵向上分三层完成:

第一层: 主烧写函数(完成将在 SDRAM 中的一段存储区域中的数据写到 Nand flash 存储空间中); 第二层: 为第一层主烧写函数提供支持的对 Nand flash 进行操作的页读、写,块擦除等函数; 第三层:为第二层提供具体 Nand flash 控制器中对特殊功能寄存器进行操作的核心函数,该层也是真正的

将数据能够在 SDRAM 和 Nand flash 之间实现传送的函数。

下面对其三层进行分述:

2.2 第三层实现说明

2.1.1 特殊功能寄存器定义

#define rNFCONF       (*(volatile unsigned int *)0x4e000000)

#define rNFCMD        (*(volatile unsigned char *)0x4e000004)

#define rNFADDR      (*(volatile unsigned char *)0x4e000008)

#define rNFDATA      (*(volatile unsigned char *)0x4e00000c)

#define rNFSTAT       (*(volatile unsigned int *)0x4e000010)

#define rNFECC         (*(volatile unsigned int *)0x4e000014)

#define rNFECC0      (*(volatile unsigned char *)0x4e000014)

#define rNFECC1      (*(volatile unsigned char *)0x4e000015)

#define rNFECC2      (*(volatile unsigned char *)0x4e000016)

2.1.2 操作的函数实现

1. 发送命令

#define NF_CMD(cmd)           {rNFCMD=cmd;} 2. 写入地址

#define NF_ADDR(addr)        {rNFADDR=addr;}

  1. Nand Flash 芯片选中

#define NF_nFCE_L()             {rNFCONF&=~(1<<11);}

  1. Nand Flash 芯片不选中

#define NF_nFCE_H()             {rNFCONF|=(1<<11);}

  1. 初始化 ECC

#define NF_RSTECC()            {rNFCONF|=(1<<12);}

6. 读数据

#define NF_RDDATA()           (rNFDATA)

7. 写数据

#define
NF_WRDATA(data) {rNFDATA=data;}

8. 获取 Nand Flash 芯片状态

#define NF_WAITRB()          {while(!(rNFSTAT&(1<<0)));}
0/假: 表示 Nand Flash 芯片忙状态

1/真:表示
Nand Flash 已经准备好

2.3
第二层实现说明

2.3.1    Nand Flash
初始化

void
NF_Init(void)

{

/* 设置 Nand Flash 配置寄存器, 每一位的取值见 1.3 节 */
rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);

/* 复位外部
Nand Flash 芯片 */

NF_Reset();

}

2.3.2   
Nand flash 复位

static void
NF_Reset(void)

{

int i;

NF_nFCE_L(); /* 片选 Nand Flash 芯片*/ NF_CMD(0xFF); /* 复位命令 */ for(i=0;i<10;i++); /* 等待 tWB = 100ns. */ NF_WAITRB(); /* wait 200~500us; */ NF_nFCE_H();        /* 取消 Nand Flash 选中*/

}

2.3.3    获取 Nand flash ID 返回值为 Nand
flash 芯片的 ID
号 unsigned
short NF_CheckId(void)

{

int i;

unsigned short
id;

NF_nFCE_L();                  /*
片选 Nand Flash 芯片*/
NF_CMD(0x90);    /* 发送读 ID 命令到 Nand Flash
芯片 */ NF_ADDR(0x0);                            /* 指定地址 0x0,芯片手册要求                   */
for(i=0;i<10;i++);                                  /*
等待 tWB = 100ns.   */
id=NF_RDDATA()<<8; /* 厂商 ID(K9S1208V:0xec)
*/ id|=NF_RDDATA();         /* 设备 ID(K9S1208V:0x76)  */

NF_nFCE_H();                   /*
取消 Nand Flash 选中*/ return id;

}

2.3.4    Nand flash 写入

以页为单位写入. 参数说明:block  块号

page   页号

buffer  指向内存中待写入 Nand flash 中的数据起始位置

返回值:      0:写错误

1:写成功

static int
NF_WritePage(unsigned int block, unsigned int page, unsigned char *buffer)

{

int i;

unsigned int blockPage = (block<<5)+page; unsigned char *bufPt
= buffer;

NF_RSTECC();     /* 初始化 ECC               */
NF_nFCE_L(); /* 片选 Nand Flash 芯片*/
NF_CMD(0x0);    /* 从 A 区开始写    */ NF_CMD(0x80); /* 写第一条命令   */ NF_ADDR(0);              /* 
A0~A7 位(Column Address)  */

NF_ADDR(blockPage&0xff); /* A9­A16, (Page Address) */
NF_ADDR((blockPage>>8)&0xff); /* A17­A24, (Page Address) */
NF_ADDR((blockPage>>16)&0xff); 
/* A25,  (Page Address) */

for(i=0;i<512;i++)

{

NF_WRDATA(*bufPt++);                /* 写一个页 512
字节到 Nand Flash 芯片
*/

}

/*

* OOB 一共 16 Bytes, 每一个字节存放什么由程序员自己定义, 通常,

* 我们在 Byte0­Byte2
存 ECC 检验码. Byte6 存放坏块标志.

*/

seBuf[0]=rNFECC0;
/* 读取
ECC 检验码
0 */ seBuf[1]=rNFECC1; /* 读取 ECC 检验码 1 */ seBuf[2]=rNFECC2; /* 读取 ECC 检验码 2 */
seBuf[5]=0xff;            /* 非坏块标志      */

for(i=0;i<16;i++)

{

NF_WRDATA(seBuf[i]);
/* 写该页的
OOB 数据块 */

}

NF_CMD(0x10);   /* 结束写命令 */

/* 等待 Nand Flash 处于准备状态 */ for(i=0;i<10;i++);

NF_WAITRB();

/* 发送读状态命令给 Nand Flash */ NF_CMD(0x70);

for(i=0;i<3;i++);

if
(NF_RDDATA()&0x1)

{   
/*如果写有错, 则标示为坏块   */ NF_nFCE_H(); /* 取消 Nand Flash 选中*/ NF_MarkBadBlock(block);

return 0;

} else { /* 正常退出 */

NF_nFCE_H(); /* 取消
Nand Flash 选中*/ return 1;

}

}

2.3.5    
Nand flash 读取

参数说明:block:块号

page:页号 buffer:指向将要读取到内存中的起始位置

返回值:1:读成功

0:读失败

static int
NF_ReadPage(unsigned int block, unsigned int page, unsigned char *buffer)

{

int i;

unsigned int blockPage; unsigned char ecc0, ecc1, ecc2; unsigned
char *bufPt=buffer; unsigned char se[16];

page=page&0x1f;
blockPage=(block<<5)+page; NF_RSTECC();   /* 初始化 ECC          */
NF_nFCE_L();            /* 片选 Nand Flash 芯片*/ NF_CMD(0x00);   /* 从
A 区开始读     */

NF_ADDR(0);       /*  A0~A7 位(Column Address)  */ NF_ADDR(blockPage&0xff);  /* A9­A16, (Page Address) */
NF_ADDR((blockPage>>8)&0xff);       /*
A17­A24, (Page Address) */
NF_ADDR((blockPage>>16)&0xff); 
/* A25,  (Page Address) */

/* 等待 Nand Flash 处于再准备状态 */ for(i=0;i<10;i++);

NF_WAITRB();   
/*等待 tR(max 12us) */

/* 读整个页, 512 字节            */ for(i=0;i<512;i++)

{

*bufPt++=NF_RDDATA();

}

/* 读取 ECC 码 */ ecc0=rNFECC0; ecc1=rNFECC1;
ecc2=rNFECC2;

/* 读取该页的 OOB 块 */ for(i=0;i<16;i++)

{

se[i]=NF_RDDATA();

}

NF_nFCE_H();   /* 取消 Nand
Flash 选中*/

/* 校验 ECC 码, 并返回 */

if(ecc0==se[0] && ecc1==se[1] &&
ecc2==se[2]) return 1;

else

}

return 0;

2.3.6     Nand flash 标记坏块

如果是坏块, 通过写 OOB 块的 Byte6 把该块标记为坏块。

参数说明:block 块号

返回值:1:ok,成功完成标记。

0:表示写 OOB 块正确.

static int
NF_MarkBadBlock(unsigned int block)

{

int i;

unsigned int
blockPage=(block<<5);

seBuf[0]=0xff;
seBuf[1]=0xff; seBuf[2]=0xff;

seBuf[5]=0x44;   /* 设置坏块标记 */

NF_nFCE_L(); /* 片选 Nand Flash 芯片*/
NF_CMD(0x50);   /* 从 C 区开始写                       */

NF_CMD(0x80); /* 发送编程命令, 让 Nand Flash 处理写状态 */
NF_ADDR(0x0); /* A0~A7 位(Column Address) */ NF_ADDR(blockPage&0xff); /* A9­A16, (Page
Address) */ NF_ADDR((blockPage>>8)&0xff); /* A17­A24, (Page Address)
*/ NF_ADDR((blockPage>>16)&0xff); 
/* A25,  (Page Address) */

/* 写 OOB 数据块 */ for(i=0;i<16;i++)

{

NF_WRDATA(seBuf[i]);

}

NF_CMD(0x10);    /* 结束写命令 */

/* 等待 NandFlash 准备好 */ for(i=0;i<10;i++); /* tWB = 100ns. */ NF_WAITRB();

/*读 NandFlash 的写状态 */ NF_CMD(0x70);

for(i=0;i<3;i++); /* twhr=60ns */ if (NF_RDDATA()&0x1)

{

} else {

}

NF_nFCE_H(); /* 取消
Nand Flash 选中*/ return 0;

NF_nFCE_H();
/* 取消 Nand Flash 选中*/

return 1;

}

2.3.7     Nand Flash 检查坏块

检查指定块是否是坏块. 参数说明:block:块号 返回值:1:指定块是坏块

0:指定块不是坏块。 static
int NF_IsBadBlock(U32 block)

{

int i;

unsigned int blockPage; U8 data;

blockPage=(block<<5);

NF_nFCE_L();          /* 片选 Nand Flash 芯片*/ NF_CMD(0x50);            /* Read OOB 数据块          */ NF_ADDR(517&0xf); /* A0~A7 位(Column Address) */
NF_ADDR(blockPage&0xff); /* A9­A16, (Page Address) */

NF_ADDR((blockPage>>8)&0xff);     /* A17­A24, (Page Address) */
NF_ADDR((blockPage>>16)&0xff); 
/* A25,  (Page Address) */

/* 等待 NandFlash 准备好 */ for(i=0;i<10;i++);   /*
wait tWB(100ns) */ NF_WAITRB();

/* 读取读出值 */ data=NF_RDDATA();

NF_nFCE_H(); 
/* 取消 Nand Flash 选中*/

/* 如果 data 不为 0xff 时, 表示该块是坏块 */ if(data != 0xff)

return 1;

else

}

return 0;

2.3.8 擦除指定块中数据

参数说明:block 块号

返回值:0:擦除错误。(若是坏块直接返回 0;若擦除出现错误则标记为坏块然后返回 0)

1:成功擦除。

static int
NF_EraseBlock(unsigned int block)

{

unsigned int blockPage=(block<<5); int i;

/* 如果该块是坏块, 则返回 */ if(NF_IsBadBlock(block))

return 0;

NF_nFCE_L();       /* 片选 Nand Flash 芯片*/
NF_CMD(0x60);  /* 设置擦写模式   */

NF_ADDR(blockPage&0xff); /*
A9­A16, (Page Address) , 是基于块擦*/ NF_ADDR((blockPage>>8)&0xff);     /* A17­A24, (Page Address) */
NF_ADDR((blockPage>>16)&0xff); /* A25, (Page Address) */
NF_CMD(0xd0);   /* 发送擦写命令, 开始擦写 */

/* 等待 NandFlash 准备好 */ for(i=0;i<10;i++); /* tWB(100ns) */ NF_WAITRB();

/* 读取操作状态        */ NF_CMD(0x70);

if
(NF_RDDATA()&0x1)

{

}  else  {

}

}

NF_nFCE_H();
/* 取消
Nand Flash 选中*/
NF_MarkBadBlock(block); /* 标记为坏块 */
return 0;

NF_nFCE_H();
/* 取消 Nand Flash 选中*/ return 1;

2.4 第一层的实现

2.4.1 NandFlash 烧写主函数说明 参数说明: block
块号

srcAddress SDRAM 中数据起始地址 fileSize
要烧写的数据长度

返回值: 无

void
K9S1208_Program(unsigned int block, unsigned int srcAddress, unsigned int
fileSize)

{

int i;

int programError=0; U32 blockIndex;

U8  *srcPt, *saveSrcPt;

srcPt=(U8 *)srcAddress; /* 文件起始地址 */ blockIndex = block; /* 块号 */

while(1)

{

saveSrcPt=srcPt;

/* 如果当前块是坏块, 跳过当前块
*/ if(NF_IsBadBlock(blockIndex))

{

blockIndex++; /* 到下一个块 */ continue;

}

/* 在写之前, 必须先擦除, 如果擦除不成功, 跳过当前块 */ if(!NF_EraseBlock(blockIndex))

{

blockIndex++; /* 到下一个块 */
continue;

}

/* 写一个块, 一块有 32 页 */ for(i=0;i<32;i++)

{

/* 写入一个页, 如果出错, 停止写当前块 */
if(!NF_WritePage(blockIndex,i,srcPt))

{

programError=1; break;

}

/* 如果操作正常, 文件的写位置加上 1 页偏移,到下一页的起始位置
*/ srcPt+=512;

/* 如果写地址没有超过文件长度,
继续; 超出则终止写 */

if((U32)srcPt>=(srcAddress+fileSize)) break;

}

/* 如果写一个块时, 其中某一页写失败, 则把写地址恢复写该块之前, 并跳过当前块
*/ if(programError==1)

{

blockIndex++; srcPt=saveSrcPt; programError=0; continue;

}

/* 如果写地址没有超过文件长度, 继续; 超出则终止写 */ if((U32)srcPt >=
(srcAddress+fileSize))

break;

/* 如果正常写成功, 继续写下一个块 */ blockIndex++;

}

}