2020-12-24

// ***************************************************************************************

//

// Copyright(c) 2000, micas Telecommunications Ltd., All right reserved

//

// Filename        :    spi_master.v

// Projectname     :    spi interface

// Author          :    huangbiao

// Email           :    huangbiao336@163.com

// Date            :    August 15st, 2025

// Version         :    1.0

// Company         :    xxx Ltd.

// synthesize tool :    vivado 2018.3

// sim tool        :    questa

//

// Description     :

//

// Modification History

// Date            By               Review        Revision        Change Description

// ---------------------------------------------------------------------------------------

// 2025/08/15      huangbiao                      1.0             initial

//

//

// ***************************************************************************************

// DEFINE

//  

//

// ***************************************************************************************

//

// Module       :    spi_master.v

// Called by    :

//

// ***************************************************************************************

//

// ***************************************************************************************

//

// ***************************************************************************************

//

// soft control lsb or msb first transmit

// lsb mode:address[0] --> address[14] -->w/r --> data[0] --> data[7]

// msb mode:w/r -->address[14] --> address[0] --> data[7] --> data[0]

// ***************************************************************************************

`resetall

`timescale 1ps/1ps

module spi_master #(

    parameter                               D_WIDTH     = 64,

    parameter                               CPOL        = 1'b0,           // cpol = 0 : idle clock is low,cpol = 1 : idle clock is high

    parameter                               CPHA        = 1'b1,           // cpha = 0 : first edge sample data,the second edge transmit data,cpha = 1 : first edge transmit data,the second edge sample data

    parameter                               CSN_SETUP   = 5'd2,           // 2 clocks,  the setup time is configed must be less 32 internal high frequence clocks

    parameter                               CSN_HOLD    = 5'd2,           // 2 clocks,  the hold time is configed must be less 32 internal high frequence clocks    

    parameter                               SLAVE_ACC   = 5'd5            // 5 clocks,  the slave turn round time is configed must be less 32 internal high frequence clocks      

)(    

    input                                   clk,                      

    input                                   clk_rst,                  

    //cfg signal                    

    input                                   spi_type,                  // 1: 3 wire  ; 0: 4 wire          

    input      [                  3 : 0]    cfg_clk_divid,             // cfg spi clock dicide index,4 divide value is 3

    input                                   lmsb_mod,                  // lsb or msb first transmit,0:lsb mode,1:msb mode

    input      [           D_WIDTH-1: 0]    cfg_addr_pha,              // cfg w/r + address

    input      [           D_WIDTH-1: 0]    cfg_data_pha,              // cfg data

    input      [$clog2(D_WIDTH+1)-1 : 0]    cfg_phase1_len,            // cfg w/r + address length indicate

    input      [$clog2(D_WIDTH+1)-1 : 0]    cfg_phase2_len,            // cfg data length indicate

    input                                   cmd_start,                 // cfg 0-->1,rising start

    input                                   clear,                     // 1:clear 0:no

    output reg                              spi_rd_data_vld,

    output reg [         D_WIDTH-1: 0]      spi_rd_data,               // spi read data

    output reg                              spi_busy,                  // spi interface status indicate,1:busy,0:idle

    output reg                              spi_abort,                 // 1:abort 0:normal


    output reg                              spi_cs,

    output reg                              spi_clk,

    inout                                   spi_dio,  

    input                                   spi_di

    );

//******************************************************

    localparam                              CNTWIDTH = $clog2(D_WIDTH+1) +1;

    reg        [           D_WIDTH-1: 0]    addr_pha;

    reg        [           D_WIDTH-1: 0]    data_pha;

    reg        [          CNTWIDTH-1: 0]    cfg_trans_len;             //trandmit bit length

    reg        [          CNTWIDTH-1: 0]    trans_cnt;

    reg                                     cmd_start_d1;

    reg                                     pos_start;

    wire                                    first_edge;

    wire                                    second_edge;

    reg                                     first_edge_1d;

    reg                                     second_edge_1d;  

    reg                                     first_edge_2d;

    reg                                     second_edge_2d;      

    reg        [                31: 0]      start_shreg;

    reg        [                31: 0]      wait_shreg;

    reg        [                31: 0]      end_shreg;    

    reg        [                3 : 0]      divide_cnt;                // divide count

    wire                                    mask;                      // when mask signal enabled, the spi clk generated must be stopped


    reg                                     spi_cs_tmp;

    reg                                     spi_cs_tmp1;

    reg                                     spi_clk_tmp;

    reg        [         D_WIDTH-1: 0]      msb_out_data;

    reg        [         D_WIDTH-1: 0]      lsb_out_data;

    reg        [         D_WIDTH-1: 0]      msb_in_data;

    reg        [         D_WIDTH-1: 0]      lsb_in_data;


    reg                                     op_rd_wr;

    reg                                     spi_bi_dir_switch_tmp;


    reg                                     spi_sdo;

    wire                                    spi_sdi;

    reg                                     spi_bi_dir_switch;              


//*************************************************************************************************

assign spi_dio  = (spi_type == 1'b1) ? ((spi_bi_dir_switch == 1'b1) ? spi_sdo : 1'bz) : spi_sdo;

assign spi_sdi  = (spi_type == 1'b1) ? ((spi_bi_dir_switch == 1'b0) ? spi_dio : 1'bz) : spi_di ;

//*************************************************************************************************

always@(posedge clk)

begin

    if (lmsb_mod)

        addr_pha <= cfg_addr_pha <<(D_WIDTH-cfg_phase1_len);

    else

        addr_pha <= cfg_addr_pha;

end

always@(posedge clk)

begin

    if (lmsb_mod)

        data_pha <= cfg_data_pha <<(D_WIDTH-cfg_phase2_len);

    else

        data_pha <= cfg_data_pha;

end

//transmit bit = address phase length + data phase length

always@(posedge clk or posedge clk_rst)

begin

    if(clk_rst == 1'b1)

        cfg_trans_len <= 'd0;

    else

        cfg_trans_len <= cfg_phase1_len + cfg_phase2_len;

end

//generate spi interface start signal

always@(posedge clk or posedge clk_rst)

begin

    if(clk_rst == 1'b1)

        begin

            cmd_start_d1 <= 1'b0;

            pos_start <= 1'b0;            

        end

    else

        begin

            cmd_start_d1 <= cmd_start;

            pos_start    <= cmd_start&(!cmd_start_d1);

        end

end

//generate mask signal for meeting csn setup time , hold time and readback turnround time

always @ (posedge clk)

begin

    start_shreg <= {start_shreg[30:0], pos_start};

    end_shreg   <= {end_shreg[30:0], pos_end};

    wait_shreg  <= {wait_shreg[30:0], pos_wait};    

end

assign  pos_wait = ((~spi_cs_tmp) & (divide_cnt == cfg_clk_divid -1) & (trans_cnt == cfg_phase1_len-1))&op_rd_wr;

assign  pos_end  = (~spi_cs_tmp) & (divide_cnt == cfg_clk_divid -1) & (trans_cnt == cfg_trans_len-1);

assign  mask     = (|start_shreg[CSN_SETUP-1:0]) | (|wait_shreg[SLAVE_ACC-1:0]) |  (|end_shreg[CSN_HOLD-1:0]) ;

//clk divide to generate spi clk

always@(posedge clk or posedge clk_rst)

begin

    if(clk_rst == 1'b1)

        divide_cnt <= 4'd0;

    else if(~spi_cs_tmp)                                                           //cs valid to generate spi clk

        if (~mask)

            divide_cnt <= (divide_cnt == cfg_clk_divid -1) ? 'd0 : divide_cnt + 4'd1;

        else

            ;

    else

        divide_cnt <= 4'd0;

end

//actual transmit bit count

always@(posedge clk or posedge clk_rst)

begin

    if(clk_rst == 1'b1)

        trans_cnt <= 'd0;

    else if (pos_start)

        trans_cnt <= 'd0;

    else if (~spi_cs_tmp)

        if (trans_cnt >=cfg_trans_len-1 )

            trans_cnt <= trans_cnt;

        else

            trans_cnt <= (divide_cnt == cfg_clk_divid -1) ? trans_cnt +1'b1 : trans_cnt;

    else

        trans_cnt <= 'd0;

end

//generate spi cs

always@(posedge clk or posedge clk_rst)

begin

    if(clk_rst == 1'b1)

        spi_cs_tmp <= 1'b1;

    else if(pos_start == 1'b1)

        spi_cs_tmp <= 1'b0;

    else if (end_shreg[CSN_HOLD-1])

        spi_cs_tmp <= 1'b1;

    else;

end

//generate spi clk

always@(posedge clk or posedge clk_rst)

begin

    if(clk_rst == 1'b1)

        spi_clk_tmp <= CPOL;

    else if(spi_cs_tmp == 1'b0)

        spi_clk_tmp <=(~mask)&((divide_cnt == 'd0 )|(divide_cnt == (cfg_clk_divid>>1))) ?  ~spi_clk_tmp : spi_clk_tmp;

    else

        spi_clk_tmp <= CPOL;

end

assign  first_edge = (~spi_cs_tmp) &(~mask)& (divide_cnt == 'd0);

assign  second_edge = (~spi_cs_tmp) &(~mask)& (divide_cnt == cfg_clk_divid>>1);

always@(posedge clk)

begin

    if(CPHA)

        begin

            if (first_edge&(trans_cnt =='d0))

                msb_out_data <= addr_pha;

            else if (first_edge&(trans_cnt ==cfg_phase1_len))

                msb_out_data <= data_pha;

            else

                msb_out_data <= first_edge ? {msb_out_data[D_WIDTH-2:0],1'b0} : msb_out_data;

        end        

    else

        begin

            if (pos_start)

                msb_out_data <= addr_pha;

            else if (second_edge&(trans_cnt ==cfg_phase1_len-1))

                msb_out_data <= data_pha;

            else

                msb_out_data <= second_edge ? {msb_out_data[D_WIDTH-2:0],1'b0} : msb_out_data;

        end                                              

end

always@(posedge clk)

begin

    if(CPHA)

        begin

            if (first_edge&(trans_cnt =='d0))

                lsb_out_data <= addr_pha;

            else if (first_edge&(trans_cnt ==cfg_phase1_len))

                lsb_out_data <= data_pha;

            else

                lsb_out_data <= first_edge ? {1'b0, lsb_out_data[D_WIDTH-1:1]} : lsb_out_data;

        end        

    else

        begin

            if (pos_start)

                lsb_out_data <= addr_pha;

            else if (second_edge&(trans_cnt ==cfg_phase1_len-1))

                lsb_out_data <= data_pha;

            else

                lsb_out_data <= second_edge ? {1'b0, lsb_out_data[D_WIDTH-1:1]} : lsb_out_data;

        end                                              

end

always@(posedge clk or posedge clk_rst)

begin

    if(clk_rst == 1'b1)

        spi_sdo <= 1'b0;

    else

        spi_sdo <= lmsb_mod ? msb_out_data[D_WIDTH-1]:lsb_out_data[0];

end

always@(posedge clk or posedge clk_rst)

begin

    if (clk_rst)

        begin

            first_edge_1d  <= 1'b0;

            first_edge_2d  <= 1'b0;            

            second_edge_1d <= 1'b0;  

            second_edge_2d <= 1'b0;              

        end

    else

        begin

            first_edge_1d  <= first_edge;

            first_edge_2d  <= first_edge_1d;            

            second_edge_1d <= second_edge;  

            second_edge_2d <= second_edge_1d;              

        end

end

always@(posedge clk)

begin

    if (pos_start)

        msb_in_data <= 'd0;

    else if(~CPHA)        

        msb_in_data <= ((~spi_bi_dir_switch)&first_edge_2d) ? {msb_in_data[D_WIDTH-2:0],spi_sdi} : msb_in_data;

    else

        msb_in_data <= ((~spi_bi_dir_switch)&second_edge_2d) ? {msb_in_data[D_WIDTH-2:0],spi_sdi} : msb_in_data;

end  

always@(posedge clk)

begin

    if (pos_start)

        lsb_in_data <= 'd0;    

    else if(~CPHA)        

        lsb_in_data <= ((~spi_bi_dir_switch)&first_edge_2d) ? {spi_sdi, lsb_in_data[D_WIDTH-1:1]}  : lsb_in_data;

    else

        lsb_in_data <= ((~spi_bi_dir_switch)&second_edge_2d) ? {spi_sdi, lsb_in_data[D_WIDTH-1:1]} : lsb_in_data;

end  

always@(posedge clk or posedge clk_rst)

begin

    if(clk_rst)

        spi_rd_data <= 'd0;

    else if (end_shreg[CSN_HOLD])

        spi_rd_data <= lmsb_mod ? msb_in_data : lsb_in_data>>(D_WIDTH-cfg_phase2_len);

    else

        ;

end

always@(posedge clk or posedge clk_rst)

begin

    if(clk_rst)

        spi_rd_data_vld <= 1'b0;

    else

        spi_rd_data_vld <= op_rd_wr & end_shreg[CSN_HOLD];

end

always@(posedge clk or posedge clk_rst)

begin

    if(clk_rst == 1'b1)

        begin

            spi_clk      <= CPOL;

            spi_cs_tmp1  <= 1'b1;

            spi_cs       <= 1'b1;

        end

    else

        begin

            spi_clk      <= spi_clk_tmp;

            spi_cs_tmp1  <= spi_cs_tmp;

            spi_cs       <= spi_cs_tmp1;

        end

end

always@(posedge clk or posedge clk_rst)

begin

    if(clk_rst == 1'b1)

        spi_busy <= 1'b0;

    else if(pos_start)

        spi_busy <= 1'b1;

    else if(end_shreg[CSN_HOLD])

        spi_busy <= 1'b0;

    else;

end

always@(posedge clk or posedge clk_rst)

begin

    if(clk_rst == 1'b1)

        spi_abort <= 1'b0;

    else if(clear == 1'b1)

        spi_abort <= 1'b0;

    else if((trans_cnt != cfg_trans_len-1'b1) && (spi_cs_tmp == 1'b1) && (spi_cs_tmp1 == 1'b0))

        spi_abort <= 1'b1;

    else;

end

always@(posedge clk or posedge clk_rst)

begin

    if(clk_rst)

        op_rd_wr <= 1'b0;

    else

        op_rd_wr <= cfg_addr_pha[cfg_phase1_len-1];

end

//generate spi bi dir switch

always@(posedge clk or posedge clk_rst)

begin

    if(clk_rst == 1'b1)

        spi_bi_dir_switch_tmp <= 1'b1;

    else if(pos_start | end_shreg[CSN_HOLD])

        spi_bi_dir_switch_tmp <= 1'b1;

    else if((((second_edge&(trans_cnt ==cfg_phase1_len-1) &(~CPHA)) | (first_edge&(trans_cnt ==cfg_phase1_len)&CPHA)))& op_rd_wr)                   //transmit bit done to desert

        spi_bi_dir_switch_tmp <= 1'b0;

    else

        ;

end

always@(posedge clk or posedge clk_rst)

begin

    if(clk_rst == 1'b1)

        spi_bi_dir_switch      <= 1'b1;

    else

        spi_bi_dir_switch      <= spi_bi_dir_switch_tmp;

end

endmodule

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容