nor flash的一般操作与分析
是现在市场上两种主要的非易失闪存技术。Intel于1988年首先开发出NOR Flash?技术,彻底改变了原先由EPROM(Electrically Programmable Read-Only-Memory电可编程序只读存储器)和EEPROM(电可擦只读存储器Electrically Erasable Programmable Read - Only Memory)一统天下的局面。紧接着,1989年,东芝公司发表了NAND Flash?结构,强调降低每比特的成本,有更高的性能,并且像磁盘一样可以通过接口轻松升级。NOR Flash?的特点是芯片内执行(XIP?,eXecute In Place),这样应用程序可以直接在Flash闪存内运行,不必再把代码读到系统RAM。 这样我们知道程序能直接在norlash执行的原因在XIP 那:什么是XIP?工作原理又是怎么样的? XIP eXecute In Place eXecute In Place,即芯片内执行,指应用程序可以直接在flash闪存内运行,不必再把代码读到系统RAM中。flash内执行是指nor flash?不需要初始化,可以直接在flash内执行代码。但往往只执行部分代码,比如初始化RAM. (注:片内执行不是说程序在存储器内执行哦,CPU的基本功能就是取指、译码和执行。norflash能在芯片内执行,就是指CPU的取指模块能够直接从norflash中把指令取出来,供后面的译码和执行模块使用) --------------------- 本文来自 yjzh_td 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/yjzh_td/article/details/72870044?utm_source=copy? ? ? 转:https://blog.csdn.net/thisway_diy/article/details/79397638 Nor Flash的连接线有地址线,数据线,片选信号读写信号等,Nor Flash的接口属于内存类接口,Nor Flash可以向内存一样读,但是不能像内存一样写,需要做一些特殊的操作才能进行写操作,读只需像内存一样读很简单。 Nor Flash原理图如图: Flash介绍常用的Flash类型有Nor Flash和NAND Flash两种。 Nor Flash由Intel公司在1988年发明,以替代当时在市场上占据主要地位的EPROM和E2PROM。NAND Flash由Toshiba公司在1989年发明。两者的主要差别如下表:
Nor Flash支持XIP,即代码可以直接在Nor Flash上执行,无需复制到内存中。这是由于NorF lash的接口与RAM完全相同,可以随机访问任意地址的数据。Nor Flash进行读操作的效率非常高,但是擦除和写操作的效率很低,另外,Nor Flash的容量一般比较小。NAND Flash进行擦除和写操作的效率更高,并且容量更大。一般而言,Nor Flash用于存储程序,NAND Flash用于存储数据。基于NAND Flash的设备通常也要搭配Nor Flash以存储程字。 Flash存储器件由擦除单元(也称为块)组成,当要写某个块时,需要确保这个块己经 Nor Flash的接口与RAM完全相同,可以随意访问任意地址的数据。而NAND Flash的 容量相同的情况下,NAND Flash的体积更小,对于空间有严格要求的系统,NAND Flash可以节省更多空间。市场上Nor Flash的容量通常为IMB~4MB(也有32MB的Nor Flash),NAND Flash的容量为8MB~512MB。容量的差别也使得Nor Flash多用于存储程序,NAND Flash多用于存储数据。 对于Flash存储器件的可靠性需要考虑3点:位反转、坏块和可擦除次数。所有Flash器件都遭遇位反转的问题:由于Flash固有的电器特性,在读写数据过程中,偶然会产生一位或几位数据错误(这种概率很低),而NAND Flash出现的概率远大于Nor Flash,当位反转发生在关键的代码、数据上时,有可能导致系统崩溃。当仅仅是报告位反转,重新读取即可:如果确实发生了位反转,则必须有相应的错误检测/恢复措施。在NAND Flash上发生位反转的概率史高,推荐使用EDC/ECC进行错误检测和恢复。NAND Flash上面会有坏块随机分布在使用前需要将坏块扫描出来,确保不再使用它们,否则会使产品含有严重的故障。NAND Flash每块的可擦除次数通常在100000次左右,是Nor Flash的10倍。另外,因为NAND Flash的块大小通常是NorF lash的1/8,所以NAND Flash的寿命远远超过Nor Flash。 嵌入式Linux对Nor、NAND Flash的软件支持都很成熟。在Nor Flash上常用jffs2文 Nor Flash的操作下面我们使用u-boot来体验Nor Flash的操作(开发板设置Nor启动,进入u-boot)。 1).使用OpenJTAG烧写UBOOT到Nor Flash 那么我们怎么用u-boot来操作呢? Nor Flash手册里会有一个命令的表格,如图: 下面简单的举一些例子: 复位(reset):往任何一个地址写入F0。 读ID(ReadSiliconID):很多的Nor Flash可以配置成位宽16bit(Word),位宽8bit(Byte)。对于我们使用的jz2440开发板使用是位宽16bit,怎样读ID呢? 根据前面得图可知,往Nor Flash的555地址写AA,再往2AA的地址写入55,再往555的地址写入90,然后就可以读ADI地址,就可以读到DDI数据了。 实例1 读数据: 在u-boot上执行:md.b0 结果(和我们烧进去的数据完全一样): 00000000:170000ea14f09fe514f09fe514f09fe5................ 00000010:14f09fe514f09fe514f09fe514f09fe5................ 00000020:6001f833c001f8332002f8337002f833`..3...3..3...3 00000030:e002f8330004f8332004f833efbeadde...3...3..3....
可以得出结论:u-boot可以像读内存一样来读nor flash 实例2 读ID(参考Nor手册)
下图为2440和Nor Flash的简易连接图: 2440的A1接到Nor的A0所以2440发出的地址是,Nor Flash收到的地址左移一位。比如:2440发出(555H<<1)地址,Nor Flash才能收到555H这个地址。 下面对在Nor Flash的操作,2440的操作,U-BOOT上的操作进行比较,如下表:
1).当执行过 2).当执行过 结果(输出设备ID): 3).当执行 执行: Nor Flash的两种规范通常内核里面要识别一个 Nor Flash 有两种方法: 一种是 jedec 探测,就是在内核里面事先定义一个数组,该数组里面放有不同厂家各个芯片的一些参数,探测的时候将 flash 的 ID 和数组里面的 ID 一一比较,如果发现相同的,就使用该数组的参数。 一种是 CFI(common flash interface)探测,就是直接发各种命令来读取芯片的信息,比如 ID、容量等,芯片本身就包含了电压有多大,容量有有多少等信息。 下面对在Nor Flash上操作,2440上操作,U-BOOT上操作cfi 探测(读取芯片信息)进行比较参考芯片手册。
Nor Flash写数据我们在Nor Flash的10000的地址读数据, 怎样把数据写进Nor Flash进去呢? 写数据之前必须保证,要写的地址是擦除的。 下面是Nor Flash的写操作,如下表:
1).U-BOOT执行完上述指令后,0x1234,就被写到0x100000地址处, 执行: 2).我们再次往0x100000地址处,写入0x5678,执行如下命令: 先擦除(参考Nor Flash芯片手册) 执行完上述指令后测试 执行: 再烧写 测试烧写结果 总结:我们烧写时,如果上面的数据,不是0ffff,没有被擦除过,我们就要先擦出,擦除完后,才可以烧写,擦除烧写的命令可以从芯片手册里面获得。 = 第002节Nor Flash编程识别 = 16 17 /* offset是基于NOR的角度看到 */ 18 void nor_cmd(unsigned int offset,unsigned int cmd) 19 { 20 nor_write_word(NOR_FLASH_BASE,offset,cmd); 21 }
读取函数 nor_read_word函数是从NOR Flash 读取两个字节(本开发板位宽16bit),读取数据的地址,是基于2440,所以读取NOR Flash某个地址上的数据时,需要把NOR Flash对应的地址左移一位(地址乘以2)。 23 unsigned int nor_read_word(unsigned int base,unsigned int offset) 24 { 25 volatile unsigned short *p = (volatile unsigned short *)(base + (offset << 1)); 26 return *p; 27 }
读取地址中的数据 向nor_dat函数中写入NOR Flash某个地址,返回该NOR Flash地址上的数据。 29 unsigned int nor_dat(unsigned int offset) 30 { 31 return nor_read_word(NOR_FLASH_BASE,offset); 32 }
进入NOR FLASH的CFI模式,读取各类信息 do_scan_nor_flash函数代码如下,该函数的功能:进入CFI模式读取NOR Flash中的厂家ID,设备ID,容量等信息。 50/* 进入NOR FLASH的CFI模式 51 * 读取各类信息 52 */ 53 void do_scan_nor_flash(void) 54 { 55 char str[4]; 56 unsigned int size; 57 int regions,i; 58 int region_info_base; 59 int block_addr,blocks,block_size,j; 60 int cnt; 61 62 int vendor,device; 63 64 /* 打印厂家ID、设备ID */ 65 nor_cmd(0x555,0xaa); /* 解锁 */ 66 nor_cmd(0x2aa,0x55); 67 nor_cmd(0x555,0x90); /* read id */ 68 vendor = nor_dat(0); 69 device = nor_dat(1); 70 nor_cmd(0,0xf0); /* reset */ 71 72 nor_cmd(0x55,0x98); /* 进入cfi模式 */ 073 74 str[0] = nor_dat(0x10); 75 str[1] = nor_dat(0x11); 76 str[2] = nor_dat(0x12); 77 str[3] = ‘ ‘; 78 printf("str = %s",str); 79 80 /* 打印容量 */ 81 size = 1<<(nor_dat(0x27)); 82 printf("v=0x%x,d=0x%x,s=0x%x,%dM",vendor,device,size,size/(1024*1024)); 83 84 /* 打印各个扇区的起始地址 */ 85 /* 名词解释: 86 * erase block region : 里面含有1个或多个block,它们的大小一样 87 * 一个nor flash含有1个或多个region 88 * 一个region含有1个或多个block(扇区) 89 90 * Erase block region information: 91 * 前2字节+1 : 表示该region有多少个block 92 * 后2字节*256 : 表示block的大小 93 */ 94 95 regions = nor_dat(0x2c); 96 region_info_base = 0x2d; 97 block_addr = 0; 98 printf("Block/Sector start Address:"); 99 cnt = 0; 100 for (i = 0; i < regions; i++) 101 { 102 blocks = 1 + nor_dat(region_info_base) + (nor_dat(region_info_base+1)<<8); 103 block_size=256*(nor_dat(region_info_base+2)+(nor_dat(region_info_base+3)<<8)); 104 region_info_base += 4; 105 106 //printf("…………"); 107 108 for (j = 0; j < blocks; j++) 109 { 110 /* 打印每个block的起始地址 */ 111 //printf("0x%08x ",block_addr); 112 printHex(block_addr); 113 putchar(‘ ‘); 114 cnt++; 115 block_addr += block_size; 116 if (cnt % 5 == 0) 117 printf("nr"); 118 } 119 } 120 printf("nr"); 121 /* 退出CFI模式 */ 122 nor_cmd(0,0xf0); 123 }
第65,66行 这两步是解锁,解锁之后就进入读ID状态,就可以读取厂家和设备ID了。 第68行 是把读取到的厂家ID的值,复制给vendor变量。 第69行 是把读取到的设备ID的值,复制给device变量。 第70行 退出读ID状态: 给任意地址写F0H。 第72行,往地址0x55地址写入数据0x98,是进入cfi模式。 第74,75,76行是读取NOR Flash地址0x10,0x11,x012中的字符,赋值给字符串str。 第81行,根据芯片手册可知道,读取NOR Flash地址0x27处的数据,得到的是NOR Flash容量大小2的幂数,所以把1左移读取到的数据,就可得到NOR Flash的容量。 第95行读取NOR Flash地址0x2c地址中的数据,可以得到NOR Flash中有多少region。 第102行根据Erase block region information:的信息可以知道读取[2E,2D]这两个字节的地址+1,可以得到一个region有多少block(参考芯片手册)。代码中的region_info_base变量的值是0x2d,0x2d是前两个字节中的低字节,0x2e是前两个字节中的高字节,所以需要左移8位,然后加上1就得到了一个region有多少block.。 第103行参考芯片手册,读取[30,2F]这两个字节地址,然后乘上256就可以得到一个块的大小。 第104行,地址加4,读取下一个region有多少block和每个block的大小。 第112,115行,由于NOR Flash的基地址是0,所以第一个block的首地址是0,下一个block的首地址,就是上一个block的首地址加上block的大小。 第112行往0地址写入0xf0,退出CFI模式。 Nor Flash的测试 nor_flash_test函数通过switch语句,分别处理识别NOR Flash,擦除NOR Flash某个扇区,编写某个地址,读某个地址。代码如下: 232 void nor_flash_test(void) 233 { 234 char c; 235 236 while (1) 237 { 238 /* 打印菜单,供我们选择测试内容 */ 239 printf("[s] Scan nor flashnr"); 240 printf("[e] Erase nor flashnr"); 241 printf("[w] Write nor flashnr"); 242 printf("[r] Read nor flashnr"); 243 printf("[q] quitnr"); 244 printf("Enter selection: "); 245 246 c = getchar(); 247 printf("%c",c); 248 249 /* 测试内容: 250 * 1. 识别nor flash 251 * 2. 擦除nor flash某个扇区 252 * 3. 编写某个地址 253 * 4. 读某个地址 254 */ 255 switch (c) 256 { 257 case ‘q‘: 258 case ‘Q‘: 259 return; 260 break; 261 262 case ‘s‘: 263 case ‘S‘: 264 do_scan_nor_flash(); 265 break; 266 267 case ‘e‘: 268 case ‘E‘: 269 do_erase_nor_flash(); 270 break; 271 272 case ‘w‘: 273 case ‘W‘: 274 do_write_nor_flash(); 275 break; 276 277 case ‘r‘: 278 case ‘R‘: 279 do_read_nor_flash(); 280 break; 281 default: 282 break; 283 } 284 } 285 }
主函数 main函数代码如下所示。把timer中断去掉,否则: 测试NOR Flash时进入CFI等模式时,如果发生了中断,cpu必定读NOR Flash,那么读不到正确的指令,导致程序崩溃。 12 int main(void) 13 { 14 led_init(); 15 //interrupt_init(); /* 初始化中断控制器 */ 16 key_eint_init(); /* 初始化按键,设为中断源 */ 17 //timer_init(); 18 19 puts("nrg_A = "); 20 printHex(g_A); 21 puts("nr"); 22 23 nor_flash_test(); 24 25 return 0; 26 }
第003节Nor Flash编程擦写读本实例的目的目的:擦除nor flash某个扇区,编写某个地址,读某个地址。 等待烧写 等待烧写完成 : 读数据,Q6无变化时表示结束 (参考芯片手册), 35 void wait_ready(unsigned int addr) 36 { 37 unsigned int val; 38 unsigned int pre; 39 40 pre = nor_dat(addr>>1); 41 val = nor_dat(addr>>1); 42 while ((val & (1<<6)) != (pre & (1<<6))) 43 { 44 pre = val; 45 val = nor_dat(addr>>1); 46 } 47}
擦除NOR Flash 某个扇区 do_erase_nor_flash函数的代码如下。参考芯片手册,就可以知道擦除某个扇区,还是相对比较简单的。 125 void do_erase_nor_flash(void) 126 { 127 unsigned int addr; 128 129 /* 获得地址 */ 130 printf("Enter the address of sector to erase: "); 131 addr = get_uint(); 132 133 printf("erasing ..."); 134 nor_cmd(0x555,0xaa); /* 解锁 */ 135 nor_cmd(0x2aa,0x55); 136 nor_cmd(0x555,0x80); /* erase sector */ 137 138 nor_cmd(0x555,0xaa); /* 解锁 */ 139 nor_cmd(0x2aa,0x55); 140 nor_cmd(addr>>1,0x30); /* 发出扇区地址 */ 141 wait_ready(addr); 142 }
第131行,get_uint函数用于获取输入的地址。 第134,135这两行是解锁。 第136行是erase sector。 第138,139行是再次解锁。 第140行是对发出的扇区地址。 第 141行等待擦除完成。 写NOR Flash do_write_nor_flash的代码如下所示,开发板上的NOR Flash的位宽是16bit,所以可以把要写的数据构造出16bit然后在写进NOR Flash中。 144 void do_write_nor_flash(void) 145 { 146 unsigned int addr; 147 unsigned char str[100]; 148 int i,j; 149 unsigned int val; 150 151 /* 获得地址 */ 152 printf("Enter the address of sector to write: "); 153 addr = get_uint(); 154 155 printf("Enter the string to write: "); 156 gets(str); 157 158 printf("writing ...nr"); 159 160 /* str[0],str[1]==>16bit 161 * str[2],str[3]==>16bit 162 */ 163 i = 0; 164 j = 1; 165 while (str[i] && str[j]) 166 { 167 val = str[i] + (str[j]<<8); 168 169 /* 烧写 */ 170 nor_cmd(0x555,0xaa); /* 解锁 */ 171 nor_cmd(0x2aa,0x55); 172 nor_cmd(0x555,0xa0); /* program */ 173 nor_cmd(addr>>1,val); 174 /* 等待烧写完成 : 读数据,Q6无变化时表示结束 */ 175 wait_ready(addr); 176 177 i += 2; 178 j += 2; 179 addr += 2; 180 } 181 182 val = str[i]; 183 /* 烧写 */ 184 nor_cmd(0x555,0xaa); /* 解锁 */ 185 nor_cmd(0x2aa,0x55); 186 nor_cmd(0x555,0xa0); /* program */ 187 nor_cmd(addr>>1,val); 188 /* 等待烧写完成 : 读数据,Q6无变化时表示结束 */ 189 wait_ready(addr); 190 }
第153行把通过get_uint获得的地址赋值给addr变量, 第156行通过gets函数获得输入的字符串。 第168行两个8位的数据,组合成一个16位的数据赋值给变量val。 读NOR Flash do_read_nor_flash函数代码如下,由于NOR Flash是内存类接口,可以像内存一样读取。 191 void do_read_nor_flash(void) 192 { 193 unsigned int addr; 194 volatile unsigned char *p; 195 int i,j; 196 unsigned char c; 197 unsigned char str[16]; 198 199 /* 获得地址 */ 200 printf("Enter the address to read: "); 201 addr = get_uint(); 202 203 p = (volatile unsigned char *)addr; 204 205 printf("Data : nr"); 206 /* 长度固定为64 */ 207 for (i = 0; i < 4; i++) 208 { 209 /* 每行打印16个数据 */ 210 for (j = 0; j < 16; j++) 211 { 212 /* 先打印数值 */ 213 c = *p++; 214 str[j] = c; 215 printf("%02x ",c); 216 } 217 218 printf(" ; "); 219 220 for (j = 0; j < 16; j++) 221 { 222 /* 后打印字符 */ 223 if (str[j] < 0x20 || str[j] > 0x7e) /* 不可视字符 */ 224 putchar(‘.‘); 225 else 226 putchar(str[j]); 227 } 228 printf("nr"); 229 }
第201行中的get_uint函数,从串口中获得输入的地址。 --------------------- 本文来自 韦东山 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/thisway_diy/article/details/79397638?utm_source=copy? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |