In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

Click the blue text

Follow us

1. Introduction to SPI

SPI (Serial Peripheral Interface) is a synchronous, serial, full-duplex communication interface commonly used for data exchange between microcontrollers, sensors, memory, and other external devices. Examples include: EEPROM, RTC (Real-Time Clock), ADC (Analog-to-Digital Converter), DAC (Digital-to-Analog Converter), LCD, audio ICs, temperature and pressure sensors, MMC or SD cards, etc.

2. SPI Communication Structure

SPI is a synchronous data bus, which requires a clock signal for synchronous communication. This means that SPI uses separate data lines and a single clock signal to ensure perfect synchronization between the sender and receiver. The communication structure is shown in the figure below:

In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

  • SCK: The clock signal for SPI communication, generated by the master, with the slave receiving the clock. There is only one master in the SPI communication structure, but there can be multiple slaves.

  • MOSI: (Master Output Slave Input) The master outputs data to the slave.

  • MISO: (Master Input Slave Output) The master receives data from the slave.

  • NSS: (Negative Slave Select) The slave select signal, active low (sent from the master to the slave).

3. Multi-Slave Mode

  1. Conventional Mode: The master needs to provide a separate chip select signal for each slave. Once the master pulls the chip select signal low, the clock and data on the MOSI or MISO lines can be used for the selected slave. If multiple chip select signals are pulled low, the data on the MISO line will be corrupted because the master cannot identify which slave is transmitting data.

    In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

  2. Daisy Chain Mode: All slave chip select signals are connected together, and data propagates from one slave to the next. All slaves receive the same SPI clock simultaneously. Data from the master is sent directly to the first slave, which then provides the data to the next slave, and so on.

    In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

4. Clock Polarity (CPOL) and Clock Phase (CPHA)

In SPI, the master needs to configure the clock polarity and clock phase.

  • During idle state, the CPOL bit sets the polarity of the clock signal. The idle state refers to the period when CS is high before transitioning to low, and when the transmission ends with CS low before transitioning to high. CPOL = 0: Clock is low during idle; CPHA = 1: Clock is high during idle.

  • CPHA bit selects the clock phase. Depending on the state of the CPHA bit, data is sampled or sent on the rising or falling edge of the clock. CPHA = 0: Sample on the first edge of the clock signal SCK; CPHA = 1: Sample on the second edge of the clock signal SCK.

  • The master must select the clock polarity and clock phase according to the requirements of the slave. Based on the selection of CPOL and CPHA bits, four SPI modes are available.

In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

The four transmission modes are shown below, with the start and end of transmission indicated by green dashed lines, the sampling edge indicated by orange dashed lines, and the sending edge indicated by blue dashed lines.

  1. SPI Mode 0, CPOL = 0, CPHA = 0: CLK idle state = low, data is sent on the falling edge and sampled on the rising edge.

    In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

  2. SPI Mode 1, CPOL = 0, CPHA = 1: CLK idle state = low, data is sent on the rising edge and sampled on the falling edge.

    In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

  3. SPI Mode 2, CPOL = 1, CPHA = 0: CLK idle state = high, data is sent on the rising edge and sampled on the falling edge.

    In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

  4. SPI Mode 3, CPOL = 1, CPHA = 1: CLK idle state = high, data is sent on the falling edge and sampled on the rising edge.

    In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

5. SPI Communication Process

  1. The master pulls the NSS signal low to select the corresponding slave before accessing it; when the master wants to end the communication, it pulls the NSS signal high again.

  2. The master sends the SCLK clock signal.

  3. The master sends data bit by bit to the slave via MOSI according to the configured clock polarity and clock phase.

  4. The slave receives data bit by bit at the corresponding position based on the clock polarity and clock phase configured by the master.

In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

6. Implementing the SPI Master Send Program

6.1 Waveform Analysis

In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

  1. When no data is being sent, spi_en is low. When there is a request to send data, the spi_en signal is pulled high and the data to be sent is buffered.

  2. Then, the cs signal is pulled low to start the clock counting.

  3. Every half cycle, the first clock edge signal is pulled high, and when a full cycle is counted, the second clock edge signal is pulled high.

  4. If CPOL=0, the clock is low during idle, and the first clock edge is the rising edge. If CPOL=1, it is the opposite.

  5. Every time the first clock edge arrives, the bit counter for the sent bits increments.

  6. If CPHA=0, data is sampled on the first clock edge (rising edge), as shown by the red line above. For example, if the data sent is 10010110, the sampled data will also be 10010110.

  7. If CPHA=1, data is sampled on the second clock edge (falling edge), as shown by the red line below. For example, if the data sent is 10010110, the sampled data will also be 10010110.

6.2 Verilog Code

  1. `timescale 1ns/1ns

  2. module spi_master_tx#

  3. (

  4. parameter SYS_CLK_FREQ = 50000000, // Input clock frequency 50M

  5. parameter SPI_CLK_FREQ = 500000, // Output SPI clock 500K

  6. parameter CPOL = 1’b0, // Clock polarity set to 0

  7. parameter CPHA = 1’b0 // Clock phase set to 0

  8. )

  9. (

  10. input sys_clk , // Input system clock

  11. input rst_n , // System reset

  12. input spi_tx_req , // Request to send data

  13. input [7:0] spi_tx_data , // Data to be sent

  14. output reg spi_cs , // CS chip select signal

  15. output spi_clk , // SPI clock sent by the master

  16. output spi_busy, // SPI busy signal

  17. output spi_mosi // MOSI

  18. );

  19. localparam [9:0] spi_clk_cnt_max = SYS_CLK_FREQ / SPI_CLK_FREQ; // Maximum count for one SPI clock

  20. localparam [9:0] spi_clk_cnt_max_div2 = spi_clk_cnt_max/2; // Maximum count for half a clock cycle

  21. reg [9:0] clk_div_cnt ; // SPI clock counter

  22. reg spi_en ; // SPI send enable

  23. reg [7:0] spi_tx_data_reg ; // Buffer for data to be sent

  24. reg clk1_en ; // First clock edge

  25. reg clk2_en ; // Second clock edge

  26. reg spi_clk_temp ; // SPI clock

  27. reg [3:0] tx_cnt ; // Bit counter for sent data

  28. reg spi_strobe_en ; // Valid range for SPI send data enable

  29. wire strobe ; // SPI send data enable signal

  30. // If clock polarity = 1, invert the clock signal

  31. assign spi_clk = (CPOL == 1’b1)? ~spi_clk_temp : spi_clk_temp;

  32. // Send high bit data first

  33. assign spi_mosi = spi_tx_data_reg[7];

  34. // If clock phase = 1, sample data on the second clock edge, send data on the first clock edge

  35. assign strobe = (CPHA == 1’b1)? clk1_en&spi_strobe_en : clk2_en&spi_strobe_en;

  36. // SPI busy signal equals SPI send enable signal

  37. assign spi_busy = spi_en;

  38. // Wait for upstream to provide send request and send data. After sending 8 bits, the transmission is complete.

  39. always @(posedge sys_clk or negedge rst_n) begin

  40. if((rst_n == 1’b0) || (clk1_en == 1’b1 && tx_cnt == 4’d8)) begin

  41. spi_en <= 1’b0;

  42. spi_tx_data_reg <= ‘d0;

  43. end

  44. else if((spi_tx_req == 1’b1) && (spi_en == 1’b0))begin

  45. spi_en <= 1’b1;

  46. spi_tx_data_reg <= spi_tx_data;

  47. end

  48. else if(spi_en == 1’b1 && strobe)

  49. spi_tx_data_reg <= {spi_tx_data_reg[6:0],1’b0};

  50. else

  51. spi_tx_data_reg <= spi_tx_data_reg;

  52. end

  53. // Pull down cs signal before sending starts

  54. always @(posedge sys_clk or negedge rst_n) begin

  55. if(rst_n == 1’b0)

  56. spi_cs <= 1’b1;

  57. else if(spi_en == 1’b1)

  58. spi_cs <= 1’b0;

  59. else

  60. spi_cs <= 1’b1;

  61. end

  62. // When the clock counter counts to half, pull high the first clock edge signal, and when a full clock cycle is counted, pull high the second clock signal

  63. always @(posedge sys_clk or negedge rst_n) begin

  64. if((rst_n == 1’b0) || (spi_cs == 1’b1))begin

  65. clk_div_cnt <= 0;

  66. clk1_en <= 1’b0;

  67. clk2_en <= 1’b0;

  68. end

  69. else if(clk_div_cnt == spi_clk_cnt_max – 1)begin

  70. clk_div_cnt <= 0;

  71. clk2_en <= 1’b1;

  72. end

  73. else if(clk_div_cnt == spi_clk_cnt_max_div2 – 1)begin

  74. clk1_en <= 1’b1;

  75. clk_div_cnt <= clk_div_cnt + 1’b1;

  76. end

  77. else begin

  78. clk_div_cnt <= clk_div_cnt + 1’b1;

  79. clk1_en <= 1’b0;

  80. clk2_en <= 1’b0;

  81. end

  82. end

  83. // Every time the first clock edge comes, it indicates that a number has been sent

  84. always @(posedge sys_clk or negedge rst_n) begin

  85. if((rst_n == 1’b0)||(spi_en == 1’b0))

  86. tx_cnt <= ‘d0;

  87. else if(clk1_en == 1’b1)

  88. tx_cnt <= tx_cnt + 1’b1;

  89. else

  90. tx_cnt <= tx_cnt;

  91. end

  92. always @(posedge sys_clk or negedge rst_n) begin

  93. if(rst_n == 1’b0)

  94. spi_strobe_en <= 1’b0;

  95. else if(tx_cnt < 4’d8)

  96. if(clk1_en == 1’b1 )

  97. spi_strobe_en <= 1’b1;

  98. else

  99. spi_strobe_en <= spi_strobe_en;

  100. else

  101. spi_strobe_en <= 1’b0;

  102. end

  103. always @(posedge sys_clk or negedge rst_n) begin

  104. if(rst_n == 1’b0)

  105. spi_clk_temp <= 1’b0;

  106. else if(clk2_en == 1’b1)

  107. spi_clk_temp <= 1’b0;

  108. else if((clk1_en == 1’b1)&&(tx_cnt <4’d8))

  109. spi_clk_temp <= 1’b1;

  110. else

  111. spi_clk_temp <= spi_clk_temp;

  112. end

  113. endmodule

6.3 Data Sending Control Module

A simple data sending control module is set up, where the data sent starts from 1 and increments. The code is as follows:

  1. `timescale 1ns / 1ps

  2. module spi_top

  3. (

  4. input sys_clk , // Input clock

  5. input rst_n , // System reset

  6. output spi_clk , // SPI send clock

  7. output spi_mosi // SPI send data

  8. );

  9. wire spi_cs ;

  10. wire spi_busy ; // SPI busy signal

  11. reg spi_tx_req ; // SPI send req signal, pulled high when there is a sending demand

  12. reg [7:0] spi_tx_data ; // Data to be sent storage

  13. reg [1:0] state ; // State machine

  14. // SPI send state machine

  15. always @(posedge sys_clk) begin

  16. if(!rst_n) begin

  17. spi_tx_req <= 1’b0;

  18. spi_tx_data <= 8’d0;

  19. state <= 2’d0;

  20. end

  21. else begin

  22. case(state)

  23. 0:if(!spi_busy)begin // Start transmission when the bus is not busy

  24. spi_tx_req <= 1’b1; // Pull req signal high to start transmission

  25. spi_tx_data <= spi_tx_data + 1’b1; // Test data

  26. state <= 2’d1;

  27. end

  28. 1:if(spi_busy)begin // If the SPI bus is busy, clear spi_tx_req

  29. spi_tx_req <= 1’b0;

  30. state <= 2’d0;

  31. end

  32. default:state <= 2’d0;

  33. endcase

  34. end

  35. end

  36. // Instantiate SPI Master transmitter

  37. spi_master_tx#(

  38. .SYS_CLK_FREQ ( 50000000 ),

  39. .SPI_CLK_FREQ ( 500000 ),

  40. .CPOL ( 1’b0 ),

  41. .CPHA ( 1’b0 )

  42. )u_spi_master_tx(

  43. .sys_clk ( sys_clk ),

  44. .rst_n ( rst_n ),

  45. .spi_tx_req ( spi_tx_req ),

  46. .spi_tx_data ( spi_tx_data ),

  47. .spi_cs ( spi_cs ),

  48. .spi_clk ( spi_clk ),

  49. .spi_busy ( spi_busy ),

  50. .spi_mosi ( spi_mosi )

  51. );

  52. endmodule

6.4 Simulation Code Writing and Simulation Result Analysis

The tb code is as follows:

In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

Set CPOL=0, CPHA=0 to observe the simulation results. We randomly select a data point, for example, sending 8’d37, the result is as follows:

In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

8’d37 in binary is 00100101, so the collected data on the rising edge of the clock is also 00100101, which is correct.

Now modify CPOL=0, CPHA=1 to observe the same result, as shown below:

In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

The collected data is on the falling edge of the clock, and the collected data is also 00100101, which is correct.

7. Verilog Implementation of SPI Slave Receive Program

7.1 Waveform Analysis

In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

  1. The receiver first checks if the cs line is pulled low. If it is low, it enters the receiving state and pulls rx_en high.

  2. Then, based on the values of CPOL and CPHA, it finds the edge of the signal to sample.

  3. Each time a sampling edge arrives, it samples the data and performs serial-to-parallel conversion.

7.2 Verilog Code

In-Depth Explanation of the SPI Communication Protocol and FPGA ImplementationIn-Depth Explanation of the SPI Communication Protocol and FPGA ImplementationIn-Depth Explanation of the SPI Communication Protocol and FPGA ImplementationIn-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

7.3 Simulation Code and Simulation Result Analysis

The tb file is written as follows:

  1. `timescale 1ns / 1ps

  2. module tb_spi_slave_rtx();

  3. localparam BYTES = 8;

  4. localparam TCNT = BYTES*8*2-1;

  5. localparam CPOL = 1;

  6. localparam CPHA = 1;

  7. reg I_clk; // System clock

  8. reg [7:0] i; // Counter for generating SPI clock count

  9. reg I_rstn; // System reset

  10. reg I_spi_clk; // SPI clock

  11. reg I_spi_ss; // SPI Slave select signal

  12. reg [3:0] bit_cnt; // Bit counter

  13. reg [7:0] spi_tx_buf; // Send buffer (shift register)

  14. reg [7:0] spi_tx_buf_r; // Send buffer for generating test data

  15. reg first_data_flag; // Whether to change data on one clock

  16. wire O_spi_rvalid; // SPI data receive valid, when this signal is valid, it indicates that valid data has been received

  17. wire [7:0] O_spi_rdata; // SPI read data

  18. wire I_spi_rx; // SPI data bus

  19. // The tb simulates the SPI test data received at I_spi_rx

  20. assign I_spi_rx = spi_tx_buf[7];

  21. // Instantiate SPI receive module

  22. spi_slave_rx#(

  23. .BITS_LEN ( 8 ),

  24. .CPOL ( CPOL ),

  25. .CPHA ( CPHA )

  26. )u_spi_slave_rx(

  27. .sys_clk ( I_clk ),

  28. .rst_n ( I_rstn ),

  29. .spi_cs ( I_spi_ss ),

  30. .spi_clk ( I_spi_clk ),

  31. .spi_mosi ( I_spi_rx ),

  32. .rx_data_valid ( O_spi_rvalid ),

  33. .rx_data ( O_spi_rdata )

  34. );

  35. initial begin

  36. I_clk = 1’b0;

  37. I_rstn = 1’b0;

  38. #100;

  39. I_rstn = 1’b1;

  40. end

  41. always #10 I_clk = ~I_clk; // Clock signal toggles to generate system clock

  42. initial begin

  43. #100;

  44. i = 0;

  45. forever begin

  46. I_spi_clk = CPOL; // Set clock polarity

  47. I_spi_ss = 1; // Set SPI SS control signal

  48. #2000;

  49. I_spi_ss = 0;

  50. for(i=0;i<TCNT;i=i+1) #1000 I_spi_clk = ~ I_spi_clk; // Generate SPI clock

  51. #2000;

  52. I_spi_ss = 1;

  53. end

  54. end

  55. initial begin

  56. #100;

  57. bit_cnt = 0;

  58. first_data_flag =0;

  59. spi_tx_buf[7:0] = 8’ha0;

  60. spi_tx_buf_r[7:0] = 8’ha0;

  61. forever begin

  62. // SPI SS control is used to enable transmission

  63. wait(I_spi_ss);// SPI SS

  64. bit_cnt = 0;

  65. spi_tx_buf[7:0] = 8’ha0;

  66. spi_tx_buf_r[7:0] = 8’ha0;

  67. if((CPHA == 1 && CPOL ==0)||(CPHA == 1 && CPOL ==1))// The case where the first clock edge changes the data

  68. first_data_flag = 1; // Set first_data_flag=1 to skip the first edge in the following sending sequence

  69. // Start data transmission when SS is low

  70. wait(!I_spi_ss);

  71. while(!I_spi_ss)begin

  72. // CPOL=0 CPHA=0 default SCLK is low, for the sender, the first bit data is placed on the bus in advance

  73. if(CPHA == 0 && CPOL ==0)begin

  74. @(negedge I_spi_clk) begin // Update the bit to be sent on the falling edge of each clock

  75. if(bit_cnt == 7)begin// During continuous sending, after sending 8 bits, update the data

  76. bit_cnt = 0;

  77. spi_tx_buf_r = spi_tx_buf_r + 1’b1;// Generate new test data

  78. spi_tx_buf = spi_tx_buf_r;// Update the sending register

  79. end

  80. else begin

  81. spi_tx_buf = {spi_tx_buf[6:0],1’b0};// Data shifts, update data

  82. bit_cnt = bit_cnt + 1’b1;// SPI send bit counter

  83. end

  84. end

  85. end

  86. // CPHA=0 CPOL=1 default SCLK is high, for the sender, the first bit data is placed on the bus in advance

  87. if(CPHA == 0 && CPOL ==1)begin

  88. @(posedge I_spi_clk) begin // Update the bit to be sent on the rising edge of each clock

  89. if(bit_cnt == 7)begin // During continuous sending, after sending 8 bits, update the data

  90. bit_cnt = 0;

  91. spi_tx_buf_r = spi_tx_buf_r + 1’b1;// Generate new test data

  92. spi_tx_buf = spi_tx_buf_r; // Update the sending register

  93. end

  94. else begin

  95. spi_tx_buf = {spi_tx_buf[6:0],1’b0};// Data shifts, update data

  96. bit_cnt = bit_cnt + 1’b1;// SPI send bit counter

  97. end

  98. end

  99. end

  100. // CPHA=1 CPOL=0 default SCLK is low, for the sender, the first SCLK transition updates

  101. if(CPHA == 1 && CPOL ==0)begin

  102. @(posedge I_spi_clk) begin

  103. if(first_data_flag == 1’b1)begin // On the first clock edge, since the first data to be sent has already been initialized, skip the first transition edge

  104. first_data_flag = 0;

  105. //spi_tx_buf[7:0] = 8’ha0;// The first data to be sent can also be initialized on the first transition edge

  106. end

  107. else begin

  108. if(bit_cnt == 7)begin

  109. bit_cnt = 0;

  110. spi_tx_buf_r = spi_tx_buf_r + 1’b1;// Generate new test data

  111. spi_tx_buf = spi_tx_buf_r;// Update the sending register

  112. end

  113. else begin

  114. spi_tx_buf = {spi_tx_buf[6:0],1’b0}; // Data shifts, update data

  115. bit_cnt = bit_cnt + 1’b1;// SPI send bit counter

  116. end

  117. end

  118. end

  119. end

  120. // CPHA=1 CPOL=1 default SCLK is high, for the sender, the first SCLK transition updates

  121. if(CPHA == 1 && CPOL==1)begin

  122. @(negedge I_spi_clk) begin

  123. if(first_data_flag == 1’b1)begin // On the first clock edge, since the first data to be sent has already been initialized, skip the first transition edge

  124. first_data_flag = 0;

  125. //spi_tx_buf[7:0] = 8’ha0;// The first data to be sent can also be initialized on the first transition edge

  126. end

  127. else begin

  128. if(bit_cnt == 7)begin

  129. bit_cnt = 0;

  130. spi_tx_buf_r = spi_tx_buf_r + 1’b1;// Generate new test data

  131. spi_tx_buf = spi_tx_buf_r;// Update the sending register

  132. end

  133. else begin

  134. spi_tx_buf = {spi_tx_buf[6:0],1’b0};// Data shifts, update data

  135. bit_cnt = bit_cnt + 1’b1;// SPI send bit counter

  136. end

  137. end

  138. end

  139. end

  140. end

  141. end

  142. end

  143. endmodule

The test results are shown in the figure below, where the upstream sends data a0, and the received module also receives a0.

In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

8. SPI Loopback Test

8.1 Top-Level Code

Connect the SPI sender and SPI receiver together, and simulate sending data through the top-level.

Top-level code:

  1. `timescale 1ns / 1ps

  2. module spi_top

  3. (

  4. input sys_clk , // Input clock

  5. input rst_n , // System reset

  6. input spi_clk_in ,

  7. input spi_miso ,

  8. output spi_clk , // SPI send clock

  9. output spi_mosi // SPI send data

  10. );

  11. reg spi_cs ;

  12. wire spi_busy ; // SPI busy signal

  13. reg spi_tx_req ; // SPI send req signal, pulled high when there is a sending demand

  14. reg [7:0] spi_tx_data ; // Data to be sent storage

  15. reg [1:0] state ; // State machine

  16. reg [10:0] delay_cnt ;

  17. wire rx_data_valid ;

  18. wire [7:0] rx_data ;

  19. always @(posedge sys_clk or negedge rst_n) begin

  20. if(rst_n == 1’b0)

  21. delay_cnt <= ‘d0;

  22. else if(delay_cnt[10] == 1’b1)

  23. delay_cnt <= ‘d0;

  24. else

  25. delay_cnt <= delay_cnt +1’b1;

  26. end

  27. // SPI send state machine

  28. always @(posedge sys_clk) begin

  29. if(!rst_n) begin

  30. spi_tx_req <= 1’b0;

  31. spi_tx_data <= 8’d0;

  32. state <= 2’d0;

  33. spi_cs <= 1’b1;

  34. end

  35. else begin

  36. case(state)

  37. 0:if((delay_cnt[10]==1’b1) &&(spi_busy == 1’b0))begin

  38. spi_cs <= 1’b1;

  39. state <= 2’d1;

  40. end

  41. 1:if((delay_cnt[10]==1’b1) &&(spi_busy == 1’b0))begin

  42. spi_cs <= 1’b0;

  43. state <= 2’d2;

  44. end

  45. 2:if((delay_cnt[10]==1’b1) &&(spi_busy == 1’b0))begin // Start transmission when the bus is not busy

  46. spi_tx_req <= 1’b1; // Pull req signal high to start transmission

  47. spi_tx_data <= spi_tx_data + 1’b1; // Test data

  48. state <= 2’d3;

  49. end

  50. 3:if(spi_busy)begin // If the SPI bus is busy, clear spi_tx_req

  51. spi_tx_req <= 1’b0;

  52. state <= 2’d0;

  53. end

  54. default:state <= 2’d0;

  55. endcase

  56. end

  57. end

  58. // Instantiate SPI Master transmitter

  59. spi_master_tx#(

  60. .SYS_CLK_FREQ ( 50000000 ),

  61. .SPI_CLK_FREQ ( 500000 ),

  62. .CPOL ( 1’b0 ),

  63. .CPHA ( 1’b1 )

  64. )u_spi_master_tx(

  65. .sys_clk ( sys_clk ),

  66. .rst_n ( rst_n ),

  67. .spi_tx_req ( spi_tx_req ),

  68. .spi_tx_data ( spi_tx_data ),

  69. .spi_cs ( ),

  70. .spi_clk ( spi_clk ),

  71. .spi_busy ( spi_busy ),

  72. .spi_mosi ( spi_mosi )

  73. );

  74. spi_slave_rx#(

  75. .BITS_LEN ( 8 ),

  76. .CPOL ( 1’b0 ),

  77. .CPHA ( 1’b1 )

  78. )u_spi_slave_rx(

  79. .sys_clk ( sys_clk ),

  80. .rst_n ( rst_n ),

  81. .spi_cs ( spi_cs ),

  82. .spi_clk ( spi_clk_in ),

  83. .spi_mosi ( spi_miso ),

  84. .rx_data_valid ( rx_data_valid ),

  85. .rx_data ( rx_data )

  86. );

  87. ila_0 u_ila (

  88. .clk(sys_clk), // input wire clk

  89. .probe0(rx_data_valid), // input wire [0:0] probe0

  90. .probe1(rx_data) // input wire [7:0] probe1

  91. );

  92. endmodule

8.2 Simulation Code and Simulation Result Analysis

In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

The simulation results are as follows:

In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

The top-level simulation sends incrementing numbers to the sender, which is then connected to the receiver, and the data reception is consistent.

8.3 On-Board Verification

We instantiate an ILA to capture the rx_data and rx_data_valid signals in the spi_rx module and enable the capture mode. For specific capture mode principles and usage, please refer to the Vivado ILA Capture Control mode and Advanced Trigger functionality, as well as the TSM (Trigger State Machine) writing. The configuration diagram is as follows:

In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

After downloading, we set the capture mode to BASIC and select the trigger condition as rx_data_valid. Observing the waveform, we find that the data is continuously incrementing, indicating that the SPI loopback was successful.

In-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

In-Depth Explanation of the SPI Communication Protocol and FPGA ImplementationIn-Depth Explanation of the SPI Communication Protocol and FPGA Implementation

*Disclaimer: This article is original or forwarded by the author. If it inadvertently infringes on any party’s intellectual property rights, please inform us for deletion.

The above images and text are sourced from the internet. If there is any infringement, please contact us in a timely manner, and we will delete it within 24 hours.

The content of the article represents the author’s personal views, and the Automotive Ethernet Technology Research Laboratory reprints it only to convey a different perspective, which does not represent the Automotive Ethernet Technology Research Laboratory’s endorsement or support of this view. If there are any objections, please feel free to contact the Automotive Ethernet Technology Research Laboratory.

Original link:

https://blog.csdn.net/qq_43156031/article/details/137227371

Leave a Comment