spi从机的Verilog实现2.0

  前面已经提过了SPI协议的主从机,并用代码实现了。不过之前的版本是用系统时钟实现的,现在是直接通过SPI的时钟敏感进行边沿采样。参考了下github上一位大神的代码如下链接https://github.com/nandland/spi-slave

  1 ///////////////////////////////////////////////////////////////////////////////
  2 // Description: SPI (Serial Peripheral Interface) Slave
  3 //              Creates slave based on input configuration.
  4 //              Receives a byte one bit at a time on MOSI
  5 //              Will also push out byte data one bit at a time on MISO.  
  6 //              Any data on input byte will be shipped out on MISO.
  7 //              Supports multiple bytes per transaction when CS_n is kept 
  8 //              low during the transaction.
  9 //
 10 // Note:        i_Clk must be at least 4x faster than i_SPI_Clk
 11 //              MISO is tri-stated when not communicating.  Allows for multiple
 12 //              SPI Slaves on the same interface.
 13 //
 14 // Parameters:  SPI_MODE, can be 0, 1, 2, or 3.  See above.
 15 //              Can be configured in one of 4 modes:
 16 //              Mode | Clock Polarity (CPOL/CKP) | Clock Phase (CPHA)
 17 //               0   |             0             |        0
 18 //               1   |             0             |        1
 19 //               2   |             1             |        0
 20 //               3   |             1             |        1
 21 //              More info: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Mode_numbers
 22 ///////////////////////////////////////////////////////////////////////////////
 23 
 24 module SPI_Slave
 25   #(parameter SPI_MODE = 0)
 26   (
 27    // Control/Data Signals,
 28    input            i_Rst_L,    // FPGA Reset, active low
 29    input            i_Clk,      // FPGA Clock
 30    output reg       o_RX_DV,    // Data Valid pulse (1 clock cycle)
 31    output reg [7:0] o_RX_Byte,  // Byte received on MOSI
 32    input            i_TX_DV,    // Data Valid pulse to register i_TX_Byte
 33    input  [7:0]     i_TX_Byte,  // Byte to serialize to MISO.
 34 
 35    // SPI Interface
 36    input      i_SPI_Clk,
 37    output       o_SPI_MISO,
 38    input      i_SPI_MOSI,
 39    input      i_SPI_CS_n        // active low
 40    );
 41 
 42 
 43   // SPI Interface (All Runs at SPI Clock Domain)
 44   wire w_CPOL;     // Clock polarity
 45   wire w_CPHA;     // Clock phase
 46   wire w_SPI_Clk;  // Inverted/non-inverted depending on settings
 47   wire w_SPI_MISO_Mux;
 48   
 49   reg [2:0] r_RX_Bit_Count;
 50   reg [2:0] r_TX_Bit_Count;
 51   reg [7:0] r_Temp_RX_Byte;
 52   reg [7:0] r_RX_Byte;
 53   reg r_RX_Done, r2_RX_Done, r3_RX_Done;
 54   reg [7:0] r_TX_Byte;
 55   reg r_SPI_MISO_Bit, r_Preload_MISO;
 56 
 57   // CPOL: Clock Polarity
 58   // CPOL=0 means clock idles at 0, leading edge is rising edge.
 59   // CPOL=1 means clock idles at 1, leading edge is falling edge.
 60   assign w_CPOL  = (SPI_MODE == 2) | (SPI_MODE == 3);
 61 
 62   // CPHA: Clock Phase
 63   // CPHA=0 means the "out" side changes the data on trailing edge of clock
 64   //              the "in" side captures data on leading edge of clock
 65   // CPHA=1 means the "out" side changes the data on leading edge of clock
 66   //              the "in" side captures data on the trailing edge of clock
 67   assign w_CPHA  = (SPI_MODE == 1) | (SPI_MODE == 3);
 68 
 69   assign w_SPI_Clk = w_CPHA ? ~i_SPI_Clk : i_SPI_Clk;
 70 
 71 
 72 
 73   // Purpose: Recover SPI Byte in SPI Clock Domain
 74   // Samples line on correct edge of SPI Clock
 75   always @(posedge w_SPI_Clk or posedge i_SPI_CS_n)
 76   begin
 77     if (i_SPI_CS_n)
 78     begin
 79       r_RX_Bit_Count <= 0;
 80       r_RX_Done      <= 1'b0;
 81     end
 82     else
 83     begin
 84       r_RX_Bit_Count <= r_RX_Bit_Count + 1;
 85 
 86       // Receive in LSB, shift up to MSB
 87       r_Temp_RX_Byte <= {r_Temp_RX_Byte[6:0], i_SPI_MOSI};
 88     
 89       if (r_RX_Bit_Count == 3'b111)
 90       begin
 91         r_RX_Done <= 1'b1;
 92         r_RX_Byte <= {r_Temp_RX_Byte[6:0], i_SPI_MOSI};
 93       end
 94       else if (r_RX_Bit_Count == 3'b010)
 95       begin
 96         r_RX_Done <= 1'b0;        
 97       end
 98 
 99     end // else: !if(i_SPI_CS_n)
100   end // always @ (posedge w_SPI_Clk or posedge i_SPI_CS_n)
101 
102 
103 
104   // Purpose: Cross from SPI Clock Domain to main FPGA clock domain
105   // Assert o_RX_DV for 1 clock cycle when o_RX_Byte has valid data.
106   always @(posedge i_Clk or negedge i_Rst_L)
107   begin
108     if (~i_Rst_L)
109     begin
110       r2_RX_Done <= 1'b0;
111       r3_RX_Done <= 1'b0;
112       o_RX_DV    <= 1'b0;
113       o_RX_Byte  <= 8'h00;
114     end
115     else
116     begin
117       // Here is where clock domains are crossed.
118       // This will require timing constraint created, can set up long path.
119       r2_RX_Done <= r_RX_Done;
120 
121       r3_RX_Done <= r2_RX_Done;
122 
123       if (r3_RX_Done == 1'b0 && r2_RX_Done == 1'b1) // rising edge
124       begin
125         o_RX_DV   <= 1'b1;  // Pulse Data Valid 1 clock cycle
126         o_RX_Byte <= r_RX_Byte;
127       end
128       else
129       begin
130         o_RX_DV <= 1'b0;
131       end
132     end // else: !if(~i_Rst_L)
133   end // always @ (posedge i_Bus_Clk)
134 
135 
136   // Control preload signal.  Should be 1 when CS is high, but as soon as
137   // first clock edge is seen it goes low.
138   always @(posedge w_SPI_Clk or posedge i_SPI_CS_n)
139   begin
140     if (i_SPI_CS_n)
141     begin
142       r_Preload_MISO <= 1'b1;
143     end
144     else
145     begin
146       r_Preload_MISO <= 1'b0;
147     end
148   end
149 
150 
151   // Purpose: Transmits 1 SPI Byte whenever SPI clock is toggling
152   // Will transmit read data back to SW over MISO line.
153   // Want to put data on the line immediately when CS goes low.
154   always @(posedge w_SPI_Clk or posedge i_SPI_CS_n)
155   begin
156     if (i_SPI_CS_n)
157     begin
158       r_TX_Bit_Count <= 3'b111;  // Send MSb first
159       r_SPI_MISO_Bit <= r_TX_Byte[3'b111];  // Reset to MSb
160     end
161     else
162     begin
163       r_TX_Bit_Count <= r_TX_Bit_Count - 1;
164 
165       // Here is where data crosses clock domains from i_Clk to w_SPI_Clk
166       // Can set up a timing constraint with wide margin for data path.
167       r_SPI_MISO_Bit <= r_TX_Byte[r_TX_Bit_Count];
168 
169     end // else: !if(i_SPI_CS_n)
170   end // always @ (negedge w_SPI_Clk or posedge i_SPI_CS_n_SW)
171 
172 
173   // Purpose: Register TX Byte when DV pulse comes.  Keeps registed byte in 
174   // this module to get serialized and sent back to master.
175   always @(posedge i_Clk or negedge i_Rst_L)
176   begin
177     if (~i_Rst_L)
178     begin
179       r_TX_Byte <= 8'h00;
180     end
181     else
182     begin
183       if (i_TX_DV)
184       begin
185         r_TX_Byte <= i_TX_Byte; 
186       end
187     end // else: !if(~i_Rst_L)
188   end // always @ (posedge i_Clk or negedge i_Rst_L)
189 
190   // Preload MISO with top bit of send data when preload selector is high.
191   // Otherwise just send the normal MISO data
192   assign w_SPI_MISO_Mux = r_Preload_MISO ? r_TX_Byte[3'b111] : r_SPI_MISO_Bit;
193 
194   // Tri-state MISO when CS is high.  Allows for multiple slaves to talk.
195   assign o_SPI_MISO = i_SPI_CS_n ? 1'bZ : w_SPI_MISO_Mux;
196 
197 endmodule // SPI_Slave

  笔者写的代码如下:

  1 //**************************************************************************
  2 // *** file name      : SPI_slave_if.v
  3 // *** version        : 1.0
  4 // *** Description    : SPI_slave_if
  5 // *** Blogs          : https://www.cnblogs.com/WenGalois123/
  6 // *** Author         : Galois_V
  7 // *** Date           : 2023.7.9
  8 // *** Changes        : Initial
  9 //**************************************************************************
 10 `timescale 1ns/10ps
 11 module SPI_slave_if
 12 #(
 13     parameter                            DATA_WIDTH = 8
 14 )
 15 (
 16     input                                i_sys_clk        ,
 17     input                                i_sys_rstn       ,
 18     input                                i_spi_cs         ,
 19     input                                i_spi_sclk       ,
 20     input                                i_spi_mosi       ,
 21     output    reg                        o_spi_miso       ,
 22     input                                i_spi_cpha       ,
 23     input                                i_spi_cpol       ,
 24     input            [DATA_WIDTH-1:0]    i_tx_data        ,
 25     input                                i_tx_valid       ,
 26     output    reg    [DATA_WIDTH-1:0]    o_rx_data        ,
 27     output    reg                        o_rx_valid        
 28 );
 29     wire                               w_spi_sclk       ;
 30     reg            [DATA_WIDTH-1:0]    r_tx_data        ;
 31     reg            [7:0]               r_tx_cnt         ;
 32     reg                                r_spi_miso       ;
 33     reg            [DATA_WIDTH-1:0]    r_rx_data        ;
 34     reg            [7:0]               r_rx_cnt         ;
 35     reg                                r_rx_done        ;
 36     reg            [2:0]               r_rx_valid       ;
 37 /******************************************************************************\
 38 SPI slave mode select and cs control sclk
 39 \******************************************************************************/
 40     assign w_spi_sclk = i_spi_cs ? 1'b0 : ((i_spi_cpha ^ i_spi_cpol) ? ~i_spi_sclk : i_spi_sclk);
 41 /******************************************************************************\
 42 SPI slave miso
 43 \******************************************************************************/
 44     always@(posedge i_sys_clk)
 45     begin
 46         if(~i_sys_rstn)
 47             r_tx_data <= 'd0;
 48         else if(i_tx_valid)
 49             r_tx_data <= i_tx_data;
 50     end
 51     always@(negedge w_spi_sclk)
 52     begin
 53         if(~i_sys_rstn)
 54         begin
 55             o_spi_miso <= i_spi_cpha ? 'd0 : r_tx_data[DATA_WIDTH-1];    //cpha = 1,1st edge tx 1st bit data,cpha = 0 2sec edge tx second bit data 
 56             r_tx_cnt   <= i_spi_cpha ? DATA_WIDTH-1 : DATA_WIDTH-2;
 57         end
 58         else
 59         begin
 60             if(r_rx_cnt == 0)
 61             begin
 62                 r_rx_cnt <= DATA_WIDTH - 1;
 63             end
 64             else
 65             begin
 66                 r_rx_cnt <= r_rx_cnt -1'b1;
 67             end
 68             o_spi_miso <= r_tx_data[r_tx_cnt];
 69         end
 70     end
 71 /******************************************************************************\
 72 SPI slave mosi
 73 \******************************************************************************/
 74     always@(posedge w_spi_sclk)
 75     begin
 76         if(~i_sys_rstn)
 77         begin
 78             r_rx_cnt  <= 'd0;
 79             r_rx_data <= 'd0;
 80             r_rx_done <= 'd0;
 81         end
 82         else if(r_rx_cnt == DATA_WIDTH-1)
 83         begin
 84             r_rx_done <= 1'b1;
 85             r_rx_cnt  <= r_rx_cnt + 1'b1;
 86             r_rx_data <= {r_rx_data[DATA_WIDTH-2:0],i_spi_mosi};
 87         end
 88         else
 89         begin
 90             r_rx_cnt  <= r_rx_cnt + 1'b1;
 91             r_rx_done <= 'd0;
 92             r_rx_data <= {r_rx_data[DATA_WIDTH-2:0],i_spi_mosi};
 93         end
 94     end
 95     always@(posedge w_spi_sclk)
 96     begin
 97         if(~i_sys_rstn)
 98             r_rx_valid <= 'd0;
 99         else
100             r_rx_valid <= {r_rx_valid[1:0],r_rx_done};
101     end
102     always@(posedge i_sys_clk)
103     begin
104         if(~i_sys_rstn)
105         begin
106             o_rx_data  <= 'd0;
107             o_rx_valid <= 'd0;
108         end
109         else if(r_rx_valid[1]&(~r_rx_valid[2]))
110         begin
111             o_rx_valid <= 1'b1;
112             o_rx_data  <= r_rx_data;
113         end
114         else
115         begin
116             o_rx_valid <= 1'b0;
117             o_rx_data  <= 'd0;
118         end
119     end
120     
121 endmodule

 

posted on 2023-07-10 23:43  Galois_V  阅读(1489)  评论(0编辑  收藏  举报