SPI主机Verilog代码实现

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

  下面是SPI_master的代码

  SPI_master.v

 

  1 //**************************************************************************
  2 // *** file name      : SPI_master.v
  3 // *** version        : 1.0
  4 // *** Description    : SPI master timing generation, supports four SPI modes
  5 // *** Blogs          : 
  6 // *** Author         : Galois_V
  7 // *** Date           : 2022.02.14
  8 // *** Changes        : Initial
  9 //**************************************************************************
 10 `timescale 1ns/1ps
 11 module SPI_master
 12 #(
 13     parameter                            TX_WIDTH = 16     ,
 14     parameter                            RX_WIDTH = 16
 15 )
 16 (
 17     input                                i_sys_clk         ,
 18     input                                i_sys_rstn        ,
 19     output    reg                        o_spi_cs          ,
 20     output    reg                        o_spi_sclk        ,
 21     output                               o_spi_mosi        ,
 22     input                                i_spi_miso        ,
 23     input                                i_spi_cpol        ,
 24     input                                i_spi_cpha        ,
 25     input                                i_spi_start       ,
 26     output                               o_spi_state       ,
 27     input            [15:0]              i_spi_rate        ,
 28     input            [1:0]               i_spi_ctrl        ,
 29     input                                i_spi_tx_valid    ,
 30     input            [TX_WIDTH-1:0]      i_spi_tx_data     ,
 31     output                               o_spi_tx_req      ,
 32     output                               o_spi_rx_valid    ,
 33     output           [RX_WIDTH-1:0]      o_spi_rx_data    
 34 );
 35     wire                                 w_sclk_edge_over  ;
 36     wire                                 w_spi_read_en     ;
 37     wire                                 w_spi_write_en    ;
 38     wire                                 w_spi_sclk_reverse_en;
 39     wire                                 w_spi_rx_valid    ;
 40     reg                                  r_spi_over        ;
 41     reg                                  r_spi_state_en    ;
 42     reg                                  r_spi_start       ;
 43     reg             [15:0]               r_sclk_edge_cnt   ;
 44     reg                                  r_spi_get_state   ;
 45     reg                                  r_spi_over_dly    ;
 46     reg             [15:0]               r_spi_sclk_cnt    ;
 47     reg             [3:0]                r_spi_sclk_bit_cnt;
 48     reg             [TX_WIDTH-1:0]       r_spi_tx_data     ;
 49     reg             [2:0]                r_spi_rx_en       ;
 50     reg             [RX_WIDTH-1:0]       r_spi_rx_data     ;
 51     reg             [2:0]                r_spi_miso        ;
 52     reg             [3:0]                r_spi_rx_valid    ;
 53     localparam                           EDGE_CNT = TX_WIDTH * 2 - 1 54     assign w_spi_read_en  = i_spi_ctrl[0];
 55     assign w_spi_write_en = i_spi_ctrl[1];
 56     always@(posedge i_sys_clk)
 57     begin
 58         if(~i_sys_rstn)
 59         begin
 60             r_spi_start <= 'd0;
 61         end
 62         else
 63         begin
 64             r_spi_start <= i_spi_start;
 65         end
 66     end
 67     always@(posedge i_sys_clk)
 68     begin
 69         if(~i_sys_rstn)
 70         begin
 71             r_spi_state_en <= 'd0;
 72         end
 73         else if(r_spi_over)
 74         begin
 75             r_spi_state_en <= 'd0;
 76         end
 77         else if(r_spi_start)
 78         begin
 79             r_spi_state_en <= 1'b1;
 80         end
 81     end
 82     assign o_spi_state = ~r_spi_state_en;
 83 /******************************************************************************\
 84 sclk edge count
 85 \******************************************************************************/
 86     always@(posedge i_sys_clk)
 87     begin
 88         if(~i_sys_rstn)
 89         begin
 90             r_sclk_edge_cnt <= 'd0;
 91         end
 92         else if(r_spi_start)
 93         begin
 94             r_sclk_edge_cnt <= 'd0
 95         end
 96         else if(r_spi_over)
 97         begin
 98             r_sclk_edge_cnt <= r_sclk_edge_cnt;
 99         end
100         else if(w_sclk_edge_over)
101         begin
102             r_sclk_edge_cnt <= r_sclk_edge_cnt + 1'b1;
103         end
104     end
105     always@(posedge i_sys_clk)
106     begin
107         if(~i_sys_rstn)
108         begin
109             r_spi_over <= 'd0;
110         end
111         else if (w_sclk_edge_over & (r_sclk_edge_cnt == EDGE_CNT)) 
112         begin
113             r_spi_over <= 1'b1;
114         end
115         else
116         begin
117             r_spi_over <= 'd0;
118         end
119     end
120     always@(posedge i_sys_clk)
121     begin
122         if(~i_sys_rstn)
123         begin
124             r_spi_get_state <= 'd0;
125         end
126         else if(r_spi_get_state & i_spi_tx_valid | r_spi_over)
127         begin
128             r_spi_get_state <= 'd0;
129         end
130         else if(w_spi_write_en & r_spi_start)
131         begin
132             r_spi_get_state <= 1'b1;
133         end
134     end
135     assign o_spi_tx_req = (~r_spi_over & r_spi_get_state & i_spi_valid) & w_spi_write_en;
136 /******************************************************************************\
137 Generate SPI CS
138 \******************************************************************************/
139     always@(posedge i_sys_clk)
140     begin
141         if(~i_sys_rstn)
142         begin
143             r_spi_over_dly <= 'd0;
144         end
145         else
146         begin
147             r_spi_over_dly <= r_spi_over;
148         end
149     end
150     always@(posedge i_sys_clk)
151     begin
152         if(~i_sys_rstn)
153         begin
154             o_spi_cs <= 'd1;
155         end
156         else if(r_spi_over_dly)
157         begin
158             o_spi_cs <= 1'b1;
159         end
160         else if(i_spi_start)
161         begin
162             o_spi_cs <= 'd0;
163         end
164     end
165 /******************************************************************************\
166 Generatte SPI sclk
167 \******************************************************************************/
168     always@(posedge i_sys_clk)
169     begin
170         if(~i_sys_rstn)
171         begin
172             r_spi_sclk_cnt <= 'd0;
173         end
174         else if(r_spi_start | w_spi_sclk_reverse_en)
175         begin
176             r_spi_sclk_cnt <= 'd0;
177         end
178         else
179         begin
180             r_spi_sclk_cnt <= r_spi_sclk_cnt + 1'b1;
181         end
182     end
183     assign w_spi_sclk_reverse_en = (r_spi_sclk_cnt == i_spi_rate);
184     always@(posedge i_sys_clk)
185     begin
186         if(~i_sys_rstn)
187         begin
188             r_spi_sclk_bit_cnt <= 'd0;
189         end
190         else if(r_spi_start | r_spi_over)
191         begin
192             r_spi_sclk_bit_cnt <= 'd0;
193         end
194         else if(w_spi_sclk_reverse_en)
195         begin
196             r_spi_sclk_bit_cnt <= r_spi_sclk_bit_cnt + 1'b1;
197         end
198     end
199     assign w_sclk_edge_over = w_spi_sclk_reverse_en & r_spi_state_en;
200 /******************************************************************************\
201 SPI MODE
202 \******************************************************************************/
203     always@(posedge i_sys_clk)
204     begin
205         if(~i_sys_rstn)
206         begin
207             o_spi_sclk <= 'd0;
208         end
209         else if(r_spi_start)
210         begin
211             o_spi_sclk <= i_spi_cpol ^ i_spi_cpha;
212         end
213         else if(~r_spi_state_en | ((r_sclk_edge_cnt== EDGE_CNT) & w_sclk_edge_over))
214         begin
215             o_spi_sclk <= i_spi_cpol;
216         end
217         else if(w_spi_sclk_reverse_en)
218         begin
219             o_spi_sclk <= ~o_spi_sclk;
220         end
221     end
222 /******************************************************************************\
223 Generate SPI MOSI
224 \******************************************************************************/
225     always@(posedge i_sys_clk)
226     begin
227         if(~i_sys_rstn)
228         begin
229             r_spi_tx_data <= 'd0;
230         end
231         else if(o_spi_tx_req)
232         begin
233             r_spi_tx_data <= i_spi_tx_data;
234         end
235         else if(r_spi_state_en & r_spi_sclk_bit_cnt[0] & w_spi_sclk_reverse_en)
236         begin
237             r_spi_tx_data <= r_spi_tx_data << 1;
238         end
239     end
240     assign o_spi_mosi = r_spi_tx_data[TX_WIDTH-1];
241 /******************************************************************************\
242 Generate SPI MISO
243 \******************************************************************************/
244     always@(posedge i_sys_clk)
245     begin
246         if(~i_sys_rstn)
247         begin
248             r_spi_miso <= 'd0;
249         end
250         else
251         begin
252             r_spi_miso <= {r_spi_miso[1:0],i_spi_miso};
253         end
254     end
255     always@(posedge i_sys_clk)
256     begin
257         if(~i_sys_rstn)
258         begin
259             r_spi_rx_en <= 'd0;
260         end
261         else
262         begin
263             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)};
264         end
265     end
266     always@(posedge i_sys_clk)
267     begin
268         if(~i_sys_rstn)
269         begin
270             r_spi_rx_data <= 'd0;
271         end
272         else if(r_spi_rx_en[2])
273         begin
274             r_spi_rx_data <= {r_spi_rx_data[RX_WIDTH-2:0],r_spi_miso[2]};
275         end
276     end
277     assign o_spi_rx_data = r_spi_rx_data;
278 /******************************************************************************\
279 SPI valid delay 4clock ensure data stability
280 \******************************************************************************/
281     always@(posedge i_sys_clk)
282     begin
283         if(~i_sys_rstn)
284         begin
285             r_spi_rx_valid <= 'd0;
286         end
287         else
288         begin
289             r_spi_rx_valid <= {r_spi_rx_valid[2:0],w_spi_rx_valid};
290         end
291     end
292     assign w_spi_rx_valid = w_sclk_edge_over & (r_sclk_edge_cnt == EDGE_CNT) & w_spi_read_en;
293     assign o_spi_rx_valid = r_spi_rx_valid[3];
294 endmodule

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

posted on 2022-12-05 18:28  Galois_V  阅读(941)  评论(0编辑  收藏  举报