Experimental Manuals FPGA Tutor Risc-V

Learning the basic principles of asynchronous IIC bus, and the IIC communication protocol, reading and writing EEPROM – Altera Risc-V IIC Protocol Transmission FPGA Tutorial – FII-PRA040 FPGA Board Experimental 11

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 SignalTap II and verify it with the display of the segment decoders.

11.3 Experiment

11.3.1 Introduction of EEPROM and IIC Protocol

  1. Introduction of EEPROM

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 24LC02 with a capacity of 256 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 Introduction of Hardware

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 24LC02 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. EEPROM reads and writes data from the FPGA through the I2C_SCL clock line and the I2C_SDA data line.

Figure 11.2 EEPROM schematics of IIC device

11.3.3 Introduction to the program

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

module iic_com(

input clk,

input rst_n,

input [7:0] data,

input sw1,sw2,

inout scl,

inout sda,

output reg iic_done,

output [7:0] dis_data

);

The input 8-bit data is needed to be written into the EEPROM, provided by an 8-bit DIP switch.

Step 2: Create clock I2C_CLK

reg [2:0] cnt;

reg [8:0] cnt_delay;

reg scl_r;

reg scl_link ;

always @ (posedge clk or negedge rst_n)

begin

if (!rst_n)

cnt_delay <= 9’d0;

else if (cnt_delay == 9’d499)

cnt_delay <= 9’d0;

else

cnt_delay <= cnt_delay + 1’b1;

end

always @ (posedge clk or negedge rst_n)

begin

if (!rst_n)

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 negedge rst_n)

begin

if (!rst_n)

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, use the system 50 MHz clock to get a 100 kHz clock with a period of 10us by frequency division as the transmission clock of the IIC protocol. Then, the rising edge, the high state, the falling edge and the low state of the clock are defined by the counter, prepared for the subsequent data reading and writing and the beginning of the IIC protocol. The last line of code means to define a data valid signal. Only when the signal is high, that is, when the data is valid, the IIC clock is valid again, otherwise it is in high impedance. This is also set according to the IIC transport 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

parameter IDLE = 4’d0;

parameter START1 = 4’d1;

parameter ADD1 = 4’d2;

parameter ACK1 = 4’d3;

parameter ADD2 = 4’d4;

parameter ACK2 = 4’d5;

parameter START2 = 4’d6;

parameter ADD3 = 4’d7;

parameter ACK3 = 4’d8;

parameter DATA = 4’d9;

parameter ACK4 = 4’d10;

parameter STOP1 = 4’d11;

parameter STOP2 = 4’d12;

reg [7:0] db_r;

reg [7:0] read_data;

reg [3:0] cstate;

reg sda_r;

reg sda_link;

reg [3:0] num;

always @ (posedge clk or negedge rst_n)

begin

if (!rst_n) 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 (sw1_r || sw2_r) begin

db_r <= `DEVICE_WRITE;

cstate <= START1;

end

else cstate <= IDLE;

end

START1 :

begin

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 (sw1_r) begin

cstate <= DATA;

db_r <= data_tep;

end

else if (sw2_r) 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 (sw2_r) 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 (sw1_r) 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 (`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 receiving the read enable or write enable signal sw1_r || sw2_r, assign the write control word to the intermediate variable db_r <= `DEVICE_WRITE, and jump to the start state START1;
  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 ADD1;
  3. Device address status ADD1: After the write control word (device address plus one ‘0’ bit) 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 ACK1, waiting for the response signal from the slave (EEPROM).
  4. The first response status ACK1: If the data line is pulled low, it proves that the slave receives the data normally, otherwise the data is not written into EEPROM, and then the rewriting or stopping is decided by the user. There is no temporary judgment and processing here, jump directly to the write register address state ADD2, and assign the address BYTE_ADDR written to the intermediate variable (this experiment writes the data into the third register, i.e. BYTE_ADDR = 0000_0011 )
  5. Register address status ADD2: Same as (3), it transfers register address to slave and jump to second response status ACK2
  6. The second response state ACK2: At this time, it is urgent to judge. If it is the write state sw1, it jumps to the data transfer state DATA, and at the same time assigns the written data to the intermediate variable. If it is the read state sw2, it jumps to the second start state START2 and assign the read control word to the intermediate variable.
  7. The second start state START2: it produces a start signal identical to (2) and jumps to the read register address state ADD3
  8. Read register address status ADD3: it jumps to the third response status ACK3S after the transfer of the register address that needs to be read out
  9. The third response state ACK3: it jumps directly to the data transfer state DATA. In the read state, the data to be read is directly read out immediately following the register address.
  10. Data transfer status DATA: it needs to be judged here. If it is the read status, the data will be directly output. If it is the write status, the data to be written will be transferred to the data line SDA. Both states need to jump to the fourth response state. ACK4
  11. The fourth response status ACK4: it direct jumps to stop transmission STOP1
  12. Stop transmission STOP1: it pulls up data line when the clock line is high, generating a stop signal, and jumps to the transfer completion status STOP2
  13. Transfer completion status STOP2: it releases all clock lines and data lines, and after a 5ms delay, returns to the IDLE state to wait 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: pin assignments

Table 11.1 IIC protocol transmission experiment pin mapping

Signal Name Network Label FPGA Pin Port Description
clk CLK_50M G21 System clock 50 MHz
rst_n PB3 Y6 Reset
sm_db[0] SEG_PA B15 Segment a
sm_db [1] SEG_PB E14 Segment b
sm_db [2] SEG_PC D15 Segment c
sm_db [3] SEG_PD C15 Segment d
sm_db [4] SEG_PE F13 Segment e
sm_db [5] SEG_PF E11 Segment f
sm_db [6] SEG_PG B16 Segment g
sm_db [7] SEG_DP A16 Segment h
sm_cs1_n SEG_3V3_D1 D19 Segment 1
sm_cs2_n SEG_3V3_D0 F14 Segment 0
data[0] SW0 U11 Switch input
data[1] SW1 V11 Switch input
data[2] SW2 U10 Switch input
data[3] SW3 V10 Switch input
data[4] SW4 V9 Switch input
data[5] SW5 W8 Switch input
data[6] SW6 Y8 Switch input
data[7] SW7 W6 Switch input
sw1 PB4 AB4 Write EEPROM button
sw2 PB6 AA4 Read EEPROM button
scl I2C_SCL D13 EEPROM clock line
sda I2C_SDA C13 EEPROM data line

Step 2: board downloading verification

After the pin assignment is completed, the compilation is performed, and the board is verified after passing.

After the program is downloaded to the board, press the LEFT key to write the 8-bit value represented by SW7~SW0 to EEPROM. Then press the RIGHT key to read the value from the write position. Observe the consistency between the value displayed on the segment display on the experiment board and the value written in the 8’h03 register of the EEPROM address (SW7~SW0) (this experiment writes 8’h34). The read value is displayed on the segment display. The experimental phenomenon is shown in Figure 11.3.

Figure 11.3 Observe experiment result

Related posts