NORFLASH工作原理NorFlash具有像内存一样的接口,它可以像内存一样读,却不可以像内存一样写,NorFlash的写、擦除都需要发出特定的命令。谈到NorFlash通常就会涉及到CFI([CommonFlashInterface)接口,一般NorFlash都支持发命令来读取厂家ID和设备ID等基本信息,但并不是所有的NorFlash都支持发命令来获取和芯片本身容量大小、扇区数、擦除块大小等信息。为了让将来的NorFlash兼容性更好,引进了CFI接口,将芯片有关的信息都写入芯片内部,通过CFI命令就可以获取这些信息。Linux内核中对各种型号的NorFlash都有很好的支持,但是其组织复杂,不利于分析。这里选用u-boot里面的NorFlash代码来分析。代码位于:u-boot-2010.06/board/samsung/smdk2410/flash.c。通常内核里面要识别一个NorFlash有两种方法:一种是jedec探测,就是在内核里面事先定义一个数组,该数组里面放有不同厂家各个芯片的一些参数,探测的时候将flash的ID和数组里面的ID一一比较,如果发现相同的,就使用该数组的参数。另一种是cfi探测,就是直接发各种命令来读取芯片的信息,比如ID、容量等。jedec探测的优点就是简单,缺点是如果内核要支持的flash种类很多,这个数组就会很庞大。../samsung/smdk2410/flash.c文件采用的是第一种方法,但是还是有些区别的,内核里面用jedec探测一个芯片时,是先通过发命令来获取flash的ID,然后和数组比较,但是flash.c中连ID都是自己通过宏配置的。unsignedlongflash_init(void){for(i=0;iCONFIG_SYS_MAX_FLASH_BANKS;i++){ulongflashbase=0;//设置flash_id,这个标志保存厂家ID和设备IDflash_info[i].flash_id=#ifdefined(CONFIG_AMD_LV400)(AMD_MANUFACT&FLASH_VENDMASK)|(AMD_ID_LV400B&FLASH_TYPEMASK);#elifdefined(CONFIG_AMD_LV800)(AMD_MANUFACT&FLASH_VENDMASK)|(AMD_ID_LV800B&FLASH_TYPEMASK);#else#errorUnknownflashconfigured#endif//设置flash大小和扇区数flash_info[i].size=FLASH_BANK_SIZE;flash_info[i].sector_count=CONFIG_SYS_MAX_FLASH_SECT;//对于flash的每个扇区,都需要保存扇区的首地址for(j=0;jflash_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]);returnsize;//返回flash大小}flash_init()函数主要是做一些flash的初始化,比如设置flash的ID、大小、扇区数等来构造flash_info_t结构体,但是从上面的代码可以看出,在该初始化函数中并没有做任何与硬件有关的初始化,所有的值都是通过外部赋值,也就是说我们可以给这些成员变量赋任何我们想要的值,哪怕这些值并不是flash真正的参数,虽然这些值并不影响本函数的调用,但是和下面这些函数就有密切关系。intflash_erase(flash_info_t*info,ints_first,ints_last){//参看是否有写保护扇区,有直接返回错误prot=0;for(sect=s_first;sect=s_last;++sect){if(info-protect[sect])prot++;}if(prot)returnERR_PROTECTED;//关闭中断等,防止擦除过程被中断cflag=icache_status();icache_disable();iflag=disable_interrupts();/*Starteraseonunprotectedsectors*/for(sect=s_first;sect=s_last&&!ctrlc();sect++){printf(Erasingsector%2d...,sect);/*armsimple,noninterruptdependenttimer*/reset_timer_masked();if(info-protect[sect]==0)//此处的判断有点多余{/*notprotected*///取扇区的首地址vu_short*addr=(vu_short*)(info-start[sect]);//发解锁和擦除扇区命令MEM_FLASH_ADDR1=CMD_UNLOCK1;//往地址0x5551写入0xAAMEM_FLASH_ADDR2=CMD_UNLOCK2;//往地址0x2AA1写入0x55MEM_FLASH_ADDR1=CMD_ERASE_SETUP;//往地址0x5551写入0x80MEM_FLASH_ADDR1=CMD_UNLOCK1;MEM_FLASH_ADDR2=CMD_UNLOCK2;*addr=CMD_ERASE_CONFIRM;//往地址0x5551写入0x30/*waituntilflashisready*/chip=0;do{result=*addr;//读取该扇区首地址里面的值/*checktimeout*/if(get_timer_masked()CONFIG_SYS_FLASH_ERASE_TOUT){MEM_FLASH_ADDR1=CMD_READ_ARRAY;chip=TMO;break;}//BIT_ERASE_DONE=0x80,即判断DQ7是否为1if(!chip&&(result&0xFFFF)&BIT_ERASE_DONE)chip=READY;//BIT_PROGRAM_ERROR=0x20,即判断DQ5是否为1if(!chip&&(result&0xFFFF)&BIT_PROGRAM_ERROR)chip=ERR;}while(!chip);MEM_FLASH_ADDR1=CMD_READ_ARRAY;//往地址0x5551写入0xF0......printf(ok.n);}else{/*itwasprotected*/printf(protected!n);}}....../*allowflashtosettle-wait10ms*/udelay_masked(10000);returnrc;}对于擦除工作,不可能瞬间完成,如何检测芯片是否已经完成擦除工作是我们所关心的,当然你可以用一个很长的延时来确保芯片肯定已经完成擦除,但是一款芯片一定会提供相应的状态供我们检测,利用这些状态位可以减少程序中不必要的等待。下面这些描述是摘自芯片手册。SectorEraseCommandSequenceWhentheEmbeddedErasealgorithmiscomplete,thedevicereturnstoreadingarraydataandaddressesarenolongerlatched.ThesystemcandeterminethestatusoftheeraseoperationbyusingDQ7,DQ6,DQ2,orRY/BY#.(Referto“WriteOperationStatus”forinformationonthesestatusbits.)WRITEOPERATIONSTATUSThedeviceprovidesseveralbitstodeterminethestatusofawriteoperation:DQ2,DQ3,DQ5,DQ6,DQ7,andRY/BY#.Table10andthefollowingsubsectionsdescribethefunctionsofthesebits.DQ7,RY/BY#,andDQ6eachofferamethodfordeterminingwhetheraprogramoreraseoperationiscompleteorinprogress.Thesethreebitsarediscussedfirst.对于擦除过程:DuringtheEmbeddedErasealgorithm,Data#Pollingproducesa“0”onDQ7.WhentheEmbeddedErasealgorithmiscomplete,orifthedeviceenterstheEraseSuspendmode,Data#Pollingproducesa“1”onDQ7.Thisisanalogoustothecomplement/truedatumoutputdescribedfortheEmbeddedProgramalgorithm:theerasefunctionchangesallthebitsinasectorto“1”;priortothis,thedeviceoutputsthe“complement,”or“0.”ThesystemmustprovideanaddresswithinanyofthesectorsselectedforerasuretoreadvalidstatusinformationonDQ7.EmbeddedErasealgorithm是指”嵌入的擦除算法程序“,当我们发出擦除命令的时候NorFlash内部就会执行一系列指令来进行擦除工作,在这过程它通过检测Data=FF?(如上图所示)来判断擦除状态,但是这是NorFlash内部的判断方法,与之对应,外部的内存控制器可以通过Data#Polling来检测。WhenthesystemdetectsDQ7haschangedfromthecomplementtotruedata,itcanreadvaliddataatDQ7–DQ0onthefollowingreadcycles.ThisisbecauseDQ7maychangeasynchronouslywithDQ0–DQ6whileOutputEnable(OE#)isassertedlow.Figure19,Data#PollingTimings(DuringEmbeddedAlgorithms),inthe“ACCharacteristics”sectionillustratesthis.如果你实在不想花太多时间看手册,或对英文文档头疼,可以看看下面的总结。NorFlash提供几个数据位来确定一个写操作的状态,它们分别是:DQ2,DQ3,DQ5,DQ6,DQ7,andRY/BY#。其中DQ7,RY/BY#引脚,和DQ6中的每一个都提供了一种方法来判断一个编程或者擦除操作是否已经完成或正在进行中。实际编程中只需要使用其中的一种。DQ7:Data#Pollingbit,在编程过程从正在编程