├── LICENSE ├── README.md ├── spi_clgen.v ├── spi_clgen_tb.v ├── spi_defines.v ├── spi_shift_reg.v ├── spi_shift_reg_tb.v ├── spi_slave.v ├── spi_top.v ├── tb.v └── wishbone_master.v /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License 2 | 3 | You are free to: 4 | - Share — copy and redistribute the material in any medium or format 5 | - Adapt — remix, transform, and build upon the material 6 | 7 | Under the following terms: 8 | - Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 9 | - NonCommercial — You may not use the material for commercial purposes. 10 | - ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. 11 | 12 | For more details, refer to https://creativecommons.org/licenses/by-nc-sa/4.0/ 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SPI_Serial_Peripheral_Interface_Verilog_Modules 2 | Implementation of a Serial Peripheral Interface(SPI) using Verilog and testing various modes of the SPI Device 3 | 4 | Simulated & Verified using ModelSim 5 | 6 | This project report provides a detailed account of the design and implementation of the SPI (Serial Peripheral Interface) Master Core in Verilog. The objective of this project was to gain hands-on experience in designing and implementing digital circuits using Verilog, as well as to gain a deeper understanding of the SPI protocol and its applications. 7 | 8 | 9 | SPI MASTER CORE ARCHITECTURE & DESING BLOCKS OF SPI MASTER CORE ARCHITECTURE 10 | ![image](https://github.com/daringpatil3134/SPI_Serial_Peripheral_Interface_Verilog_Modules/assets/83998270/f46a1888-ce12-4d1b-a7f6-89d9c95a936b) 11 | 12 |

13 | 14 |

15 | 16 | Features of SPI Master Core Include Full duplex synchronous serial data, Variable length of transfer word up to 128 bits, MSB or LSB first data transfer, Rx and Tx on both rising or falling edge of serial clock independently, Up to 32 slave select lines, Auto Slave Select, Interrupt Enable & Clock Divider. 17 | 18 | To meet specific system requirements and size constraints on behalf of the functionality, the SPI Master core can be configured by setting the appropriate define directives in the “spi_defines.v” source file. 19 | 20 | CLOCK GENERATOR WAVEFORM 21 | ![image](https://github.com/daringpatil3134/SPI_Serial_Peripheral_Interface_Verilog_Modules/assets/83998270/eb52d3fd-50cc-4678-98b8-019117840e7b) 22 | 23 | SHIFT REGISTER BLOCK WAVEFORM 24 | ![image](https://github.com/daringpatil3134/SPI_Serial_Peripheral_Interface_Verilog_Modules/assets/83998270/b19e3c3e-586c-48db-a2d9-34cc6711b7ae) 25 | 26 | MASTER TEST BENCH OUTPUTS 27 | SET 1: TX_NEG = 1, RX_NEG = 0, LSB = 1, CHAR_LEN = 4 28 | ![image](https://github.com/daringpatil3134/SPI_Serial_Peripheral_Interface_Verilog_Modules/assets/83998270/1668c63c-b995-4d89-b303-be1033795590) 29 | 30 | SET 2: TX_NEG = 1, RX_NEG = 0, LSB = 0, CHAR_LEN = 4 31 | ![image](https://github.com/daringpatil3134/SPI_Serial_Peripheral_Interface_Verilog_Modules/assets/83998270/8047805f-b798-4441-9710-a182c0c8cdfa) 32 | 33 | SET 3: TX_NEG = 0, RX_NEG = 1, LSB = 1, CHAR_LEN = 4 34 | ![image](https://github.com/daringpatil3134/SPI_Serial_Peripheral_Interface_Verilog_Modules/assets/83998270/b1e5b156-44bc-4709-b452-178254348cdb) 35 | 36 | ## License 37 | This project is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. You are free to: 38 | 39 | * Share — copy and redistribute the material in any medium or format 40 | * Adapt — remix, transform, and build upon the material 41 | 42 | Under the following terms: 43 | 44 | * Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 45 | * NonCommercial — You may not use the material for commercial purposes. 46 | * ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. 47 | 48 | For more details, refer to the license. 49 | 50 | For More Details Please Refer The Project Report :- [SPI Design Project Report](https://drive.google.com/file/d/1IAZNETm_P4-l9MCdvRWQpI7wAFOC6SgX/view?usp=share_link) 51 | -------------------------------------------------------------------------------- /spi_clgen.v: -------------------------------------------------------------------------------- 1 | `include "spi_defines.v" 2 | 3 | module spi_clgen (wb_clk_in, 4 | wb_rst, 5 | tip, 6 | go, 7 | last_clk, 8 | divider, 9 | sclk_out, 10 | cpol_0, 11 | cpol_1); 12 | 13 | input wb_clk_in; 14 | input wb_rst; 15 | input tip; 16 | input go; 17 | input last_clk; 18 | input [`SPI_DIVIDER_LEN-1:0] divider; 19 | output sclk_out; 20 | output cpol_0; 21 | output cpol_1; 22 | 23 | reg sclk_out; 24 | reg cpol_0; 25 | reg cpol_1; 26 | 27 | reg [`SPI_DIVIDER_LEN-1:0] cnt; 28 | 29 | 30 | 31 | 32 | // Counter counts half period 33 | always@(posedge wb_clk_in or posedge wb_rst) 34 | begin 35 | if(wb_rst) 36 | begin 37 | cnt <= {{`SPI_DIVIDER_LEN{1'b0}},1'b1}; 38 | end 39 | else if(tip) 40 | begin 41 | if(cnt == (divider + 1)) 42 | begin 43 | cnt <= {{`SPI_DIVIDER_LEN{1'b0}},1'b1}; 44 | end 45 | else 46 | begin 47 | cnt <= cnt + 1; 48 | end 49 | end 50 | else if(cnt == 0) 51 | begin 52 | cnt <= {{`SPI_DIVIDER_LEN{1'b0}},1'b1}; 53 | end 54 | end 55 | 56 | 57 | // Generation of the serial clock 58 | always@(posedge wb_clk_in or posedge wb_rst) 59 | begin 60 | if(wb_rst) 61 | begin 62 | sclk_out <= 1'b0; 63 | end 64 | else if(tip) 65 | begin 66 | if(cnt == (divider + 1)) 67 | begin 68 | if(!last_clk || sclk_out) 69 | sclk_out <= ~sclk_out; 70 | end 71 | end 72 | end 73 | 74 | 75 | // Posedge and negedge detection of sclk 76 | always@(posedge wb_clk_in or posedge wb_rst) 77 | begin 78 | if(wb_rst) 79 | begin 80 | cpol_0 <= 1'b0; 81 | cpol_1 <= 1'b0; 82 | end 83 | else 84 | begin 85 | cpol_0 <= 0; 86 | cpol_1 <= 0; 87 | if(tip) 88 | begin 89 | if(~sclk_out) 90 | begin 91 | if(cnt == divider) 92 | begin 93 | cpol_0 <= 1; 94 | end 95 | end 96 | end 97 | if(tip) 98 | begin 99 | if(sclk_out) 100 | begin 101 | if(cnt == divider) 102 | begin 103 | cpol_1 <= 1; 104 | end 105 | end 106 | end 107 | end 108 | end 109 | 110 | 111 | endmodule -------------------------------------------------------------------------------- /spi_clgen_tb.v: -------------------------------------------------------------------------------- 1 | `include "spi_defines.v" 2 | 3 | module spi_clgen_tb; 4 | 5 | // Define the signals for the clock generator module 6 | reg wb_clk_in; 7 | reg wb_rst; 8 | reg go; 9 | reg tip; 10 | reg last_clk; 11 | wire sclk_out; 12 | wire cpol_0; 13 | wire cpol_1; 14 | reg [`SPI_DIVIDER_LEN-1:0] divider; 15 | 16 | // Instantiate the Device Under Test (DUT) 17 | spi_clgen dut ( 18 | .wb_clk_in(wb_clk_in), 19 | .wb_rst(wb_rst), 20 | .go(go), 21 | .tip(tip), 22 | .last_clk(last_clk), 23 | .divider(divider), 24 | .sclk_out(sclk_out), 25 | .cpol_0(cpol_0), 26 | .cpol_1(cpol_1) 27 | ); 28 | 29 | // Clock generation 30 | always begin 31 | #5 wb_clk_in = ~wb_clk_in; // Toggle the clock every 5 time units 32 | end 33 | 34 | // Initialize signals 35 | initial begin 36 | wb_clk_in <= 0; 37 | wb_rst <= 1; 38 | #13; 39 | wb_rst <= 0; 40 | divider <= 1; 41 | tip <= 0; 42 | go <= 0; 43 | #17; 44 | go <= 1; 45 | #10; 46 | tip <= 1; 47 | last_clk <= 0; 48 | end 49 | 50 | initial begin 51 | #200; 52 | end 53 | 54 | endmodule -------------------------------------------------------------------------------- /spi_defines.v: -------------------------------------------------------------------------------- 1 | // 2 | // Clock Generator Divider Length 3 | // 4 | 5 | `define SPI_DIVIDER_LEN_8 6 | //`define SPI_DIVIDER_LEN_16 7 | //`define SPI_DIVIDER_LEN_24 8 | //`define SPI_DIVIDER_LEN_32 9 | 10 | `ifdef SPI_DIVIDER_LEN_8 11 | `define SPI_DIVIDER_LEN 2 // Can be set from 1 to 8 12 | //`define SPI_DIVIDER_LEN 4 // Can be set from 1 to 8 13 | `endif 14 | 15 | `ifdef SPI_DIVIDER_LEN_16 16 | `define SPI_DIVIDER_LEN 16 // Can be set from 1 to 16 17 | `endif 18 | 19 | `ifdef SPI_DIVIDER_LEN_24 20 | `define SPI_DIVIDER_LEN 24 // Can be set from 1 to 24 21 | `endif 22 | 23 | `ifdef SPI_DIVIDER_LEN_32 24 | `define SPI_DIVIDER_LEN 32 // Can be set from 1 to 32 25 | `endif 26 | 27 | 28 | // 29 | // Max Number Of Bits That Can Be Sent/Recieved At Once 30 | // 31 | 32 | `define SPI_MAX_CHAR_8 33 | //`define SPI_MAX_CHAR_16 34 | //`define SPI_MAX_CHAR_24 35 | //`define SPI_MAX_CHAR_32 36 | //`define SPI_MAX_CHAR_64 37 | //`define SPI_MAX_CHAR_128 38 | 39 | `ifdef SPI_MAX_CHAR_8 40 | `define SPI_MAX_CHAR 8 41 | `define SPI_CHAR_LEN_BITS 3 42 | `endif 43 | 44 | `ifdef SPI_MAX_CHAR_16 45 | `define SPI_MAX_CHAR 16 46 | `define SPI_CHAR_LEN_BITS 4 47 | `endif 48 | 49 | `ifdef SPI_MAX_CHAR_24 50 | `define SPI_MAX_CHAR 24 51 | `define SPI_CHAR_LEN_BITS 5 52 | `endif 53 | 54 | `ifdef SPI_MAX_CHAR_32 55 | `define SPI_MAX_CHAR 32 56 | `define SPI_CHAR_LEN_BITS 5 57 | `endif 58 | 59 | `ifdef SPI_MAX_CHAR_64 60 | `define SPI_MAX_CHAR 64 61 | `define SPI_CHAR_LEN_BITS 6 62 | `endif 63 | 64 | `ifdef SPI_MAX_CHAR_128 65 | `define SPI_MAX_CHAR 128 66 | `define SPI_CHAR_LEN_BITS 7 67 | `endif 68 | 69 | 70 | // 71 | // Number of Devices Select Signals 72 | // 73 | 74 | `define SPI_SS_NB_8 75 | //`define SPI_SS_NB_16 76 | //`define SPI_SS_NB_24 77 | //`define SPI_SS_NB_32 78 | 79 | `ifdef SPI_SS_NB_8 80 | `define SPI_SS_NB 8 81 | `endif 82 | `ifdef SPI_SS_NB_16 83 | `define SPI_SS_NB 16 84 | `endif 85 | `ifdef SPI_SS_NB_24 86 | `define SPI_SS_NB 24 87 | `endif 88 | `ifdef SPI_SS_NB_32 89 | `define SPI_SS_NB 32 90 | `endif 91 | 92 | 93 | // 94 | // Register Offsets 95 | // 96 | 97 | `define SPI_RX_0 5'b00000 98 | `define SPI_RX_1 5'b00100 99 | `define SPI_RX_2 5'b01000 100 | `define SPI_RX_3 5'b01100 101 | `define SPI_TX_0 5'b00000 102 | `define SPI_TX_1 5'b00100 103 | `define SPI_TX_2 5'b01000 104 | `define SPI_TX_3 5'b01100 105 | `define SPI_CTRL 5'b10000 106 | `define SPI_DIVIDE 5'b10100 107 | `define SPI_SS 5'b11000 108 | 109 | 110 | // 111 | // No. of Bits in CTRL Register 112 | // 113 | 114 | `define SPI_CTRL_BIT_NB 14 115 | 116 | 117 | // 118 | // Control Register Bit Position 119 | // 120 | 121 | `define SPI_CTRL_ASS 13 122 | `define SPI_CTRL_IE 12 123 | `define SPI_CTRL_LSB 11 124 | `define SPI_CTRL_TX_NEGEDGE 10 125 | `define SPI_CTRL_RX_NEGEDGE 9 126 | `define SPI_CTRL_GO 8 127 | `define SPI_CTRL_RES_1 7 128 | `define SPI_CTRL_CHAR_LEN 6:0 -------------------------------------------------------------------------------- /spi_shift_reg.v: -------------------------------------------------------------------------------- 1 | `include "spi_defines.v" 2 | 3 | module spi_shift_reg(rx_negedge, 4 | tx_negedge, 5 | byte_sel, 6 | latch, 7 | len, 8 | p_in, 9 | wb_clk_in, 10 | wb_rst, 11 | go, 12 | miso, 13 | lsb, 14 | sclk, 15 | cpol_0, 16 | cpol_1, 17 | p_out, 18 | last, 19 | mosi, 20 | tip); 21 | 22 | input rx_negedge, 23 | tx_negedge, 24 | wb_clk_in, 25 | wb_rst, 26 | go,miso, 27 | lsb, 28 | sclk, 29 | cpol_0, 30 | cpol_1; 31 | 32 | input [3:0] byte_sel,latch; 33 | input [`SPI_CHAR_LEN_BITS-1:0] len; 34 | input [31:0] p_in; 35 | 36 | output [`SPI_MAX_CHAR-1:0] p_out; 37 | output reg tip,mosi; 38 | output last; 39 | 40 | reg [`SPI_CHAR_LEN_BITS:0] char_count; 41 | reg [`SPI_MAX_CHAR-1:0] master_data; // shift register 42 | reg [`SPI_CHAR_LEN_BITS:0] tx_bit_pos; // next bit position 43 | reg [`SPI_CHAR_LEN_BITS:0] rx_bit_pos; // next bit position 44 | wire rx_clk; // rx clock enable 45 | wire tx_clk; // tx clock enable 46 | 47 | // Character bit counter............... 48 | always@(posedge wb_clk_in or posedge wb_rst) 49 | begin 50 | if(wb_rst) 51 | begin 52 | char_count <= 0; 53 | end 54 | else 55 | begin 56 | if(tip) 57 | begin 58 | if(cpol_0) 59 | begin 60 | char_count <= char_count - 1; 61 | end 62 | end 63 | else 64 | char_count <= {1'b0,len}; // This stores the character bits other than 128 bits 65 | end 66 | end 67 | 68 | // Calculate transfer in progress 69 | always@(posedge wb_clk_in or posedge wb_rst) 70 | begin 71 | if(wb_rst) 72 | begin 73 | tip <= 0; 74 | end 75 | else 76 | begin 77 | if(go && ~tip) 78 | begin 79 | tip <= 1; 80 | end 81 | else if(last && tip && cpol_0) 82 | begin 83 | tip <= 0; 84 | end 85 | end 86 | end 87 | 88 | // Calculate last 89 | assign last = ~(|char_count); 90 | 91 | // Calculate the serial out 92 | always@(posedge wb_clk_in or posedge wb_rst) 93 | begin 94 | if(wb_rst) 95 | begin 96 | mosi <= 0; 97 | end 98 | else 99 | begin 100 | if(tx_clk) 101 | begin 102 | mosi <= master_data[tx_bit_pos[`SPI_CHAR_LEN_BITS-1:0]]; 103 | end 104 | end 105 | end 106 | 107 | // Calculate tx_clk,rx_clk 108 | assign tx_clk = ((tx_negedge)?cpol_1 : cpol_0) && !last; 109 | assign rx_clk = ((rx_negedge)?cpol_1 : cpol_0) && (!last || sclk); 110 | 111 | // Calculate TX_BIT Position 112 | always@(lsb,len,char_count) 113 | begin 114 | if(lsb) 115 | begin 116 | tx_bit_pos = ({~{|len},len}-char_count); 117 | end 118 | else 119 | begin 120 | tx_bit_pos = char_count-1; 121 | end 122 | end 123 | 124 | // Calculate RX_BIT Position based on rx_negedge as miso depends on rx_clk 125 | always@(lsb,len,rx_negedge,char_count) 126 | begin 127 | if(lsb) 128 | begin 129 | if(rx_negedge) 130 | rx_bit_pos = {~(|len),len}-(char_count+1); 131 | else 132 | rx_bit_pos = {~(|len),len}-char_count; 133 | end 134 | else 135 | begin 136 | if(rx_negedge) 137 | begin 138 | rx_bit_pos = char_count; 139 | end 140 | else 141 | begin 142 | rx_bit_pos = char_count-1; 143 | end 144 | end 145 | end 146 | 147 | // Calculate p_out 148 | assign p_out = master_data; 149 | 150 | // Latching of data 151 | always@(posedge wb_clk_in or posedge wb_rst) 152 | begin 153 | if(wb_rst) 154 | master_data <= {`SPI_MAX_CHAR{1'b0}}; 155 | 156 | // Recieving bits from the parallel line 157 | `ifdef SPI_MAX_CHAR_128 158 | else if(latch[0] && !tip) // TX0 is selected 159 | begin 160 | if(byte_sel[0]) 161 | begin 162 | master_data[7:0] <= p_in[7:0]; 163 | end 164 | if(byte_sel[1]) 165 | begin 166 | master_data[15:8] <= p_in[15:8]; 167 | end 168 | if(byte_sel[2]) 169 | begin 170 | master_data[23:16] <= p_in[23:16]; 171 | end 172 | if(byte_sel[3]) 173 | begin 174 | master_data[31:24] <= p_in[31:24]; 175 | end 176 | end 177 | else if(latch[1] && !tip) // TX1 is selected 178 | begin 179 | if(byte_sel[0]) 180 | begin 181 | master_data[39:32] <= p_in[7:0]; 182 | end 183 | if(byte_sel[1]) 184 | begin 185 | master_data[47:40] <= p_in[15:8]; 186 | end 187 | if(byte_sel[2]) 188 | begin 189 | master_data[55:48] <= p_in[23:16]; 190 | end 191 | if(byte_sel[3]) 192 | begin 193 | master_data[63:56] <= p_in[31:24]; 194 | end 195 | end 196 | else if(latch[2] && !tip) // TX2 is selected 197 | begin 198 | if(byte_sel[0]) 199 | begin 200 | master_data[71:64] <= p_in[7:0]; 201 | end 202 | if(byte_sel[1]) 203 | begin 204 | master_data[79:72] <= p_in[15:8]; 205 | end 206 | if(byte_sel[2]) 207 | begin 208 | master_data[87:80] <= p_in[23:16]; 209 | end 210 | if(byte_sel[3]) 211 | begin 212 | master_data[95:88] <= p_in[31:24]; 213 | end 214 | end 215 | else if(latch[3] && !tip) // TX3 is selected 216 | begin 217 | if(byte_sel[0]) 218 | begin 219 | master_data[103:96] <= p_in[7:0]; 220 | end 221 | if(byte_sel[1]) 222 | begin 223 | master_data[111:104] <= p_in[15:8]; 224 | end 225 | if(byte_sel[2]) 226 | begin 227 | master_data[119:112] <= p_in[23:16]; 228 | end 229 | if(byte_sel[3]) 230 | begin 231 | master_data[127:120] <= p_in[31:24]; 232 | end 233 | end 234 | `else 235 | `ifdef SPI_MAX_CHAR_64 236 | else if(latch[0] && !tip) // TX0 is selected 237 | begin 238 | if(byte_sel[0]) 239 | begin 240 | master_data[7:0] <= p_in[7:0]; 241 | end 242 | if(byte_sel[1]) 243 | begin 244 | master_data[15:8] <= p_in[15:8]; 245 | end 246 | if(byte_sel[2]) 247 | begin 248 | master_data[23:16] <= p_in[23:16]; 249 | end 250 | if(byte_sel[3]) 251 | begin 252 | master_data[31:24] <= p_in[31:24]; 253 | end 254 | end 255 | else if(latch[1] && !tip) // TX1 is selected 256 | begin 257 | if(byte_sel[0]) 258 | begin 259 | master_data[39:32] <= p_in[7:0]; 260 | end 261 | if(byte_sel[1]) 262 | begin 263 | master_data[47:40] <= p_in[15:8]; 264 | end 265 | if(byte_sel[2]) 266 | begin 267 | master_data[55:48] <= p_in[23:16]; 268 | end 269 | if(byte_sel[3]) 270 | begin 271 | master_data[63:56] <= p_in[31:24]; 272 | end 273 | end 274 | `else 275 | else if(latch[0] && !tip) //TX0 is selected 276 | begin 277 | 278 | `ifdef SPI_MAX_CHAR_8 279 | if(byte_sel[0]) 280 | begin 281 | master_data[7:0] <= p_in[7:0]; 282 | end 283 | `endif 284 | 285 | `ifdef SPI_MAX_CHAR_16 286 | if(byte_sel[0]) 287 | begin 288 | master_data[7:0] <= p_in[7:0]; 289 | end 290 | if(byte_sel[1]) 291 | begin 292 | master_data[15:8] <= p_in[15:8]; 293 | end 294 | `endif 295 | 296 | `ifdef SPI_MAX_CHAR_24 297 | if(byte_sel[0]) 298 | begin 299 | master_data[7:0] <= p_in[7:0]; 300 | end 301 | if(byte_sel[1]) 302 | begin 303 | master_data[15:8] <= p_in[15:8]; 304 | end 305 | if(byte_sel[2]) 306 | begin 307 | master_data[23:16] <= p_in[23:16]; 308 | end 309 | `endif 310 | 311 | `ifdef SPI_MAX_CHAR_32 312 | if(byte_sel[0]) 313 | begin 314 | master_data[7:0] <= p_in[7:0]; 315 | end 316 | if(byte_sel[1]) 317 | begin 318 | master_data[15:8] <= p_in[15:8]; 319 | end 320 | if(byte_sel[2]) 321 | begin 322 | master_data[23:16] <= p_in[23:16]; 323 | end 324 | if(byte_sel[3]) 325 | begin 326 | master_data[31:24] <= p_in[31:24]; 327 | end 328 | `endif 329 | end 330 | `endif 331 | `endif 332 | 333 | // Receiving bits from the serial line 334 | else 335 | begin 336 | if(rx_clk) 337 | master_data[rx_bit_pos[`SPI_CHAR_LEN_BITS-1:0]] <= miso; 338 | end 339 | end 340 | endmodule -------------------------------------------------------------------------------- /spi_shift_reg_tb.v: -------------------------------------------------------------------------------- 1 | `include "spi_defines.v" 2 | 3 | `timescale 1us/1ns 4 | 5 | module spi_shift_reg_tb; 6 | 7 | reg rx_negedge, 8 | tx_negedge, 9 | wb_clk_in, 10 | wb_rst, 11 | go, 12 | miso, 13 | lsb, 14 | sclk, 15 | cpol_0, 16 | cpol_1; 17 | 18 | reg [3:0] byte_sel,latch ; 19 | reg [`SPI_CHAR_LEN_BITS-1:0] len; 20 | reg [`SPI_MAX_CHAR-1:0] p_in; 21 | 22 | wire [`SPI_MAX_CHAR-1:0]p_out; 23 | wire tip,mosi; 24 | wire last; 25 | 26 | parameter T = 10; 27 | parameter [`SPI_DIVIDER_LEN-1:0] divider_value = 4'b0010; 28 | 29 | // Instantiate the DUT 30 | spi_shift_reg DUT (rx_negedge, 31 | tx_negedge, 32 | byte_sel, 33 | latch, 34 | len, 35 | p_in, 36 | wb_clk_in, 37 | wb_rst, 38 | go, 39 | miso, 40 | lsb, 41 | sclk, 42 | cpol_0, 43 | cpol_1, 44 | p_out, 45 | last, 46 | mosi, 47 | tip); 48 | 49 | initial 50 | begin 51 | wb_clk_in = 1'b0; 52 | forever 53 | #(T/2) wb_clk_in = ~wb_clk_in; 54 | end 55 | 56 | initial 57 | begin 58 | sclk = 1'b0; 59 | forever 60 | begin 61 | repeat(divider_value + 1) 62 | @(posedge wb_clk_in); 63 | sclk = ~sclk; 64 | end 65 | end 66 | 67 | task rst(); 68 | begin 69 | wb_rst = 1'b1; 70 | #13; 71 | wb_rst = 1'b0; 72 | end 73 | endtask 74 | 75 | initial 76 | begin 77 | cpol_1 = 1'b0; 78 | forever 79 | begin 80 | repeat(divider_value*2 + 1) 81 | @(posedge wb_clk_in); 82 | cpol_1 = 1'b1; 83 | @(posedge wb_clk_in) 84 | cpol_1 = 1'b0; 85 | repeat(divider_value*2 + 1) 86 | @(posedge wb_clk_in); 87 | cpol_1 = 1'b1; 88 | @(posedge wb_clk_in) 89 | cpol_1 = 1'b0; 90 | end 91 | end 92 | 93 | initial 94 | begin 95 | cpol_0 = 1'b0; 96 | repeat(divider_value) 97 | @(posedge wb_clk_in); 98 | cpol_0 = 1'b1; 99 | @(posedge wb_clk_in) 100 | cpol_0 = 1'b0; 101 | forever 102 | begin 103 | repeat(divider_value*2 + 1) 104 | @(posedge wb_clk_in); 105 | cpol_0 = 1'b1; 106 | @(posedge wb_clk_in) 107 | cpol_0 = 1'b0; 108 | end 109 | end 110 | 111 | task t1; 112 | begin 113 | @(negedge wb_clk_in) 114 | rx_negedge = 1'b1; 115 | tx_negedge = 1'b0; 116 | end 117 | endtask 118 | 119 | task t2; 120 | begin 121 | @(negedge wb_clk_in) 122 | rx_negedge = 1'b0; 123 | tx_negedge = 1'b1; 124 | end 125 | endtask 126 | 127 | task initialize; 128 | begin 129 | len = 3'b000; 130 | lsb = 1'b0; 131 | p_in = 32'h0000; 132 | byte_sel = 4'b0000; 133 | latch = 4'b0000; 134 | go = 1'b0; 135 | miso = 1'b0; 136 | cpol_1 = 1'b0; 137 | cpol_0 = 1'b0; 138 | end 139 | endtask 140 | 141 | initial 142 | begin 143 | initialize; 144 | rst; 145 | @(negedge wb_clk_in) 146 | len = 32'h0004; 147 | lsb = 1'b1; 148 | p_in = 32'haa55; 149 | latch = 4'b0001; 150 | byte_sel = 4'b0001; 151 | t1; 152 | #10; 153 | go = 1'b1; 154 | #40; 155 | miso = 1'b1; 156 | #20; 157 | miso = 1'b0; 158 | #20; 159 | miso = 1'b1; 160 | #20; 161 | miso = 1'b0; 162 | #20; 163 | miso = 1'b1; 164 | #60; 165 | miso = 1'b0; 166 | #30; 167 | #100; 168 | end 169 | 170 | endmodule 171 | -------------------------------------------------------------------------------- /spi_slave.v: -------------------------------------------------------------------------------- 1 | `include "spi_defines.v" 2 | 3 | module spi_slave (input sclk,mosi, 4 | input [`SPI_SS_NB-1:0]ss_pad_o, 5 | output miso); 6 | 7 | reg rx_slave = 1'b0; //Slave recieving from SPI_MASTER 8 | reg tx_slave = 1'b0; //Slave transmitting to SPI_MASTER 9 | 10 | //Initial value of temp is 0 11 | reg [127:0]temp1 = 0; 12 | reg [127:0]temp2 = 0; 13 | 14 | reg miso1 = 1'b0; 15 | reg miso2 = 1'b1; 16 | 17 | always@(posedge sclk) 18 | begin 19 | if ((ss_pad_o != 8'b11111111) && ~rx_slave && tx_slave) //Posedge of the Serial Clock 20 | begin 21 | temp1 <= {temp1[126:0],mosi}; 22 | end 23 | end 24 | 25 | always@(negedge sclk) 26 | begin 27 | if ((ss_pad_o != 8'b11111111) && rx_slave && ~tx_slave) //Negedge of the Serial Clock 28 | begin 29 | temp2 <= {temp2[126:0],mosi}; 30 | end 31 | end 32 | 33 | always@(negedge sclk) 34 | begin 35 | if (rx_slave && ~tx_slave) //Posedge of the Serial Clock 36 | begin 37 | miso1 <= temp1[127]; 38 | end 39 | end 40 | 41 | always@(negedge sclk) 42 | begin 43 | if (~rx_slave && tx_slave) //Posedge of the Serial Clock 44 | begin 45 | miso2 <= temp2[127]; 46 | end 47 | end 48 | 49 | assign miso = miso1 || miso2; 50 | 51 | endmodule 52 | -------------------------------------------------------------------------------- /spi_top.v: -------------------------------------------------------------------------------- 1 | `include "spi_defines.v" 2 | 3 | //SPI Master Core 4 | 5 | module spi_top(wb_clk_in, 6 | wb_rst_in, 7 | wb_adr_in, 8 | wb_dat_o, 9 | wb_sel_in, 10 | wb_we_in, 11 | wb_stb_in, 12 | wb_cyc_in, 13 | wb_ack_out, 14 | wb_int_o, 15 | wb_dat_in, 16 | ss_pad_o, 17 | sclk_out, 18 | mosi, 19 | miso); 20 | 21 | input wb_clk_in, 22 | wb_rst_in, 23 | wb_we_in, 24 | wb_stb_in, 25 | wb_cyc_in, 26 | miso; 27 | 28 | input [4:0] wb_adr_in; 29 | input [31:0] wb_dat_in; 30 | input [3:0] wb_sel_in; 31 | 32 | output reg [31:0] wb_dat_o; 33 | 34 | output wb_ack_out,wb_int_o,sclk_out,mosi; 35 | 36 | reg wb_ack_out,wb_int_o; 37 | 38 | output [`SPI_SS_NB-1:0] ss_pad_o; 39 | 40 | //Internal signals...................................... 41 | 42 | wire rx_negedge; //miso is sampled on negative edge 43 | wire tx_negedge; //mosi is driven on negative edge 44 | wire [3:0] spi_tx_sel; //tx_1 register selected 45 | wire [`SPI_CHAR_LEN_BITS-1:0] char_len; //char len 46 | wire go,ie,ass; //go 47 | wire lsb; 48 | wire cpol_0,cpol_1,last,tip; 49 | wire [`SPI_MAX_CHAR-1:0] rx; 50 | wire spi_divider_sel,spi_ctrl_sel,spi_ss_sel; 51 | reg [`SPI_DIVIDER_LEN-1:0] divider; //Divider register 52 | reg [31:0] wb_temp_dat; 53 | reg [`SPI_CTRL_BIT_NB-1:0] ctrl; //Control and status register 54 | reg [`SPI_SS_NB-1:0] ss; //Slave select register 55 | 56 | //Instantiate the SPI_CLK_GENERATOR Module 57 | spi_clgen SC(wb_clk_in, 58 | wb_rst_in, 59 | go, 60 | tip, 61 | last, 62 | divider, 63 | sclk_out, 64 | cpol_0, 65 | cpol_1); 66 | 67 | //Instantiate the SPI shift register 68 | spi_shift_reg SR(rx_negedge, 69 | tx_negedge, 70 | wb_sel_in, 71 | (spi_tx_sel[3:0] & {4{wb_we_in}}), 72 | char_len, 73 | wb_dat_in, 74 | wb_clk_in, 75 | wb_rst_in, 76 | go, 77 | miso, 78 | lsb, 79 | sclk_out, 80 | cpol_0, 81 | cpol_1, 82 | rx, 83 | last, 84 | mosi, 85 | tip); 86 | 87 | //Address decoder 88 | assign spi_divider_sel = wb_cyc_in & wb_stb_in & (wb_adr_in == (5'b10100)); 89 | assign spi_ctrl_sel = wb_cyc_in & wb_stb_in & (wb_adr_in == (5'b10000)); 90 | assign spi_ss_sel = wb_cyc_in & wb_stb_in & (wb_adr_in == (5'b11000)); 91 | assign spi_tx_sel[0] = wb_cyc_in & wb_stb_in & (wb_adr_in == (5'b00000)); 92 | assign spi_tx_sel[1] = wb_cyc_in & wb_stb_in & (wb_adr_in == (5'b00100)); 93 | assign spi_tx_sel[2] = wb_cyc_in & wb_stb_in & (wb_adr_in == (5'b01000)); 94 | assign spi_tx_sel[3] = wb_cyc_in & wb_stb_in & (wb_adr_in == (5'b01100)); 95 | 96 | //Read from registers 97 | always@(*) 98 | begin 99 | case(wb_adr_in) 100 | `ifdef SPI_MAX_CHAR_128 101 | `SPI_RX_0 : wb_temp_dat = rx[31:0]; 102 | `SPI_RX_1 : wb_temp_dat = rx[63:32]; 103 | `SPI_RX_2 : wb_temp_dat = rx[95:64]; 104 | `SPI_RX_3 : wb_temp_dat = rx[127:96]; 105 | `else 106 | `ifdef SPI_MAX_CHAR_64 107 | `SPI_RX_0 : wb_temp_dat = rx[31:0]; 108 | `SPI_RX_1 : wb_temp_dat = rx[63:32]; 109 | `SPI_RX_2 : wb_temp_dat = 0; 110 | `SPI_RX_3 : wb_temp_dat = 0; 111 | `else 112 | `SPI_RX_0 : wb_temp_dat = rx[`SPI_MAX_CHAR-1:0]; 113 | `SPI_RX_1 : wb_temp_dat = 32'b0; 114 | `SPI_RX_2 : wb_temp_dat = 32'b0; 115 | `SPI_RX_3 : wb_temp_dat = 32'b0; 116 | `endif 117 | `endif 118 | `SPI_CTRL : wb_temp_dat = ctrl; 119 | `SPI_DIVIDE : wb_temp_dat = divider; 120 | `SPI_SS : wb_temp_dat = ss; 121 | default : wb_temp_dat = 32'dx; 122 | endcase 123 | end 124 | 125 | //WB data out 126 | always@(posedge wb_clk_in or posedge wb_rst_in) 127 | begin 128 | if(wb_rst_in) 129 | wb_dat_o <= 32'd0; 130 | else 131 | wb_dat_o <= wb_temp_dat; 132 | end 133 | 134 | //WB acknowledge 135 | always@(posedge wb_clk_in or posedge wb_rst_in) 136 | begin 137 | if(wb_rst_in) 138 | begin 139 | wb_ack_out <= 0; 140 | end 141 | else 142 | begin 143 | wb_ack_out <= wb_cyc_in & wb_stb_in & ~wb_ack_out; 144 | end 145 | end 146 | 147 | //Interrupt 148 | always@(posedge wb_clk_in or posedge wb_rst_in) 149 | begin 150 | if (wb_rst_in) 151 | wb_int_o <= 1'b0; 152 | else if (ie && tip && last && cpol_0) 153 | wb_int_o <= 1'b1; 154 | else if (wb_ack_out) 155 | wb_int_o <= 1'b0; 156 | end 157 | 158 | //Selecting Slave device from a group of 32 slave devices 159 | assign ss_pad_o = ~((ss & {`SPI_SS_NB{tip & ass}}) | (ss & {`SPI_SS_NB{!ass}})); 160 | 161 | //Divider register 162 | always@(posedge wb_clk_in or posedge wb_rst_in) 163 | begin 164 | if(wb_rst_in) 165 | begin 166 | divider <= 0; 167 | end 168 | else if(spi_divider_sel && wb_we_in && !tip) 169 | begin 170 | `ifdef SPI_DIVIDER_LEN_8 171 | if(wb_sel_in[0]) 172 | divider <= 1; 173 | `endif 174 | `ifdef SPI_DIVIDER_LEN_16 175 | if(wb_sel_in[0]) 176 | divider[7:0] <= wb_dat_in[7:0]; 177 | if(wb_sel_in[1]) 178 | divider[15:8] <= wb_dat_in[`SPI_DIVIDER_LEN-1:8]; 179 | `endif 180 | `ifdef SPI_DIVIDER_LEN_24 181 | if(wb_sel_in[0]) 182 | divider[7:0] <= wb_dat_in[7:0]; 183 | if(wb_sel_in[1]) 184 | divider[15:8] <= wb_dat_in[15:8]; 185 | if(wb_sel_in[2]) 186 | divider[23:16] <= wb_dat_in[`SPI_DIVIDER_LEN-1:16]; 187 | `endif 188 | `ifdef SPI_DIVIDER_LEN_32 189 | if(wb_sel_in[0]) 190 | divider[7:0] <= wb_dat_in[7:0]; 191 | if(wb_sel_in[1]) 192 | divider[15:8] <= wb_dat_in[15:8]; 193 | if(wb_sel_in[2]) 194 | divider[23:16] <= wb_dat_in[23:16]; 195 | if(wb_sel_in[3]) 196 | divider[31:24] <= wb_dat_in[`SPI_DIVIDER_LEN-1:24]; 197 | `endif 198 | end 199 | end 200 | 201 | //Control and status register 202 | always@(posedge wb_clk_in or posedge wb_rst_in) 203 | begin 204 | if(wb_rst_in) 205 | ctrl <= 0; 206 | else 207 | begin 208 | if(spi_ctrl_sel && wb_we_in && !tip) 209 | begin 210 | if(wb_sel_in[0]) 211 | ctrl[7:0] <= wb_dat_in[7:0] | {7'd0, ctrl[0]}; 212 | if(wb_sel_in[1]) 213 | ctrl[`SPI_CTRL_BIT_NB-1:8] <= wb_dat_in[`SPI_CTRL_BIT_NB-1:8]; 214 | end 215 | else if(tip && last && cpol_0) 216 | ctrl[`SPI_CTRL_GO] <= 1'b0; 217 | end 218 | end 219 | 220 | assign rx_negedge = ctrl[`SPI_CTRL_RX_NEGEDGE]; 221 | assign tx_negedge = ctrl[`SPI_CTRL_TX_NEGEDGE]; 222 | assign lsb = ctrl[`SPI_CTRL_LSB]; 223 | assign ie = ctrl[`SPI_CTRL_IE]; 224 | assign ass = ctrl[`SPI_CTRL_ASS]; 225 | assign go = ctrl[`SPI_CTRL_GO]; 226 | assign char_len = ctrl[`SPI_CTRL_CHAR_LEN]; 227 | 228 | //Slave select 229 | always@(posedge wb_clk_in or posedge wb_rst_in) 230 | begin 231 | if(wb_rst_in) 232 | begin 233 | ss <= 0; 234 | end 235 | else 236 | begin 237 | if(spi_ss_sel && wb_we_in && !tip) 238 | begin 239 | `ifdef SPI_SS_NB_8 240 | if(wb_sel_in[0]) 241 | ss <= wb_dat_in[`SPI_SS_NB-1:0]; 242 | `endif 243 | 244 | `ifdef SPI_SS_NB_16 245 | if(wb_sel_in[0]) 246 | ss <= wb_dat_in[7:0]; 247 | if(wb_sel_in[1]) 248 | ss <= wb_dat_in[`SPI_SS_NB-1:8]; 249 | `endif 250 | 251 | `ifdef SPI_SS_NB_24 252 | if(wb_sel_in[0]) 253 | ss <= wb_dat_in[7:0]; 254 | if(wb_sel_in[1]) 255 | ss <= wb_dat_in[15:8]; 256 | if(wb_sel_in[2]) 257 | ss <= wb_dat_in[`SPI_SS_NB-1:16]; 258 | `endif 259 | 260 | `ifdef SPI_SS_NB_32 261 | if(wb_sel_in[0]) 262 | ss <= wb_dat_in[7:0]; 263 | if(wb_sel_in[1]) 264 | ss <= wb_dat_in[15:8]; 265 | if(wb_sel_in[2]) 266 | ss <= wb_dat_in[23:16]; 267 | if(wb_sel_in[3]) 268 | ss <= wb_dat_in[`SPI_SS_NB-1:24]; 269 | `endif 270 | end 271 | end 272 | end 273 | 274 | endmodule -------------------------------------------------------------------------------- /tb.v: -------------------------------------------------------------------------------- 1 | `include "spi_defines.v" 2 | 3 | module tb; 4 | 5 | reg wb_clk_in, wb_rst_in; 6 | wire wb_we_in, wb_stb_in, wb_cyc_in, miso; 7 | 8 | wire [4:0]wb_adr_in; 9 | wire [31:0]wb_dat_in; 10 | wire [3:0]wb_sel_in; 11 | wire [31:0]wb_dat_o; 12 | 13 | wire wb_ack_out, wb_int_o, sclk_out, mosi; 14 | 15 | wire [`SPI_SS_NB-1:0]ss_pad_o; 16 | 17 | parameter T = 20; 18 | 19 | wishbone_master MASTER(wb_clk_in, 20 | wb_rst_in, 21 | wb_ack_out, 22 | wb_err_in, 23 | wb_dat_o, 24 | wb_adr_in, 25 | wb_cyc_in, 26 | wb_stb_in, 27 | wb_we_in, 28 | wb_dat_in, 29 | wb_sel_in); 30 | 31 | spi_top SPI_CORE(wb_clk_in, 32 | wb_rst_in, 33 | wb_adr_in, 34 | wb_dat_o, 35 | wb_sel_in, 36 | wb_we_in, 37 | wb_stb_in, 38 | wb_cyc_in, 39 | wb_ack_out, 40 | wb_int_o, 41 | wb_dat_in, 42 | ss_pad_o, 43 | sclk_out, 44 | mosi, 45 | miso); 46 | 47 | spi_slave SLAVE(sclk_out, 48 | mosi, 49 | ss_pad_o, 50 | miso); 51 | 52 | initial 53 | begin 54 | wb_clk_in = 1'b0; 55 | forever 56 | #(T/2) wb_clk_in = ~wb_clk_in; 57 | end 58 | 59 | task rst(); 60 | begin 61 | wb_rst_in = 1'b1; 62 | #13; 63 | wb_rst_in = 1'b0; 64 | end 65 | endtask 66 | 67 | //tx_neg=1, rx_neg=0, LSB=1, char_len=4 68 | /*initial 69 | begin 70 | rst; 71 | //initialize the WISHBONE output signals 72 | MASTER.initialize; 73 | //configure control register with go_busy being low 74 | MASTER.single_write(5'h10,32'h0000_3c04,4'b1111); 75 | //configure divider with go_busy being low 76 | MASTER.single_write(5'h14,32'h0000_0004,4'b1111); 77 | //configure slave register with go_busy being low 78 | MASTER.single_write(5'h18,32'h0000_0001,4'b1111); 79 | //configure tx register with go_busy being low and processor is sending 4 bits 80 | MASTER.single_write(5'h00,32'h0000_236f,4'b1111); 81 | //configure control register with go_busy beingg high 82 | MASTER.single_write(5'h10,32'h0000_3d04,4'b1111); 83 | repeat(100) 84 | @(negedge wb_clk_in); 85 | $finish; 86 | #10000 $finish; 87 | end*/ 88 | 89 | //tx_neg=1, rx__neg=0, LSB=0, char_len=4 90 | /*initial 91 | begin 92 | rst; 93 | //initialize the WISHBONE output signals 94 | MASTER.initialize; 95 | //configure control register with go_busy being low 96 | MASTER.single_write(5'h10,32'h0000_3404,4'b1111); 97 | //configure divider with go_busy being low 98 | MASTER.single_write(5'h14,32'h0000_0004,4'b1111); 99 | //configure slave register with go_busy being low 100 | MASTER.single_write(5'h18,32'h0000_0001,4'b1111); 101 | //configure tx register with go_busy being low and processor is sending 4 bits 102 | MASTER.single_write(5'h00,32'h0000_236f,4'b1111); 103 | //configure control register with go_busy beingg high 104 | MASTER.single_write(5'h10,32'h0000_3504,4'b1111); 105 | repeat(100) 106 | @(negedge wb_clk_in); 107 | $finish; 108 | #10000 $finish; 109 | end*/ 110 | 111 | //tx_neg=0, rx__neg=1, LSB=1, char_len=4 112 | initial 113 | begin 114 | rst; 115 | //initialize the WISHBONE output signals 116 | MASTER.initialize; 117 | //configure control register with go_busy being low 118 | MASTER.single_write(5'h10,32'h0000_3A04,4'b1111); 119 | //configure divider with go_busy being low 120 | MASTER.single_write(5'h14,32'h0000_0004,4'b1111); 121 | //configure slave register with go_busy being low 122 | MASTER.single_write(5'h18,32'h0000_0001,4'b1111); 123 | //configure tx register with go_busy being low and processor is sending 4 bits 124 | MASTER.single_write(5'h00,32'h0000_236f,4'b1111); 125 | //configure control register with go_busy beingg high 126 | MASTER.single_write(5'h10,32'h0000_3B04,4'b1111); 127 | repeat(100) 128 | @(negedge wb_clk_in); 129 | $finish; 130 | #10000 $finish; 131 | end 132 | 133 | endmodule -------------------------------------------------------------------------------- /wishbone_master.v: -------------------------------------------------------------------------------- 1 | module wishbone_master(input clk_in, rst_in,ack_in,err_in, 2 | input [31:0]dat_in, 3 | output reg [4:0]adr_o, 4 | output reg cyc_o,stb_o,we_o, 5 | output reg [31:0]dat_o, 6 | output reg [3:0]sel_o); 7 | 8 | //Internal Signals 9 | integer adr_temp,sel_temp,dat_temp; 10 | reg we_temp,cyc_temp,stb_temp; 11 | 12 | //Initialize task 13 | task initialize; 14 | begin 15 | {adr_temp,cyc_temp,stb_temp,we_temp,dat_temp,sel_temp} = 0; 16 | end 17 | endtask 18 | 19 | //Wishbone Bus Cycles Single Read/Write 20 | task single_write; 21 | input [4:0]adr; 22 | input [31:0]dat; 23 | input [3:0]sel; 24 | begin 25 | @(negedge clk_in); 26 | adr_temp = adr; 27 | sel_temp = sel; 28 | we_temp = 1; 29 | dat_temp = dat; 30 | cyc_temp = 1; 31 | stb_temp = 1; 32 | @(negedge clk_in); 33 | wait(~ack_in) 34 | @(negedge clk_in); 35 | adr_temp = 5'dz; 36 | sel_temp = 4'd0; 37 | we_temp = 1'b0; 38 | dat_temp = 32'dz; 39 | cyc_temp = 1'b0; 40 | stb_temp = 1'b0; 41 | end 42 | endtask 43 | 44 | always@(posedge clk_in) 45 | begin 46 | adr_o <= adr_temp; 47 | end 48 | 49 | always@(posedge clk_in) 50 | begin 51 | we_o <= we_temp; 52 | end 53 | 54 | always@(posedge clk_in) 55 | begin 56 | dat_o <= dat_temp; 57 | end 58 | 59 | always@(posedge clk_in) 60 | begin 61 | sel_o <= sel_temp; 62 | end 63 | 64 | always@(posedge clk_in) 65 | begin 66 | if(rst_in) 67 | cyc_o <= 0; 68 | else 69 | cyc_o <= cyc_temp; 70 | end 71 | 72 | always@(posedge clk_in) 73 | begin 74 | if(rst_in) 75 | stb_o <= 0; 76 | else 77 | stb_o <= stb_temp; 78 | end 79 | 80 | endmodule --------------------------------------------------------------------------------