电子说
本文主要是关于NOR Flash的相关介绍,并着重对NOR Flash烧写flash锁死现象进行了详尽的阐述。
NOR FLASH 是很常见的一种存储芯片,数据掉电不会丢失.NOR FLASH支持Execute ON Chip,即程序可以直接在FLASH片内执行。这点和NAND FLASH不一样。因此,在嵌进是系统中,NOR FLASH很适合作为启动程序的存储介质。
NOR FLASH的读取和RAM很类似,但不可以直接进行写操纵。对NOR FLASH的写操纵需要遵循特定的命令序列,终极由芯片内部的控制单元完成写操纵。从支持的最小访问单元来看,NOR FLASH一般分为 8 位的和16位的(当然,也有很多NOR FLASH芯片同时支持8位模式和是16 位模式,具体的工作模式通过特定的管脚进行选择) 。 对8位的 NOR FLASH芯片,或是工作在8-BIT模式的芯片来说,一个地址对应一个BYTE(8-BIT)的数据。例如一块8-BIT的NOR FLASH,假设容量为4个 BYTE.那芯片应该有8个数据信号D7-D0 和2个地址信号,A1-A0.地址0x0对应第0个 BYTE,地址0x1对应于第1BYTE,地址0x2对应于第2个 BYTE,而地址0x3则对应于第3 个BYTE对16位的 NOR FLASH芯片,或是工作在16-BIT模式的芯片来说,一个地址对应于一个HALF-WORD(16-BIT)的数据。例如,一块16-BIT的 NOR FLASH,假设其容量为4个BYTE.那芯片应该有16 个数据信号线D15-D0 和1个地址信号A0.地址 0x0对应于芯片内部的第0个 HALF-WORD,地址0x1对应于芯片内部的第1个 HALF-WORD. FLASH一般都分为很多个SECTOR,每个SECTOR包括一定数目的存储单元。对有些大容量的FLASH,还分为不同的BANK,每个BANK包括一定数目的SECTOR.FLASH的擦除操纵一般都是以SECTOR,BANK或是整片FLASH为单位的。
Flash锁死是由于在Flash的密码部分写入了密码,如果在烧写的过程中,受到干扰就有机会导致Flash锁死,如果试了下面的方法仍旧没能解锁,只有更换芯片。C2000烧写的过程(clear) -----》erase----》depletion ------》program------》 verify。如果在Erase的时候,芯片强行断电,供电不稳定导致类似于强行短点的情况,时钟不稳定,那么FLASH的密码段有可能成为随机值或全0.
解决方法
1、确认一下是不是有程序放在FLASH的密码区,如果是那么查看.out中对应地址的数据,就是密码。
2、断电,上电用CCS---》memory看看FLASH区是不是全0,用GEL功能中的Code Security Module -》Unlock_CSM解锁
3、每次联编完成,先不要烧FLASH,先看看.out文件对应密码的地址是否被使用,确保PASSWDS的used为0
4、若已经锁死,不要更改DSP的源程序,使用CCS3.3在线CPU仿真模式,View--》Memory看密码区烧进的数据
NorFlash程序烧写的解锁
一块空板子(flash是空的)是可以通过K9正常读写flash的,但当flash中已经写入程序后,在测试过程中很容易出现不能通过K9重新烧写flash。
我做了一下实验,在一块可以正常读写的板子上向flash中正常启动地址写入一个文本文件,让板子不能正常启动,这时候再用K9擦除flash时会报“无法停止目标板上的器件”,在设置中选用“特殊停止”可以清除“无法停止目标板器件”的错误,但K9会一直停在“初始化flash“状态,尝试K9各种配置组合都不能再继续下去了。再焊一片空flash到板子上,K9又可以继续烧写flash。我做了几次这样的试验,可以确定当flash非空而程序又不正确时,会重现上述过程。
分析上述现像,不难发现是因为向片内烧写了非法的代码,导致芯片上电运行后异常,影响仿真器连接。问题的根本是无法停止内核,如果能停止内核,那一切都好办的多。通常情况下,CNW5602这种比较高性能ARM芯片支持比较多的启动模式。于是我建议这位客户切换芯片的启动模式,不过这位用户告诉我,板上只设计了从NorFlash启动;并且由于是BGA的片子,也没法设置跳线。没有什么别的好办法,难道说必须再次把Flash芯片取下然后换块空的?
后面实在没什么办法,他把板寄给了我,要我帮忙弄。
既然无法切换启动模式,那是否可以从源头解决问题,让NorFlash中数据不能被芯片读取出来?
于是就有了如下的方法:
我这边又试了下,之前发的方式有些时候不行。下面是比较稳定的操作方法:
加载附件中的初始化宏。这个文件我有改动,在最开始加了个5秒的延时,最后加了擦除整个Flash的命令序列。
短接Flash的RESET引脚到地,重新上电;
进入仿真器自检界面,点击自检,在弹出第一个延时5秒时,断开RESET接地设置,然后进行后续的自检。执行初始化宏的最后操作时,会执行擦除Flash的命令。(如果能出现第1个5秒的延时,说明器件已经停下来了)。
执行完成后,重新上电,基本就能正常连接了。
可能在第3步操作时,成功的次数比较小,多试几次。我这边烧了这个随机数文件,出现无法停止的问题。最后是用此方法解决的。
正常烧时,请用原来的初始化文件。
上述方法其实有点复杂,并不需要在初始化宏中插入擦除序列。可以直接点击擦除按钮,要在第1个5秒延时,断开RESET引脚即可,因为此时芯片已经停下,后续就可以调用算法擦除芯片。
第一个5秒延时的作用是预留时间断开NorFlash的RESET引脚,恢复Flash正常工作。这样仿真器才能正常的执行后续的初始化序列和擦除算法。
这个方法的原理是先让Flash芯片一直处于复位状态,芯片不能读取代码,自然就不能从NorFlash启动。待芯片启动完成后,仿真器再去连接,此时就可以连接并停止内核,可以执行各种读取Memory的操作。在停止内核后,可以让Flash恢复正常工作,此时仿真器再去擦除Flash。
8-BIT FLASH 烧写驱动实例 - HY29F040
HY29F040是现代公司的一款8-BIT的NOR FLASH.在这个小节里,我们以这个芯片为例子,先容如何对8-BIT NOR FLASH进行操纵。
HY29F040的容量为512K-BYTE,总共包括8 个SECTOR,每个SECTOR 的容量是64K-BYTE.该芯片支持SECTOR擦除,整片擦除和以BYTE 为基本单位的写操纵.HY29F040的命令定义如表-1所示。
下面,我们来看看如何实现基本的擦除和编程操纵。在本节后面的描述中,我们使用了下面的2 个定义:
U32 sysbase; //该变量用来表示 FLASH 的起始地址
#define SysADDR8(sysbase, offset) ((volatile U8*)(sysbase)+(offset)) //用来方便对指定的 FALSH 地址进行操纵
先解释一下 SysAddr8 的定义。这个宏定义了一个 BYTE(8-BIT)指针,其地址为(sysbase + offset)。假设 FLASH 的起始地址为0x10000000,假如要将0xAB写到FLASH的第一个BYTE中往,可以用下面的代码:
*SysAddr8(0x10000000, 0x1) = 0xAB;
留意:
在本节后面的描述中,SYSBASE代表的是 FLASH的起始地址,而SysAddr8中的OFFSET则代表了相对于FLASH起始地址的BYTE偏移量.OFFSET也是8-BIT FLASH在自己的地址信号An-A0上看到的地址。
整片擦除操纵
整片擦除操纵共需要6个周期的总线写操纵
1 – 将 0xAA写到 FLASH 地址 0x5555
2 – 将 0x55 写到 FLASH 地址 0x2AAA
3 – 将 0x80 写到 FLASH 地址 0x5555
4 – 将 0xAA写到 FLASH 地址 0x5555
5 – 将 0x55 写到 FLASH 地址 0x2AAA
6 – 将 0x10 写到 FLASH 地址 0x5555
对应的代码:
*SysAddr8(sysbase, 0x5555) = 0xAA; //将值 0xAA写到 FLASH 地址 0x5555
*SysAddr8(sysbase, 0x2AAA) = 0x55; //将值 0x55 写到 FLASH 地址 0x2AAA
*SysAddr8(sysbase, 0x5555) = 0x80; //将值 0x80 写到 FLASH 地址 0x5555
*SysAddr8(sysbase, 0x5555) = 0xAA; //将值 0xAA写到 FLASH 地址 0x5555
*SysAddr8(sysbase, 0x2AAA) = 0x55; //将值 0x55 写到 FLASH 地址 0x2AAA
*SysAddr8(sysbase, 0x5555) = 0x10; //将值 0x10 写到 FLASH 地址 0x5555
SECTOR 擦除操纵
SECTOR的擦除操纵共需要6个周期的总线写操纵
1 – 将 0xAA写到 FLASH 地址 0x5555
2 – 将 0x55 写到 FLASH 地址 0x2AAA
3 – 将 0x80 写到 FLASH 地址 0x5555
4 – 将 0xAA写到 FLASH 地址 0x5555
5 – 将 0x55 写到 FLASH 地址 0x2AAA
6 – 将 0x30 写到要擦除的 SECTOR 对应的地址
对应的代码:
*SysAddr8(sysbase, 0x5555) = 0xAA; //将值 0xAA写到 FLASH 地址 0x5555
*SysAddr8(sysbase, 0x2AAA) = 0x55; //将值 0x55 写到 FLASH 地址 0x2AAA
*SysAddr8(sysbase, 0x5555) = 0x80; //将值 0x80 写到 FLASH 地址 0x5555
*SysAddr8(sysbase, 0x5555) = 0xAA; //将值 0xAA写到 FLASH 地址 0x5555
*SysAddr8(sysbase, 0x2AAA) = 0x55; //将值 0x55 写到 FLASH 地址 0x2AAA
*SysAddr8(sysbase, addr) = 0x30; //将值 0x30 写到要擦除的 SECTOR 对应的地址
BYTE 编程操纵
写一个BYTE 的数据到FLASH中往,需要 4个周期的总线写操纵
1 – 将 0xAA写到 FLASH 地址 0x5555
2 – 将 0x55 写到 FLASH 地址 0x2AAA
3 – 将 0xA0 写到 FLASH 地址 0x5555
4 – 将编程数据(BYTE)写到对应的编程地址上往
对应的代码:
*SysAddr8(sysbase, 0x5555) = 0xAA; //将值 0xAA写到 FLASH 地址 0x5555
*SysAddr8(sysbase, 0x2AAA) = 0x55; //将值 0x55 写到 FLASH 地址 0x2AAA
*SysAddr8(sysbase, 0x5555) = 0xA0; //将值 0xA0 写到 FLASH 地址 0x5555
*SysAddr8(sysbase, addr) = data; //将一个 BYTE的数据写到期看的地址
6. 16-BIT FLASH 烧写驱动实例 - SST39VF160
SST39VF160是SST公司的一款16-BIT的NOR FLASH. 在这个小节里, 我们以SST39VF160为例子, 先容如何对16-BIT NOR FLASH进行操纵。对8-BIT FLASH的操纵很好理解,但对16-BIT FLASH的操纵理解起来要晦涩很多。我尽力描述得清楚些。
SST39VF160的容量为2M-BYTE , 总共包括512个SECTOR, 每个SECTOR 的容量是4K-BYTE. 该芯片支持SECTOR擦除,整片擦除和以 HALF-WORD 为基本单位的写操纵.SST39VF160 的命令定义如表-2 所示。在表 2 中,由于所有命令都是从FLASH的角度来定义的。 所以, 所有的地址都是HALF-WORD地址, 指的是16-BIT FLASH在自己的地址信号An-A0上看到的地址。
在本节后面的描述中,我们使用了下面的2个定义:
U32 sysbase; //该变量用来表示 FLASH 的起始地址
#define SysAddr16(sysbase, offset) ((volatile U16*)(sysbase)+(offset)) //用来方便对指定的 FALSH 地址进行操纵
SysAddr16(sysbase, offset)首先定义了一个16-BIT HALF-WORD的指针,指针的地址为sysbase,然后根据offset做个偏移操纵。 由于HALF-WORD指针的地址是2个BYTE对齐的, 所以每个偏移操纵会使得地址加2. 终极, SysAddr16 (sysbase, offset)相当于定义了一个HALF-WORD的指针,其终极地址为(sysbase + 2offset) 。在使用SysAddr16 的时候,将sysbase设置成 FLASH 的起始地址,offset 则可以理解为相对于 FLASH 起始地址的 HALF-WORD 偏移量或是偏移地址。假设 FLASH 的起始地址为 0x10000000,SysAddr16(0x10000000, 0)指向 16-BIT FLASH 的第 0 个 HALF-WORD, SysAddr16(0x10000000, 1指向16-BIT FLASH的第1 个HALF-WORD.依次类推。假如要将0xABCD分别写到FLASH 的第0个和第 1个HALF-WORD 中往,可以用下面的代码:
*SysAddr16(0x10000000, 0x0) = 0xABCD;
*SysAddr16(0x10000000, 0x1) = 0xABCD;
接下来,我们分别从ARM处理器的角度和FLASH的角度来具体分析一下。
从 ARM 的角度来看:
假设 FLASH 的起始地址为 0x10000000,由于 ARM 处理器知道 FLASH 的地址空间为 0x10000000 ~ (0x10000000 +FLASH容量 – 1),所以在对这个地址空间进行访问的时候,会设置好FLASH的片选信号,并将低位的地址输出到 地址信号上。以*SysAddr16(0x10000000, 0x1) = 0xABCD 为例。从ARM 处理器的角度来看,该操纵是把0xABCD写到地址0x10000002上往。所以ARM处理器终极会在它的地址信号An-A0输出地址0x2,同时会在D15-D0 上输出0xABCD.
从 FLASH 的角度来看:
还是以 *SysAddr16(0x10000000, 0x1) = 0xABCD 为例,FLASH看到的地址是多少呢?接着分析.ARM 处理器在执行操纵的时候,会设置好相应的FLASH片选使能信号,并在ARM的地址信号An-A0上输出 0x2.由于 ARM和 16-BIT FLASH的地址信号的连接是错开一位的, 所以, FLASH终极在自己的地址An-A0上看到的信号是0x1, 相当于将ARM
处理器输出的地址往右做了一个移位操纵,恰好对应的是FLASH的第1 个HALF-WORD.同时,FLASH会在自己的D15-D0上看到数据0xABCD.
通过上面的分析,我们知道 SysAddr16 中指定的 offset 的值就是 16-BIT FLASH 在自己的地址 An-A0 上看到的值。所以,我们可以很方便的通过 SysAddr16(sysbase, offset) 对 FLASH 进行操纵,其中 sysbase 代表 FLASH 起始地址,offset 则代表了FLASH 的第几个HALF-WORD(HALF-WORD偏移量或偏移地址) 。
留意:
1. 在本节后面的描述中,SysAddr16中的 SYSBASE代表的是FLASH的起始地址,而SysAddr16中的 OFFSET则代表了相对于FLASH起始地址的 HALF-WORD 偏移量或偏移地址.OFFSET 的值也是16-BIT FLASH在自己的地址信号An-A0上看到的值。
2.在SST39VF160的命令定义中,所有的地址都是针对FLASH的HALF-WORD地址,指的是在FLASH自己的地址信号An-A0上看到的地址。
整片擦除操纵
整片擦除操纵共需要6个周期的总线写操纵
1 – 将 0x00AA写到 FLASH HALF-WORD 地址 0x5555
2 – 将 0x0055 写到 FLASH HALF-WORD地址 0x2AAA
3 – 将 0x0080 写到 FLASH HALF-WORD地址 0x5555
4 – 将 0x00AA写到 FLASH HALF-WORD 地址 0x5555
5 – 将 0x0055 写到 FLASH HALF-WORD地址 0x2AAA
6 – 将 0x0010 写到 FLASH HALF-WORD地址 0x5555
对应的代码:
*SysAddr16(sysbase, 0x5555) = 0x00AA; //将值 0x00AA 写到 FLASH HALF-WORD地址 0x5555
*SysAddr16(sysbase, 0x2AAA) = 0x0055; //将值 0x0055 写到 FLASH HALF-WORD地址 0x2AAA
*SysAddr16(sysbase, 0x5555) = 0x0080; //将值 0x0080 写到 FLASH HALF-WORD地址 0x5555
*SysAddr16(sysbase, 0x5555) = 0x00AA; //将值 0x00AA 写到 FLASH HALF-WORD地址 0x5555
*SysAddr16(sysbase, 0x2AAA) = 0x0055; //将值 0x0055 写到 FLASH HALF-WORD地址 0x2AAA
*SysAddr16(sysbase, 0x5555) = 0x0010; //将值 0x0010 写到 FLASH HALF-WORD地址 0x5555
SECTOR 擦除操纵
SECTOR的擦除操纵共需要6个周期的总线写操纵
1 – 将 0x00AA写到 FLASH HALF-WORD 地址 0x5555
2 – 将 0x0055 写到 FLASH HALF-WORD地址 0x2AAA
3 – 将 0x0080 写到 FLASH HALF-WORD地址 0x5555
4 – 将 0x00AA写到 FLASH HALF-WORD 地址 0x5555
5 – 将 0x0055 写到 FLASH HALF-WORD地址 0x2AAA
6 – 将 0x0030 写到要擦除的 SECTOR 对应的 HALF-WORD地址
对应的代码:
*SysAddr16(sysbase, 0x5555) = 0x00AA; //将值 0x00AA 写到 FLASH HALF-WORD地址 0x5555
*SysAddr16(sysbase, 0x2AAA) = 0x0055; //将值 0x0055 写到 FLASH HALF-WORD地址 0x2AAA
*SysAddr16(sysbase, 0x5555) = 0x0080; //将值 0x0080 写到 FLASH HALF-WORD地址 0x5555
*SysAddr16(sysbase, 0x5555) = 0x00AA; //将值 0x00AA 写到 FLASH HALF-WORD地址 0x5555
*SysAddr16(sysbase, 0x2AAA) = 0x0055; //将值 0x0055 写到 FLASH HALF-WORD地址 0x2AAA
*SysAddr16(sysbase, addr 》》 1) = 0x0030; //将值 0x0030 写到要擦除的 SECTOR 对应的
HALF-WORD地址
留意:
上面的代码中第6个操纵周期中的ADDR 是从ARM处理器的角度来看的BYTE地址,由于在擦除的时候,用户希看指定的是从 ARM 的角度看到的地址,这样更方便和更直观。而在 SysAddr16 的宏定义中,OFFSET 表示的是相对于FLASH起始地址的 HALF-WORD 偏移量,或是FLASH在自己的地址信号An-A0上看到的地址。所以需要执行一个右移操纵,把ADDR转换成 HALF-WORD 地址。
举例说明,SST39VF160 每个 SECTOR 的大小是 4K-BYTE.从 ARM 处器的角度和用户的角度来看,SECTOR-0 相对于FLASH起始地址的BYTE地址是0x0;从FLASH来看SECTOR-0 的HALF-WORD地址是0x0.从ARM处理器的角度和用户的角度来看, FLASH SECTOR-1相对于FLASH起始地址的BYTE地址0x1000; 从FLASH来看, SECTOR-1的HALF-WORD地址应该是(0x1000 》》 1) = 0x800.
假如要擦除SECTOR-0,上面代码的第6条指令应该是:
*SysAddr16(sysbase, 0x0 》》 1) = 0x0030;
假如要擦除SECTOR-1,上面代码的第6条指令应该是:
*SysAddr16(sysbase, 0x1000 》》 1) = 0x0030;
HALF-WORD 编程操纵
写一个HALF-WORD的数据到FLASH中往,需要4个周期的总线写操纵
1 – 将 0x00AA写到 FLASH HALF-WORD 地址 0x5555
2 – 将 0x0055 写到 FLASH HALF-WORD地址 0x2AAA
3 – 将 0x00A0 写到 FLASH HALF-WORD 地址 0x5555
4 – 将编程数据(HALF-WORD)写到对应的 HALF-WORD地址
对应的代码:
*SysAddr16(sysbase, 0x5555) = 0x00AA; //将值 0x00AA 写到 FLASH 地址 0x5555
*SysAddr16(sysbase, 0x2AAA) = 0x0055; //将值 0x0055 写到 FLASH 地址 0x2AAA
*SysAddr16(sysbase, 0x5555) = 0x00A0; //将值 0x00A0 写到 FLASH 地址 0x5555
*SysAddr16(sysbase, addr 》》 1) = data; //将数据写到对应的 HALF-WORD 地址
留意:
上面的代码中第4个操纵周期中的ADDR是从ARM处理器的角度来看的BYTE地址, 由于在执行写操纵的时候,用户希看指定的是从 ARM 的角度看到的地址,这样会更方便和更直观。而在 SysAddr16 的宏定义中,OFFSET表示的是相对于FLASH起始地址的HALF-WORD偏移量。 所以需要执行一个右移操纵, 把它转换成HALF-WORD
地址。
举例说明,假如要数据 0x0123 写到地址 0x0 往,对应的是 FLASH 的第 0 个 HAFL-WORD,对应的 HALF-WORD 地址应该是0x0,上面代码的第4条指令应该是:
*SysAddr16(sysbase, 0x0 》》 1) = 0x0123;
假如要数据0x4567写到地址0x2往, 对应的是FLASH的第1个 HALF-WORD, 对应的HALF-WORD地址应该是0x1, 上面代码的第4条指令应该是:
*SysAddr16(sysbase, 0x2 》》 1) = 0x4567;
假如要数据0x89AB写到地址0x4往, 对应的是FLASH的第2个HALF-WORD, 对应的HALF-WORD地址应该是0x2,上面代码的第4条指令应该是:
*SysAddr16(sysbase, 0x4 》》 1) = 0x89AB;
假如要数据 0xCDEF 写到地址 0x6 往,对应的是 FLASH 的第 3 个 HALF-WORD,对应的 HALF-WORD 地址应该是0x3,上面代码的第4条指令应该是:
*SysAddr16(sysbase, 0x6 》》 1) = 0xCDEF;
关于NOR Flash的相关介绍就到这了, 如有不足之处欢迎指正。
全部0条评论
快来发表一下你的评论吧 !