Nor Flash工作原理
http://blog.chinaunix.net/uid-26876150-id-3723678.html
Nor Flash 具有像内存一样的接口,它可以像内存一样读,却不可以像内存一样写,Nor Flash 的写、擦除都需要发出特定的命令。谈到 Nor Flash 通常就会涉及到 CFI ([Common Flash Interface) 接口,一般 Nor Flash 都支持发命令来读取厂家 ID 和 设备 ID 等基本信息,但并不是所有的 Nor Flash 都支持发命令来获取和芯片本身容量大小、扇区数、擦除块大小等信息。为了让将来的 Nor Flash 兼容性更好,引进了 CFI 接口,将芯片有关的信息都写入芯片内部,通过 CFI 命令就可以获取这些信息。
Linux 内核中对各种型号的 Nor Flash 都有很好的支持 ,但是其组织复杂,不利于分析。这里选用 u-boot 里面的 Nor Flash 代码来分析。代码位于:u-boot-2010.06/board/samsung/smdk2410/flash.c 。
通常内核里面要识别一个 Nor Flash 有两种方法:一种是 jedec 探测,就是在内核里面事先定义一个数组,该数组里面放有不同厂家各个芯片的一些参数,探测的时候将 flash 的 ID 和数组里面的 ID 一一比较,如果发现相同的,就使用该数组的参数。另一种是 cfi 探测,就是直接发各种命令来读取芯片的信息,比如 ID、容量等。jedec 探测的优点就是简单,缺点是如果内核要支持的 flash 种类很多,这个数组就会很庞大。../samsung/smdk2410/flash.c 文件采用的是第一种方法,但是还是有些区别的,内核里面用 jedec 探测一个芯片时,是先通过发命令来获取 flash 的 ID,然后和数组比较,但是 flash.c 中连 ID 都是自己通过宏配置的。
unsigned long flash_init (void)
{
for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++)
{
ulong flashbase = 0;
//设置 flash_id ,这个标志保存厂家 ID 和 设备 ID
flash_info[i].flash_id =
#if defined(CONFIG_AMD_LV400)
(AMD_MANUFACT & FLASH_VENDMASK) | (AMD_ID_LV400B & FLASH_TYPEMASK);
#elif defined(CONFIG_AMD_LV800)
(AMD_MANUFACT & FLASH_VENDMASK) | (AMD_ID_LV800B & FLASH_TYPEMASK);
#else
#error "Unknown flash configured"
#endif
//设置 flash 大小和扇区数
flash_info[i].size = FLASH_BANK_SIZE;
flash_info[i].sector_count = CONFIG_SYS_MAX_FLASH_SECT;
//对于 flash 的每个扇区,都需要保存扇区的首地址
for (j = 0; j < flash_info[i].sector_count; j++)
{
......
flash_info[i].start[j] = flashbase + (j - 3) * MAIN_SECT_SIZE;
}
size += flash_info[i].size; //片外所有flash 的总大小
}
//对代码区的扇区设置写保护,这里只是软件的一种设定
flash_protect (FLAG_PROTECT_SET, CONFIG_SYS_FLASH_BASE,
CONFIG_SYS_FLASH_BASE + monitor_flash_len - 1,
&flash_info[0]);
//如果环境变量保存在 nor 里面,还需对这些扇区设置写保护
flash_protect (FLAG_PROTECT_SET, CONFIG_ENV_ADDR,
CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, &flash_info[0]);
return size; //返回 flash 大小
}
flash_init() 函数主要是做一些 flash 的初始化,比如设置 flash 的 ID、大小、扇区数等来构造 flash_info_t 结构体,但是从上面的代码可以看出,在该初始化函数中并没有做任何与硬件有关的初始化,所有的值都是通过外部赋值,也就是说我们可以给这些成员变量赋任何我们想要的值,哪怕这些值并不是 flash 真正的参数,虽然这些值并不影响本函数的调用,但是和下面这些函数就有密切关系。
int flash_erase (flash_info_t * info, int s_first, int s_last)
{
//参看是否有写保护扇区,有直接返回错误
prot = 0;
for (sect = s_first; sect <= s_last; ++sect)
{
if (info->protect[sect])
prot++;
}
if (prot)
return ERR_PROTECTED;
//关闭中断等,防止擦除过程被中断
cflag = icache_status ();
icache_disable ();
iflag = disable_interrupts ();
/* Start erase on unprotected sectors */
for (sect = s_first; sect <= s_last && !ctrlc (); sect++)
{
printf ("Erasing sector %2d ... ", sect);
/* arm simple, non interrupt dependent timer */
reset_timer_masked ();
if (info->protect[sect] == 0) //此处的判断有点多余
{ /* not protected */
//取扇区的首地址
vu_short *addr = (vu_short *) (info->start[sect]);
//发解锁和擦除扇区命令
MEM_FLASH_ADDR1 = CMD_UNLOCK1; //往地址 0x555<<1 写入 0xAA
MEM_FLASH_ADDR2 = CMD_UNLOCK2; //往地址 0x2AA<<1 写入 0x55
MEM_FLASH_ADDR1 = CMD_ERASE_SETUP;//往地址 0x555<<1 写入 0x80
MEM_FLASH_ADDR1 = CMD_UNLOCK1;
MEM_FLASH_ADDR2 = CMD_UNLOCK2;
*addr = CMD_ERASE_CONFIRM; //往地址 0x555<<1 写入 0x30
/* wait until flash is ready */
chip = 0;
do
{
result = *addr; //读取该扇区首地址里面的值
/* check timeout */
if (get_timer_masked () > CONFIG_SYS_FLASH_ERASE_TOUT)
{
MEM_FLASH_ADDR1 = CMD_READ_ARRAY;
chip = TMO;
break;
}
//BIT_ERASE_DONE = 0x80,即判断 DQ7 是否为 1
if (!chip && (result & 0xFFFF) & BIT_ERASE_DONE)
chip = READY;
//BIT_PROGRAM_ERROR = 0x20,即判断 DQ5 是否为 1
if (!chip && (result & 0xFFFF) & BIT_PROGRAM_ERROR)
chip = ERR;
} while (!chip);
MEM_FLASH_ADDR1 = CMD_READ_ARRAY; //往地址 0x555<<1 写入 0xF0
......
printf ("ok.\n");
}
else
{ /* it was protected */
printf ("protected!\n");
}
}
......
/* allow flash to settle - wait 10 ms */
udelay_masked (10000);
return rc;
}
对于擦除工作,不可能瞬间完成,如何检测芯片是否已经完成擦除工作是我们所关心的,当然你可以用一个很长的延时来确保芯片肯定已经完成擦除,但是一款芯片一定会提供相应的状态供我们检测,利用这些状态位可以减少程序中不必要的等待。下面这些描述是摘自芯片手册。
Sector Erase Command Sequence
When the Embedded Erase algorithm is complete, the device returns to reading array data and addresses are no longer latched. The system can determine the status of the erase operation by using DQ7, DQ6, DQ2, or RY/BY#. (Refer to “Write Operation Status” for information on these status bits.)
WRITE OPERATION STATUS
The device provides several bits to determine the status of a write operation: DQ2, DQ3, DQ5, DQ6,DQ7, and RY/BY#. Table 10 and the following subsections describe the functions of these bits. DQ7, RY/BY#, and DQ6 each offer a method for determining whether a program or erase operation is complete or in progress. These three bits are discussed first.
对于擦除过程:
During the Embedded Erase algorithm, Data# Polling produces a “0” on DQ7. When the Embedded Erase algorithm is complete, or if the device enters the Erase Suspend mode, Data# Polling produces a “1” on DQ7. This is analogous to the complement/true datum output described for the Embedded Program algorithm: the erase function changes all the bits in a sector to “1”; prior to this, the device outputs the “complement,” or“0.” The system must provide an address within any of the sectors selected for erasure to read valid status information on DQ7.
Embedded Erase algorithm 是指”嵌入的擦除算法程序“,当我们发出擦除命令的时候 Nor Flash 内部就会执行一系列指令来进行擦除工作,在这过程它通过检测 Data = FF?(如上图所示)来判断擦除状态,但是这是 Nor Flash 内部的判断方法,与之对应,外部的内存控制器可以通过Data# Polling 来检测 。
When the system detects DQ7 has changed from the complement to true data, it can read valid data at DQ7–DQ0 on the following read cycles. This is because DQ7 may change asynchronously with DQ0–DQ6 while Output Enable (OE#) is asserted low. Figure 19, Data#Polling Timings (During Embedded Algorithms), in the“AC Characteristics” section illustrates this.
如果你实在不想花太多时间看手册,或对英文文档头疼,可以看看下面的总结。
Nor Flash 提供几个数据位来确定一个写操作的状态,它们分别是: DQ2, DQ3, DQ5, DQ6,DQ7, and RY/BY# 。其中DQ7, RY/BY#引脚, 和 DQ6 中的每一个都提供了一种方法来判断一个编程或者擦除操作是否已经完成或正在进行中。实际编程中只需要使用其中的一种。
DQ7:Data# Polling bit,在编程过程从正在编程的地址中读出的数据的DQ7为要写入数据位的补码。比如写入的数据为0x0000,即输入的DQ7为 0 ,则在编程中读出的数据为 1 ;当编程完成时读出的数据又变回输入的数据 0 。在擦除过程中DQ7输出为 0 ;擦除完成后输出为 1 ;注意读取的地址必须是擦除范围内的地址。RY/BY#:高电平表示‘就绪’,低电平表示‘忙’。
DQ6:轮转位1(Toggle Bit 1),在编程和擦除期间,读任意地址都会导致DQ6的轮转(0,1间相互变换)。当操作完成后,DQ6停止转换。
DQ2:轮转位2(Toggle Bit 2),当某个扇区被选中擦除时,读有效地址(地址都在擦除的扇区范围内)会导致DQ2的轮转。
注意:DQ2只能判断一个特定的扇区是否被选中擦除,但不能区分这个扇区是否正在擦除中或者正处于擦除暂停状态。相比之下,DQ6可以区分Nor Flash是否处于擦除中或者擦除暂停状态,但不能区分哪个扇区被选中擦除,因此需要这2个位来确定扇区和模式状态信息。
DQ5: 超时位(Exceeded Timing Limits) ,当编程或擦除操作超过了一个特定内部脉冲计数时 DQ5=1,表明操作失败。当编程时把 0 改为 1 就会导致 DQ5=1,因为只有擦除擦做才能把 0 改为 1。当错误发生后需要执行复位命令才能返回到读数据状态。
DQ3: (扇区擦除计时位)Sector Erase Timer ,只在扇区擦除指令时起作用。当擦除指令真正开始工作时 DQ3=1 ,此时输入的命令(除擦除暂停命令外)都被忽略,DQ3=0 时,可以添加附加的扇区用于多扇区擦除。
看了上面的分析就知道为什么在 flash_erase() 函数中当发出擦除命令后要检测 DQ7 是否为 1,if (!chip && (result & 0xFFFF) & BIT_ERASE_DONE) 。因为擦除过程用到了扇区的首地址,所以在 flash_init() 函数中就不能随便设置这些值,要保持和实际的 flash 一致。
/* Copy memory to flash. */
int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
{
ulong cp, wp;
int l;
int i, rc;
ushort data;
//保持16位地址对齐
wp = (addr & ~1); /* get lower word aligned address */
/* handle unaligned start bytes */
if ((l = addr - wp) != 0)
{
data = 0;
for (i = 0, cp = wp; i < l; ++i, ++cp)
data = (data >> 8) | (*(uchar *) cp << 8);
for (; i < 2 && cnt > 0; ++i)
{
data = (data >> 8) | (*src++ << 8);
--cnt;
++cp;
}
//条件不成立,此语句不起作用
for (; cnt == 0 && i < 2; ++i, ++cp)
data = (data >> 8) | (*(uchar *) cp << 8);
if ((rc = write_hword (info, wp, data)) != 0)
return (rc);
wp += 2;
}
/* handle word aligned part */
while (cnt >= 2)
{
data = *((vu_short *) src);
if ((rc = write_hword (info, wp, data)) != 0)
return (rc);
src += 2;
wp += 2;
cnt -= 2;
}
if (cnt == 0)
return ERR_OK;
/* handle unaligned tail bytes */
data = 0;
for (i = 0, cp = wp; i < 2 && cnt > 0; ++i, ++cp)
{
data = (data >> 8) | (*src++ << 8);
--cnt;
}
//条件不成立,此语句不起作用
for (; i < 2; ++i, ++cp)
data = (data >> 8) | (*(uchar *) cp << 8);
return write_hword (info, wp, data);
}
write_buff() 函数拷贝内存里面的数据到 Nor Flash ,这个函数首先要保证16位地址对齐,由 /* handle unaligned start bytes */ 包含的语句处理,虽然写得比较复杂,但是做得事情很简单。假如要往 flash 地址 3 处(这里的地址 3 是相对于 ARM 而言)开始写一段数据,先把地址 2 处(这里的地址 2 是相对于 ARM 而言)的数据读出,和要写入地址 3 的数据组成一个16为的数据,写入地址 2 处,这样地址 2 里面的数据不变,地址 3 处就是写入的数据。
注意:这里的地址都是站在 ARM 角度说的,对于 flash 地址 2 对于它的地址 1,这里面涉及到地址线的对齐关系。
write_buff() 函数里面是调用 write_hword() 来实现底层的写操作。
static int write_hword (flash_info_t * info, ulong dest, ushort data)
{
vu_short *addr = (vu_short *) dest;
ushort result;
int rc = ERR_OK;
int cflag, iflag;
int chip;
/* Check if Flash is (sufficiently) erased */
result = *addr;
if ((result & data) != data)
return ERR_NOT_ERASED;
/*
* Disable interrupts which might cause a timeout
* here. Remember that our exception vectors are
* at address 0 in the flash, and we don't want a
* (ticker) exception to happen while the flash
* chip is in programming mode.
*/
cflag = icache_status ();
icache_disable ();
iflag = disable_interrupts ();
MEM_FLASH_ADDR1 = CMD_UNLOCK1; //往地址 0x555<<1 写入 0xAA
MEM_FLASH_ADDR2 = CMD_UNLOCK2; //往地址 0x2AA<<1 写入 0x55
MEM_FLASH_ADDR1 = CMD_UNLOCK_BYPASS; //往地址 0x555<<1 写入 0x20
*addr = CMD_PROGRAM; //往地址 0x555<<1 写入 0xA0 编程
*addr = data; //往地址 addr 写入数据
/* arm simple, non interrupt dependent timer */
reset_timer_masked ();
/* wait until flash is ready */
chip = 0;
do
{
result = *addr;
/* check timeout */
if (get_timer_masked () > CONFIG_SYS_FLASH_ERASE_TOUT)
{
chip = ERR | TMO;
break;
}
if (!chip && ((result & 0x80) == (data & 0x80)))
chip = READY;
if (!chip && ((result & 0xFFFF) & BIT_PROGRAM_ERROR))
{
result = *addr;
if ((result & 0x80) == (data & 0x80))
chip = READY;
else
chip = ERR;
}
} while (!chip);
*addr = CMD_READ_ARRAY; //往地址 0x555<<1 写入 0xF0 复位
......
return rc;
}
写入数据后通过判断 DQ7 是否和写入的一致来确认编程操作结束,对应的代码是 if (!chip && ((result & 0x80) == (data & 0x80))) 。
During the Embedded Program algorithm, the device outputs on DQ7 the complement of the datum programmed to DQ7. This DQ7 status also applies to programming during Erase Suspend. When the Embedded Program algorithm is complete, the device outputs the datum programmed to DQ7. The system
must provide the program address to read valid status information on DQ7. If a program address falls within a protected sector, Data# Polling on DQ7 is active for
approximately 1 μs, then the device returns to reading array data.
关于Unlock Bypass的相关概念和操作可以参考手册里面的介绍,简单来讲,Unlock Bypass 就是忽略解锁的意思,因为每次编程 flash 都要发出解锁命令,再发出编程命令 0xA0,最后才写入16位数据,设想如果写入的数据量很大,每次写入一个 word(16位) 都要重新解锁一下,效率很低。Unlock Bypass 允许我们写入数据的时候忽略解锁命令,直接发出 0xA0 命令后再往相应地址写入数据。
Unlock Bypass Command Sequence
The unlock bypass feature allows the system to program bytes or words to the device faster than using the standard program command sequence. The unlock bypass
command sequence is initiated by first writing two unlock cycles. This is followed by a third write cycle containing the unlock bypass command, 20h. The device then enters the unlock bypass mode. A two-cycle unlock bypass program command sequence is all that is required to program in this mode. The first cycle in this sequence contains the unlock bypass program command, A0h; the second cycle contains the program address and data. Additional data is programmed in the same manner. This mode dispenses with the initial two unlock cycles required in the standard program command sequence, resulting in faster total programming time. Table 9 shows the requirements for the command sequence.
Nor Flash工作原理的更多相关文章
- mini2440 Nor Flash工作原理分析
我的mini2440上是只接了一块Nor Flash,型号是S29AL016M90TAI02,这是一块2M Byte,16位宽度的Nor Flash,用于引导扇区的闪存.原理图里面关键的引脚是: 地址 ...
- Exadata Smart Flash Logging工作原理
Exadata在V2时代,ORACLE为了进一步拓宽客户人群,除了宣称Exadata适用OLAP系统,同时也适用于OLTP系统,那怎么才能满足OLTP系统的高IOPS要求呢?于是Exadata引入了闪 ...
- Nand Flash 控制器工作原理
对 Nand Flash 存储芯片进行操作, 必须通过 Nand Flash 控制器的专用寄存器才能完成.所以,不能对 Nand Flash 进行总线操作.而 Nand Flash 的写操作也必须块方 ...
- SPI协议及工作原理分析
说明.文章摘自:SPI协议及其工作原理分析 http://blog.csdn.net/skyflying2012/article/details/11710801 一.概述. SPI, Serial ...
- Android系统Recovery工作原理之使用update.zip升级过程分析(一)
通过分析update.zip包在具体Android系统升级的过程,来理解Android系统中Recovery模式服务的工作原理.我们先从update.zip包的制作开始,然后是Android系统的启动 ...
- Java Web程序工作原理
Web开发的最重要的基本功能是HTTP:Java Web开发的最重要的基本功是Servlet Specification.HTTP和Servlet Specitication对于Web Server和 ...
- camera理论基础和工作原理
写在前面的话,本文是因为工作中需要编写摄像头程序,因为之前没有做过这类产品,所以网上搜索的资料,先整理如下,主要参考文章如下,如果有侵权,请联系我:另外,转载请注明出处.本文不一定全部正确,如果发现错 ...
- web基础-web工作原理,http协议,浏览器缓存
1,web工作原理 2,http协议 3,浏览器缓存 4,cookie和session -------------------------------------------------------- ...
- Android系统Recovery工作原理之使用update.zip升级过程---updater-script脚本语法简介以及执行流程(转)
目前update-script脚本格式是edify,其与amend有何区别,暂不讨论,我们只分析其中主要的语法,以及脚本的流程控制. 一.update-script脚本语法简介: 我们顺着所生成的脚本 ...
随机推荐
- HDU - 5126 stars (CDQ分治)
题目链接 题目大意:一共有Q(1<=Q<=50000)组操作,操作分为两种: 1.在x,y,z处添加一颗星星 2.询问以(x1,y1,z1)与(x2,y2,z2)为左上和右下顶点的矩形之间 ...
- Redis底层探秘(一):简单动态字符串(SDS)
redis是我们使用非常多的一种缓存技术,他的性能极高,读的速度是110000次/s,写的速度是81000次/s.这么高的性能背后,到底是怎么样的实现在支撑,这个系列的文章,我们一起去看看. redi ...
- vue项目错误集
1.报错:vue.esm.js?efeb:591 [Vue warn]: Avoid using non-primitive value as key, use string/number value ...
- mysql之 [ERROR] InnoDB: Unable to lock ./ibdata1, error: 11
问题描述:启动MySQL后,出现连接不上,报 [ERROR] InnoDB: Unable to lock ./ibdata1, error: 11[root@mysql01 ~]# service ...
- bae3.0第一步 添加框架支持
1.克隆bae上应用代码: 先在本地linux机器上创建文件夹bae并进入, 再执行git clone https://git.duapp.com/appidd01iud80bg 结果会在bae文件夹 ...
- MQTT事件回调流程
TLS 如下强调: 1.每个IOT设备应该有一对独有的公钥/私钥 2.SERVER的认证通过SERVER的"root certificate" SSL产生过程: $ openssl ...
- ucos ii 百度官方介绍
μC/OS II(Micro-Controller Operating System Two)是一个可以基于ROM运行的.可裁剪的.抢占式.实时多任务内核,具有高度可移植性,特别适合于微处 ...
- JAVA生成Word文档(经过测试)
首先告诉大家这篇文章的原始出处:http://www.havenliu.com/java/514.html/comment-page-1#comment-756 我也是根据他所描述完成的,但是有一些地 ...
- $route路由
<!DOCTYPE html><html ng-app="AngularApp"> <head> <meta charset=" ...
- mybatis 学习六 MyBatis主配置文件
在定义sqlSessionFactory时需要指定MyBatis主配置文件: <bean id="sqlSessionFactory" class="org.myb ...