本文主要是關(guān)于NOR flash的相關(guān)介紹,并著重對(duì)NOR flash的識(shí)別及其工作原理進(jìn)行了詳盡的闡述。
NOR flash工作原理
Nor Flash 具有像內(nèi)存一樣的接口,它可以像內(nèi)存一樣讀,卻不可以像內(nèi)存一樣寫(xiě),Nor Flash 的寫(xiě)、擦除都需要發(fā)出特定的命令。談到 Nor Flash 通常就會(huì)涉及到 CFI ([Common Flash Interface) 接口,一般 Nor Flash 都支持發(fā)命令來(lái)讀取廠家 ID 和 設(shè)備 ID 等基本信息,但并不是所有的 Nor
Flash 都支持發(fā)命令來(lái)獲取和芯片本身容量大小、扇區(qū)數(shù)、擦除塊大小等信息。為了讓將來(lái)的 Nor Flash 兼容性更好,引進(jìn)了 CFI 接口,將芯片有關(guān)的信息都寫(xiě)入芯片內(nèi)部,通過(guò) CFI 命令就可以獲取這些信息。
Linux 內(nèi)核中對(duì)各種型號(hào)的 Nor Flash 都有很好的支持 ,但是其組織復(fù)雜,不利于分析。這里選用 u-boot 里面的 Nor Flash 代碼來(lái)分析。代碼位于:u-boot-2010.06/board/samsung/smdk2410/flash.c 。
通常內(nèi)核里面要識(shí)別一個(gè) Nor Flash 有兩種方法:一種是 jedec 探測(cè),就是在內(nèi)核里面事先定義一個(gè)數(shù)組,該數(shù)組里面放有不同廠家各個(gè)芯片的一些參數(shù),探測(cè)的時(shí)候?qū)?flash 的 ID 和數(shù)組里面的 ID 一一比較,如果發(fā)現(xiàn)相同的,就使用該數(shù)組的參數(shù)。另一種是 cfi 探測(cè),就是直接發(fā)各種命令來(lái)讀取芯片的信息,比如 ID、容量等。jedec 探測(cè)的優(yōu)點(diǎn)就是簡(jiǎn)單,缺點(diǎn)是如果內(nèi)核要支持的 flash 種類(lèi)很多,這個(gè)數(shù)組就會(huì)很龐大。。。/samsung/smdk2410/flash.c 文件采用的是第一種方法,但是還是有些區(qū)別的,內(nèi)核里面用
jedec 探測(cè)一個(gè)芯片時(shí),是先通過(guò)發(fā)命令來(lái)獲取 flash 的 ID,然后和數(shù)組比較,但是 flash.c 中連 ID 都是自己通過(guò)宏配置的。
unsigned long flash_init (void)
{
for (i = 0; i 《 CONFIG_SYS_MAX_FLASH_BANKS; i++)
{
ulong flashbase = 0;
//設(shè)置 flash_id ,這個(gè)標(biāo)志保存廠家 ID 和 設(shè)備 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
//設(shè)置 flash 大小和扇區(qū)數(shù)
flash_info[i].size = FLASH_BANK_SIZE;
flash_info[i].sector_count = CONFIG_SYS_MAX_FLASH_SECT;
//對(duì)于 flash 的每個(gè)扇區(qū),都需要保存扇區(qū)的首地址
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 的總大小
}
//對(duì)代碼區(qū)的扇區(qū)設(shè)置寫(xiě)保護(hù),這里只是軟件的一種設(shè)定
flash_protect (FLAG_PROTECT_SET, CONFIG_SYS_FLASH_BASE,
CONFIG_SYS_FLASH_BASE + monitor_flash_len - 1,
&flash_info[0]);
//如果環(huán)境變量保存在 nor 里面,還需對(duì)這些扇區(qū)設(shè)置寫(xiě)保護(hù)
flash_protect (FLAG_PROTECT_SET, CONFIG_ENV_ADDR,
CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, &flash_info[0]);
return size; //返回 flash 大小
}
flash_init() 函數(shù)主要是做一些 flash 的初始化,比如設(shè)置 flash 的 ID、大小、扇區(qū)數(shù)等來(lái)構(gòu)造flash_info_t 結(jié)構(gòu)體,但是從上面的代碼可以看出,在該初始化函數(shù)中并沒(méi)有做任何與硬件有關(guān)的初始化,所有的值都是通過(guò)外部賦值,也就是說(shuō)我們可以給這些成員變量賦任何我們想要的值,哪怕這些值并不是
flash 真正的參數(shù),雖然這些值并不影響本函數(shù)的調(diào)用,但是和下面這些函數(shù)就有密切關(guān)系。
int flash_erase (flash_info_t * info, int s_first, int s_last)
{
//參看是否有寫(xiě)保護(hù)扇區(qū),有直接返回錯(cuò)誤
prot = 0;
for (sect = s_first; sect 《= s_last; ++sect)
{
if (info-》protect[sect])
prot++;
}
if (prot)
return ERR_PROTECTED;
//關(guān)閉中斷等,防止擦除過(guò)程被中斷
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) //此處的判斷有點(diǎn)多余
{ /* not protected */
//取扇區(qū)的首地址
vu_short *addr = (vu_short *) (info-》start[sect]);
//發(fā)解鎖和擦除扇區(qū)命令
MEM_FLASH_ADDR1 = CMD_UNLOCK1; //往地址 0x555《《1 寫(xiě)入 0xAA
MEM_FLASH_ADDR2 = CMD_UNLOCK2; //往地址 0x2AA《《1 寫(xiě)入 0x55
MEM_FLASH_ADDR1 = CMD_ERASE_SETUP;//往地址 0x555《《1 寫(xiě)入 0x80
MEM_FLASH_ADDR1 = CMD_UNLOCK1;
MEM_FLASH_ADDR2 = CMD_UNLOCK2;
*addr = CMD_ERASE_CONFIRM; //往地址 0x555《《1 寫(xiě)入 0x30
/* wait until flash is ready */
chip = 0;
do
{
result = *addr; //讀取該扇區(qū)首地址里面的值
/* 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 寫(xiě)入 0xF0
。。.。。.
printf (“ok.n”);
}
else
{ /* it was protected */
printf (“protected!n”);
}
}
。。.。。.
/* allow flash to settle - wait 10 ms */
udelay_masked (10000);
return rc;
}
如何自動(dòng)識(shí)別是NOR flash
Norflash啟動(dòng)時(shí):
4K的內(nèi)部SRAM都被映射到了0x40000000-0x40001000
Nandflash啟動(dòng)時(shí):
4K的內(nèi)部SRAM都被映射到了0x40000000,同時(shí)還被映射到了0x00000000-0x00001000
所有,我們可以通過(guò)判斷0x0000003c處的數(shù)據(jù)是否和0x4000003c處的數(shù)據(jù)相等來(lái)判斷是哪種啟動(dòng)方式。選擇這個(gè)地址是因?yàn)檫@個(gè)地址處的值固定是0xdeadbeef
3c = 60 = 4*15
.globl _start_start: b start_code ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq _undefined_instruction: .word undefined_instruction_software_interrupt: .word software_interrupt_prefetch_abort: .word prefetch_abort_data_abort: .word data_abort_not_used: .word not_used_irq: .word irq_fiq: .word fiq .balignl 16,0xdeadbeef
在start.S里的代碼如下
/***************** CHECK_BOOT_FLASH ******************************************/ ldr r1, =( (4《《28)|(3《《4)|(3《《2) ) /* address of Internal SRAM 0x4000003C*/ mov r0, #0 /* r0 = 0 */ str r0, [r1] mov r1, #0x3c /* address of men 0x0000003C*/ ldr r0, [r1] cmp r0, #0 bne relocate /* recovery */ ldr r0, =(0xdeadbeef) ldr r1, =( (4《《28)|(3《《4)|(3《《2) ) str r0, [r1]/***************** CHECK_BOOT_FLASH ******************************************/
Uboot里的流程:?jiǎn)?dòng)時(shí)先將0x4000003C處的數(shù)據(jù)清零,再讀出0x0000003C處地址的數(shù)據(jù),如果為0就意味著,這個(gè)地址是同時(shí)映射在0x4000003C處和0x0000003C處即為nandflash啟動(dòng),不相等則為norflash啟動(dòng)。
但是最后有一點(diǎn)很重要:如果是Nand flash啟動(dòng),必須要復(fù)原清零的數(shù)據(jù)。原因是:在nand boot過(guò)后,會(huì)核對(duì)內(nèi)部SRAM中的4K程序,和從Nand中拷貝到SDRAM的前4K程序是否一致,如果不一致會(huì)進(jìn)入死循環(huán)。
nor flash是如何讀寫(xiě)數(shù)據(jù)的
(1) Nor Flash工作模式
Nor Flash上電后處于數(shù)據(jù)讀取狀態(tài)(Reading Array Data)。此狀態(tài)可以進(jìn)行正常的讀。這和讀取SDRAM/SRAM/ROM一樣。(要是不一樣的話,芯片上電后如何從Nor Flash中讀取啟動(dòng)代碼。~)
一般再對(duì)Flash進(jìn)行操作前都要讀取芯片信息比如設(shè)備ID號(hào)。這樣做的主要目的是為了判斷自己寫(xiě)的程序是否支持該設(shè)備。 Nor Flash支持2種方式獲取ID號(hào)。一種是編程器所用的方法需要高電壓(11.5V-12.5V)。另一種方法就是所謂的in-system方法,就是在系統(tǒng)中通過(guò)Nor Flash的命令寄存器來(lái)完成。本文中只對(duì)in-system方法進(jìn)行說(shuō)明。此時(shí)需要切換到自動(dòng)選擇(Autoselect Command),這要通過(guò)發(fā)送命令來(lái)完成。命令類(lèi)型見(jiàn)下圖。注意:
進(jìn)入自動(dòng)選擇(Autoselect Command)模式后需要發(fā)送復(fù)位命令才能回到數(shù)據(jù)讀取狀態(tài)(Reading Array Data)。
在完成信息獲取后一般就要擦除數(shù)據(jù)。 Nor Flash支持扇區(qū)擦(Sector Erase)除和整片擦除(Chip Erase)。這2種模式都有對(duì)應(yīng)的命令序列。在完成擦除命令后會(huì)自動(dòng)返回到數(shù)據(jù)讀?。≧eading Array Data)狀態(tài)。在返回前可查詢編程的狀態(tài)。
完成擦除后就需要對(duì)芯片進(jìn)行寫(xiě)入操作也就是編程。這就需要進(jìn)入編程(Program)狀態(tài)。在完成編程命令后會(huì)自動(dòng)返回到數(shù)據(jù)讀?。≧eading Array Data)狀態(tài)。在返回前可查詢編程的狀態(tài)。注意:編程前一定要先擦除。因?yàn)榫幊讨荒軐ⅰ?’改寫(xiě)為’0’,通過(guò)擦寫(xiě)可以將數(shù)據(jù)全部擦寫(xiě)為‘1’。
(2)Nor Flash 硬件連接
1. 引腳47 為BYTE# :當(dāng)其為高電平時(shí)數(shù)據(jù)輸出為16bit模式(此時(shí)地址線為A19:A0)。低電平為8bit模式。 (此時(shí)地址線為A19:A1)上圖中Pin47加VCC選用的是16bit模式有效地址線為A19:A0.
2. 對(duì)于16bit模式要16bit對(duì)齊因此S3C2440A的LADDR1要與A0連接。此時(shí)要注意的是NorFlash片內(nèi)地址0x555對(duì)應(yīng)S3C2440A的地址為baseaddr+0x555*2;其中baseaddr與NorFlash映射的地址有關(guān)。一般NorFlash放在Bank0.所以baseaddr=0,但是開(kāi)啟mmu后baseaddr=地址0映射到的新地址。0x555*2的原因是LADDR1與A0連接。也就是0x555表示片內(nèi)第0x555個(gè)word(16bit)。
3. 引腳15為RYBY#輸出引腳。用于輸出Ready與Busy信號(hào)。實(shí)際用時(shí)可以不接??捎妹畈樵僋orFlash狀態(tài)代替。
( 3) Nor Flash 模式編程
1. 讀ID
2. 扇區(qū)擦除(Sector Erase)
扇區(qū)擦除命令序列的每個(gè)周期均為寫(xiě)周期。
3. 編程(Program)
4. 寫(xiě)操作狀態(tài)(WRITE OPERATION STATUS)
Nor Flash 提供幾個(gè)數(shù)據(jù)位來(lái)確定一個(gè)寫(xiě)操作的狀態(tài),它們分別是: DQ2, DQ3, DQ5, DQ6,DQ7, and RY/BY#. 如上圖所示。其中DQ7, RY/BY#引腳, 和 DQ6 中的每一個(gè)都提供了一種方法來(lái)判斷一個(gè)編程或者擦除操作是否已經(jīng)完成或正在進(jìn)行中。實(shí)際編程中只需要使用其中的一種。
DQ7:Data# Polling bit,DQ7在編程時(shí)的狀態(tài)變化。
在編程過(guò)程中從正在編程的地址中讀出的數(shù)據(jù)的DQ7為要寫(xiě)入數(shù)據(jù)的補(bǔ)碼。比如寫(xiě)入的數(shù)據(jù)為0x0000,及輸入的DQ7為‘0’,則在編程中讀出的數(shù)據(jù)為‘1’;當(dāng)編程完成時(shí)讀出的數(shù)據(jù)又變回輸入的數(shù)據(jù)即’0’。在擦除過(guò)程中DQ7輸出為’0’;擦除完成后輸出為’1’;注意讀取的地址必須是擦除范圍內(nèi)的地RY/BY#:高電平表示‘就緒’,低電平表示‘忙’。
DQ6:輪轉(zhuǎn)位1(Toggle Bit 1)。
編程和擦除期間,讀任意地址都會(huì)導(dǎo)致DQ6的輪轉(zhuǎn)(0,1間相互變換)。。當(dāng)操作完成后,DQ6停止轉(zhuǎn)換。
DQ2:輪轉(zhuǎn)位2(Toggle Bit 2)。當(dāng)某個(gè)扇區(qū)被選中擦除時(shí),讀有效地址(地址都在擦除的扇區(qū)范圍內(nèi))會(huì)導(dǎo)致DQ2的輪轉(zhuǎn)。
注意:DQ2只能判斷一個(gè)特定的扇區(qū)是否被選中擦除。但不能區(qū)分這個(gè)快是否正在擦除中或者正處于擦除暫停狀態(tài)。相比之下,DQ6可以區(qū)分NorFlash是否處于擦除中或者擦除狀態(tài),但不能區(qū)分哪個(gè)快被選中擦除。因此需要這2個(gè)位來(lái)確定扇區(qū)和模式狀態(tài)信息。
DQ5: 超時(shí)位(Exceeded Timing Limits),當(dāng)編程或擦除操作超過(guò)了一個(gè)特定內(nèi)部脈沖計(jì)數(shù)是DQ5=1;這表明操作失敗。當(dāng)編程時(shí)把’0’改為’1’就會(huì)導(dǎo)致DQ5=1,因?yàn)橹挥胁脸磷霾拍馨选?’改為’1’。當(dāng)錯(cuò)誤發(fā)生后需要執(zhí)行復(fù)位命令(見(jiàn)圖1-1)才能返回到讀數(shù)據(jù)狀態(tài)。
DQ3: (扇區(qū)擦除計(jì)時(shí)位)Sector Erase Timer,只在扇區(qū)擦除指令時(shí)起作用。當(dāng)擦除指令真正開(kāi)始工作是DQ3=1,此時(shí)輸入的命令(除擦除暫停命令外)都被忽略。DQ3=0,是可以添加附加的扇區(qū)用于多扇區(qū)擦除
結(jié)語(yǔ)
關(guān)于NOR flash的相關(guān)介紹就到這了,如有不足之處歡迎指正。
-
存儲(chǔ)器
+關(guān)注
關(guān)注
38文章
7434瀏覽量
163519 -
NOR flash
+關(guān)注
關(guān)注
2文章
89瀏覽量
22967
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論