摘要: 本篇部落格具體包括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)的影響。
訊號線說明:
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的主從裝置須配置相同的時序模式,見下圖。
【W25Q16BV操作說明】
板載Flash晶片型號:W25Q16BV;總容量為16M bit,即2M位元組;儲存陣列被分成8192個可程式設計頁,每頁容量256位元組;支援Standard SPI、Dual SPI和Quad SPI三種SPI通訊協議,最大讀/寫傳輸速率達50MB/s。它支援多種擦除操作,包括扇區擦除(4KB)、塊擦除(32KB或64KB)以及全晶片擦除。
1、介面處理與結構邏輯
上圖可以看到,除SPI經典的四個引腳外,還有/WP和/HOLD,分別用於防寫和輸入保持。對於不同倍率SPI協議,引腳使用搭配是有區別的。
W25Q16BV內部儲存結構的邏輯和功能組織見下圖,SPI介面負責接收命令和地址,並透過控制邏輯進行解析和執行。狀態暫存器(Status Register)用於儲存裝置的狀態資訊,如是否忙碌(BUSY位);寫控制透過IO2引腳控制,用於啟動寫操作;高電平生成器(High Voltage Generators)用於執行如擦除等操作;控制邏輯(Control Logic)負責解析SPI介面接收到的命令,並根據需要控制儲存器的讀/寫操作。列解碼(Column Decode)和頁面緩衝(256-Byte Page Buffer)用於支援頁面的讀/寫操作。
儲存器部分被分割成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
。
寫使能(Write Enable ):該指令用於設定(狀態暫存器中)寫使能鎖存器(WEL)位為1,確儲存儲器處於可寫狀態。
首先,將/CS(Chip Select)引腳拉低,SPI通訊啟動。在CLK上升沿,將資料輸入(DI)引腳上的資料位設定為指令程式碼“06h”,即0000 0110。完成後,將/CS引腳拉高,SPI通訊的結束和寫使能完成。
讀操作(Read Data):這個指令允許從儲存器中順序地讀取一個或多個資料位元組。首先,透過將/CS引腳拉低來啟動指令,將指令程式碼“03h”(0000 0011)和一個24位地址(A23-A0)透過DI引腳移位輸入,程式碼和地址位在CLK上升沿時被鎖定。最後,透過將/CS引腳拉高完成指令。
如果在擦除、程式設計或寫入週期正在進行時(BUSY=1)發出讀取資料指令,該指令將被忽略。
晶片全擦除操作(Chip Erase):Chip Erase 指令會將裝置內的所有儲存器設定為擦除狀態,即所有位都設為1。
①擦除啟動:在執行晶片擦除指令之前,必須先執行一個“寫使能”指令,使裝置能夠接受晶片擦除指令(狀態暫存器的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或其他目標裝置中,流程見下圖。
下載程式時,目標.jic檔案後,右下指示框很明顯看到下載至Flash(這裡是EPCS16類似W25Q16),僅勾選Program/Configure 進行程式固化下載,僅勾選Erase表示擦除Flash操作。
2、Verilog程式實現Flash晶片擦除操作(SPI)
根據Flash讀/寫時序要求,片選訊號拉低後,需進行5ns(tSLCH≥5ns)等待時間,及後寫入寫使能指令。寫入完成後,再次進行5ns(tCHSH≥5ns)等待,拉高片選線維持≥100ns,接續完成寫入操作指令流程。
Flash晶片資料讀操作的時脈頻率(SCK)上限為50MHz,這裡透過四分頻設定SCK頻率為12.5Mhz。單個時鐘週期寫入1 bit資料,完整的單位元組指令需要8個完整的SCK時鐘週期,即32個完整的系統時鐘,系統時脈頻率為50MHz,完整指令的寫入需要640ns。
mosi
訊號輸出採用了狀態機轉移的邏輯方法:
狀態 | 說明 | 狀態 | 說明 |
---|---|---|---|
IDLE |
空閒初始狀態,等待觸發訊號 | WR_EN |
傳送寫使能指令 |
DELAY |
等待狀態,保持時序 | CH_ER |
晶片擦除指令傳送 |
當key_flag
訊號有效時,從IDLE
轉移到WR_EN
狀態,在WR_EN
和CH_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
作為其在有效時間段內計數器,使得sck
按sys_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結束擦除操作。
根據模擬結果顯示,sys_clk
拉高後,程式正常工作,當檢測到key_flag
為高電平後,片選線SS_n
拉低,指令操作開啟。等待一個有效建立時間後(這裡是640ns,便於編寫程式碼),sck
正式輸出八段時鐘切換(12.5Mhz)。首先,按寫使能指令,輸出一個時序(0000_0110,06h)。等待一個片選線建立時間後拉高,進入等待狀態(≥100ns,640ns)接續完成晶片擦除指令的錄入。cnt_byte
和state
的狀態,隨cnt_clk
時鐘計數變換。
文獻參考:
[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/