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