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也用总线配置。