
前言
上一篇文章介紹了SPI的四種工作模式及其時序特性,相信各位同學已經掌握了SPI通信的核心原理。
本文用純Verilog設計了功能完整的4線SPI主機,并詳細說明了模塊編碼思路和使用注意事項,最后分享了源碼。
一、模塊功能
本Verilog功能模塊——SPI主機實現了SPI協議要求的完整時序控制,具體功能如下:
支持所有4種SPI工作模式
支持任意數據位寬
支持任意串行時鐘頻率fsclk
支持指定CS下降沿到第一個SCLK邊沿的延時
支持指定最后SCLK邊沿到CS上升沿的延時
支持指定CS高電平持續時間
二、模塊框圖

三、信號接口
3.1 參數列表
參數名 | 類型 | 默認值 | 說明 |
---|---|---|---|
SPI_MODE | integer | 3 | SPI模式, 可選0, 1, 2, 3 (默認) |
DATA_WIDTH | integer | 16 | 單次通信發送或接收數據的位寬, 最小為2, 常見8/16 |
SCLK_PERIOD_CLK_NUM | integer | 4 | fSCLK, SCLK周期對應CLK數, 必須為偶數, 最小為2 |
CS_EDGE_TO_SCLK_EDGE_CLK_NUM | integer | 1 | TCC, CS_N下降沿到SCLK的第一個邊沿對應CLK數, 最小為1 |
SCLK_EDGE_TO_CS_EDGE_CLK_NUM | integer | 3 | TCCH, 最后一個SCLK邊沿到CS_N上升沿對應CLK數, 最小為3 |
CS_KEEP_HIGH_CLK_NUM | integer | 2 | TCWH, CS_N低電平后保持高電平的時間對應CLK數, 最小為2 |
CLK_FREQ_MHZ | integer | 100 | 模塊工作時鐘, 常用100/120 |
3.2 接口信號列表
信號分組 | 信號名 | 方向 | 說明 |
---|---|---|---|
外部控制SPI信號 | spi_begin | input | SPI單次通信開始, 高電平有效, 僅在spi_is_busy為低時起作用 |
spi_end | output | SPI單次通信結束, 高電平有效, 只會持續一個時鐘周期 | |
spi_is_busy | output | SPI繁忙指示, 高電平表示SPI正在工作 | |
spi_master_tx_data[DATA_WIDTH-1:0] | input | SPI發送數據, 數據總是高位先發 | |
spi_master_rx_data[DATA_WIDTH-1:0] | output | SPI接收數據, 最先讀出的數據在最高位 | |
spi_master_rx_data_valid | output | SPI接收數據有效指示,高電平有效 | |
SPI硬線鏈接 | spi_cs_n | output | 片選, 低電平有效 |
spi_sclk | output | SPI時鐘, 主機提供 | |
spi_mosi | output | 主機輸出從機輸入 | |
spi_miso | input | 主機輸入從機輸出 | |
時鐘與復位 | clk | input | 模塊工作時鐘 |
rstn | input | 模塊復位, 低電平有效 |
四、編碼思路
通過參數控制SPI模式、數據位寬、時鐘頻率與三種時序參數
parameter?integer?SPI_MODE?????????????????????=?3,??//?SPI模式,?可選0,?1,?2,?3?(默認)
parameter?integer?DATA_WIDTH???????????????????=?16,?//?單次通信發送或接收數據的位寬,?最小為2,?常見8/16
parameter?integer?SCLK_PERIOD_CLK_NUM??????????=?4,??//?fSCLK,?SCLK周期對應CLK數,?必須為偶數,?最小為2
parameter?integer?CS_EDGE_TO_SCLK_EDGE_CLK_NUM?=?1,??//?TCC,?CS_N下降沿到SCLK的第一個邊沿對應CLK數,?最小為1
parameter?integer?SCLK_EDGE_TO_CS_EDGE_CLK_NUM?=?3,??//?TCCH,?最后一個SCLK邊沿到CS_N上升沿對應CLK數,?最小為3
parameter?integer?CS_KEEP_HIGH_CLK_NUM?????????=?2,??//?TCWH,?CS_N低電平后保持高電平的時間對應CLK數,?最小為2
parameter?integer?CLK_FREQ_MHZ?????????????????=?100?//?模塊工作時鐘,?常用100/120注意這里的時延并不是真實值,而是以模塊時鐘CLK為單位的相對值,且因為三段式狀態機輸出時序邏輯的限制,時延參數并不能到0,有最小值的限制。這對于實際應用是沒有影響的,實際應用中這些參數也不可能為0,且即使延時略長(如TCCH=30ns)同樣可以正常工作。
對參數進行有效性檢查,限制參數賦值
//++?參數有效性檢查?++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
initial?begin
??if?(SPI_MODE?!=?0?&&?SPI_MODE?!=?1?&&?SPI_MODE?!=?2?&&?SPI_MODE?!=?3)
????$error("SPI_MODE?must?be?0,?1,?2,?3");
??if?(DATA_WIDTH?<?2)
????$error("DATA_WIDTH?must?be?>=?2");
??if?(SCLK_PERIOD_CLK_NUM?<?2?||?(SCLK_PERIOD_CLK_NUM?%?2?!=?0))
????$error("SCLK_PERIOD_CLK_NUM?must?even?and?>=?2");
??if?(CS_EDGE_TO_SCLK_EDGE_CLK_NUM?<?1)
????$error("CS_EDGE_TO_SCLK_EDGE_CLK_NUM?must?>=?1");
??if?(SCLK_EDGE_TO_CS_EDGE_CLK_NUM?<?3)
????$error("SCLK_EDGE_TO_CS_EDGE_CLK_NUM?must?>=?3");
??if?(CS_KEEP_HIGH_CLK_NUM?<?2)
????$error("CS_KEEP_HIGH_CLK_NUM?must?>=?2");
end
//--?參數有效性檢查?------------------------------------------------------------使用三段式狀態機控制整個SPI傳輸過程,狀態機如下圖所示

五、使用說明
//?外部控制SPI信號
input??wire?spi_begin,???//?SPI單次通信開始,?高電平有效,?僅在spi_is_busy為低時起作用
output?wire?spi_end,?????//?SPI單次通信結束,?高電平有效,?只會持續一個時鐘周期
output?wire?spi_is_busy,?//?SPI繁忙指示,?高電平表示SPI正在工作
input??wire?[DATA_WIDTH-1:0]?spi_master_tx_data,?//?SPI發送數據,?數據總是高位先發
output?reg??[DATA_WIDTH-1:0]?spi_master_rx_data,?//?SPI接收數據,?最先讀出的數據在最高位
output?reg???????????????????spi_master_rx_data_valid,?//?SPI接收數據有效,高電平有效
外部模塊通過以上信號控制SPI讀寫,相關說明如下:
spi_is_busy為高時,此SPI主機模塊不響應任何外部信號,所以,外部模塊需要監測spi_is_busy信號,在它為低電平時,發送spi_begin控制spi控制開始
SPI主機模塊在會在spi_begin高電平且spi_is_busy低電平時,鎖存spi_master_tx_data,作為待發送數據
此接口和AXI Stream接口用法基本一致。
六、仿真驗證
見本系列文章——Verilog功能模塊--SPI主機和從機(04)--SPI主機從機回環仿真與實測。
七、源碼分享
源碼在Gitee與Github開源,兩平臺同步:
Gitee:[Verilog功能模塊--SPI主機和從機](https://gitee.com/xuxiaokang/verilog-function-module--SPI-Master-Slave)
Github:[zhengzhideakang/Verilog--SPI-Master-Slave](https://github.com/zhengzhideakang/Verilog--SPI-Master-Slave)
如果本文對你有所幫助,歡迎點贊、轉發、收藏、評論讓更多人看到,贊賞支持就更好了。
如果對文章內容有疑問,請務必清楚描述問題,留言評論或私信告知我,我看到會回復。

徐曉康的博客持續分享高質量硬件、FPGA與嵌入式知識,軟件,工具等內容,歡迎大家關注。