
前言
上一篇文章介紹了Verilog功能模塊——SPI主機,包括主機設計思路與使用方法。
本文則用純Verilog設計了功能完整的4線SPI從機,與網上一些以高頻率clk時鐘模擬從機不同,本文中的SPI從機工作時鐘來源于主機的sclk,符合SPI同步通信的原則。
本文詳細說明了模塊編碼思路和使用注意事項,最后分享了源碼。
一、模塊功能
本Verilog功能模塊——SPI從機實現了SPI協議要求的完整時序控制,具體功能如下:
- 同步通信,工作時鐘來源于主機的sclk
- 支持所有4種SPI模式,通過SPI_MODE參數配置;
- 數據位寬通過DATA_WIDTH參數可配置(1-32位);
- 采用異步復位設計。
二、模塊框圖

三、信號接口
3.1 參數列表
參數名 | 類型 | 默認值 | 說明 |
---|---|---|---|
SPI_MODE | integer | 3 | SPI模式, 可選0, 1, 2, 3 (默認) |
DATA_WIDTH | integer | 16 | 單次通信發送或接收數據的位寬, 最小為2, 常見8/16 |
3.2 接口信號列表
信號分組 | 信號名 | 方向 | 說明 |
---|---|---|---|
外部控制SPI信號 | spi_slave_tx_is_busy | output | SPI繁忙指示, 高電平表示SPI正在工作 |
spi_slave_tx_data[DATA_WIDTH-1:0] | input | SPI發送數據, 數據總是高位先發 | |
spi_slave_rx_data[DATA_WIDTH-1:0] | output | SPI接收數據, 最先讀出的數據在最高位 | |
spi_slave_rx_data_valid | output | SPI接收數據有效指示,高電平有效 | |
SPI硬線鏈接 | spi_cs_n | input | 片選, 低電平有效 |
spi_sclk | input | SPI時鐘, 主機提供 | |
spi_mosi | input | 主機輸出從機輸入 | |
spi_miso | output | 主機輸入從機輸出 | |
復位 | arstn | input | 異步復位, 低電平有效 |
四、編碼思路
-
通過參數控制SPI模式和數據位寬
parameter integer SPI_MODE = 3, // SPI模式, 可選0, 1, 2, 3 (默認) parameter integer DATA_WIDTH = 16 // 單次通信發送或接收數據的位寬, 最小為1, 常見8/16
-
對參數進行有效性檢查,限制參數賦值
//++ 參數有效性檢查 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ initial beginif (SPI_MODE != 0 && SPI_MODE != 1 && SPI_MODE != 2 && SPI_MODE != 3)$error("SPI_MODE must be 0, 1, 2, 3");if (DATA_WIDTH <= 0)$error("DATA_WIDTH must be >= 1"); end //-- 參數有效性檢查 ------------------------------------------------------------
-
暫存cs_n信號的上一狀態,作為加載發送數據的控制信號
//++ 片選狀態跟蹤 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ reg spi_cs_n_old; // 片選跳變前一瞬間的狀態, 跳變結束后會變為新值 always @(posedge spi_cs_n or negedge spi_cs_n or negedge arstn) beginif (~arstn)spi_cs_n_old <= 1'b1;elsespi_cs_n_old <= spi_cs_n; end //-- 片選狀態跟蹤 ------------------------------------------------------------//++ 發送數據 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ /* 原則是先采樣再移位 */ reg [$clog2(DATA_WIDTH+1)-1:0] sample_cnt; // 采樣計數 reg [DATA_WIDTH-1:0] tx_data_lsfr;// 移位寄存器generate if (SPI_MODE == 0 || SPI_MODE == 3) begin // 下降沿移位, 第一個下降沿移位always @(negedge spi_sclk or negedge spi_cs_n) beginif (spi_cs_n_old)tx_data_lsfr <= spi_slave_tx_data; // 片選下降沿加載發送數據else if (~spi_cs_n && sample_cnt != 'd0)tx_data_lsfr <= tx_data_lsfr << 1;elsetx_data_lsfr <= tx_data_lsfr;end end else begin // 上升沿移位, 第一個上升沿不移位always @(posedge spi_sclk or negedge spi_cs_n) beginif (spi_cs_n_old)tx_data_lsfr <= spi_slave_tx_data; // 片選下降沿加載發送數據else if (~spi_cs_n && sample_cnt != 'd0)tx_data_lsfr <= tx_data_lsfr << 1;elsetx_data_lsfr <= tx_data_lsfr;end end endgenerateassign spi_miso = spi_slave_tx_is_busy ? tx_data_lsfr[DATA_WIDTH-1] : 1'bz; // 三態輸出控制 //-- 發送數據 ------------------------------------------------------------
注意這里的spi_cs_n_old,它是SPI同步從機最關鍵的代碼,如果用
~spi_cs_n
,那么在整個傳輸中,spi_cs_n都是低電平,后續代碼都無法執行了,所以只能用spi_cs_n_old,它只在spi_cs_n下降沿執行一次。
五、使用說明
// SPI從機外部控制信號
output wire spi_slave_tx_is_busy, // 指示SPI從機正在發送, 高電平有效
input wire [DATA_WIDTH-1:0] spi_slave_tx_data, // 發送數據
output reg [DATA_WIDTH-1:0] spi_slave_rx_data, // 接收數據
output reg spi_slave_rx_data_valid, // 接收數據有效
外部模塊只需控制spi_slave_tx_data即可,此信號會在每個spi_cs_n下降沿鎖存。
六、仿真驗證
見本系列文章 ——Verilog 功能模塊 --SPI 主機和從機 (04)–SPI 主機從機回環仿真與實測
七、源碼分享
源碼在Gitee與Github開源,兩平臺同步:
Gitee:Verilog功能模塊–SPI主機和從機: Verilog功能模塊–SPI主機和從機 https://gitee.com/xuxiaokang/verilog-function-module–SPI-Master-Slave
Github:zhengzhideakang/Verilog–SPI-Master-Slave: verilog-function-module–SPI-Master-Slave https://github.com/zhengzhideakang/Verilog–SPI-Master-Slave
如果本文對你有所幫助,歡迎點贊、轉發、收藏、評論讓更多人看到,贊賞支持就更好了。
如果對文章內容有疑問,請務必清楚描述問題,留言評論或私信告知我,我看到會回復。

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