SPI Driver Implemented by GA

1. What is SPI protocol

         SPI (Serial Peripheral Interface) communication protocol is a synchronous serial interface technology proposed by Motorola Company. It is a high-speed, full-duplex, synchronous communication bus. Only four pins are used to control and transmit data in the chip. It is widely used in EEPROM, Flash, RTC (real-time clock), ADC (digital-to-analog converter), DSP (Digital signal processor) and digital signal decoder are one of the common and important low-speed communication protocols.

         The advantage of SPI protocol is that it supports full-duplex communication, and the communication mode is simple, and the relative data transmission rate is fast. The disadvantage is that there is no specified flow control, no response mechanism, and there is some deficiency in data reliability.

2. SPI Agreement Details

2.1, SPI Protocol Physical Layer

         The communication mode of SPI communication device is master-slave communication mode. There are master-slave communication modes for both sides of communication, depending on the slave device.   Number of connections between SPI communication devices can be divided into one master slave and one master slave.

 

         

         SPI bus transmission can be accomplished with only four wires, each of which serves the following purposes:

  • SCK (Serial Clock): A clock signal line used to synchronize communication data. Generated by the communication host, determines the rate of communication. Different devices support different maximum clock frequencies.
  • MOSI (Master Output, Slave Input): Primary device output/Primary device input pin. Host data is output from this signal line, and read from the host to the data sent from the host in the direction from the host to the slave.
  • MISO (Master Input, Slave Output): Primary device input/output pin. Host reads data from this signal line, from this signal line to host, and the data direction is from machine to host.
  • CS   (Chip Select): Selection signal line. When more than one SPI is connected from the device to the SPI host, the other signal lines SCK, MOSI and MISO of the device are simultaneously connected to the same SPI bus, that is, they are used together regardless of how many slave devices there are. Each slave device has its own chip selection signal line, that is, how many slave devices there are and how many chip selection signal lines there are. In a communication system consisting of SPI, the slave device of communication is determined by CS chip selection signal. Low level valid during communication, indicating corresponding slave is selected

2.2, SPI protocol layer

2.2.1, CPOL/CPHA and communication modes

         There are four modes for SPI bus transmission, which are defined by clock polarity (CPOL, Clock Polarity) and clock phase (CPHA, Clock Phase). The CPOL parameter specifies the level of idle state of SCK clock signal, and the CPHA specifies whether the data is sampled along the rise or decrease of the SCK clock.

                 Polarity of SPI Bus--Clock Polarity

                         Clock polarity determines whether the clock signal is high or low when the SPI bus is idle. CPOL = 1: indicates a high level when idle; CPOL = 0: indicates a low level when idle.

                 Phase of SPI Bus--Clock Phase

                        The clock phase determines which jump along the SPI bus begins to sample data. CPHA = 0: Sampling along the first hop of the clock signal SCK; CPHA = 1: Sample along the second hop of the clock signal SCK.

         The time series of these four modes are shown in the following figure:

  • Mode 0: CPOL= 0, CPHA=0. The SCK serial clock line is idle for low level, data is sampled at the rising edge of the SCK clock, and data is switched at the decreasing edge of the SCK clock.
  • Mode 1: CPOL= 0, CPHA=1. The SCK serial clock line is idle for low level, data is sampled at the descending edge of the SCK clock, and data is switched at the rising edge of the SCK clock.
  • Mode 2: CPOL= 1, CPHA=0. The SCK serial clock line is idle for high level, data is sampled along the descending edge of the SCK clock, and data is switched along the rising edge of the SCK clock
  • Mode 3: CPOL= 1, CPHA=1. The SCK serial clock line is idle for high level, data is sampled at the rising edge of the SCK clock, and data is switched at the decreasing edge of the SCK clock.

         Mode 0 and mode 3 are often used (after all, few data are collected along the downward edge). The following figure describes the relationship between the data switching (Toggling) locations and the data sampling locations for the four mode data lines MOSI and MIO.

2.3, SPI protocol communication process

         The following is an example of mode 0 to illustrate the basic communication process of SPI:

         SCK, MOSI, CS_N signals are generated by host control, SCK is clock signal to synchronize data, MOSI is host output slave input signal, host transmits data to slave through this signal line, CS_N is the chip selection signal used to select slave equipment, low level is valid; The signal of the MISO is generated by the slave, through which the host reads the slave data. The signals of MOSI and MISO are only in CS_N is only valid at low levels and transmits one bit of data at each clock cycle of SKK, MOSI and MIO.  

         At icon 1 in the diagram, CS_ The N signal line changes from high to low, which is the starting signal for SPI communication. CS_N is the exclusive signal line for each slave when the slave is in its own CS_ When the N-line detects the start signal, it knows that it is selected by the host and starts preparing to communicate with the host. At symbol 6 in the diagram, CS_ The N signal changes from low to high, which is the stop signal of SPI communication. It indicates the end of this communication and is cancelled from the selected state of the computer.

         SPI uses MOSI and MISO signal lines to transmit data and SCK signal lines to synchronize data. The MOSI and MISO data lines transmit one bit of data in each clock cycle of the SKK, and the data input and output occur simultaneously. MSB-first or LSB-first is not mandatory for data transmission, but MSB-first mode is generally used to ensure the same protocol is used between two SPI communication devices. The output of MOSI and MISO data varies during the decline of the SK and is sampled during the rise of the SK. That is, at the rising time of SCK, the data of MOSI and MISO are valid, high level means data "1" and low level means data "0". At other times, the data is invalid, and MOSI and MISO prepare for the next representation.

         SPI can transfer data 8 or 16 bits per time, and there is no limit on the number of units per transmission.

2.4. Characteristics of SPI protocol

  • The SPI protocol is a master-slave architecture that differentiates slaves (addressing methods) by the chip selection signal CS.
  • The SPI protocol is a Synchronous transmission protocol in which both sides of the communication use the clock signal SCK generated by the host as the reference signal for data exchange.
  • SPI protocol is a full duplex serial communication protocol, in which both master and slave can exchange data.
  • SPI protocol has 4 communication modes and communicates according to the agreed mode between the two parties

2.5. Advantages and disadvantages of SPI protocol

         Advantages:

  • Full Duplex Serial Communication
  • Simple hardware architecture
  • High-speed data transfer rate (compared to UART, IIC)
  • Flexible data transmission, not limited to 8 bits, can be any size of word

         Inferiority:

  • Only one primary device is supported
  • Slightly more pins (compared to UART, IIC)
  • No hardware slave response signal (host may not be able to send without knowing)

3. Design and implementation of driver code

        The following SPI driver code features are implemented: MSB first; Mode 0 only; 8 bits (1 BYTE) per transmission.

3.1. Interface definition and overall design

         The overall block diagram, input and output signals of the SPI driver are as follows:

    The signal is described as follows:  

     

         The module can be used as follows:

  • Pull up SPI transmission start signal spi_start a cycle while sending data to data_send, the module sends the completion flag when the data is sent   send_done pulls up a period, marking a BYTE's data sent to the slave via the SPI bus
  • Similarly, when receiving the completion flag rec_ When done is elevated by this module, it means that the host successfully receives a BTYE data from the host
  • When the host wants to end the transmission, it can end the SPI signal spi_ If the end is increased by one period, the module will end the SPI transmission after sending the last module, which also means that if the SPI end signal is not ended, the SPI transmission will continue to proceed in order to achieve the SPI transmission of multiple BYTE s

3.2, Verilog code

         Verilog code is not complex, and in conjunction with the SPI communication process shown in the following figure, the following points can be found:

  • SCK is well suited for a 4-way clock with a system clock because it needs to be operated four times in a single SCK
  • Send and receive data by shifting the generated SCK's up and down edges, respectively.
  • Additionally, as you can see from the figure below, SPI drivers are well suited for writing using state machines and are interested in trying them yourself.

`timescale 1ns/1ns		//Time Unit/Precision	
// Mode 0
module spi_drive
(
// system interface
    input               sys_clk		, 			// Global clock 50MHz
    input               sys_rst_n	, 			// Reset signal, low level effective
// User Interface	
    input               spi_start	,			// Send transmission start signal, a high level
    input              	spi_end		,			// Send end of transmission signal, a high level
    input        [7:0]  data_send   , 			// Data to send
    output  reg  [7:0]  data_rec  	, 			// Received data
    output  reg         send_done	, 			// Host sends a byte completion flag bit    
    output  reg         rec_done	, 			// Host receives a byte completion flag bit    
// SPI Physical Interface
    input               spi_miso	, 			// SPI Serial Input to receive slave data
    output  reg         spi_sclk	, 			// SPI Clock
    output  reg         spi_cs    	, 			// SPI Picture Selection Signal, Low Level Effective
    output  reg         spi_mosi				// SPI Output, used to send data to slaves          
);

reg	[1:0]	cnt;								//4-crossover counter
reg	[3:0]	bit_cnt_send;						//Send Counter
reg	[3:0]	bit_cnt_rec;						//Receive Counter
reg			spi_end_req;						//End Request

//4-crossover counter
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		cnt <= 2'd0;						
	else if(!spi_cs)begin
		if(cnt == 2'd3)
			cnt <= 2'd0;
		else
		cnt <= cnt + 1'b1;		
	end
	else 
		cnt <= 2'd0;	
end
// Generate spi_sclk clock
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		spi_sclk <= 1'b0;			//Mode 0 defaults to low level					
	else if(!spi_cs)begin			//During SPI transfer
		if(cnt == 2'd0 )
			spi_sclk <= 1'b0;
		else if (cnt == 2'd2)
			spi_sclk <= 1'b1;
		else 
			spi_sclk <= spi_sclk;	
	end
	else 
		spi_sclk <= 1'b0;			//Mode 0 defaults to low level		
end
// Generate Pick Signal spi_cs
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		spi_cs <= 1'b1;				//Default to high level						
	else if(spi_start)				//Start SPI ready for transmission, pull down the selected signal
		spi_cs <= 1'b0;
	//Received SPI end signal and ended the last BYTE
	else if(spi_end_req && (cnt == 2'd1 && bit_cnt_rec == 4'd0))
		spi_cs <= 1'b1;				//Pull up the chip selection signal to end SPI transmission
end
// Generate end request signal (capture spi_end signal)
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		spi_end_req <= 1'b0;		//Default not enabled					
	else if(spi_cs)					
		spi_end_req <= 1'b0;		//Lower Request After Ending SPI Transfer
	else if(spi_end)				
		spi_end_req <= 1'b1;		//Raise the end request after receiving the SPI end signal
end
// Sending data process----------------------------------------------------------------------------------------------------------------------------------------------------------------------

// send data
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		spi_mosi <= 1'b0;						//Mode 0 idle
		bit_cnt_send <= 4'd0;
	end
	else if(cnt == 2'd0 && !spi_cs)begin		//Rising edge of mode 0
		spi_mosi <= data_send[7-bit_cnt_send];	//Send Data Shift
		if(bit_cnt_send == 4'd7)				//Send 8bit
			bit_cnt_send <= 4'd0;
		else
			bit_cnt_send <= bit_cnt_send + 1'b1;	
	end
	else if(spi_cs)begin						//Non-transmission period
		spi_mosi <= 1'b0;						//Mode 0 idle
		bit_cnt_send <= 4'd0;
	end
	else begin
		spi_mosi <= spi_mosi;
		bit_cnt_send <= bit_cnt_send;
	end
end
// Send Data Flag
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		send_done <= 1'b0;			
	else if(cnt == 2'd0 && bit_cnt_send == 4'd7)		//8bit data sent
		send_done <= 1'b1;								//Pull up a period to indicate that the send is complete	
	else 
		send_done <= 1'b0;			
end

// Receiving data process--------------------------------------------------------------------------------------------------------------------------------------------------------------------

// Receive data spi_miso
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		data_rec <= 8'd0;		
		bit_cnt_rec <= 4'd0;
	end
	else if(cnt == 2'd2 && !spi_cs)begin				//Rising edge of mode 0
		data_rec[7-bit_cnt_rec] <= 	spi_miso;			//Shift reception
		if(bit_cnt_rec == 4'd7)							//8bit received
			bit_cnt_rec <= 4'd0;
		else
			bit_cnt_rec <= bit_cnt_rec + 1'b1;	
	end
	else if(spi_cs)begin								
		bit_cnt_rec <= 4'd0;
	end
	else begin
		data_rec <= data_rec;
		bit_cnt_rec <= bit_cnt_rec;
	end
end
// Receive Data Flag
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		rec_done <= 1'b0;									
	else if(cnt == 2'd2 && bit_cnt_rec == 4'd7)			//8bit received
		rec_done <= 1'b1;								//Pull up a period to indicate that reception is complete			
	else 
		rec_done <= 1'b0;					
end

endmodule

4. Testbench and simulation results

4.1. Simulations of Single BYTE

        Using this SPI driver, send a single BYTE data 8'b010101 to the slave machine to see if the simulation sequence is correct:

//------------------------------------------------
//--SPI driver simulation (mode 0, 1 BYTE)
//------------------------------------------------
`timescale 1ns/1ns		//Time Unit/Precision

//----------------<Module and Port Statement>--------------------------------------------------------------------------------------------------------------------------------
module tb_spi_drive();
//system interface
reg				sys_clk		;			// Global clock 50MHz
reg				sys_rst_n	;   		// Reset signal, low level effective
//User Interface                      		
reg				spi_start 	;   		// Send transmission start signal, a high level
reg				spi_end   	;   		// Send end of transmission signal, a high level
reg		[7:0]  	data_send   ;   		// Data to send
wire  	[7:0]  	data_rec  	;   		// Received data
wire         	send_done	;   		// Host sends a byte completion flag bit    
wire         	rec_done	;   		// Host receives a byte completion flag bit    
//SPI Physical Interface                   		
reg				spi_miso	;   		// SPI Serial Input to receive slave data
wire         	spi_sclk	;   		// SPI Clock
wire			spi_cs    	;   		// SPI Picture Selection Signal
wire         	spi_mosi	;   		// SPI Output, used to send data to slaves
//Simulation
reg		[3:0]  	cnt_send 	;			//Send Data Counter, 0-15      

//-------------- <instantiated SPI driver module (mode 0)>--------------------------------------------------------------------------------------------------------------------------------------
spi_drive	spi_drive_inst(
	.sys_clk		(sys_clk	), 			
	.sys_rst_n		(sys_rst_n	), 			
		
	.spi_start		(spi_start	), 			
	.spi_end		(spi_end	),
	.data_send		(data_send	), 			
	.data_rec  		(data_rec	), 			
	.send_done		(send_done	), 			
	.rec_done		(rec_done	), 			
				
	.spi_miso		(spi_miso	), 			
	.spi_sclk		(spi_sclk	), 			
	.spi_cs    		(spi_cs		), 			
	.spi_mosi		(spi_mosi	)			
);

//---------------- <Set Initial Test Conditions>----------------------------------------------------------------------------------------------------------------------------
initial begin
	sys_clk = 1'b0;						//Initial clock is 0
	sys_rst_n <= 1'b0;					//Initial Reset
	spi_start <= 1'b0;	
	data_send <= 8'd0;	
	spi_miso <= 1'bz;	
	spi_end <= 1'b0;	
	#80 									// After 80 Clock Cycles
	sys_rst_n <= 1'b1;					//Pull up and reset the system to work
	#30 									// Pull up the SPI start signal after 30 clock cycles to start SPI transmission
	spi_start <= 1'b1;data_send <= 8'b01010101;
	#20	
	spi_start <= 1'b0;
	@(posedge send_done)				//A BYTE send is complete
	spi_end <= 1'b1;	#20 	 Spi_ End <= 1'b0; 	// Pull up the cycle end signal	
	
end
	
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------
always #10 sys_clk = ~sys_clk; 					// System clock cycle 20ns

endmodule

        The simulation results are as follows:

          As you can see, spi_is being pulled up After start starts transmitting the signal, the host starts sending data. The data on the MOSI is 010101. After sending a BYTE, send_done pulls up. Pull up the end signal spi_at this time End, the SPI transfer is terminated, and the SPI transfer of a single BYTE is completed.

4.2, Simulation of Multiple BYTE s

         Using this SPI driver, send data 8'd0~8'd10 from the slave machine in turn to see if the simulation sequence is correct:

//------------------------------------------------
//--SPI Drive Simulation (Mode 0)
//------------------------------------------------
`timescale 1ns/1ns		//Time Unit/Precision

//----------------<Module and Port Statement>--------------------------------------------------------------------------------------------------------------------------------
module tb_spi_drive();
//system interface
reg				sys_clk		;			// Global clock 50MHz
reg				sys_rst_n	;   		// Reset signal, low level effective
//User Interface                      		
reg				spi_start 	;   		// Send transmission start signal, a high level
reg				spi_end   	;   		// Send end of transmission signal, a high level
reg		[7:0]  	data_send   ;   		// Data to send
wire  	[7:0]  	data_rec  	;   		// Received data
wire         	send_done	;   		// Host sends a byte completion flag bit    
wire         	rec_done	;   		// Host receives a byte completion flag bit    
//SPI Physical Interface                   		
reg				spi_miso	;   		// SPI Serial Input to receive slave data
wire         	spi_sclk	;   		// SPI Clock
wire			spi_cs    	;   		// SPI Picture Selection Signal
wire         	spi_mosi	;   		// SPI Output, used to send data to slaves
//Simulation
reg		[3:0]  	cnt_send 	;			//Send Data Counter, 0-15      

//-------------- <instantiated SPI driver module (mode 0)>--------------------------------------------------------------------------------------------------------------------------------------
spi_drive	spi_drive_inst(
	.sys_clk		(sys_clk	), 			
	.sys_rst_n		(sys_rst_n	), 			
		
	.spi_start		(spi_start	), 			
	.spi_end		(spi_end	),
	.data_send		(data_send	), 			
	.data_rec  		(data_rec	), 			
	.send_done		(send_done	), 			
	.rec_done		(rec_done	), 			
				
	.spi_miso		(spi_miso	), 			
	.spi_sclk		(spi_sclk	), 			
	.spi_cs    		(spi_cs		), 			
	.spi_mosi		(spi_mosi	)			
);

//---------------- <Set Initial Test Conditions>----------------------------------------------------------------------------------------------------------------------------
initial begin
	sys_clk = 1'b0;						//Initial clock is 0
	sys_rst_n <= 1'b0;					//Initial Reset
	spi_start <= 1'b0;	
	data_send <= 8'd0;	
	spi_miso <= 1'bz;	
	spi_end <= 1'b0;	
	#80 									// After 80 Clock Cycles
	sys_rst_n <= 1'b1;					//Pull up and reset the system to work
	#30 									// Pull up the SPI start signal after 30 clock cycles to start SPI transmission
	spi_start <= 1'b1;	#20	spi_start <= 1'b0;
end

always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		data_send <= 8'd0;			
		spi_end <= 1'b0;			
		cnt_send <= 4'd0; 		
	end
	else if(send_done)begin						//Data Send Complete		
		if(cnt_send == 4'd10)begin		
			cnt_send <= 4'd0; 
			spi_end <= 1'b1;					//Pull up the end flag to end the SPI transfer process	
			data_send <= 8'd0;
		end
		else begin
			cnt_send <= cnt_send + 4'd1; 
			spi_end <= 1'b0;					
			data_send <= data_send + 4'd1;		//Send Data Accumulation	
		end
	end
	else begin
		data_send <= data_send;
		spi_end <= 1'b0;						//Keep SPI transmission on other occasions (do not end)	
	end
end
	
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------
always #10 sys_clk = ~sys_clk; 					// System clock cycle 20ns

endmodule

         The simulation results are as follows:


         As you can see, spi_is being pulled up The host has been sending data since start started transmitting signal. The data on the MOSI is 8'd0~8'd10. After sending one BYTE data at a time, send_done pulls up once. When the end signal spi_ When the end is raised, the SPI transmission is terminated.

5. Other

  • It is important to note that since there is no offline response, MISO s are all high resistance (blue)
  • Next article combines slave machine (FLASH chip) to simulate and verify receiving data function
  • Friends who want the whole project can leave their mailbox in the comments area

 

Keywords: Verilog Single-Chip Microcomputer FPGA SPI

Added by new_programmer on Tue, 26 Oct 2021 20:31:28 +0300