Chinaunix首页 | 论坛 | 博客
  • 博客访问: 130016
  • 博文数量: 30
  • 博客积分: 141
  • 博客等级: 入伍新兵
  • 技术积分: 175
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-10 10:00
文章分类

全部博文(30)

文章存档

2021年(1)

2018年(2)

2017年(6)

2016年(1)

2015年(5)

2014年(15)

我的朋友

分类: LINUX

2016-10-28 11:37:16

原文地址:Nand flash 基础操作 作者:xuxiyao8888

nand结构

NAND Flash的数据是以bit 的方式保存在memory cell,一般来说,一个cell 中只能存储一个bit。这些cell 以8 个或者16 个为单位,连成bit line,形成所谓的byte(x8)/word(x16),这就是NAND Device 的位宽。这些Line 会再组成Page。
Nand Flash 根据的page的大小,可以分为small page和big page, 下面以samsung的K9F1G08U0B和K9F5608U0D分别说明:
K9F5608U0D:

page = 512+16 = 528 bytes (small page), 32个page组成一个block。
block = 16K +512 = 0x40000 bytes

K9F1G08U0B

page = (2K + 64)bytes (big page), 64个page组成一个block。
block = (128K + 4K)bytes

page是由两个部分组成的, 一个是main area, 是用来存储数据的, 另外一个叫Spare Area, 是用于存储ECC的校验值的。

Nand flash 以page为单位读写数据,而以block为单位擦除数据。按照这样的组织方式可以形成所谓的三类地址: --Block Address -- Page Address --Column Address(即为页内偏移地址)

对于NAND Flash 来讲,地址和命令只能在I/O[7:0]上传递,数据宽度是8 位。

512byte需要9bit来表示,对于528byte系列的NAND,这512byte被分成1st half和2nd half,各自的访问由地址指针命令来选择,A[7:0]就是所谓的column address。32 个page 需要5bit 来表示,占用A[13:9],即该page 在块内的相对地址。Block的地址是由A14 以上的bit 来表示,例如512Mb 的NAND,共4096block,因此,需要12 个bit 来表示,即A[25:14],如果是1Gbit 的528byte/page的NAND Flash,则block address用A[26:14]表示。而page address就是blcok address|page address in block, NAND Flash 的地址表示为: Block Address|Page Address in block|halfpage pointer|Column Address 地址传送顺序是Column Address,Page Address,Block Address。 由于地址只能在I/O[7:0]上传递,因此,必须采用移位的方式进行。 例如,对于512Mbit x8 的NAND flash,地址范围是0~0x3FF_FFFF,只要是这个范围内的数值表示的地址都是有效的。以NAND_ADDR 为例:第1 步是传递column address,就是NAND_ADDR[7:0],不需移位即可传递到I/O[7:0]上,而halfpage pointer 即bit8 是由操作指令决定的,即指令决定在哪个halfpage 上进行读写。而真正的bit8 的值是don\'t care 的。 第2 步就是将NAND_ADDR 右移9 位,将NAND_ADDR[16:9]传到I/O[7:0]上 第3 步将NAND_ADDR[24:17]放到I/O 上 第4 步需要将NAND_ADDR[25]放到I/O 上 因此,整个地址传递过程需要4 步才能完成,即4-step addressing。 如果NAND Flash 的容量是256Mbit 以下,那么,block adress 最高位只到bit24,因此寻址 只需要3 步。
对于big page需要两个周期才能将column address写入到flash, 两种flash的具体写address的周期,请见下表:
small page address写入到flash中的周期:


I/0 0 I/0 1 I/O 2 I/0 3 I/0 4 I/0 5 I/0 6 I/0 7
1st Cycle A0 A1 A2 A3 A4 A5 A6 A7
2st Cycle A9 A10 A11 A12 A13 A14 A15 A16
3st Cycle A17 A18 A19 A20 A21 A22 A23 A24

big page address写入到flash中的周期:


I/0 0 I/0 1 I/O 2 I/0 3 I/0 4 I/0 5 I/0 6 I/0 7
1st Cycle A0 A1 A2 A3 A4 A5 A6 A7
2st Cycle A8 A9 A10 A11 0 0 0 0
3st Cycle A12 A13 A14 A15 A16 A17 A18 A19
4st Cycle A20 A21 A22 A23 A24 A25 A26 A27

对应周期数,会随着flash的大小变化的, 下面会对nand flash K9f1208读写擦举例,虽然这个也是samll page的,但是有4个周期。
就x16 的NAND flash 器件稍微进行一下说明。 由于一个page 的main area 的容量为256word,仍相当于512byte。但是,这个时候没有所谓 的1st halfpage 和2nd halfpage 之分了,所以,bit8就变得没有意义了,也就是这个时候 bit8 完全不用管,地址传递仍然和x8 器件相同。除了,这一点之外,x16 的NAND使用方法和 x8 的使用方法完全相同。


读写擦

nand flash指令

每个nand flash都有一定的区别:

K9F5608U0D

function 1st. Cycle 2nd Cycle Acceptable Command during Busy
read 00h/01h -
read 2 50h -
read ID 90h -
reset FFh - 0
page program 80h 10h
copy back program 00h 8Ah
block erase 60h D0h
read status 70h - 0

K9F1G08U0B

function 1st. Cycle 2nd Cycle Acceptable Command during Busy
read 1 00h 30h
read for copy back 00h 35h
read ID 90h -
reset FFh - 0
page program 80h 10h
copy back program 85h 10h
block erase 60h D0h
random data input 85h -
random data output 05h E0h
read status 70h
0
read EDC status 7Bh
0


坏块查找

Nand Flash在出厂时会在每个block的0页面的space(512--527)空间中标记出坏块, small page和big page的地址还是不一样的, small page是在第5个字节处标明, big page是在第1个字节处标明,此页是否为坏块。如果为0xff则不是坏块,这个是nand flash出厂时的坏块检测方法。当nand flash在使用过程中出现了坏块,如何发现并且标记它呢?
答:通过某种坏块检测的软件算法对每一页的512个字节进行一定的判断,计算出一个ECC码,然后将其放入00B中,等若干时间后再用同样的算法对其进行检测,再次得到一个ECC码,将这个ECC码和之前计算出的ECC码进行比较,如果相等则不是坏块,如果不等则为坏块,然后就将其标记。
须掌握:如何遍历坏块:
ECC算法有两种:软件ECC算法,通用性好,每256个字节算出一个3byte的ECC码,故512个字节算出6byte的ECC码。


下面以small page为例,举例读写擦的过程, 对于下一节, 几乎将的都以big page为例,也有部分small page的东西。
K9f1208的寻址分为4个cycle。分别是:A[0:7]、A[9:16]、A[17:24]、A[25]。
读操作的过程为: 1、发送读取指令;2、发送第1个cycle地址;3、发送第2个cycle地址;4、发送第3个cycle地址;5、发送第4个cycle地址;6、读取数据至页末。
K9f1208提供了两个读指令,‘0x00’、‘0x01’。这两个指令区别在于‘0x00’可以将A[8]置为0,选中上半页;而‘0x01’可以将A[8]置为1,选中下半页。
虽然读写过程可以不从页边界开始,但在正式场合下还是建议从页边界开始读写至页结束。下面通过分析读取页的代码,阐述读过程。

static void ReadPage(U32 addr, U8 *buf)
//addr表示flash中的第几页,即‘flash地址>>9’
{
U16 i;
NFChipEn(); //使能NandFlash
WrNFCmd(READCMD0); //发送读指令‘0x00’,由于是整页读取,所以选用指令‘0x00’
WrNFAddr(0); //写地址的第1个cycle,即Column Address,由于是整页读取所以取0
WrNFAddr(addr); //写地址的第2个cycle,即A[9:16]
WrNFAddr(addr>>8); //写地址的第3个cycle,即A[17:24]
WrNFAddr(addr>>16); //写地址的第4个cycle,即A[25]。
WaitNFBusy(); //等待系统不忙
for(i=0; i<512; i++)
buf[i] = RdNFDat(); //循环读出1页数据
NFChipDs(); //释放NandFlash
}


写操作的过程为: 1、发送写开始指令;2、发送第1个cycle地址;3、发送第2个cycle地址;4、发送第3个cycle地址;5、发送第4个cycle地址;6、写入数据至页末;7、发送写结束指令
下面通过分析写入页的代码,阐述读写过程。

static void WritePage(U32 addr, U8 *buf)
//addr表示flash中的第几页,即‘flash地址>>9’
{
U32 i;
NFChipEn(); //使能NandFlash
WrNFCmd(PROGCMD0); //发送写开始指令’0x80’
WrNFAddr(0); //写地址的第1个cycle
WrNFAddr(addr); //写地址的第2个cycle
WrNFAddr(addr>>8); //写地址的第3个cycle
WrNFAddr(addr>>16); 写地址的第4个cycle
WaitNFBusy(); //等待系统不忙
for(i=0; i<512; i++)
WrNFDat(buf[i]); //循环写入1页数据
WrNFCmd(PROGCMD1); //发送写结束指令’0x10’
NFChipDs(); //释放NandFlash
}


static U32 EraseBlock(U32 addr)
{
UINT8T stat;
// addr &= ~0x1f;

NFChipEn(); //芯片使能,片选拉低,Nand flash使能
WrNFCmd(ERASECMD0); //写命令0x60表示块擦除操作将开始
WrNFAddr(addr); //地址分三次传送
WrNFAddr(addr>>8); //先传a9--a16,

if(NandAddr)
WrNFAddr(addr>>16); //后传a17-a24 ,a25

WrNFCmd(ERASECMD1); //传完地址后,写命令0xd0h(按时序要求)
stat = WaitNFBusy(); //写完擦除命令后,执行等待
uart_printf(".......................!\n");
NFChipDs(); //关闭片选

if(stat == 0)
{
uart_printf("erase success!\n");
}
else
{
uart_printf("erase error!\n");
}

#ifdef ER_BAD_BLK_TEST
if(!((addr+0xe0)&0xff)) stat = 1; //just for test bad block
#endif

//printf("Erase block 0x%x %s\n", addr, stat?"fail":"ok");
uart_printf (".");
return stat;
}

ECC校验

当往NAND Flash的page中写入数据的时候,每256字节我们生成一个ECC校验和,称之为原ECC校验和,保存到PAGE的OOB(out-of-band)数据区中。
当从NAND Flash中读取数据的时候,每256字节我们生成一个ECC校验和,称之为新ECC校验和。
校验的时候,根据上述ECC生成原理不难推断:将从OOB区中读出的原ECC校验和新ECC校验和按位异或,若结果为0,则表示不存在错(或是出现了 ECC无法检测的错误);若3个字节异或结果中存在11个比特位为1,表示存在一个比特错误,且可纠正;若3个字节异或结果中只存在1个比特位为1,表示 OOB区出错;其他情况均表示出现了无法纠正的错误。

了能检测到单个位错误,把每256字节的数据划分为一张8x256共2048位的表,使用22位校验码进行校验,16位为行校验码,进行横向校验,6位为列校验码,按进行纵向校验。下图描述了这22位校验码产生的原理:


列校验:

CP0:对所有字节的Bit 6,4,2,0进行异或操作的结果

CP1:对所有字节的Bit 7,5,3,1进行异或操作的结果

.

.

CP5:对所有字节的Bit 7,6,5,4进行异或操作的结果


行校验:

LP0:对0,2,4,6...254字节的所有位进行异或操作的结果

LP1:对1,3,5,7...255字节的所有位进行异或操作的结果

.

.

LP15:对128~255字节的所有位进行异或操作的结果

22位校验码需要三个字节保存,前两字节保存16位行校验码LP0~LP15,第三字节保存6位列校验码CP0~CP5,剩余2位置1:

把数据写入Nand Flash时,我们产生校验码并将它保存到Flash的Spare Data Area。从Flash读出数据时,产生一个新的ECC校验码,将它和Spare Data Area读出的旧校验码进行异或操作:

结果为0:数据正确

结果有11位为1:数据有1位错误(可纠正)

其他结果:数据超过1位发生错误,无法纠正


没有优化的ECC算法

下面是ECC产生算法的c++实现代码(仅满足示意用途,为了便于理解没有经过优化,在实际使用中,大量的异或运算可转为查表操作)

  1. static inline void SetBit(unsigned int& dat, int bit, int v)  
2. {
3. if (v)
4. dat |= (1< 5. else
6. dat &= ~(1< 7. }
8.
9.
10. static inline int GetBit(unsigned int dat, int bit)
11. {
12. if (dat & (1< 13. return 1;
14. else
15. return 0;
16. }
17.
18.
19. static inline int XorLP(unsigned char c)
20. {
21. return (GetBit(c, 7) ^ GetBit(c, 6) ^ GetBit(c, 5) ^ GetBit(c, 4)
^ GetBit(c, 3) ^ GetBit(c, 2) ^ GetBit(c, 1) ^ GetBit(c, 0) );
22. }
23.
24.
25. static inline int XorCP0(unsigned char c)
26. {
27. return (GetBit(c, 6) ^ GetBit(c, 4) ^ GetBit(c, 2) ^ GetBit(c, 0) );
28. }
29.
30. static inline int XorCP1(unsigned char c)
31. {
32. return (GetBit(c, 7) ^ GetBit(c, 5) ^ GetBit(c, 3) ^ GetBit(c, 1) );
33. }
34.
35. static inline int XorCP2(unsigned char c)
36. {
37. return (GetBit(c, 5) ^ GetBit(c, 4) ^ GetBit(c, 1) ^ GetBit(c, 0) );
38. }
39.
40. static inline int XorCP3(unsigned char c)
41. {
42. return (GetBit(c, 7) ^ GetBit(c, 6) ^ GetBit(c, 3) ^ GetBit(c, 2) );
43. }
44.
45. static inline int XorCP4(unsigned char c)
46. {
47. return (GetBit(c, 3) ^ GetBit(c, 2) ^ GetBit(c, 1) ^ GetBit(c, 0) );
48. }
49.
50. static inline int XorCP5(unsigned char c)
51. {
52. return (GetBit(c, 7) ^ GetBit(c, 6) ^ GetBit(c, 5) ^ GetBit(c, 4) );
53. }
54.
55.
56.
57. unsigned int ECC(const unsigned char* data)
58. {
59. unsigned int ecc = 0xFFFFFFFF;
60.
61. for(size_t i=0; i<256; i++)
62. {
63. unsigned char c=data[i];
64.
65. int lp = XorLP(c);
66.
67. // Bit 0
68. if (i & 0x01)
69. SetBit(ecc, 1, lp ^ GetBit(ecc, 1) );
70. else
71. SetBit(ecc, 0, lp ^ GetBit(ecc, 0) );
72.
73. // Bit 1
74. if (i & 0x02)
75. SetBit(ecc, 3, lp ^ GetBit(ecc, 3) );
76. else
77. SetBit(ecc, 2, lp ^ GetBit(ecc, 2) );
78.
79. // Bit 2
80. if (i & 0x04)
81. SetBit(ecc, 5, lp ^ GetBit(ecc, 5) );
82. else
83. SetBit(ecc, 4, lp ^ GetBit(ecc, 4) );
84.
85. // Bit 3
86. if (i & 0x08)
87. SetBit(ecc, 7, lp ^ GetBit(ecc, 7) );
88. else
89. SetBit(ecc, 6, lp ^ GetBit(ecc, 6) );
90.
91. // Bit 4
92. if (i & 0x10)
93. SetBit(ecc, 9, lp ^ GetBit(ecc, 8) );
94. else
95. SetBit(ecc, 8, lp ^ GetBit(ecc, 8) );
96.
97. // Bit 5
98. if (i & 0x20)
99. SetBit(ecc, 11, lp ^ GetBit(ecc, 11) );
100. else
101. SetBit(ecc, 10, lp ^ GetBit(ecc, 10) );
102.
103. // Bit 6
104. if (i & 0x40)
105. SetBit(ecc, 13, lp ^ GetBit(ecc, 13) );
106. else
107. SetBit(ecc, 12, lp ^ GetBit(ecc, 12) );
108.
109. // Bit 7
110. if (i & 0x80)
111. SetBit(ecc, 15, lp ^ GetBit(ecc, 15) );
112. else
113. SetBit(ecc, 14, lp ^ GetBit(ecc, 14) );
114.
115. SetBit(ecc, 18, XorCP0(c) ^ GetBit(ecc, 18) );
116. SetBit(ecc, 19, XorCP1(c) ^ GetBit(ecc, 19) );
117. SetBit(ecc, 20, XorCP2(c) ^ GetBit(ecc, 20) );
118. SetBit(ecc, 21, XorCP3(c) ^ GetBit(ecc, 21) );
119. SetBit(ecc, 22, XorCP4(c) ^ GetBit(ecc, 22) );
120. SetBit(ecc, 23, XorCP5(c) ^ GetBit(ecc, 23) );
121. }
122.
123. SetBit(ecc, 16, 1);
124. SetBit(ecc, 17, 1);
125.
126. return ecc;
127. }


使用ecc table的ECC算法的实现

static const u_char nand_ecc_precalc_table[] =
{
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
};

//Creates non-inverted ECC code from line parity
static void nand_trans_result(u_char reg2, u_char reg3,u_char *ecc_code)
{
u_char a, b, i, tmp1, tmp2;

/* Initialize variables */
a = b = 0x80;
tmp1 = tmp2 = 0;

/* Calculate first ECC byte */
for (i = 0; i < 4; i++)
{
if (reg3 & a)    /* LP15,13,11,9 --> ecc_code[0] */
tmp1 |= b;
b >>= 1;
if (reg2 & a)    /* LP14,12,10,8 --> ecc_code[0] */
tmp1 |= b;
b >>= 1;
a >>= 1;
}

/* Calculate second ECC byte */
b = 0x80;
for (i = 0; i < 4; i++)
{
if (reg3 & a)    /* LP7,5,3,1 --> ecc_code[1] */
tmp2 |= b;
b >>= 1;
if (reg2 & a)    /* LP6,4,2,0 --> ecc_code[1] */
tmp2 |= b;
b >>= 1;
a >>= 1;
}

/* Store two of the ECC bytes */
ecc_code[0] = tmp1;
ecc_code[1] = tmp2;
}
// Calculate 3 byte ECC code for 256 byte block
void nand_calculate_ecc (const u_char *dat, u_char *ecc_code)
{
u_char idx, reg1, reg2, reg3;
int j;
/* Initialize variables */
reg1 = reg2 = reg3 = 0;
ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;

/* Build up column parity */
for(j = 0; j < 256; j++)
{
/* Get CP0 - CP5 from table */
idx = nand_ecc_precalc_table[dat[j]];
reg1 ^= (idx & 0x3f);
/* All bit XOR = 1 ? */
if (idx & 0x40) {
reg3 ^= (u_char) j;
reg2 ^= ~((u_char) j);
}
}
/* Create non-inverted ECC code from line parity */
nand_trans_result(reg2, reg3, ecc_code);
/* Calculate final ECC code */
ecc_code[0] = ~ecc_code[0];
ecc_code[1] = ~ecc_code[1];
ecc_code[2] = ((~reg1) << 2) | 0x03;
}

// Detect and correct a 1 bit error for 256 byte block
int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc)
{
u_char a, b, c, d1, d2, d3, add, bit, i;

/* Do error detection */
d1 = calc_ecc[0] ^ read_ecc[0];
d2 = calc_ecc[1] ^ read_ecc[1];
d3 = calc_ecc[2] ^ read_ecc[2];
if ((d1 | d2 | d3) == 0)
{
/* No errors */
return 0;
}
else
{
a = (d1 ^ (d1 >> 1)) & 0x55;
b = (d2 ^ (d2 >> 1)) & 0x55;
c = (d3 ^ (d3 >> 1)) & 0x54;

/* Found and will correct single bit error in the data */
if ((a == 0x55) && (b == 0x55) && (c == 0x54))
{
c = 0x80;
add = 0;
a = 0x80;
for (i=0; i<4; i++)
{
if (d1 & c)
add |= a;
c >>= 2;
a >>= 1;
}
c = 0x80;
for (i=0; i<4; i++)
{
if (d2 & c)
add |= a;
c >>= 2;
a >>= 1;
}
bit = 0;
b = 0x04;
c = 0x80;
for (i=0; i<3; i++)
{
if (d3 & c)
bit |= b;
c >>= 2;
b >>= 1;
}
b = 0x01;
a = dat[add];
a ^= (b << bit);
dat[add] = a;
return 1;
}
else
{
i = 0;
while (d1)
{
if (d1 & 0x01)
++i;
d1 >>= 1;
}
while (d2)
{
if (d2 & 0x01)
++i;
d2 >>= 1;
}
while (d3)
{
if (d3 & 0x01)
++i;
d3 >>= 1;
}
if (i == 1)
{
/* ECC Code Error Correction */
read_ecc[0] = calc_ecc[0];
read_ecc[1] = calc_ecc[1];
read_ecc[2] = calc_ecc[2];
return 2;
}
else
{
/* Uncorrectable Error */
return -1;
}
}
}
/* Should never happen */
return -1;
}

flash的烧写

这里的烧写是指打开software check ecc,如果使用nand 子指令是没有问题的,会自动跳过bad block,还会计算出ecc并写入flash中,但是前提在flash中有可用的bootcode,但是如果是空的,那就必须使用烧code机烧写 flash了, 如果使用烧code机就不一样了,需要将要烧如flash的binary处理一下,将binary中包含oob信息,这里我们自己写了两个tools来处理这件事, 两个tools分别是针对small page和big page的。
命令的使用方法:big_page/small_page [source file] [dest file] [oob file]
命令的原理:就是在适当的位置,插入oob的ECC值。
用烧code机烧flash时应注意:
erase时, access mothod中Invalid Block Management要选择Do not use, 这样擦的时候不会检测是不是坏块, Spare Area Usage选择User Data with IB Info forced。
write时,需要将Invalid Block Management改为Skip IB,写时会跳过坏块,Spare Area Usage选择User Data with IB Info forced, 选择binary中的ecc信息, 即ECC_SOFTWARE。

阅读(1778) | 评论(0) | 转发(0) |
0

上一篇:vim 参考手册

下一篇:linux 文件处理command

给主人留下些什么吧!~~