Verilog -- SPI協議
簡介
SPI是一種全雙工通信,並且是一種同步傳輸方式(slave的接收clk需要master給出)
SPI總線是一種4線總線,因其硬件功能很強,所以與SPI有關的軟件就相當簡單,使中央處理器(Central Processing Unit,CPU)有更多的時間處理其他事務。正是因為這種簡單易用的特性,越來越多的芯片集成了這種通信協議,比如AT91RM9200。SPI是一種高速、高效率的串行接口技術。通常由一個主模塊和一個或多個從模塊組成,主模塊選擇一個從模塊進行同步通信,從而完成數據的交換。SPI是一個環形結構,通信時需要至少4根線(事實上在單向傳輸時3根線也可以)。
SPI的通信原理很簡單,它以主從方式工作,這種模式通常有一個主設備和一個或多個從設備,需要至少4根線,事實上3根也可以(單向傳輸時)。也是所有基於SPI的設備共有的,它們是MISO(主設備數據輸入)、MOSI(主設備數據輸出)、SCLK(時鍾)、CS(片選)。
(1)MISO– Master Input Slave Output,主設備數據輸入,從設備數據輸出;
(2)MOSI– Master Output Slave Input,主設備數據輸出,從設備數據輸入;
(3)SCLK – Serial Clock,時鍾信號,由主設備產生;
(4)CS – Chip Select,從設備使能信號,由主設備控制。
其中,CS是從芯片是否被主芯片選中的控制信號,也就是說只有片選信號為預先規定的使能信號時(高電位或低電位),主芯片對此從芯片的操作才有效。這就使在同一條總線上連接多個SPI設備成為可能。
(以上來自百度百科)

SPI最大傳輸速率
SPI是一種事實標准,由Motorola開發,並沒有一個官方標准。已知的有的器件SPI已達到50Mbps。具體到產品中SPI的速率主要看主從器件SPI控制器的性能限制。
受以下幾個條件影響:
SPI的最大時鍾頻率
CPU處理SPI數據的能力
輸出端驅動能力(PCB所允許的最大信號傳輸速率)
SPI的最大時鍾頻率
一般情況下,SPI模塊的最大時鍾頻率為系統時鍾頻率的1/2。雖然SPI的傳輸速率主要受限於CPU處理SPI數據的能力,但在同另一個非常高速率的SPI設備通訊時,SPI的最大時鍾頻率將有可能制約其傳輸速率。
CPU處理SPI數據的能力
通常情況下,考慮到系統中CPU有可能需要處理其他任務,以及對所接收SPI數據的具體運算處理方法,CPU處理SPI數據的能力將影響到整體的傳輸速率。
下面是verilog代碼:
module spi_master
(
input I_clk , // 全局時鍾50MHz
input I_rst_n , // 復位信號,低電平有效
input I_en , // en信號
input [7:0] I_data_in , // 要發送的數據
output reg [7:0] O_data_out , // 接收到的數據
output reg O_tx_done , // 發送一個字節完畢標志位
output reg O_rx_done , // 接收一個字節完畢標志位
// 四線標准SPI信號定義
input I_spi_miso , // SPI串行輸入,用來接收從機的數據
output reg O_spi_sck , // SPI時鍾
output reg O_spi_cs , // SPI片選信號
output reg O_spi_mosi // SPI輸出,用來給從機發送數據
);
reg [3:0] R_state ;
always @(posedge I_clk or negedge I_rst_n)
begin
if(!I_rst_n)
begin
R_state <= 4'd0 ;
O_spi_cs <= 1'b1 ;
O_spi_sck <= 1'b0 ;
O_spi_mosi <= 1'b0 ;
O_tx_done <= 1'b0 ;
O_rx_done <= 1'b0 ;
O_data_out <= 8'd0 ;
end
else if(I_en) // en信號打開的情況下
begin
O_spi_cs <= 1'b0 ; // 把片選CS拉低
R_state <= R_state + 1'b1 ;
case(R_state)
// 發送
4'd0: // 發送第7位
begin
O_spi_mosi <= I_data_in[7] ;
O_spi_sck <= 1'b0 ;
O_tx_done <= 1'b0 ;
end
4'd2: // 發送第6位
begin
O_spi_mosi <= I_data_in[6] ;
O_spi_sck <= 1'b0 ;
O_tx_done <= 1'b0 ;
end
4'd4: // 發送第5位
begin
O_spi_mosi <= I_data_in[5] ;
O_spi_sck <= 1'b0 ;
O_tx_done <= 1'b0 ;
end
4'd6: // 發送第4位
begin
O_spi_mosi <= I_data_in[4] ;
O_spi_sck <= 1'b0 ;
O_tx_done <= 1'b0 ;
end
4'd8: // 發送第3位
begin
O_spi_mosi <= I_data_in[3] ;
O_spi_sck <= 1'b0 ;
O_tx_done <= 1'b0 ;
end
4'd10: // 發送第2位
begin
O_spi_mosi <= I_data_in[2] ;
O_spi_sck <= 1'b0 ;
O_tx_done <= 1'b0 ;
end
4'd12: // 發送第1位
begin
O_spi_mosi <= I_data_in[1] ;
O_spi_sck <= 1'b0 ;
O_tx_done <= 1'b0 ;
end
4'd14: // 發送第0位
begin
O_spi_mosi <= I_data_in[0] ;
O_spi_sck <= 1'b0 ;
O_tx_done <= 1'b1 ;
end
// 接收
4'd1: // 接收第7位
begin
O_spi_sck <= 1'b1 ;
O_rx_done <= 1'b0 ;
O_data_out[7] <= I_spi_miso ;
end
4'd3: // 接收第6位
begin
O_spi_sck <= 1'b1 ;
O_rx_done <= 1'b0 ;
O_data_out[6] <= I_spi_miso ;
end
4'd5: // 接收第5位
begin
O_spi_sck <= 1'b1 ;
O_rx_done <= 1'b0 ;
O_data_out[5] <= I_spi_miso ;
end
4'd7: // 接收第4位
begin
O_spi_sck <= 1'b1 ;
O_rx_done <= 1'b0 ;
O_data_out[4] <= I_spi_miso ;
end
4'd9: // 接收第3位
begin
O_spi_sck <= 1'b1 ;
O_rx_done <= 1'b0 ;
O_data_out[3] <= I_spi_miso ;
end
4'd11: // 接收第2位
begin
O_spi_sck <= 1'b1 ;
O_rx_done <= 1'b0 ;
O_data_out[2] <= I_spi_miso ;
end
4'd13: // 接收第1位
begin
O_spi_sck <= 1'b1 ;
O_rx_done <= 1'b0 ;
O_data_out[1] <= I_spi_miso ;
end
4'd15: // 接收第0位
begin
O_spi_sck <= 1'b1 ;
O_rx_done <= 1'b1 ;
O_data_out[0] <= I_spi_miso ;
end
default:R_state <= 4'd0 ;
endcase
end
else
begin
R_state <= 4'd0 ;
O_tx_done <= 1'b0 ;
O_rx_done <= 1'b0 ;
O_spi_cs <= 1'b1 ;
O_spi_sck <= 1'b0 ;
O_spi_mosi <= 1'b0 ;
O_data_out <= 8'd0 ;
end
end
endmodule
testbench:
`timescale 1ns/1ps
module spi_master_tb();
reg I_clk;
reg I_rst_n;
reg I_en;
reg [7:0]I_data_in;
wire [7:0]O_data_out;
wire O_tx_done;
wire O_rx_done;
wire I_spi_miso;
wire O_spi_sck;
wire O_spi_cs;
wire O_spi_mosi;
always #1 I_clk = ~ I_clk;
assign I_spi_miso = O_spi_mosi;
integer i;
initial begin
I_clk = 1;
I_en = 0;
I_data_in = 0;
I_rst_n = 1; #2 I_rst_n = 0; #2 I_rst_n = 1;
#20;
@(posedge I_clk) begin
I_en <= 1;
I_data_in <= 0;
end
for(i=1;i<20;i=i+1) begin
@(negedge O_tx_done) begin
I_data_in <= i;
end
end
I_en <= 0;
end
spi_master U_SPI_MASTER_0(
.I_clk ( I_clk ),
.I_rst_n ( I_rst_n ),
.I_en ( I_en ),
.I_data_in ( I_data_in ),
.O_data_out ( O_data_out ),
.O_tx_done ( O_tx_done ),
.O_rx_done ( O_rx_done ),
.I_spi_miso ( I_spi_miso ),
.O_spi_sck ( O_spi_sck ),
.O_spi_cs ( O_spi_cs ),
.O_spi_mosi ( O_spi_mosi )
);
initial begin
$fsdbDumpvars();
$fsdbDumpMDA();
$dumpvars();
#1000 $finish;
end
endmodule
仿真波形:

測試程序中將MOSI和MISO短接了,所以spi發送的數據被自己接收,從波形圖中可以看到,發送和接收的邏輯正確。