作者:Bird
上一回【Maker電子學】Flash 記憶體的原理與應用—PART13,我們說明了寫入指令的使用方式,以及寫入時需要注意的事項。這一回我們要介紹 SPI NOR flash 很重要的一道防線:寫入保護機制,這個機制可以設定某些記憶體區域不會被意外寫入或抹除。
層層關卡 #
SPI NOR flash 很常用來儲存嵌入式系統的 firmware 或是開機引導程式如 boot loader 之類的,這些以 binary 形式存在的程式碼,只要錯了一個 byte,通常就會讓整個系統爛掉(flash 雖然設計成可以重複抹寫,但我們也不希望它裡面的資料在不預期的狀況下被意外改變),因此 W25Q32JV 設計了一些保護機制來處理這個問題,說穿了,其實就是讓寫入或抹除的程序變得稍微複雜一點,意外就沒那麼容易發生。
W25Q32JV 可以針對記憶體中的不同區域各別設定寫入保護,當一個區域被設定寫入保護後,對這個區域執行抹除或寫入的指令就不會有作用,必須先將寫入保護解除,才能對這個區域執行抹除或寫入。
設定保護區塊的方式有兩種:Block Protect 與 Inidividual Block Lock,我們先來說說 Block Protect,這是比較常使用的功能。
Block Protect #
這個功能可以保護 W25Q32JV 整個 4M byte 的記憶體空間中,最前面(從位址 0 開始)或最後面(從位址 0x3FFFFF 開始倒著算回去)特定大小的連續區域。設定的方法稍微複雜了點,但原理很簡單。
首先,我們要決定保護的區域是在最前面還是最後面。如果要保護最前面,在狀態暫存器 SR1 中的 TB bit 就要設為 1,反之則設為 0(TB 這個 bit 的意思是 TOP/BOTTOM,換句話說位址 0 就是 top,位址 0x3FFFFF 就是 bottom)。

(圖片來源:Bird 提供)
接著我們要決定保護的大小的單位,是要以 64K bytes 的 block 為單位,還是要以 4K bytes 的 sector 為單位。如果要以 64K bytes 的 block 為單位,就把狀態暫存器 SR1 裡的 SEC bit 設為 0,如果要以 4K bytes 的 sector 為單位,就把狀態暫存器 SR1 裡的 SEC bit 設為 1。
接下來我們要決定保護區域的大小了,這是由狀態暫存器 SR1 裡的 BP0、BP1、BP2 這三個 bit 決定的。這個設定稍微複雜一點:這三個 bit 代表的值可以從 0(000) 到 7(111);當它們的值是 1 時,代表保護 1 個 block 或 1 個 sector;當他們的值是 2 時,就代表保護 2 個 block 或 2 個 sector;當它們的值是 3 時,保護的不是 3 個 block/sector,而是 4 個;當它們的值是 4 時,保護的大小是 8 個 block 或是 sector。
聰明的讀者一定看得出來,它保護的 block/sector 數量是 2^(n-1)個,因此當設定到 7 的時候,會保護 64 個 block;不過這裡還有個例外:如果保護大小的單位是 block,BP0-BP2 可以設定為 0 到 7,但如果保護的單位設定是 sector,BP0-BP2 的值就只能設定為 0-4。
換句話說,當保護單位是 64K bytes 的 block 時,最大可以設定保護 64K bytes * 2 ^(7-1)= 64K bytes * 64 = 4096K bytes,也就是 4M bytes,剛好就是整顆 W25Q32JV 的容量;如果當保護單位時 4K byes 的 sector 時,最大可以設定保護 4K bytes * 2 ^(3-1)= 4K bytes * 8 = 32K bytes。
舉個例子:我們設定 TB=1、SEC=0、BPx = 3(0b011),表示我們要保護的區域是從 top 開始,以 64K bytes block 為單位,保護 2 ^(3-1)= 4 個 block,也就是 256K bytes,所以保護的區域就是:

(圖片來源:Bird 提供)
從 0x0000000 到 0x03FFFF 這 256K bytes 的空間都不能被寫入。
為什麼要設計這種從 top 開始或是從 bottom 開始,連續保護特定大小區塊的保護機制呢?因為很多的 MCU 或 CPU,被 reset 之後開始執行程式的位址都會在整個記憶體空間的最低或最高位址,像 ARM 的核心在 reset 後,就會從 0x00000000 開始執行,因此 bootloader 或是任何的 bootstrap 程式,一定放在這個位址。
如果我們把 W25Q32JV 直接映射到 ARM 的程式記憶體空間中,讓 ARM 的 0x00000000 等於 W25Q32JV 的 0x000000,這時 reset 之後會執行的程式碼就位於 W25Q32JV 的 top 區域附近,系統的 bootstrap 程式碼一定會放在這一帶。由於這是開機之後一定會執行的程式,如果它不小心被修改了,整個系統可能就完蛋了。因此我們通常會很小心的保護這段程式碼,非必要不會去修改或是更新它;也因此,我們就可以用 Block Protect 的功能來保護這段程式碼。
Inidividual Block Lock #
這是 W25Q32JV 另一個保護記憶體區塊機制,它可以任意設定某些位址區塊的記憶體要不要保護。
W25Q32JV 總共有 64 個 64K bytes 的 block,除了第一個和最後一個 block,中間的 62 個 block 每一個都可以各自設定要不要被保護。那第一個和最後一個 block 呢?它們不是不能被保護,而是設定的單位更細,第一個和最後一個 block,更進一步地還可以以 4K bytes 的 sector 為單位去設定裡面的保護狀態。

(圖片來源:Bird 提供)
換句話說,最前面和最後面這兩個 block,它們設定保護的單位比較細緻,這個設計同樣是為了記憶體頂部或底部常常拿來儲存重要的啟動程式碼或啟動設定而做的。
至於要怎麼設定保護呢?首先,要使用這種保護方式的話,狀態暫存器 SR3 裡的 WPS bit 要設成 1。它出廠時預設值是 0,代表使用的是前面說明的 Block Protect 模式;接著,如果要保護某一個區塊,就對 W25Q32JV 透過 SPI 介面下 0x36 這個指令:

(圖片來源:Bird 提供)
這個指令要帶一個 24-bit 的位址,告訴晶片要保護的區塊位址。如果是中間 62 個 block,這個位址的 16 個 MSB 就不會被解碼;如果是最前面或是最後面的 blocks,這個位址的 12 個 MSB 就不會被解碼。
這個指令也被歸類為一個寫入指令,所以執行它之前要先下 write enable 指令,下了這個指令之後,所設定的位址所在的 block、sector 就會被保護起來,不能被寫入;如果要解除保護,就用 0x39 這個指令,它跟 0x36 的格式完全一樣,作用相反,就是用來解除特定 block/sector 的寫入保護的。
另外還有個指令 0x3d,可以用來讀取特定 block/sector 的 Individual Block Lock 狀態。

(圖片來源:Bird 提供)
這個指令用 8 個 SPI clock 送出後,一樣再送 24 bit 的位址,接著 master 再共 8 個 clock 過去,這時 W25Q32JV 會吐出 8 個 bit,最後一個 bit,也就是 LSB,就是這個位址的 Individual Block Lock 狀態:1 是不能寫入、0 是可以寫入。
因為 SPI 介面都是以 8 個 bit 為單位在處理資料傳輸,所以即使我們只需要讀一個 bit 的狀態,在傳送完 24-bit 位址後,還是需要 8 個 SPI clock 把狀態讀出來。
小結 #
這一回我們介紹了 W25Q32JV 的寫入保護機制,包含保護頂端或地步特定連續區域的 Block Protect 模式,以及可以任意指定保護區塊的 Individual Block Lock 模式。
下一回我們會進入 SPI NOR flash 的最終回,將 SPI NOR flash 還沒有介紹的一些功能、特性說明完畢。
(責任編輯:賴佩萱)