首页 > 其他分享 >SPI主机Verilog代码实现

SPI主机Verilog代码实现

时间:2022-12-05 18:47:33浏览次数:42  
标签:en SPI 主机 spi Verilog output input reg

  前面已经提到过了SPI,在SPI从机的设计中已经讲过SPI的基本原理,这里就不再赘述。对于SPI的主机可以参考百度百科或则笔者前面写的SPI从机介绍的相关知识。

  下面是SPI_master的代码

  SPI_master.v

//**************************************************************************
// *** file name      : SPI_master.v
// *** version        : 1.0
// *** Description    : SPI bus timing generation, supports four SPI modes
// *** Blogs          : https://www.cnblogs.com/WenGalois123/
// *** Author         : Galois_V
// *** Date           : 2022.07.19
// *** Changes        : Initial
//**************************************************************************
`timescale 1ns/1ps
module SPI_master
#(
    parameter                    CLK_EDGE  = 47        ,
    parameter                    RX_WIDTH  = 16    
)
(
    input                        i_sys_clk             ,
    input                        i_sys_rstn            ,
    
    output  reg                  o_spi_cs              ,
    output  reg                  o_spi_sclk            ,
    output                       o_spi_mosi            ,
    input                        i_spi_miso            ,
    input                        i_spi_start           ,
    output                       o_spi_state           ,
    input                        i_spi_cpol            ,
    input                        i_spi_cpha            ,
    input     [15:0]             i_spi_rate            ,
    input     [1:0]              i_spi_ctrl            ,
    input                        i_spi_tx_valid        ,
    input     [23:0]             i_spi_tx_data         ,
    output                       o_spi_tx_req          ,
    output                       o_spi_rx_valid        ,
    output    [RX_WIDTH-1:0]     o_spi_rx_data    
);
    wire                         w_sclk_egde_over      ;
    reg                          r_spi_over            ;
    reg                          r_spi_state_en        ;
    reg                          r_spi_start           ;
    reg        [15:0]            r_sclk_egde_cnt       ;
    reg                          r_spi_get_state       ;
    reg                          r_spi_over_dly        ;
    reg        [15:0]            r_spi_sclk_cnt        ;
    reg        [3:0]             r_spi_sclk_bit_cnt    ;
    reg        [23:0]            r_spi_tx_buff         ;
    reg        [2:0]             r_spi_rx_en           ;
    reg        [RX_WIDTH-1:0]    r_spi_rx_buff         ;
    reg        [1:0]             r_i_spi_miso          ;
    reg        [4:0]             r_spi_rx_valid        ;
    
    wire                         w_spi_read_en         ;
    wire                         w_spi_write_en        ;
    wire                         w_spi_sclk_reverse_en ;
    wire                         w_spi_rx_valid        ;

    assign  w_spi_read_en   = i_spi_ctrl[0];
    assign  w_spi_write_en  = i_spi_ctrl[1];
    
    always@(posedge i_sys_clk)
    begin
        if(~i_sys_rstn)
        begin
            r_spi_start <= 'd0;
        end
        else
        begin
            r_spi_start <= i_spi_start;
        end
    end
    
    always@(posedge i_sys_clk)
    begin
        if(~i_sys_rstn)
        begin
            r_spi_state_en <= 'd0;
        end
        else if(r_spi_over)
        begin
            r_spi_state_en <= 'd0;
        end
        else if(i_spi_start)
        begin
            r_spi_state_en <= 1'b1;
        end
    end
    
    assign o_spi_state = ~r_spi_state_en;
/******************************************************************************\
Sample Edge Count
\******************************************************************************/
    always@(posedge i_sys_clk)
    begin
        if(~i_sys_rstn)
        begin
            r_sclk_egde_cnt <= 'd0;
        end
        else if(r_spi_start)
        begin
            r_sclk_egde_cnt <= 'd0;
        end
        else if(r_spi_over)
        begin
            r_sclk_egde_cnt <= r_sclk_egde_cnt;
        end
        else if(w_sclk_egde_over)
        begin
            r_sclk_egde_cnt <= r_sclk_egde_cnt + 1'b1;
        end
    end
    
    always@(posedge i_sys_clk)
    begin
        if(~i_sys_rstn)
        begin
            r_spi_over <= 'd0;
        end
        else if((r_sclk_egde_cnt == CLK_EDGE) & w_sclk_egde_over)
        begin
            r_spi_over <= 1'b1;
        end
        else
        begin
            r_spi_over <= 'd0;
        end
    end
    
    always@(posedge i_sys_clk)
    begin
        if(~i_sys_rstn)
        begin
            r_spi_get_state <= 'd0;
        end
        else if(r_spi_get_state & i_spi_tx_valid | r_spi_over)
        begin
            r_spi_get_state <= 'd0;
        end
        else if(w_spi_write_en & r_spi_start)
        begin
            r_spi_get_state <= 1'b1;
        end
    end
    assign o_spi_tx_req = (~r_spi_over & r_spi_get_state & i_spi_tx_valid) & w_spi_write_en;
/******************************************************************************\
Generate SPI CS
\******************************************************************************/
    always@(posedge i_sys_clk)
    begin
        if(~i_sys_rstn)
        begin
            r_spi_over_dly <= 'd0;
        end
        else
        begin
            r_spi_over_dly <= r_spi_over;
        end
    end
    always@(posedge i_sys_clk)
    begin
        if(~i_sys_rstn)
        begin
            o_spi_cs <= 1'b1;
        end
        else if(r_spi_over_dly)
        begin
            o_spi_cs <= 1'b1;
        end
        else if(i_spi_start)
        begin
            o_spi_cs <= 'd0;
        end
    end    
/******************************************************************************\
Generate SPI SCLK
\******************************************************************************/
    always@(posedge i_sys_clk)
    begin
        if(~i_sys_rstn)
        begin
            r_spi_sclk_cnt <= 'd0;
        end
        else if(r_spi_start | w_spi_sclk_reverse_en)
        begin
            r_spi_sclk_cnt <= 'd0;
        end
        else
        begin
            r_spi_sclk_cnt <= r_spi_sclk_cnt + 1'b1;
        end
    end

    assign w_spi_sclk_reverse_en = (r_spi_sclk_cnt == i_spi_rate);
    
    always@(posedge i_sys_clk)
    begin
        if(~i_sys_rstn)
        begin
            r_spi_sclk_bit_cnt <= 'd0;
        end
        else if(r_spi_start | r_spi_over)
        begin
            r_spi_sclk_bit_cnt <= 'd0;
        end
        else if(w_spi_sclk_reverse_en)
        begin
            r_spi_sclk_bit_cnt <= r_spi_sclk_bit_cnt + 1'b1;
        end
    end

    assign w_sclk_egde_over = w_spi_sclk_reverse_en & r_spi_state_en;
/******************************************************************************\
SPI Mode CPOL,CPHA
\******************************************************************************/
    always@(posedge i_sys_clk)
    begin
        if(~i_sys_rstn)
        begin
            o_spi_sclk <= 'd0;
        end
        else if(r_spi_start)
        begin
            o_spi_sclk <= i_spi_cpol ^ i_spi_cpha;
        end
        else if(~r_spi_state_en |((r_sclk_egde_cnt == CLK_EDGE) & w_sclk_egde_over))
        begin
            o_spi_sclk <= i_spi_cpol;
        end
        else if(w_spi_sclk_reverse_en)
        begin
            o_spi_sclk <= ~o_spi_sclk;
        end
    end
/******************************************************************************\
SPI MOSI
\******************************************************************************/    
    always@(posedge i_sys_clk)
    begin
        if(~i_sys_rstn | r_spi_over)
        begin
            r_spi_tx_buff <= 'd0;
        end
        else if(o_spi_tx_req)
        begin
            r_spi_tx_buff <= i_spi_tx_data;
        end
        else if(r_spi_state_en & r_spi_sclk_bit_cnt[0] & w_spi_sclk_reverse_en)
        begin
            r_spi_tx_buff <= r_spi_tx_buff << 1;
        end
    end
/******************************************************************************\
SPI MISO 
\******************************************************************************/
    always@(posedge i_sys_clk)
    begin
        if(~i_sys_rstn)
        begin
            r_i_spi_miso <= 'd0;
        end
        else if(w_spi_read_en)
        begin
            r_i_spi_miso <= {r_i_spi_miso[0],i_spi_miso};
        end
    end
    always@(posedge i_sys_clk)
    begin
        if(~i_sys_rstn)
        begin
            r_spi_rx_en <= 'd0;
        end
        else if(w_spi_read_en)
        begin
            r_spi_rx_en <= {r_spi_rx_en[1:0], (r_spi_state_en & r_spi_sclk_bit_cnt[0] & w_spi_sclk_reverse_en)};
        end
    end
/******************************************************************************\
Delayed sampling,Because of external physical delay
\******************************************************************************/
    always@(posedge i_sys_clk)
    begin
        if(~i_sys_rstn)
        begin
            r_spi_rx_buff <= 'd0;
        end
        else if(r_spi_rx_en[2])
        begin
            r_spi_rx_buff <= {r_spi_rx_buff[14:0],r_i_spi_miso[1]};
        end
    end
/******************************************************************************\
Increase effective signal delay to ensure data stability
\******************************************************************************/
    always@(posedge i_sys_clk)
    begin
        if(~i_sys_rstn)
        begin
            r_spi_rx_valid <= 'd0;
        end
        else
        begin
            r_spi_rx_valid <= {r_spi_rx_valid[3:0],w_spi_rx_valid};
        end
    end
    
    assign w_spi_rx_valid = w_sclk_egde_over & (r_sclk_egde_cnt == CLK_EDGE) & w_spi_read_en;
    assign o_spi_rx_valid = r_spi_rx_valid[4];
    assign o_spi_rx_data  = r_spi_rx_buff;
    assign o_spi_mosi     = r_spi_tx_buff[23];
endmodule

  这里提下,笔者写代码比较喜欢写通用点的代码,这里速率是可以控制,不过最大只能是系统时钟的1/4,SPI的模式也支持4种模式,至于代码是否没问题,笔者不能保证,笔者在仿真测试中还未曾发现有太大问题,也仅供参考。这里可以用AXI4_lite总线来对该模块代码进行按需控制,可以把例化的参数CLK_EDGE也用总线配置。

标签:en,SPI,主机,spi,Verilog,output,input,reg
From: https://www.cnblogs.com/WenGalois123/p/16924488.html

相关文章