spi协议--Verilog及仿真

学习文章:https://www.cnblogs.com/liujinggang/p/9609739.html

1、协议原理:

spi协议采用的是主从模式控制,支持一个master和多个slave。如果fpga作为主机,那么SCLK和CS必须由fpga产生。

SPI(Serial Peripheral Interface)串行外设接口,spi接口有四根信号线。

MOSI(SDO):主机数据输出,从机数据输入。在片选信号有效时,数据由高位到低位,在时钟的上升沿依次发送给从设备。

MISO(SDI):主机数据输入,从机数据输出。在片选信号有效时,数据由高位到低位,在时钟的上升沿依次发送给主设备。

SCLK:时钟信号,由主设备产生。

CS:从设备的片选线,由主设备控制。

spi传输有四种模式:由时钟极性(CPOL,Clock Polarity)和时钟相位(CPHA,Clock Phase)来决定,其中CPOL决定SCLK在空闲状态是高电平还是低电平,CPHA决定数据是在SCLK的上升沿被采样还是下降沿被采样。

模式0:CPOL=0,CPHA=0。低电平、上升沿采样、下降沿跳变。

模式1:CPOL=0,CPHA=1。低电平、下降沿采样、上升沿跳变。

模式2:CPOL=1,CPHA=0。高电平、下降沿采样、上升沿跳变。

模式3:CPOL=1,CPHA=1。高电平、上升沿采样、下降沿跳变。

这里代码是按照模式0逻辑设计:

 这里通过设计一个有16个状态的状态机实现spi中模式0的时序图。


2、协议代码:spi实现数据发送和接收,所以模块用到输入的引脚要有发送的数据i_data_in和接收的MISO。对应要有输出的MOSI和数据输出o_data_out。

综合代码:

module spi(
input sys_clk,
input sys_rst_n,
input i_tx_en,
input i_rx_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 MISO,
output reg SCLK,
output reg CS,
output reg MOSI
);

reg [3:0]tx_state;
reg [3:0]rx_state;

always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
tx_state<=4'd0;
rx_state<=4'd0;
o_data_out<=8'd0;
o_tx_done<=1'b0;
o_rx_done<=1'b0;
SCLK<=1'b0;
CS<=1'b0;
MOSI<=1'b0;
end
else if(i_tx_en)begin
CS<=1'b0;//片选信号为0,表示开始发送
case(tx_state)
4'd1,4'd3,4'd5,4'd7,4'd9,4'd11,4'd13,4'd15:begin
SCLK<=1'b1;
tx_state<=tx_state+1'b1;
o_tx_done<=1'b0;
end
4'd0:begin
SCLK<=1'b0;
MOSI<=i_data_in[7];
o_tx_done<=1'b0;
tx_state<=tx_state+1'b1;
end
4'd2:begin
SCLK<=1'b0;
MOSI<=i_data_in[6];
o_tx_done<=1'b0;
tx_state<=tx_state+1'b1;
end
4'd4:begin
SCLK<=1'b0;
MOSI<=i_data_in[5];
o_tx_done<=1'b0;
tx_state<=tx_state+1'b1;
end
4'd6:begin
SCLK<=1'b0;
MOSI<=i_data_in[4];
o_tx_done<=1'b0;
tx_state<=tx_state+1'b1;
end
4'd8:begin
SCLK<=1'b0;
MOSI<=i_data_in[3];
o_tx_done<=1'b0;
tx_state<=tx_state+1'b1;
end
4'd10:begin
SCLK<=1'b0;
MOSI<=i_data_in[2];
o_tx_done<=1'b0;
tx_state<=tx_state+1'b1;
end
4'd12:begin
SCLK<=1'b0;
MOSI<=i_data_in[1];
o_tx_done<=1'b0;
tx_state<=tx_state+1'b1;
end
4'd14:begin
SCLK<=1'b0;
MOSI<=i_data_in[0];
o_tx_done<=1'b1;
tx_state<=tx_state+1'b1;
end
default:tx_state<=4'd0;
endcase
end
else if(i_rx_en)begin
CS<=1'b0;//片选信号为0,表示开始接收
case(rx_state)
4'd0,4'd2,4'd4,4'd6,4'd8,4'd10,4'd12,4'd14:begin
SCLK<=1'b0;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b0;
end
4'd1:begin
SCLK<=1'b1;
o_data_out[7]<=MISO;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b0;
end
4'd3:begin
SCLK<=1'b1;
o_data_out[6]<=MISO;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b0;
end
4'd5:begin
SCLK<=1'b1;
o_data_out[5]<=MISO;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b0;
end
4'd7:begin
SCLK<=1'b1;
o_data_out[4]<=MISO;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b0;
end
4'd9:begin
SCLK<=1'b1;
o_data_out[3]<=MISO;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b0;
end
4'd11:begin
SCLK<=1'b1;
o_data_out[2]<=MISO;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b0;
end
4'd13:begin
SCLK<=1'b1;
o_data_out[1]<=MISO;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b0;
end
4'd15:begin
SCLK<=1'b1;
o_data_out[0]<=MISO;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b1;
end
default:rx_state<=4'd0;
endcase
end
else begin
tx_state<=4'd0;
rx_state<=4'd0;
o_data_out<=8'd0;
o_tx_done<=1'b0;
o_rx_done<=1'b0;
SCLK<=1'b0;
CS<=1'b1;//片选信号为1,表示停止数据的接收
MOSI<=1'b0;
end
end
endmodule 

仿真代码:这个仿真只是对spi的发送数据的仿真,接收数据没有。

`timescale 1ns/1ns
module spi_tb;
reg  sys_clk;
reg sys_rst_n;
reg i_tx_en;
reg i_rx_en;
reg [7:0]i_data_in;
reg MISO;
wire [7:0]o_data_out;
wire o_tx_done;
wire o_rx_done;
wire SCLK;
wire CS;
wire MOSI;
spi u_spi(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.i_tx_en (i_tx_en),
.i_rx_en (i_rx_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)
);
initial begin
sys_clk=0;
sys_rst_n=0;
#10 sys_rst_n=1;
i_tx_en=1;
i_rx_en=0;
i_data_in=8'b0;
MISO=0;
end
always #10 sys_clk=~sys_clk;
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
i_data_in=8'b0000_0000;
else if(i_data_in==8'b1111_1111)begin
i_tx_en=0;
i_data_in=8'b0000_0000;
end
else if(o_tx_done)
i_data_in=i_data_in+1'b1;
end
endmodule

仿真结果:

这里可以看到仿真时候i_data_in有8'b0000_0000、8'b0000_0001、8'b0000_0010等等,这里仿真时间是1us,所以未能把所有的情况都显示出波形。然后MOSI能够成功发送这三个数据,说明spi发送数据的功能成功实现!

这里明明状态4'd1时,SCLK是1'b1的,还有状态4'd2时,SCLK是1'b0的,这里好像不一样。是因为SCLK的状态要等到sys_clk的上升沿,才能变化!

所以这里能够解释,在发送8'b0000_0001,明明状态4'd14时,o_tx_done应该是1和MOSI应该是在发送最后位1,然后要推到下一个状态4'd15那里,因为其实那里算4'd14的,只是上升沿在那里所以跳变就慢一拍。

posted @ 2020-10-19 13:35  LiYiRui  阅读(1534)  评论(0编辑  收藏  举报