Experimental Manuals FII-PE7030 FPGA Board Based FPGA Products FPGA Tutor

asynchronous IIC bus, and the IIC communication protocol, reading and writing EEPROM – zynq xc7z030 board – FII-PE7030 Experiment 11 – IIC Protocol Transmission

Experiment 11 IIC Protocol Transmission

11.1 Experiment Objective

  1. Learning the basic principles of asynchronous IIC bus, and the IIC communication protocol
  2. Master the method of reading and writing EEPROM
  3. Joint debugging using logic analyzer

11.2 Experiment Implement

  1. Correctly write a number to any address in the EEPROM (this experiment writes to the register of 8’h03 address) through the FPGA (here changes the written 8-bit data value by (SW7~SW0)). After writing in successfully, read the data as well. The read data is displayed directly on the segment display.
  2. Download the program into the FPGA and press the left push button to execute the data write into EEPROM operation. Press the right push button to read the data that was just written.
  3. Determine whether it is correct or not by reading the displayed number on the segment display. If the segment display has the same value as written value, the experiment is successful.
  4. Analyze the correctness of the internal data with ILA and verify it with the display of the segment display.

11.3.1 Introduction to EEPROM and IIC Protocol

  1. EEPROM Introduction

EEPROM (Electrically Erasable Programmable Read Only M emory) refers to a charged erasable programmable read only register. It is a memory chip that does not lose data after turning off power.

On the experiment board, there is an IIC interface EEPROM chip M24C08 with a capacity of 1024 bytes. Users can store some hardware configuration data or user information due to the characteristics that the data is not lost after power-off.

  1. The overall timing protocol of IIC is as follows:
  2. Bus idle state: SDA, SCL are high
  3. Start of IIC protocol: SCL stays high, SDA jumps from high level to low level, generating a start signal
  4. IIC read and write data stage: including serial input and output of data and response signal issued by data receiver
  5. IIC transmission end bit: SCL is in high level, SDA jumps from low level to high level, and generates an end flag. See Figure 11.1.
  6. SDA must remain unchanged when SCL is high. It changes only when SCL is low

Figure 11.1 Timing protocol of IIC

11.3.2 Hardware Introduction

Each IIC device has a device address. When some device addresses are shipped from the factory, they are fixed by the manufacturer (the specific data can be found in the manufacturer’s data sheet). Some of their higher bits are determined, and the lower bits can be configured by the user according to the requirement. The higher four-bit address of the EEPROM chip M24C08 used by the develop board has been fixed to 1010 by the component manufacturer. The lower three bits are linked in the develop board as shown below, so the device address is 1010000. See Figure 11.2.

Figure 11.2 IIC schematics EEPROM part

Figure 11.3 IIC SWITCH schematics

EEPROM reads and writes data through the IIC_EEPROM_SCL clock line and the IIC_EEPROM_SDA data line with the FPGA.

Because there are many on-board IIC devices, to save I / O resources of FPGA, PCA9548 chip is used as IIC SWITCH, as shown in Figure 11.3. The FPGA uses IIC_SCL_MAIN and IIC_SDA_MAIN as IIC buses to connect to PCA9548, and then passes PCA9548 to drive IIC devices. It can drive up to 8 IIC devices at the same time and itself is driven by the IIC protocol.

11.3.3 Program Introduction

This experiment has two main modules, I2C reading and writing module and LED display module; The first module is mainly introduced here.

The first step: establishment of the main program framework

eeprom_test(

input inclk_p,

input inclk_n,

input rst,

input key_wr,

input key_rd,

input [7:0] sw,

output tube_sel1,

output tube_sel2,

output [7:0] tube_seg,

inout scl,

inout sda

);

Inputs are differential clock, reset signal rst, write control signal key_wr, read control signal key_rd, and write content are controlled by switch SW. The output signals are two segment display selection signals tube_sel1, tube_sel2, segment selection signal tube_seg, and a set of IIC bus scl clock lines and sda data lines.

The second step: create clock IIC_CLK

reg [2:0] cnt;

reg [8:0] cnt_delay;

reg scl_r;

reg scl_link ;

always @ (posedge clk or posedge rst)

begin

if (rst)

cnt_delay <= 9’d0;

else if (cnt_delay == 9’d499)

cnt_delay <= 9’d0;

else

cnt_delay <= cnt_delay + 1’b1; //10us

end

always @ (posedge clk or posedge rst)

begin

if (rst)

cnt <= 3’d5;

else begin

case (cnt_delay)

9’d124: cnt <= 3’d1; //cnt=1:scl

9’d249: cnt <= 3’d2; //cnt=2:scl

9’d374: cnt <= 3’d3; //cnt=3:scl

9’d499: cnt <= 3’d0; //cnt=0:scl

default: cnt<=3’d5;

endcase

end

end

`define SCL_POS (cnt==3’d0) //cnt=0:scl

`define SCL_HIG (cnt==3’d1) //cnt=1:scl

`define SCL_NEG (cnt==3’d2) //cnt=2:scl

`define SCL_LOW (cnt==3’d3) //cnt=3:scl

always @ (posedge clk or posedge rst)

begin

if (rst)

scl_r <= 1’b0;

else if (cnt == 3’d0)

scl_r <= 1’b1;

else if (cnt == 3’d2)

scl_r <= 1’b0;

end

assign scl = scl_link ? scl_r : 1’bz;

First, divide 50MHz system clock to obtain a 100KHz clock with a period of 10us as the transmission clock of the I2C protocol, and then use the counter to define the rising edge, high state, falling edge and low state of the clock to prepare for subsequent data reading and writing and the start and end of the I2C protocol. The last sentence means to define a data valid signal. Only when the signal is high, that is, when the data is valid, the I2C clock is valid again, otherwise it is in a high impedance state. This is also set in accordance with the I2C transmission protocol.

The third step: specific implementation of I2C transmission

`define DEVICE_READ 8’b1010_0001

`define DEVICE_WRITE 8’b1010_0000

`define BYTE_ADDR 8’b0000_0011

`define IIC_SWITCH_ADDR 7’b1110_000

`define EEPROM_CHANNEL 8’b0000_0001

parameter IDLE = 5’d0;

parameter START0 = 5’d1;

parameter ADD0 = 5’d2;

parameter ACK0 = 5’d3;

parameter CHANNEL = 5’d4;

parameter ACK_C = 5’d5;

parameter STOP0 = 5’d6;

parameter START1 = 5’d7;

parameter ADD1 = 5’d8;

parameter ACK1 = 5’d9;

parameter ADD2 = 5’d10;

parameter ACK2 = 5’d11;

parameter START2 = 5’d12;

parameter ADD3 = 5’d13;

parameter ACK3 = 5’d14;

parameter DATA = 5’d15;

parameter ACK4 = 5’d16;

parameter STOP1 = 5’d17;

parameter STOP2 = 5’d18;

always @ (posedge clk or posedge rst)

begin

if (rst) begin

cstate <= IDLE;

sda_r <= 1’b1;

scl_link <= 1’b1;

sda_link <= 1’b1;

num <= 4’d0;

read_data <= 8’b0000_0000;

cnt_5ms <= 20’h00000;

iic_done <= 1’b0;

end

else case (cstate)

IDLE :

begin

sda_link <= 1’b1;

scl_link <= 1’b1;

iic_done <= 1’b0 ;

if (wr_en) begin

db_r <= {`IIC_SWITCH_ADDR, 1’b0};

cstate <= START0;

end

else if (rd_en) begin

db_r <= {`IIC_SWITCH_ADDR, 1’b1};

cstate <= START0;

end

else cstate <= IDLE;

end

START0 :

begin

if (`SCL_HIG) begin

sda_link <= 1’b1;

sda_r <= 1’b0;

num <= 4’d0;

cstate <= ADD0;

end

else cstate <= START0;

end

 

ADD0 :

begin

if (`SCL_LOW) begin

if (num == 4’d8) begin

num <= 4’d0;

sda_r <= 1’b1;

sda_link <= 1’b0;

cstate <= ACK0;

end

else begin

cstate <= ADD0;

num <= num + 1’b1;

case (num)

4’d0 : sda_r <= db_r[7];

4’d1 : sda_r <= db_r[6];

4’d2 : sda_r <= db_r[5];

4’d3 : sda_r <= db_r[4];

4’d4 : sda_r <= db_r[3];

4’d5 : sda_r <= db_r[2];

4’d6 : sda_r <= db_r[1];

4’d7 : sda_r <= db_r[0];

default : ;

endcase

end

end

else cstate <= ADD0;

end

ACK0 :

begin

if (`SCL_NEG) begin

cstate <= CHANNEL;

db_r <= `EEPROM_CHANNEL;

end

else cstate <= ACK0;

end

CHANNEL :

begin

if (`SCL_LOW) begin

if (num == 4’d8) begin

num <= 4’d0;

sda_r <= 1’b1;

sda_link <= 1’b0;

cstate <= ACK_C;

end

else begin

sda_link <= 1’b1;

num <= num + 1’b1;

case (num)

4’d0 : sda_r <= db_r[7];

4’d1 : sda_r <= db_r[6];

4’d2 : sda_r <= db_r[5];

4’d3 : sda_r <= db_r[4];

4’d4 : sda_r <= db_r[3];

4’d5 : sda_r <= db_r[2];

4’d6 : sda_r <= db_r[1];

4’d7 : sda_r <= db_r[0];

default : ;

endcase

cstate <= CHANNEL;

end

end

else cstate <= CHANNEL;

end

ACK_C :

begin

if (wr_en)

if (`SCL_NEG)

cstate <= STOP0;

else

cstate <= ACK_C;

else if (rd_en)

sda_r <= 1’b0;

if (`SCL_NEG)

cstate <= STOP0;

else

cstate <= ACK_C;

end

STOP0 :

begin

if (`SCL_LOW) begin

sda_link <= 1’b1;

sda_r <= 1’b0;

cstate <= STOP0;

end

else if (`SCL_HIG) begin

sda_r <= 1’b1;

cstate <= START1;

end

else cstate <= STOP0;

end

 

START1 :

begin

db_r <= `DEVICE_WRITE;

if (`SCL_HIG) begin

sda_link <= 1’b1;

sda_r <= 1’b0;

num <= 4’d0;

cstate <= ADD1;

end

else cstate <= START1;

end

 

ADD1 :

begin

if (`SCL_LOW) begin

if (num == 4’d8) begin

num <= 4’d0;

sda_r <= 1’b1;

sda_link <= 1’b0;

cstate <= ACK1;

end

else begin

cstate <= ADD1;

num <= num + 1’b1;

case (num)

4’d0 : sda_r <= db_r[7];

4’d1 : sda_r <= db_r[6];

4’d2 : sda_r <= db_r[5];

4’d3 : sda_r <= db_r[4];

4’d4 : sda_r <= db_r[3];

4’d5 : sda_r <= db_r[2];

4’d6 : sda_r <= db_r[1];

4’d7 : sda_r <= db_r[0];

default : ;

endcase

end

end

else cstate <= ADD1;

end

 

ACK1 :

begin

if (`SCL_NEG) begin

cstate <= ADD2;

db_r <= `BYTE_ADDR;

end

else cstate <= ACK1;

end

 

ADD2 :

begin

if (`SCL_LOW) begin

if (num == 4’d8) begin

num <= 4’d0;

sda_r <= 1’b1;

sda_link <= 1’b0;

cstate <= ACK2;

end

else begin

sda_link <= 1’b1;

num <= num+1’b1;

case (num)

4’d0 : sda_r <= db_r[7];

4’d1 : sda_r <= db_r[6];

4’d2 : sda_r <= db_r[5];

4’d3 : sda_r <= db_r[4];

4’d4 : sda_r <= db_r[3];

4’d5 : sda_r <= db_r[2];

4’d6 : sda_r <= db_r[1];

4’d7 : sda_r <= db_r[0];

default : ;

endcase

cstate <= ADD2;

end

end

else cstate <= ADD2;

end

 

ACK2 :

begin

if (`SCL_NEG) begin

if (wr_en) begin

cstate <= DATA;

db_r <= data_tep;

end

else if (rd_en) begin

db_r <= `DEVICE_READ;

cstate <= START2;

end

end

else cstate <= ACK2;

end

 

START2 :

begin

if (`SCL_LOW) begin

sda_link <= 1’b1;

sda_r <= 1’b1;

cstate <= START2;

end

else if (`SCL_HIG) begin

sda_r <= 1’b0;

cstate <= ADD3;

end

else cstate <= START2;

end

 

ADD3 :

begin

if (`SCL_LOW) begin

if (num == 4’d8) begin

num <= 4’d0;

sda_r <= 1’b1;

sda_link <= 1’b0;

cstate <= ACK3;

end

else begin

num <= num + 1’b1;

case (num)

4’d0 : sda_r <= db_r[7];

4’d1 : sda_r <= db_r[6];

4’d2 : sda_r <= db_r[5];

4’d3 : sda_r <= db_r[4];

4’d4 : sda_r <= db_r[3];

4’d5 : sda_r <= db_r[2];

4’d6 : sda_r <= db_r[1];

4’d7 : sda_r <= db_r[0];

default: ;

endcase

end

end

else cstate <= ADD3;

end

 

ACK3 :

begin

if (`SCL_NEG) begin

cstate <= DATA;

sda_link <= 1’b0;

end

else cstate <= ACK3;

end

 

DATA :

begin

if (rd_en) begin

if (num <= 4’d7) begin

cstate <= DATA;

if(`SCL_HIG) begin

num <= num + 1’b1;

case (num)

4’d0 : read_data[7] <= sda;

4’d1 : read_data[6] <= sda;

4’d2 : read_data[5] <= sda;

4’d3 : read_data[4] <= sda;

4’d4 : read_data[3] <= sda;

4’d5 : read_data[2] <= sda;

4’d6 : read_data[1] <= sda;

4’d7 : read_data[0] <= sda;

default: ;

endcase

end

end

else if((`SCL_LOW) && (num == 4’d8)) begin

num <= 4’d0;

cstate <= ACK4;

end

else cstate <= DATA;

end

else if (wr_en) begin

sda_link <= 1’b1;

if (num <= 4’d7) begin

cstate <= DATA;

if (`SCL_LOW) begin

sda_link <= 1’b1;

num <= num + 1’b1;

case (num)

4’d0 : sda_r <= db_r[7];

4’d1 : sda_r <= db_r[6];

4’d2 : sda_r <= db_r[5];

4’d3 : sda_r <= db_r[4];

4’d4 : sda_r <= db_r[3];

4’d5 : sda_r <= db_r[2];

4’d6 : sda_r <= db_r[1];

4’d7 : sda_r <= db_r[0];

default: ;

endcase

end

end

else if ((`SCL_LOW) && (num==4’d8)) begin

num <= 4’d0;

sda_r <= 1’b1;

sda_link <= 1’b0;

cstate <= ACK4;

end

else cstate <= DATA;

end

end

 

ACK4 :

begin

if (wr_en)

if (`SCL_NEG)

cstate <= STOP1;

else

cstate <= ACK4;

else if (rd_en)

sda_r <= 1’b0;

if (`SCL_NEG)

cstate <= STOP1;

else

cstate <= ACK4;

end

 

STOP1 :

begin

if (`SCL_LOW) begin

sda_link <= 1’b1;

sda_r <= 1’b0;

cstate <= STOP1;

end

else if (`SCL_HIG) begin

sda_r <= 1’b1;

cstate <= STOP2;

end

else cstate <= STOP1;

end

 

STOP2 :

begin

if (`SCL_NEG) begin

sda_link <= 1’b0;

scl_link <= 1’b0;

end

else if (cnt_5ms==20’h3fffc) begin

cnt_5ms <= 20’h00000;

iic_done <= 1;

cstate <= IDLE;

end

else begin

cstate <= STOP2;

cnt_5ms <= cnt_5ms + 1’b1;

end

end

 

default: cstate <= IDLE;

endcase

end

The entire process is implemented using a state machine. When reset, it is idle state, while data line sda_r is pulled high, clock and data are both valid, i.e. scl_link, sda_link are high; counter num is cleared and read_data is 0. 5ms delay counter is cleared, IIC transmission end signal Iic_done is low thus invalid.

  1. IDLE state: when the write enable signal wr_en is received, the write control word is assigned to the intermediate variable db_r <= {`IIC_SWITCH_ADDR, 1’b0}, or when the read enable signal rd_en is received, the write control word is also assigned to the intermediate variable db_r < = {`IIC_SWITCH_ADDR, 1’b1} and jump to START0 state;
  2. START1 state: pull the data line low when the clock signal is high, generating the start signal of IIC transmission, and jump to the device address state ADD0;
  3. Device address status ADD0: After the write control word or read control word is transmitted according to MSB (high order priority), the sda_link is pulled low causing data bus in a high impedance state, and jump to the first response state ACK0, waiting for the response signal from the slave.
  4. Response status ACK0: If the data line is pulled low, it proves that the slave receives the data normally, otherwise the data is not written into, and then the rewriting or stopping is decided by the user. There is no temporary judgment and processing here, jump directly to the channel selection state CHANNEL, and assign the channel selection information to the intermediate variable (this experiment needs to drive EEPROM, so channel 0 is used, that is, EEPROM_CHANNEL = 8’b0000_0001).
  5. Channel selection status CHANNEL: same as(3),the channel information is transmitted to the slave (PCA9548), and jumps to the response status ACK_C.
  6. Response state ACK_C: If it is a write operation, it will jump directly to the stop state STOP0. If it is a read operation, the host will send a non-response signal and then jump to the stop state STOP0;
  7. Stop state STOP0: jump directly to the start state START1;

(The previous states are the control of IIC SWITCH, PCA9548)

  1. Start state START1: Assign the write control word of EEPROM to the intermediate variable db_r <= `DEVICE_WRITE, pull the data line low when the clock signal is high, generate the start signal of I2C transmission, and jump to the device address state ADD1;
  2. Device address state ADD1: After the write control word (device address plus one bit 0) is transmitted in MSB (high order priority), pull sda_link low to release the data bus to a high-impedance state, and jump to the first response state ACK1, waiting for the response signal from the slave (EEPROM)
  3. The first response status ACK1: if the data line is pulled low, it proves that the slave normally receives the data, otherwise the data is not written into the EEPROM. The next decision to rewrite or stop is determined by the user. There is no temporary judgment and processing here. Directly jump to the write register address state ADD2, and assign the address where the data is written to, BYTE_ADDR, to the intermediate variable (this experiment writes data to the third register, that is, BYTE_ADDR = 0000_0011)
  4. Register address state ADD2: same as(9), transfer the register address to the slave and jump to the second response state ACK2
  5. The second response state ACK2: at this state, it is urgent to judge. If it is the write state wr_en, then it will jump to the data transmission state DATA, and at the same time, the written data will be assigned to the intermediate variable. If it is the read state rd_en, it jumps to the second start state START2 and assigns the read control word to the intermediate variable.
  6. The second start state START2: same as(8), generates a start signal and jumps to the read register address state ADD3.
  7. Read register address status ADD3: Jump to the third response status ACK3 after transferring the register address that needs to read data
  8. The third response state ACK3: directly jump to the data transmission state DATA. In the read state, the data to be read will be directly read out immediately following the register address.
  9. Data transfer status DATA: here judge is needed, if it is a read state, directly output the data, if it is a write state, the data to be written is transmitted to the data line SDA, and both states need to jump to the fourth response state ACK4
  10. Fourth response status ACK4: jump directly to stop transmission transition to STOP1
  11. Stop transmission transition STOP1: pull down the data line when the clock line is high,
  12. Transfer completed state STOP2: release all clock and data lines, return to the IDLE state after a 5ms delay and waits for the next transfer instruction. This is because EEPROM stipulates that the interval between two consecutive read and write operations must not be less than 5ms.

11.4 Experiment Verification

The first step: assign the pins

See Table 11.1 for the pin assignments.

Table 11.1 Pin mapping table of IIC protocol transmission experiment

Signal Name Network Name FPGA Pin Port Description
inclk_p SYSCLK_P AC13 Input clock(differential)

200MHz

inclk_n SYSCLK_N AD13
rst GPIO_SW_2 F4 Reset
key_wr GPIO_SW_2 D4 Write operation
key_rd GPIO_SW_2 F2 Read operation
sw[0] GPIO_DIP_SW0 A17 8-bit switch
sw[1] GPIO_DIP_SW1 E8
sw[2] GPIO_DIP_SW2 C6
sw[3] GPIO_DIP_SW3 B9
sw[4] GPIO_DIP_SW4 B6
sw[5] GPIO_DIP_SW5 H6
sw[6] GPIO_DIP_SW6 H7
sw[7] GPIO_DIP_SW7 G9
tube_sel1 SEG_D0 C1 0th bit of segment display
tube_sel2 SEG_D0 E3 1st bit of segment display
tube_seg[0] SEF_PA J10 Segment selection signal
tube_seg[1] SEF_PB J9
tube_seg[2] SEF_PC A7
tube_seg[3] SEF_PD B7
tube_seg[4] SEF_PE A8
tube_seg [5] SEF_PF A9
tube _seg[6] SEF_PG A10
tube _seg[7] SEF_DP B10
scl IIC_SCL_MAIN_LS W14 IIC clock bus
sda IIC_SDA_MAIN_LS W17 IIC data bus

The second step: run the implementation, generate bitstream files, and verify the board

After successfully downloading the generated programmable bitstream file to the Zynq_7030 development board, the experimental phenomenon is shown in Figure 11.3. Press the LEFT key to write the 8-bit value represented by SW7 ~ SW0 into the EEPROM, and then press the RIGHT key to read the value entered just now. The value of the write location. Observe the consistency between the value displayed by the segment display on the experiment board and the value (SW7 ~ SW0) written in the register 8’h03 of the EEPROM address (write 8’h34 in this experiment). The read value is displayed on the segmnet display.

 

Figure 11.3 IIC transmission experimental phenomenon

Related posts