前提: norflash初始化正常,能够正常从nor上执行。
cpu总是从0地址读取指令执行程序。当cpu设置成nor启动时,0地址对应nor。cpu从nand启动时,0地址对应sram。
1.读取norFlash
我们将板子设为nor启动,那么0地址对应nor,我们先将uboot烧写到nor中。我们先看下这款NorFlash的手册,找到操作flash的命令表:
下面简单的举一些例子:
1)复位(reset):
往任何一个地址写入F0即可。
2)读ID:
很多的Nor Flash可以配置成位宽16bit(Word),位宽8bit(Byte),我们这款norflash数据位宽为16bit。下面我们按照nor手册上的命令表尝试一下:
往地址555H写入AAH(解锁) 往地址2AAH写入55H(解锁) 往地址555H写入90H(命令) 读0地址得到厂家ID(C2H) 读1地址得到设备ID(22DAH或225BH) 退出读ID状态(给任意地址写F0H就可以了)
上面的地址是对于norflash的,那么我们CPU要怎么发送地址呢?
从上面原理图我们知道CPU和nor的地址是错位相连的,也就是:
比如:
cpu地址 | nor地址 |
---|---|
A15~A1 | A14~A0 |
那么可以看到cpu的地址实际相当于是nor地址左移了一位,那么比如要想给nor上的555H地址写入AAH,那么CPU要发出的地址应该为0x555<<1,也就是nor地址的2倍。
下面对在Nor Flash的操作,cpu的操作,U-BOOT上的操作进行比较,如下表:
Nor Flash的操作 | cpu的操作 | U-BOOT上的操作 |
---|---|---|
往地址555H写入AAH(解锁) | 往地址AAAH写入AAH(解锁) | mw.w aaa aa |
往地址2AAH写入55H(解锁) | 往地址554H写入55H(解锁) | mw.w 554 55 |
往地址555H写入90H(命令) | 往地址AAAH写入90H(命令) | mw.w aaa 90 |
读0地址得到厂家ID(C2H) | 读0地址得到厂家ID(C2H) | md.w 0 1 (1:表示读一次) |
读1地址得到设备ID(22DAH或225BH) | 读2地址得到设备ID(22DAH或225BH) | md.w 2 1 |
退出读ID状态(给任意地址写F0H) | 退出读ID状态(给任意地址写F0H) | mw.w 0 f0 |
我们读出厂家id为c2, 设备id为2249,和我们的nor手册上是一致的。我们发出f0命令,进行复位,这时读取的数据就不再是厂家id和设备id了,而是我们norflash中的实际的数据17 00 00 ea。
3)读数据:
前面我们说过, nor可以像ram一样的读,所以只要做好内存控制器的初始化工作就可以直接读了。
我们再用ue以16进制打开我们烧进去的uboot.bin,发现内容一样,说明我们从norflash中读出来的数据是正确的。
4)读属性:
通常内核里面要识别一个 Nor Flash 有两种方法:
一种是 jedec 探测,就是在内核里面事先定义一个数组,该数组里面放有不同厂家各个芯片的一些参数,探测的时候将 flash 的 ID 和数组里面的 ID 一一比较,如果发现相同的,就使用该数组的参数。 jedec 探测的优点就是简单,只要通过flash的数组编号,即可访问该款flash属性,缺点是如果内核要支持的 flash 种类很多,这个数组就会很庞大。
一种是 CFI(common flash interface)探测,就是直接发各种命令来读取芯片的信息,比如 ID、容量等,芯片本身就包含了电压有多大,容量有有多少等信息。
我们的这款norflash属于cfi探测,下面对在Nor Flash上操作,2440上操作,U-BOOT上进行cfi 探测(读取芯片信息)。 下图是进入cfi模式后的一些flash属性查找表,可以按照表格命令查询norflash的一些属性(容量、电压、block信息等)。
1.根据命令表往55H地址写入98H进入cfi模式 2.读取`qry` 3.获取属性
Nor Flash上操作cfi | 2440上操作cfi | U-BOOT上操作cfi |
---|---|---|
往55H地址写入98H(进入cfi模式) | 往AAH地址写入98H | mw.w aa 98 |
读地址10H得到0051('q') | 读地址20H得到0051 | md.w 20 1 |
读地址11H得到0052('r') | 读地址22H得到0052 | md.w 22 1 |
读地址12H得到0059('y') | 读地址24H得到0059 | md.w 24 1 |
读地址27H得到容量 | 读地址4EH得到容量 | md.w 4e 1 |
读地址1BH得到VCCmin | 读地址36H得到VCCmin | md.w 36 1 |
从测试结果我们看到容量为2^21=2M,Vcc最小提供电压是2.7v.
2.写norFlash
我们之前讲过norflash可以像内存一样,用md命令直接读取,不能像内存一样直接写,不信我们试试:
①我们在Nor Flash的0x10000的地址读数据;
由于我们的uboot只有162k,烧录到norflash后,norflash上的的0x100000地址还没有被写入数据,norflash的容量为2M(0~0x200000),所以读取NorFlash的0x10000的地址数据是0xffff...
②在Nor flash的10000的地址写数据下0x1234,然后在这个地址读数据;
③从上图我们验证了norflash可以像内存一样直接读,但不能像内存一样直接写,那么要怎样才能写norflash呢?
我们按照norflash手册上的命令表:
Nor Flash上写操作 | 2440上写操作 | U-BOOT上写操作 |
---|---|---|
往地址555H写AAH(解锁) | 往地址AAAH写AAH(解锁) | mw.w aaa aa |
往地址2AAH写55H(解锁) | 往地址554H写55H(解锁) | mw.w 554 55 |
往地址555H写A0H | 往地址AAAH写A0H | mw.w aaa a0 |
往地址PA写PD | 往地址0x100000写1234h | mw.w 100000 1234 |
我们读取数据发现0x1234已被写入到地址0x100000.
④我们再次往0x100000地址处,写入0x5678;
这时我们发现0x100000地址处的数据不是0x5678,而是0x1230,为什么?
由于原来的数据已经是0x1234不是全0xffff,我们之前norflash原理中讲过只能将flash存储介质中的1变成0,不能将0变成1。所以在0x1234的基础上不去擦出直接继续去写0x5678是会有问题的。
0001 0010 0011 0100(0x1234) 0101 0110 0111 1000(0x5678) ---------------------------- 0001 0010 0011 0000(0x1230)
所以flash写入前一定要先擦除。
⑤先擦除(参考Nor Flash芯片手册)
Nor Flash擦操作 | u-boot擦操作 |
---|---|
往地址555H写AAH | mw.w aaa aa |
往地址2AAH写55H | mw.w 554 55 |
往地址555H写80H | mw.w aaa 80 |
往地址555H写AAH | mw.w aaa aa |
往地址2AAH写55H | mw.w 554 55 |
往地址PA写30H | mw.w 100000 30 |
擦除后再读取发现数据就已经变成了0xffff,后面就可以进行写操作了。
⑥再写
现在数据就变成我们的0x5678了。
注意:在写norflash时,要注意不要写0地址或者是uboot所在的地址,这样写入后norflash上的uboot程序就被破坏了。比如本测试就是写了0x100000地址,这个地址在uboot之外。
问题拓展
1.基于jz2440开发板,用uboot发送md.w 0, md.w 2, md.w 4等偶地址命令能够读取norflash,但使用md.w 1, md.w 3,md.w 5就会出现死机,为什么?
由于我们的norflash是16bit数据位宽的,访问时要2byte对齐。如果不想以2byte为单位进行访问,那么要用uboot中用md.b 1,md.b 3这种单字节读取命令。
2.uboot中操作norflash进行擦写的时候能够解锁一次,擦写多次吗?
不能,每次擦写都要进行解锁动作。
3.uboot中擦除那么是以块(sector)为单位的,那么当进行擦除时发送的地址并不是以块对齐的,会有什么结果?
也能擦除成功,会根据地址范围确定在哪一个块中。