Flash基礎知識以及常用指令
- 背景知識
最近在做的項目中,有個需要支持遠程升級固件的需求。大體架構就是通過上位機把需更新的固件下發到FPGA中,然後通過FPGA寫入用來存放固件的Flash裏。調試了一段時間,總算實現了這一功能,在實現的過程中,網上前輩們的分享幫了我很大的忙,所以作爲回饋,我也把實現過程中相關的知識點記錄下來與大家分享,可能有說得不對的地方,歡迎指正。打算分三篇文章來講述。
- Flash的簡單分類
flash閃存是非易失存儲器,可以對稱爲塊的存儲器單元塊進行擦寫和再編程。flash按照內部存儲結構不同,大體上可以分爲兩種:nor flash和nand flash。nor flash數據線和地址線分開,可以實現ram一樣的隨機尋址功能,可以讀取任何一個字節。但是擦除仍要按塊來擦。nand flash同樣是按塊擦除,但是數據線和地址線複用,不能利用地址線隨機尋址。讀取只能按頁來讀取。NOR Flash的讀取,用戶可以直接運行裝載在NOR FLASH裏面的代碼。NAND Flash沒有采取內存RAM的隨機讀取技術,它的讀取是以一次讀取一塊的形式來進行的,通常是一次讀取512個字節,採用這種技術的Flash比較廉價。用戶不能直接運行NAND Flash上的代碼,因此好多使用NAND Flash的開發板除了使用NAND Flah以外,還作上了一塊小的NOR Flash來運行啓動代碼。
現在有些芯片爲了減少啓動引腳,可以採用QSPI serial flash作爲啓動存儲器。其採用spi/qspi 的方式進行串行的讀取數據,減小引腳消耗。這個只是通信方式的改變,其內部結構一般還是nor flash或者nand flash。這個我們就不去深究了。
- Flash常用指令以及時序關係
Flash常用的指令操作有:讀ID、擦除(有三種方式)、頁編程、讀操作(讀寄存器、讀數據)、寫操作(設置寄存器)等。一般來說,Flash的地址是24bit,最大隻能支持16MB,面對更大容量的Flash,需要將地址配置爲32bit,也就是需要將flash設置成4-byte模式。
下面以微邦的一款flash芯片W25Q128手冊爲例,描述一下上述的主要操作指令。不同廠家的芯片可能指令碼可能會不大一樣,注意區別。涉及到的時序,以標準的四線SPI總線爲例,因爲這種模式用的最普遍。
FLASH常用芯片指令表(部分)
指令 |
第一字節(指令編碼) |
第二字節 |
第三字節 |
第四字節 |
第五字節 |
第六字節 |
第七-N字節 |
Write Enable |
06h |
|
|
|
|
|
|
Write Disable |
04h |
|
|
|
|
|
|
Read Status Register |
05h |
(S7–S0) |
|
|
|
|
|
Write Status Register |
01h |
(S7–S0) |
|
|
|
|
|
Read Data |
03h |
A23–A16 |
A15–A8 |
A7–A0 |
(D7–D0) |
(Next byte) |
continuous |
Fast Read |
0Bh |
A23–A16 |
A15–A8 |
A7–A0 |
dummy |
(D7–D0) |
(Next Byte) continuous |
Fast Read Dual Output |
3Bh |
A23–A16 |
A15–A8 |
A7–A0 |
dummy |
I/O = (D6,D4,D2,D0) O = (D7,D5,D3,D1) |
(one byte per 4 clocks, continuous) |
Page Program |
02h |
A23–A16 |
A15–A8 |
A7–A0 |
D7–D0 |
Next byte |
Up to 256 bytes |
Block Erase(64KB) |
D8h |
A23–A16 |
A15–A8 |
A7–A0 |
|
|
|
Sector Erase(4KB) |
20h |
A23–A16 |
A15–A8 |
A7–A0 |
|
|
|
Chip Erase |
C7h |
|
|
|
|
|
|
Power-down |
B9h |
|
|
|
|
|
|
Release Power- down / Device ID |
ABh |
dummy |
dummy |
dummy |
(ID7-ID0) |
|
|
Manufacturer/ Device ID |
90h |
dummy |
dummy |
00h |
(M7-M0) |
(ID7-ID0) |
|
JEDEC ID |
9Fh |
(M7-M0) 生產廠商 |
(ID15-ID8) 存儲器類型 |
(ID7-ID0) 容量 |
|
|
|
該表中的第一列爲指令名,第二列爲指令編碼,第三至第N列的具體內容根據指令的不同而有不同的含義。表中“A0~A23”指FLASH芯片內部存儲器組織的地址;“M0~M7”爲廠商號(MANUFACTURER ID);“ID0-ID15”爲FLASH芯片的ID;“dummy”指該處可爲任意數據;“D0~D7”爲FLASH內部存儲矩陣的內容。
- Read ID
通過指令表中的讀ID指令“JEDEC ID”可以獲取這兩個編號,該指令編碼爲“9F h”,其中“9F h”是指16進制數“9F” (相當於C語言中的0x9F)。緊跟指令編碼的三個字節分別爲FLASH芯片輸出的“(M7-M0)”、“(ID15-ID8)”及“(ID7-ID0)” 。通過read ID的指令,我們可以獲取芯片的相關ID信息。
- 擦除。也可以叫預編程吧,根據flash的特性,在進行寫入之前,需要對目標區域進行擦除,將對應的位置置1。在所列舉的芯片中,擦除分了三種類型(扇區擦除Sector_erase、塊擦除Block_erase,以及整個芯片擦除Chip_erase)。他們之間的區別是,擦除區域的大小不一樣。
扇區擦除指令將指定扇區(4K字節)內的所有存儲器設置爲全部擦除狀態置1(FFh)。需要注意的是,在執行擦除之前, 必須執行一次寫使能指令指令(狀態寄存器位WEL必須等於1)。
另外兩種擦除方式,大同小異,就不展開說了,需要注意的是,Chip_erase在指令碼後面不需要帶目標擦除區域的起始地址。
- 頁編程。Page_program。
頁編程,一次寫入256個字節的數據到flash。需注意的是,在執行頁編程之前,需執行一次寫使能指令。
- 寫操作。
這裏的寫操作,主要是指設置單個寄存器的值,例如將flash設置成4字節地址模式,設置寫使能等等。
設置狀態寄存器的值,指令碼後面,緊跟着對應的想要設置的寄存器值即可。
- 讀操作
這裏讀操作主要可以分兩種,一種是讀狀態寄存器值,另外一種是讀去flash存儲的數據。
值得注意的是,讀狀態寄存器指令在任何時候都可以執行,即使在編程,擦除或寫入時,也可以隨時使用讀狀態寄存器指令。
常規讀數據:
讀數據指令允許從存儲器中順序讀取一個或多個數據字節。該通過將/ CS引腳驅動爲低電平然後移位指令代碼“03h”然後移位a來啓動指令24位地址(A23-A0)進入DI引腳。 代碼和地址位在上升沿鎖存CLK引腳。 收到地址後,尋址存儲單元的數據字節將被移出在CLK的下降沿處的DO引腳上,最高有效位(MSB)優先。地址會自動增加。也就是說在下發讀指令的時候,只需要配置目標區域起始地址即可。然後數據會源源不斷地被讀出來,直到片選信號cs被拉高,讀數結束。
快速讀:
快速讀取指令與常規讀取數據指令類似,只是它可以在FR最高頻率下工作(參見交流電氣特性),提高讀取速度。其中dummy clock時間段裏的數據可以忽略。(該指令目前我用的比較少,沒實踐過,就不展開了)