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) begin101 if (CS & WR & addr[1] & ~addr[0]) begin102 spi_word_send <=1;103 end else begin104 spi_word_send <=0;105 end106 end107 108 /* Generating the SPI clock */109 always @ (posedge spi_clk_gen) begin110 if (~slave_cs) begin111 sclk = ~sclk;112 end else if (~CPOL) begin113 sclk = 0; 114 end else begin115 sclk = 1;116 end117 end118 119 /* Counting SPI word length */120 always @ (posedge sclk or posedge slave_cs) begin121 if (slave_cs) begin122 count = 0;123 end else begin 124 count = count + 1;125 end126 end127 128 /* Reading, writing SPI registers */129 always @ (posedge pro_clk) begin130 if (CS) begin131 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 endcase137 end138 end139 140 /* Controlling the data out enable */141 always @ (RD or data_out) begin142 if (RD) 143 data_out_en = data_out;144 else145 data_out_en = 8'bz;146 end147 148 assign data_bus = data_out_en;149 150 initial 151 begin152 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 end162 163 endmodule164 165 /********************************************** END ******************************************************************/
/******************************************************************************************* Test Bench for SPI Master* January 2007*******************************************************************************************/`timescale 10ns/1nsmodule SPI_master_test; wire [7:0] data_bus; // Bidirectionalwire mosi; // Output from main modulewire sclk; // Output from main modulewire [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"); endinitial 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 endendmodule/*************************************** END OF TB ***********************************************************************/