zoukankan      html  css  js  c++  java
  • 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的,只是上升沿在那里所以跳变就慢一拍。

  • 相关阅读:
    HDU 1207 汉诺塔II (递推)
    HDU 3172 Virtual Friends (map+并查集)
    HDU 1272 小希的迷宫(并查集)
    hihoCoder #1037 : 数字三角形 (动态规划)
    51Nod 1256 乘法逆元
    AtCoder Regular Contest 077 D
    AtCoder Regular Contest 077 C
    AtCoder Beginner Contest 066 B
    AtCoder Beginner Contest 045 C
    AtCoder Beginner Contest 045 B
  • 原文地址:https://www.cnblogs.com/FPGAer/p/13788432.html
Copyright © 2011-2022 走看看