名稱:spi flash(M25P16芯片)實驗設計Verilog代碼Quartus AX301開發(fā)板
軟件:Quartus
語言:Verilog
代碼功能:
由于 FPGA 是基于 SRAM 結構的,程序掉電后會丟失,所以需要一個外置 Flash 保存程序,F(xiàn)PGA 每次上電后去讀取 Flash 中的配置程序,在 ALINX 開發(fā)板中,很多使用的是 SPI 接口的 nor flash,這種 flash 只需要 4 根 IO。FPGA 的配置 flash 是特殊的 IO,上電時工作,F(xiàn)PGA 要使用這些IO 來讀取 Flash,讀取完成后釋放這些 IO 交給用戶使用。
本實驗做一個 SPI 主設備控制器,然后按照 spi Flash 數(shù)據(jù)手冊的命令要求發(fā)出擦除、編程、讀取等指令,每次上電后將 flash 中第一個字節(jié)讀取并顯示出來,按鍵按下時,數(shù)字加 1 再寫回 flash。
主要學習 spi 接口、spi flash 操作等,由于篇幅有限,本文不詳細介紹 SPI 協(xié)議和 spi flash 的操作時序。
FPGA代碼Verilog/VHDL代碼資源下載:www.hdlcode.com
本代碼已在AX301開發(fā)板驗證,AX301開發(fā)板如下,其他開發(fā)板可以修改管腳適配:
設計文檔:
SPI Flash 實驗
FPGA設計
1 實驗簡介
由于 FPGA 是基于 SRAM 結構的,程序掉電后會丟失,所以需要一個外置 Flash 保存程序,F(xiàn)PGA 每次上電后去讀取 Flash 中的配置程序,在 ALINX 開發(fā)板中,很多使用的是 SPI 接口的 nor flash,這種 flash 只需要 4 根 IO。FPGA 的配置 flash 是特殊的 IO,上電時工作,F(xiàn)PGA 要使用這些IO 來讀取 Flash,讀取完成后釋放這些 IO 交給用戶使用。
本實驗做一個 SPI 主設備控制器,然后按照 spi Flash 數(shù)據(jù)手冊的命令要求發(fā)出擦除、編程、讀取等指令,每次上電后將 flash 中第一個字節(jié)讀取并顯示出來,按鍵按下時,數(shù)字加 1 再寫回 flash。
主要學習 spi 接口、spi flash 操作等,由于篇幅有限,本文不詳細介紹 SPI 協(xié)議和 spi flash 的操作時序,但這些知識都是本實驗基礎。
2 實驗原理
2.1 硬件介紹
如圖所示,AX301、AX4010 開發(fā)板上有一個 SPI Flash,通常是做為 FPGA 的程序配置 Flash,但是也可以做為用戶 Flash 使用,我們可以把自己的少量數(shù)據(jù)保存在 Flash 中。
AX301、AX4010 開發(fā)板 SPI flash
2.2 Flash 時序和命令
對一個器件進行操作前,我們首先要了解 Flash 的各種特性,特別是和我們操作相關的特性,大部分芯片廠商會提供芯片的數(shù)據(jù)手冊,這些芯片手冊一般可以通過芯片廠商網(wǎng)站獲取,有些廠商需要簽訂保密協(xié)議才能提供數(shù)據(jù)手冊。所以獲取芯片數(shù)據(jù)手冊也是非常重要的學習內(nèi)容,首先通過搜索引擎搜索,在沒有搜索結果時可到芯片廠商官網(wǎng)找找,很多芯片數(shù)據(jù)手冊下載是需要注冊登錄,然后再下載。注意:在進行試驗前請先閱讀配套資料芯片手冊文件夾下的FLASH datasheet,搞清楚flash 命令,地址和數(shù)據(jù)之間的時序關系。
2.2.1 SPI 模式
SPI 可以通過 CPOL,CPHA 來配置模式,這對于剛接觸 SPI 協(xié)議比較費勁,暫且不去理會。SPI Flash 支持 2 種配置模式(These devices can be driven by a microcontroller with its SPI peripheral running in either of the two following modes):
CPOL=0, CPHA=0
CPOL=1, CPHA=1
這 2 種數(shù)據(jù)模式,數(shù)據(jù)輸入都是在串行時鐘的上升沿鎖存數(shù)據(jù),在串行時鐘的下降沿送出數(shù)據(jù)。(For these two modes, input data is latched in on the rising edge of Serial Clock (C), and output data is available from the falling edge of Serial Clock (C)).
2 種 SPI 模式數(shù)據(jù)波形
2.2.2 Flash 的主要操作
頁編程(Page programming)
編程指令就是講 Flash 的數(shù)據(jù)位由 1 變成 0,只能由 1 變成 0,如果要從 0 變成 1,只能使用 擦除操作。要編程一個數(shù)據(jù)字節(jié),需要兩個指令:寫使能(WREN),這是一個字節(jié)和一個頁編程(pp)指令,它由四字節(jié)加上數(shù)據(jù)組成。為了提高性能,頁編程(PP)指令最多允許 256 字節(jié), 當然這些數(shù)據(jù)都必須在一頁內(nèi),不能跨頁連續(xù)讀取。從頁編程指令時序圖可以看出,SPI 需要先發(fā) 送一個字節(jié)的指令,再發(fā)出 3 個字節(jié)的地址,然后再發(fā)出數(shù)據(jù),最大 256 個數(shù)據(jù)。將數(shù)據(jù)寫入后 檢查狀態(tài)寄存器 WIP 位(狀態(tài)寄存器最低位)的值,若為 1 表示處于數(shù)據(jù)寫入周期,若為 0 表示 寫入周期完成,可以進行下一步操作。
頁編程指令時序
頁編程之前需要寫使能有效,需要先發(fā)送寫使能指令,指令時序如下圖,寫使能只有一個字 節(jié)??梢苑磸桶l(fā)送寫使能。
寫使能指令時序
塊擦除指令(Bulk Erase)
塊擦除指令(BE)可以把整個 flash 都變成 1,同樣,在塊擦除之前需要先發(fā)送寫使能指令。 Flash 的擦除需要的時間很長,容量不同時間會有差異,一般需要幾分鐘擦除整片芯片。塊擦除指 令發(fā)出后,我們通過不斷讀取狀態(tài)寄存器(Status Register)來查詢擦除是否完成。
hdlcode.com
塊擦除指令時序
扇區(qū)擦除指令(Sector Erase)
扇區(qū)擦除(SE)指令可以按照扇區(qū)擦除 Flash。和塊擦除不同的是,扇區(qū)擦除是要指定扇區(qū)地 址,扇區(qū)擦除前也需要發(fā)送寫使能指令。
扇區(qū)擦寫指令
讀數(shù)據(jù)指令(Read Data Bytes)
讀 flash 是非常常見的操作,首先拉低片選信號,然后發(fā)出讀指令,3 個字節(jié)的讀地址,然后 就可以持續(xù)讀出數(shù)據(jù),地址自動累加。器件處于擦除或數(shù)據(jù)寫入周期時,數(shù)據(jù)讀取指令無效并且 對當前周期無任何影響。
讀數(shù)據(jù)指令
flash 的其他指令這里不再介紹,其他指令如下圖表格。
flash 指令列表
3 程序設計
spi flash 讀寫相對比較復雜,本實驗將 flash 操作分解為 3 層,最底層為 SPI 驅(qū)動層,每次寫一個字節(jié)返回一個字節(jié),然后是 flash 指令層,flash 指令層通過 spi 主控制器讀寫數(shù)據(jù),完成最基本的 flash 各種指令,然后是 flash 擦除、編程、讀寫層,為其他模塊提供可直接操作 flash 的接口。
SPI主設備控制器
(spi master)
Flash擦除、編程、 讀寫控制
(spi flash ctrl )
spi flash 控制器框圖
為了檢驗 flash 掉電不丟失的功能,實驗設計了一個狀態(tài)機,上電一段時間后讀取 flash 的第 一個字節(jié),并通過數(shù)碼管顯示出來,如果按鍵按下,將數(shù)字加 1,再寫回 flash,這樣下次上電會 保持新寫入的數(shù)據(jù)。
spi master 狀態(tài)機設計,主要完成一個字節(jié) spi 數(shù)據(jù)的讀寫,由于是全雙工的,寫一個字節(jié)的 同時也讀一個字節(jié)。首先空閑狀態(tài)“IDLE”接收到寫請求后進入“DCLK_IDLE”狀態(tài),這個狀態(tài)為 spi 時鐘沿變化保持一定的時間,用來控制 spi 時鐘的周期,然后進入 spi 時鐘沿的變化狀態(tài),一 個字節(jié)上升沿和下降沿一共 16 個數(shù)據(jù)沿。在最后一個數(shù)據(jù)沿進入“LAST_HALF_CYCLE”狀態(tài),為 讓最后一個沿也保持一定的時間,再進入應答狀態(tài),完成一次寫請求。
spi master 模塊狀態(tài)圖
spi_master 模塊中模擬了一個 spi 時鐘,在狀態(tài)機進入到‘DCLK_EDGE’時進行翻轉(zhuǎn)
在‘spi_flash_top’模塊中例化‘spi_master’模塊時已經(jīng)設定‘clk_div’的值為 0,目的是將 模擬的 spi 時鐘‘DCLK_reg’進行 4 分頻,也就是當‘clk_div=0’整個模塊運行時,從狀態(tài)‘IDLE’ 跑到狀態(tài)‘DCLE_EDGE’需要 4 個‘sys_clk’周期。至于其他不能夠理解的地方請大家詳細了解 spi 總線時序和 flash 讀寫時序后再來看或許會有更深的認識。當然,最直觀的方法還是仿真
信號名稱 | 方向 | 說明 |
sys_clk | in | 時鐘輸入 |
rst | in | 異步復位輸入,高復位 |
nCS | out | spi 片選信號,等于 nCS_ctrl。 |
DCLK | out | spi 串行時鐘 |
MOSI | out | spi 串行數(shù)據(jù)輸出 |
MISO | in | spi 串行數(shù)據(jù)輸入 |
CPOL | in | Clock Polarity,spi 時鐘的極性
0:空閑狀態(tài)為 0 1:空閑狀態(tài)為 1 |
CPHA | in | Clock Phase,spi 時鐘的相位,
0:第一個沿采樣, 1:第二個沿采樣 |
nCS_ctrl | in | nCS 控制 |
clk_div | in | spi 時鐘頻率控制
spi 時鐘=系統(tǒng)時鐘/(2*(2+ clk_div)) clk_div 最小值可以為 0,當為 0 時,spi 時鐘是系統(tǒng) 時鐘的 1/4 |
wr_req | in | 寫一個字節(jié)請求 |
wr_ack | out | 寫應答,高有效 |
data_in | in | 數(shù)據(jù) |
data_out | out | 返回的數(shù)據(jù),當寫應答時有效 |
spi master 端口說明
spi _flash_cmd 模塊狀態(tài)機設計,如下圖所示,在收到命令請求以后進入“S_CMD_LATCH”命 令鎖存狀態(tài),將請求的命令記錄下來,然后進入“S_CS_LOW”狀態(tài),拉低 spi 的片選信號,再進入“S_WR_CMD_CODE”狀態(tài),發(fā)送一個字節(jié)的命令碼,如果這個命令只有一個字節(jié),進入“S_KEEP_CS_LOW”狀態(tài),保持一個周期的片選拉低,然后進入“S_CS_HIGH”狀態(tài),拉高片選。 如果命令后面還有地址等數(shù)據(jù),進入“S_WRITE_BYTES”寫數(shù)據(jù)狀態(tài),或進入“S_READ_BYTES” 讀。需要注意,在 spi 數(shù)據(jù)接口‘data_recv’向數(shù)據(jù)輸出接口‘data_out’傳送數(shù)據(jù)時,是舍棄了 3 個字節(jié)的地址位,只傳送數(shù)據(jù)位。而在產(chǎn)生‘data_req’信號時‘byte_cnt’卻是到 2,為了滿 足數(shù)據(jù)寫入的時序要求,這里提前了一個時鐘周期
spi_flash_cmd 狀態(tài)機
信號名稱 | 方向 | 說明 |
sys_clk | in | 時鐘輸入 |
rst | in | 異步復位輸入,高復位 |
cmd | in | 命令編碼 |
cmd_valid | in | 命令有效 |
cmd_ack | out | 命令應答 |
addr | in | flash 地址 |
data_in | in | 命令有寫操作時的數(shù)據(jù) |
size | in | 命令+數(shù)據(jù)長度(字節(jié)) |
data_req | out | 命令有寫操作時請求數(shù)據(jù),其他 data_in 一個時鐘周 期 |
data_out | out | 命令有讀操作時讀出的數(shù)據(jù) |
data_valid | out | 命令有讀操作時讀有效 |
CS_reg | out | 對 spi master 接口,spi 片選控制 |
wr_req | out | 對 spi master 接口,spi 寫請求 |
wr_ack | in | 對 spi master 接口,spi 寫應答 |
send_data | out | 對 spi master 接口,spi 寫數(shù)據(jù) |
data_recv | in | 對 spi master 接口,spi 讀數(shù)據(jù) |
spi_flash_cmd 模塊端口
spi_flash_ctrl 模塊主要完成 flash 擦除、編程、讀操作。擦除前需要寫使能有效、等待擦除完
成等多項 flash 命令。狀態(tài)機如下圖所示:
“S_IDLE”:空閑狀態(tài)
“S_WREN”:寫使能命令狀態(tài)
“S_READ”:讀狀態(tài)
“S_WRITE”:寫狀態(tài)(編程)
“S_SE”:扇區(qū)擦除
“S_BE”:塊擦除
“S_CK_STATE”:狀態(tài)寄存器檢查,用來檢測是否擦除完成等。
“S_ACK”:請求應答
spi_flash_ctrl 狀態(tài)機
信號名稱 | 方向 | 說明 |
sys_clk | in | 時鐘輸入 |
rst | in | 異步復位輸入,高復位 |
flash_read | in | flash 讀請求 |
flash_write | in | flash 寫請求 |
flash_bulk_erase | in | 塊擦除請求 |
flash_sector_erase | in | 扇區(qū)擦除請求 |
flash_read_ack | out | 讀應答 |
flash_write_ack | out | 寫應答 |
flash_bulk_erase_ack | out | 塊擦除應答 |
flash_sector_erase_ack | out | 扇區(qū)擦除應答 |
flash_read_addr | in | 讀請求地址 |
flash_write_addr | in | 寫請求地址 |
flash_sector_addr | in | 扇區(qū)擦除地址 |
flash_write_data_in | in | 寫請求數(shù)據(jù) |
flash_read_size | in | 讀字節(jié)大小 |
flash_write_size | in | 寫字節(jié)大小 |
flash_write_data_req | out | 寫數(shù)據(jù)拉取,提前 flash_write_data_in 一個時鐘 |
flash_read_data_out | out | 讀數(shù)據(jù) |
flash_read_data_valid | out | 讀數(shù)據(jù)有效 |
cmd | out | 連接 spi_flash_cmd 模塊,命令編碼 |
cmd_valid | out | 連接 spi_flash_cmd 模塊,命令有效 |
cmd_ack | in | 連接 spi_flash_cmd 模塊,命令應答 |
addr | out | 連接 spi_flash_cmd 模塊,flash 地址 |
data_in | out | 連接 spi_flash_cmd 模塊,命令有寫操作時的數(shù)據(jù) |
size | out | 連接 spi_flash_cmd 模塊,命令+數(shù)據(jù)長度(字節(jié)) |
data_req | in | 連接 spi_flash_cmd 模塊,命令有寫操作時請求數(shù) 據(jù),其他 data_in 一個時鐘周期 |
data_out | in | 連接 spi_flash_cmd 模塊,命令有讀操作時讀出的數(shù) 據(jù) |
data_valid | in | 連接 spi_flash_cmd 模塊,命令有讀操作時讀有效 |
spi_flash_ctrl 端口
在這個模塊狀態(tài)機的‘S_CK_STATE’狀態(tài),進行狀態(tài)轉(zhuǎn)移的條件不僅有命令應答信號還需要判斷‘state_reg’寄存器的最低位,需要知道的是‘state_reg’的最低位就是 WIP 位,顯示 SPI 是否在寫入狀態(tài),為 0 時表示該狀態(tài)不忙。同時,在 spi_flash_ctrl 模塊中,我們調(diào)用了一個宏定義模塊,和 C 語言里的宏定義類似,宏定義模塊里面定義了對 flash 操作的各種命令,其用法和格式請參照例程。
4 實驗現(xiàn)象
將程序下載到開發(fā)板以后,數(shù)碼管顯示一個數(shù)字,這個數(shù)字是 flash 的第一個字節(jié),通過按下key1 鍵,數(shù)字會加一,同時擦除了 flash,并將新的數(shù)據(jù)寫入,重新上電后,加載下載程序,數(shù)碼管將顯示最后一次按按鍵的數(shù)字。注意:由于 flash 擦寫需求一定的時間,按鍵不能按的太快。
部分代碼展示:
`define CMD_WREN 8'h06 `define CMD_WRDI 8'h04 `define CMD_RDID 8'hAB //EPCS4 EPCS16 is 0'hab st spi flash is 8'h9f `define CMD_RDSR 8'h05 `define CMD_WRSR 8'h01 `define CMD_READ 8'h03 `define CMD_FAST_READ 8'h0b `define CMD_PP 8'h02 `define CMD_SE 8'hd8 `define CMD_BE 8'hc7
點擊鏈接獲取代碼文件:http://www.hdlcode.com/index.php?m=home&c=View&a=index&aid=1345