Flash驅動控制--晶片擦除(SPI協議)

Handat發表於2024-06-19

摘要: 本篇部落格具體包括SPI協議的基本原理、模式選擇以及時序邏輯要求,採用FPGA(EPCE4),透過SPI通訊協議,對flash(W25Q16BV)儲存的固化程式進行晶片擦除操作。

關鍵詞:SPI;Verilog HDL;Flash


【SPI協議通訊模式】

SPI是Motorola公司推出的一種同步序列介面,是一種高速、全雙工、同步的通訊匯流排,廣泛應用於儲存器,數模轉換器,實時時鐘等。

  • 優點:支援全雙工通訊,通訊方式簡單,相對資料傳輸速率較快。
  • 缺點:沒有指定的流控制,沒有應答機制,資料可靠性上存在缺陷。

SPI協議透過四根線進行資料傳輸,即SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、/SS(Slave Select,低電平有效)(或/CS)。

介面處理方式:一主一從(左)、一主多從【星型鏈式】(右)SPI通訊要求主機和從機具有同步的時鐘訊號,傳輸速率直接受到時脈頻率(SCLK)的影響。

image

訊號線說明:

MOSI、MISO資料在SCK同步訊號下傳輸,每個時鐘週期傳輸一位資料,輸入輸出同步進行,要保證MSB或LSB先行需一致。/SS從裝置訊號選擇線,由高變低,是SPI通訊的起始訊號。當從裝置/SS線檢測到起始訊號後,即為選中使能與主機通訊。/SS訊號由低變高,為停止訊號,從裝置的選中狀態被取消。

注:SPI每次資料傳輸可以8位或16位為單位,每次傳輸的單位數不受限制。

SPI通訊時鐘模式:

SPI通訊加入了時鐘相位(CPHA)時鐘極性(CPOL)的設定,透過組合CPOL和CPHA的不同設定,SPI共支援四種常見的時鐘配置模式。

CPOL CPHA
時鐘極性,定義時鐘訊號在空閒狀態下的電平 時鐘相位,定義資料取樣和轉換的時鐘邊沿
CPOL=0:SCK空閒為低電平 CPHA=0:SCK的第一個邊沿取樣資料並轉換輸出訊號
CPOL=1:SCK空閒為高電平 CPHA=1:SCK的第二個邊沿取樣資料並轉換輸出訊號

​ 在CPHA指示為l時, 資料就會在SCLK的第二個有效邊沿被取樣(/SS拉低後,空閒為高,為上升沿;空閒為低,下降沿),同時被鎖存到暫存器中;如果CPHA 清零,資料就會在SCLK的第一個有效邊沿被取樣(/SS拉低後,空閒為高,為下降沿;空閒為低,為上升沿),同時被鎖存起來。SPI的主從裝置須配置相同的時序模式,見下圖。

image

【W25Q16BV操作說明】

板載Flash晶片型號:W25Q16BV;總容量為16M bit,即2M位元組;儲存陣列被分成8192個可程式設計頁,每頁容量256位元組;支援Standard SPI、Dual SPI和Quad SPI三種SPI通訊協議,最大讀/寫傳輸速率達50MB/s。它支援多種擦除操作,包括扇區擦除(4KB)、塊擦除(32KB或64KB)以及全晶片擦除。

1、介面處理與結構邏輯

image

上圖可以看到,除SPI經典的四個引腳外,還有/WP和/HOLD,分別用於防寫和輸入保持。對於不同倍率SPI協議,引腳使用搭配是有區別的。

W25Q16BV內部儲存結構的邏輯和功能組織見下圖,SPI介面負責接收命令和地址,並透過控制邏輯進行解析和執行。狀態暫存器(Status Register)用於儲存裝置的狀態資訊,如是否忙碌(BUSY位);寫控制透過IO2引腳控制,用於啟動寫操作;高電平生成器(High Voltage Generators)用於執行如擦除等操作;控制邏輯(Control Logic)負責解析SPI介面接收到的命令,並根據需要控制儲存器的讀/寫操作。列解碼(Column Decode)和頁面緩衝(256-Byte Page Buffer)用於支援頁面的讀/寫操作。

image

儲存器部分被分割成32個塊(Block),單個塊區包含16個扇區(Sector),每個扇區又由16個頁組成,單頁儲存256位元組資料,共計32 x 16 x 16 x 256 ≈ 2M 位元組。儲存器透過頁地址和位元組地址來定位特定的資料位置。

24位地址線透過上述的層次結構來對映整個儲存器的地址空間,具體見下面表格。

分割槽 地址位
塊(Block)地址 23~16位(0x1F0000到0x1FFFFF)
扇區(Sector)地址 15~12位(0x00F000到0x00FFFF)
頁(Page)地址 11~8位
位元組(Byte)地址 7~0位

2、指令與時序說明

軟體命令:W25Q16BV的指令集由30條基本指令組成,指令完全透過SPI匯流排控制。指令的啟動是透過/CS的下降邊緣來觸發的,/CS拉高前須完成指令輸入,否則無效。DI輸入的第一個位元組提供指令程式碼,DI輸入上的資料是在時鐘的上升沿取樣的,首先取樣的是最高有效位(MSB)。指令的長度從單個位元組到幾個位元組不等,後面可能跟著地址位元組、資料位元組、虛擬位元組(可選),在某些情況下,還可能是它們組合。所有的讀指令都可以在任何一個時鐘位之後完成。

指令 編碼 指令 編碼
Write Enable 06h Write Disable 04h
Sector Erase (4KB) 20h Block Erase (32KB) 52h
Block Erase (64KB) D8h Chip Erase C7h/60h
Continuous Read Mode Reset FFh Read Data 03h
Fast Read 0Bh Write Status Register 01h

所有寫、程式設計或擦除指令必須在位元組邊界上完成(即,在完整的8位資料被時鐘同步後,/CS被驅動為高)。如果不在位元組邊界上完成,該指令將被終止。這一特性旨在保護裝置免受意外寫入的影響。

當記憶體正在被程式設計或擦除,或者當狀態暫存器正在被寫入時,除了讀狀態暫存器指令之外的所有指令都將被忽略,直到程式設計或擦除週期完成。

電平切換時序要求:

下圖為Serial Input Timing,其中片選線/CS待輸入完成後拉高電平保持時間(tSHSL) (for Array Read Array Read / Erase or Program Read Status Registers) :至少7/40 ns;片選訊號有效建立時間(tSLCH):至少為5ns;片選訊號有效保持時間(tCHSH):至少為5ns。注意:讀指令/寫指令時脈頻率最大為50Mhz

image

寫使能(Write Enable ):該指令用於設定(狀態暫存器中)寫使能鎖存器(WEL)位為1,確儲存儲器處於可寫狀態。

image

首先,將/CS(Chip Select)引腳拉低,SPI通訊啟動。在CLK上升沿,將資料輸入(DI)引腳上的資料位設定為指令程式碼“06h”,即0000 0110。完成後,將/CS引腳拉高,SPI通訊的結束和寫使能完成。

讀操作(Read Data):這個指令允許從儲存器中順序地讀取一個或多個資料位元組。首先,透過將/CS引腳拉低來啟動指令,將指令程式碼“03h”(0000 0011)和一個24位地址(A23-A0)透過DI引腳移位輸入,程式碼和地址位在CLK上升沿時被鎖定。最後,透過將/CS引腳拉高完成指令。

image

如果在擦除、程式設計或寫入週期正在進行時(BUSY=1)發出讀取資料指令,該指令將被忽略。

晶片全擦除操作(Chip Erase):Chip Erase 指令會將裝置內的所有儲存器設定為擦除狀態,即所有位都設為1。

image

①擦除啟動:在執行晶片擦除指令之前,必須先執行一個“寫使能”指令,使裝置能夠接受晶片擦除指令(狀態暫存器的WEL位必須等於1)。將/CS引腳拉低來啟動指令,移位輸入指令程式碼“C7h”或“60h”,在第八位被鎖定後,/CS引腳須被拉高。

②擦除過程:/CS被拉高後,自計時的晶片擦除指令將開始執行。晶片擦除週期(典型3s,最大10s)進行期間,仍然可以透過“讀取狀態暫存器”指令來檢查BUSY位的狀態(BUSY位在晶片擦除週期期間為1)。

③擦除後的狀態:WEL位會被置0,注意:儲存資料的任何部分受到塊保護(BP2、BP1和BP0)位的保護,則不會執行晶片擦除指令。

【Flash全擦除操作】

1、Quartus II進行程式固化下載及擦除

燒錄程式方式:①將程式下載到FPGA內部的SRAM之中,燒錄過程耗時較短,但掉電後程式丟失;②將程式固化到FPGA外部掛載的非易失性儲存器Flash晶片,掉電後程式不丟失。

在Quartus II的主介面中,選擇“File”選單下的“Convert Programming Files”選項,用於將編譯生成的特定檔案(如sof檔案)轉換為其他格式的檔案,如jic檔案,以便下載到FPGA板載的flash或其他目標裝置中,流程見下圖。

image

下載程式時,目標.jic檔案後,右下指示框很明顯看到下載至Flash(這裡是EPCS16類似W25Q16),僅勾選Program/Configure 進行程式固化下載,僅勾選Erase表示擦除Flash操作。

image

2、Verilog程式實現Flash晶片擦除操作(SPI)

根據Flash讀/寫時序要求,片選訊號拉低後,需進行5ns(tSLCH≥5ns)等待時間,及後寫入寫使能指令。寫入完成後,再次進行5ns(tCHSH≥5ns)等待,拉高片選線維持≥100ns,接續完成寫入操作指令流程。

image

Flash晶片資料讀操作的時脈頻率(SCK)上限為50MHz,這裡透過四分頻設定SCK頻率為12.5Mhz。單個時鐘週期寫入1 bit資料,完整的單位元組指令需要8個完整的SCK時鐘週期,即32個完整的系統時鐘,系統時脈頻率為50MHz,完整指令的寫入需要640ns。

image

mosi訊號輸出採用了狀態機轉移的邏輯方法:

狀態 說明 狀態 說明
IDLE 空閒初始狀態,等待觸發訊號 WR_EN 傳送寫使能指令
DELAY 等待狀態,保持時序 CH_ER 晶片擦除指令傳送

key_flag訊號有效時,從IDLE轉移到WR_EN狀態,在WR_ENCH_ER狀態中,分別寫入相應的寫使能和指令後,等待tCHSH時間,然後拉高片選訊號。在WR_EN狀態寫入指令後,轉移到DELAY狀態等待tSHSL時間。在DELAY狀態等待完成後,轉移到CH_ER狀態,返回IDLE狀態。相關程式碼如下:

parameter   IDLE = 4'b0001,	WREN = 4'b0010,	DELAY= 4'b0100,	CH_ER= 4'b1000;
parameter   WREN_CODE = 8'b0000_0110,	CH_ER_CODE = 8'b1100_0111;
            
always @(posedge sys_clk or negedge sys_rst)begin
    if(!sys_rst)	state <= IDLE;
    else case(state)
            IDLE :if(key_flag)state <= WREN;
            WREN :if((cnt_byte == 3'd2)&&(cnt_clk == 5'd31))state <= DELAY;
            DELAY:if((cnt_byte == 3'd3)&&(cnt_clk == 5'd31))state <= CH_ER;
            CH_ER:if((cnt_byte == 3'd6)&&(cnt_clk == 5'd31))state <= IDLE;
            default:state <= IDLE;
        endcase
end

always @(posedge sys_clk or negedge sys_rst)begin
    if(!sys_rst)	mosi <= 1'b0;
    else if((state == WREN)&&(cnt_byte == 3'd2))	 mosi <= 1'b0;
    else if((state == CH_ER)&&(cnt_byte == 3'd6))	 mosi <= 1'b0;
    else if((state == WREN)&&(cnt_byte == 3'd1)&&(cnt_sck == 2'd0))
        mosi <= WREN_CODE[7 - cnt_bit];
    else if((state == CH_ER) && (cnt_byte == 3'd5) && (cnt_sck == 2'd0))
        mosi <= CH_ER_CODE[7 - cnt_bit];
end

而對於其他的訊號邏輯處理:cnt_clk作為預期設定單位間距(640ns)的計數器,cnt_byte根據其完成7個階段的轉換。sck為寫操作的時鐘訊號,cnt_sck作為其在有效時間段內計數器,使得scksys_clk四分配輸出。

always @(posedge sys_clk or negedge sys_rst)begin
    if(!sys_rst)	 cnt_clk <= 5'd0;
    else if(state != IDLE)	cnt_clk <= cnt_clk + 1'b1;  //位寬清零
end

always @(posedge sys_clk or negedge sys_rst)begin
    if(!sys_rst)	cnt_byte <= 3'd0;
    else if((cnt_byte == 3'd6)&&(cnt_clk == 5'd31))	cnt_byte <= 3'd0;
    else if(cnt_clk == 5'd31) cnt_byte <= cnt_byte + 1'b1;
end

always @(posedge sys_clk or negedge sys_rst)begin
    if(!sys_rst)	cnt_sck <= 2'd0;
    else if((state == WREN)&&(cnt_byte == 3'd1))	cnt_sck <= cnt_sck + 1'b1;  //位寬清零
    else if((state == CH_ER)&&(cnt_byte == 3'd5))	cnt_sck <= cnt_sck + 1'b1;  //位寬清零
end

always @(posedge sys_clk or negedge sys_rst)begin
    if(!sys_rst)	cnt_bit <= 3'd0;
    else if(cnt_sck == 2'd2)	cnt_bit <= cnt_bit + 1'b1;
end

always @(posedge sys_clk or negedge sys_rst)begin
    if(!sys_rst)	ss_n <= 1'b1;
    else if(key_flag)	ss_n <= 1'b0;
    else if((cnt_byte == 3'd2)&&(cnt_clk == 5'd31)&&(state == WREN))	ss_n <= 1'b1;
    else if((cnt_byte == 3'd3)&&(cnt_clk == 5'd31)&&(state == DELAY))	ss_n <= 1'b0;
    else if((cnt_byte == 3'd6)&&(cnt_clk == 5'd31)&&(state == CH_ER))	ss_n <= 1'b1;
end

always @(posedge sys_clk or negedge sys_rst)begin
    if(!sys_rst)	sck <= 1'b0;
    else if(cnt_sck == 2'd0)	sck <= 1'b0;
    else if(cnt_sck == 2'd2)	sck <= 1'b1;
end

匯入模擬檔案,對晶片擦除操作進行模擬,由於該操作耗時長,模擬前在Flash引數檔案縮放擦除週期至4000ns。從上圖模擬結果可以看到,在4710ns處,Flash啟動晶片擦除,在8710ns結束擦除操作。

image

image

根據模擬結果顯示,sys_clk拉高後,程式正常工作,當檢測到key_flag為高電平後,片選線SS_n拉低,指令操作開啟。等待一個有效建立時間後(這裡是640ns,便於編寫程式碼),sck正式輸出八段時鐘切換(12.5Mhz)。首先,按寫使能指令,輸出一個時序(0000_0110,06h)。等待一個片選線建立時間後拉高,進入等待狀態(≥100ns,640ns)接續完成晶片擦除指令的錄入。cnt_bytestate的狀態,隨cnt_clk時鐘計數變換。

image

文獻參考:

[1]劉滿. SPI協議介面的設計與實現[D]. 陝西:西安電子科技大學,2020;

[2]基於spi協議的flash驅動控制(https://doc.embedfire.com/fpga/altera/ep4ce10_pro/zh/latest/code/spi_flash.html);

[3] W25Q16BV Datasheet(PDF) - Winbond (alldatasheet.com)


本篇文章中使用的Verilog程式模組,若有需見網頁左欄Gitee倉庫連結:https://gitee.com/silly-big-head/little-mouse-funnyhouse/tree/FPGA-Verilog/

相關文章