1 /*********************************************************************************************** 2 * SPI MASTER 3 * January 2007 4 ************************************************************************************************/ 5 `timescale 10ns/1ns 6 module SPI_Master ( miso, mosi, sclk, ss, data_bus, CS, addr, pro_clk, WR, RD); 7 8 inout [7:0] data_bus; // 8 bit bidirectional data bus 9 input pro_clk; // Host Processor clock 10 input miso; // Master in slave out 11 input [1:0] addr; // A1 and A0, lower bits of address bus 12 input CS; // Chip Select 13 input WR, RD; // Write and read enables 14 15 output mosi; // Master out slave in 16 output sclk; // SPI clock 17 output [7:0] ss; // 8 slave select lines 18 19 reg [7:0] shift_register; // Shift register 20 reg [7:0] txdata; // Transmit buffer 21 reg [7:0] rxdata; // Receive buffer 22 reg [7:0] data_out; // Data output register 23 reg [7:0] data_out_en; // Data output enable 24 reg [7:0] control, status; // Control Register COntrols things like ss, CPOL, CPHA, clock divider 25 // Status Register is a dummy register never used. 26 27 reg [7:0] clk_divide; // Clock divide counter 28 reg [3:0] count; // SPI word length counter 29 reg sclk; 30 reg slave_cs; // Slave cs flag 31 reg mosi; // Master out slave in 32 reg spi_word_send; // Will send a new spi word. 33 34 wire [7:0] data_bus; 35 wire [7:0] data_in = data_bus; 36 wire spi_clk_gen; 37 wire [2:0] divide_factor = control[2:0]; 38 wire CPOL = control[3]; 39 wire CPHA = control[4]; 40 wire [7:0]ss; 41 42 43 /* Slave Select lines */ 44 assign ss[7] = ~( control[7] & control[6] & control[5] & (~slave_cs)); 45 assign ss[6] = ~( control[7] & control[6] & ~control[5] & (~slave_cs)); 46 assign ss[5] = ~( control[7] & ~control[6] & control[5] & (~slave_cs)); 47 assign ss[4] = ~( control[7] & ~control[6] & ~control[5] & (~slave_cs)); 48 assign ss[3] = ~(~control[7] & control[6] & control[5] & (~slave_cs)); 49 assign ss[2] = ~(~control[7] & control[6] & ~control[5] & (~slave_cs)); 50 assign ss[1] = ~(~control[7] & ~control[6] & control[5] & (~slave_cs)); 51 assign ss[0] = ~(~control[7] & ~control[6] & ~control[5] & (~slave_cs)); 52 53 /* clock divide */ 54 assign spi_clk_gen = clk_divide[divide_factor]; 55 56 /* Clock Divider */ 57 always @ (negedge pro_clk) begin 58 clk_divide = clk_divide + 1; 59 end 60 61 /* Reading the miso line and shifting */ 62 always @ (posedge (sclk ^ (CPHA ^ CPOL)) or posedge spi_word_send) begin 63 if (spi_word_send) begin 64 shift_register[7:0] = txdata; 65 end else begin 66 shift_register = shift_register << 1; 67 shift_register[0] <= miso; 68 end 69 end 70 71 /* Writing the mosi */ 72 always @ (negedge (sclk ^ (CPHA ^ CPOL)) or posedge spi_word_send) begin 73 if (spi_word_send) begin 74 mosi = txdata[7]; 75 end else begin 76 mosi = shift_register[7]; 77 end 78 end 79 80 /* Contolling the interrupt bit in the status bit */ 81 always @ (posedge slave_cs or posedge spi_word_send) begin 82 if (spi_word_send) begin 83 status[0] = 0; 84 end else begin 85 status = 8'h01; 86 rxdata = shift_register; // updating read buffer 87 end 88 end 89 90 /* New SPI wrod starts when the transmit buffer is updated */ 91 always @ (posedge pro_clk) begin 92 if (spi_word_send) begin 93 slave_cs <= 0; 94 end else if ((count == 8) & ~(sclk ^ CPOL)) begin 95 slave_cs <= 1; 96 end 97 end 98 99 /* New Spi word is intiated when transmit buffer is updated */ 100 always @ (posedge pro_clk) begin 101 if (CS & WR & addr[1] & ~addr[0]) begin 102 spi_word_send <=1; 103 end else begin 104 spi_word_send <=0; 105 end 106 end 107 108 /* Generating the SPI clock */ 109 always @ (posedge spi_clk_gen) begin 110 if (~slave_cs) begin 111 sclk = ~sclk; 112 end else if (~CPOL) begin 113 sclk = 0; 114 end else begin 115 sclk = 1; 116 end 117 end 118 119 /* Counting SPI word length */ 120 always @ (posedge sclk or posedge slave_cs) begin 121 if (slave_cs) begin 122 count = 0; 123 end else begin 124 count = count + 1; 125 end 126 end 127 128 /* Reading, writing SPI registers */ 129 always @ (posedge pro_clk) begin 130 if (CS) begin 131 case (addr) 132 2'b00 : if (WR) control <= data_in; 133 2'b01 : if (RD) data_out <= status; // Void 134 2'b10 : if (WR) txdata <= data_in; 135 2'b11 : if (RD) data_out <= rxdata; 136 endcase 137 end 138 end 139 140 /* Controlling the data out enable */ 141 always @ (RD or data_out) begin 142 if (RD) 143 data_out_en = data_out; 144 else 145 data_out_en = 8'bz; 146 end 147 148 assign data_bus = data_out_en; 149 150 initial 151 begin 152 mosi = 0; 153 //sclk = 0; 154 control = 0; 155 count = 0; 156 slave_cs = 1; 157 txdata = 0; 158 rxdata = 0; 159 clk_divide = 0; 160 data_out = 0; 161 end 162 163 endmodule 164 165 /********************************************** END ******************************************************************/
/****************************************************************************************** * Test Bench for SPI Master * January 2007 *******************************************************************************************/ `timescale 10ns/1ns module SPI_master_test; wire [7:0] data_bus; // Bidirectional wire mosi; // Output from main module wire sclk; // Output from main module wire [7:0] ss; // Output from main module /* Inputs to main module */ reg miso; reg CS; reg [1:0] addr; reg pro_clk; reg WR,RD; SPI_Master tb ( miso, mosi, sclk, ss, data_bus, CS, addr, pro_clk, WR, RD); /* Internal registers defined for TB */ reg [7:0] data_send; reg [7:0] transmit_store; reg [7:0] data_receive; reg [7:0] miso_data; reg [7:0] mosi_data; assign data_bus = data_send; initial // Generates serial clock of time period 10 begin pro_clk = 0; forever #5 pro_clk = !pro_clk; end initial begin CS = 0; RD = 0; WR = 0; data_send = 0; addr = 0; miso = 0; #20 /* Updating Control register */ @ (negedge pro_clk) CS = 1; WR = 1; data_send = 0; addr = 0; /* Updating Transmit buffer */ @ (negedge pro_clk) CS = 1; WR = 1; data_send = $random; addr = 2'b10; #1 transmit_store = data_send; @ (negedge pro_clk) $display ("Transmit Buffer loaded"); $display ("SS[0] = 0, CPHA = 0, CPOL = 0 at time:",$time); $display ("Observe Waveform for spi clock frequency, spi data changing at falling egde, valid at rising edge"); CS = 0; WR = 0; data_send = 8'bz; @ (posedge ss[0]) #20 /* Checking Status */ @ (negedge pro_clk) CS = 1; RD = 1; addr = 2'b01; @ (negedge pro_clk) data_receive = data_bus; @ (negedge pro_clk) if (data_receive[0]) begin $display("Interrupt detected at time:", $time); addr = 2'b11; end else begin $display("Interrupt detect failed at time:", $time); end @ (negedge pro_clk) data_receive = data_bus; if (data_bus == miso_data) begin $display("Data received from spi slave verified", $time); end else begin $display("Data receive failed",$time); end /* Writing new control word */ @ (negedge pro_clk) CS = 1; WR = 1; RD = 0; data_send = 8'b10001001; addr = 0; @ (negedge pro_clk) CS = 1; WR = 1; data_send = $random; addr = 2'b10; #1 transmit_store = data_send; @ (negedge pro_clk) $display ("\n Transmit Buffer reloaded"); $display ("Observe Waveform for spi clock frequency, spi data changing at rising edge and valid at falling edge"); $display ("SS[4] = 0, CPHA = 0, CPOL = 1 at time:",$time); CS = 0; RD = 0; WR = 0; data_send = 8'bz; @ (posedge ss[4]) #20 /* Checking Status */ @ (negedge pro_clk) CS = 1; RD = 1; addr = 2'b01; @ (negedge pro_clk) data_receive = data_bus; @ (negedge pro_clk) if (data_receive[0]) begin $display("Interrupt detected at time:", $time); addr = 2'b11; end else begin $display("Interrupt detect failed at time:", $time); end @ (negedge pro_clk) data_receive = data_bus; if (data_bus == miso_data) begin $display("Data received from spi slave verified", $time); end else begin $display("Data receive failed",$time); end /* Writing new control word */ @ (negedge pro_clk) CS = 1; WR = 1; RD = 0; data_send = 8'b11110010; addr = 0; @ (negedge pro_clk) CS = 1; WR = 1; data_send = $random; addr = 2'b10; #1 transmit_store = data_send; @ (negedge pro_clk) $display ("\n Transmit Buffer reloaded"); $display ("Observe Waveform for spi clock frequency, spi data changing at rising edge and valid at falling edge"); $display ("SS[7] = 0, CPHA = 1, CPOL = 0 at time:",$time); CS = 0; RD = 0; WR = 0; data_send = 8'bz; @ (posedge ss[7]) #20 /* Checking Status */ @ (negedge pro_clk) CS = 1; RD = 1; addr = 2'b01; @ (negedge pro_clk) data_receive = data_bus; @ (negedge pro_clk) if (data_receive[0]) begin $display("interrupt detected at time:", $time); addr = 2'b11; end else begin $display("interrupt detect failed at time:", $time); end @ (negedge pro_clk) data_receive = data_bus; if (data_bus == miso_data) begin $display("Data received from spi slave verified", $time); end else begin $display("Data receive failed",$time); end /* Writing new control word */ @ (negedge pro_clk) CS = 1; WR = 1; RD = 0; data_send = 8'b01111100; addr = 0; @ (negedge pro_clk) CS = 1; WR = 1; data_send = $random; addr = 2'b10; #1 transmit_store = data_send; @ (negedge pro_clk) $display ("\n Transmit Buffer reloaded"); $display ("Observe Waveform for spi clock frequency, spi data changing at falling edge and valid at rising edge"); $display ("SS[3] = 0, CPHA = 1, CPOL = 1 at time:",$time); CS = 0; RD = 0; WR = 0; data_send = 8'bz; @ (posedge ss[3]) #20 /* Checking Status */ @ (negedge pro_clk) CS = 1; RD = 1; addr = 2'b01; @ (negedge pro_clk) data_receive = data_bus; @ (negedge pro_clk) if (data_receive[0]) begin $display("Interrupt detected at time:", $time); addr = 2'b11; end else begin $display("Interrupt detect failed at time:", $time); end @ (negedge pro_clk) data_receive = data_bus; if (data_bus == miso_data) begin $display("Data received from spi slave verified", $time); end else begin $display("Data receive failed",$time); end @ (negedge pro_clk) $display ("\n PASS: hit break to stop simulation"); end initial begin /* Writing MISO / Reading MOSI for random values */ #20 miso_data = $random; @ (negedge ss[0]) miso = miso_data[7]; @ (posedge sclk) mosi_data[7] = mosi; @ (negedge sclk) miso = miso_data[6]; @ (posedge sclk) mosi_data[6] = mosi; @ (negedge sclk) miso = miso_data[5]; @ (posedge sclk) mosi_data[5] = mosi; @ (negedge sclk) miso = miso_data[4]; @ (posedge sclk) mosi_data[4] = mosi; @ (negedge sclk) miso = miso_data[3]; @ (posedge sclk) mosi_data[3] = mosi; @ (negedge sclk) miso = miso_data[2]; @ (posedge sclk) mosi_data[2] = mosi; @ (negedge sclk) miso = miso_data[1]; @ (posedge sclk) mosi_data[1] = mosi; @ (negedge sclk) miso = miso_data[0]; @ (posedge sclk) mosi_data[0] = mosi; #5 if(mosi_data == transmit_store) begin $display("Data transmitted to spi slave verified", $time ); end else begin $display("Data Transmit to spi slave failed !", $time ); end /* Next set : CPOL = 1, CPHA = 0 */ @ (negedge ss[4]) miso_data = $random; miso = miso_data[7]; @ (negedge sclk) mosi_data[7] = mosi; @ (posedge sclk) miso = miso_data[6]; @ (negedge sclk) mosi_data[6] = mosi; @ (posedge sclk) miso = miso_data[5]; @ (negedge sclk) mosi_data[5] = mosi; @ (posedge sclk) miso = miso_data[4]; @ (negedge sclk) mosi_data[4] = mosi; @ (posedge sclk) miso = miso_data[3]; @ (negedge sclk) mosi_data[3] = mosi; @ (posedge sclk) miso = miso_data[2]; @ (negedge sclk) mosi_data[2] = mosi; @ (posedge sclk) miso = miso_data[1]; @ (negedge sclk) mosi_data[1] = mosi; @ (posedge sclk) miso = miso_data[0]; @ (negedge sclk) mosi_data[0] = mosi; #5 if(mosi_data == transmit_store) begin $display("Data transmitted to spi slave verified", $time ); end else begin $display("Data Transmit to spi slave failed !", $time ); end /* Next set : CPOL = 0, CPHA = 1 */ @ (negedge ss[7]) miso_data = $random; @ (posedge sclk) miso = miso_data[7]; @ (negedge sclk) mosi_data[7] = mosi; @ (posedge sclk) miso = miso_data[6]; @ (negedge sclk) mosi_data[6] = mosi; @ (posedge sclk) miso = miso_data[5]; @ (negedge sclk) mosi_data[5] = mosi; @ (posedge sclk) miso = miso_data[4]; @ (negedge sclk) mosi_data[4] = mosi; @ (posedge sclk) miso = miso_data[3]; @ (negedge sclk) mosi_data[3] = mosi; @ (posedge sclk) miso = miso_data[2]; @ (negedge sclk) mosi_data[2] = mosi; @ (posedge sclk) miso = miso_data[1]; @ (negedge sclk) mosi_data[1] = mosi; @ (posedge sclk) miso = miso_data[0]; @ (negedge sclk) mosi_data[0] = mosi; #5 if(mosi_data == transmit_store) begin $display("Data transmitted to spi slave verified", $time ); end else begin $display("Data Transmit to spi slave failed !", $time ); end /* Next set : CPOL = 1, CPHA = 1 */ @ (negedge ss[3]) miso_data = $random; @ (negedge sclk) miso = miso_data[7]; @ (posedge sclk) mosi_data[7] = mosi; @ (negedge sclk) miso = miso_data[6]; @ (posedge sclk) mosi_data[6] = mosi; @ (negedge sclk) miso = miso_data[5]; @ (posedge sclk) mosi_data[5] = mosi; @ (negedge sclk) miso = miso_data[4]; @ (posedge sclk) mosi_data[4] = mosi; @ (negedge sclk) miso = miso_data[3]; @ (posedge sclk) mosi_data[3] = mosi; @ (negedge sclk) miso = miso_data[2]; @ (posedge sclk) mosi_data[2] = mosi; @ (negedge sclk) miso = miso_data[1]; @ (posedge sclk) mosi_data[1] = mosi; @ (negedge sclk) miso = miso_data[0]; @ (posedge sclk) mosi_data[0] = mosi; #5 if(mosi_data == transmit_store) begin $display("Data transmitted to spi slave verified", $time ); end else begin $display("Data Transmit to spi slave failed !", $time ); end end endmodule /*************************************** END OF TB ***********************************************************************/