Reading and Writing EEPROM, the Basic Principles of Asynchronous IIC Bus, the IIC Communication Protocol, IIC transmitting Experiment – FPGA Beginner Study Board PRA006, PRA010 Experiment 11
Experiment 11 IIC transmitting Experiment
11.1 Experiment Objective
- Learning the basic principles of asynchronous IIC bus, and the IIC communication protocol
- Master the method of reading and writing EEPROM
- Joint debugging using logic analyzer
11.2 Experiment Implement
- 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.
- Program 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.
- 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.
- Analyze the correctness of the internal data with SignalTap II and verify it with the illustration of the segment display.
11.3 Experiment
11.3.1 Introduction to EEPROM and IIC Protocol
- Introduction of EEPROM
EEPROM (Electrically Erasable Programmable Read Only Memory) 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.
- The overall timing protocol of IIC is as follows
- Bus idle state: SDA, SCL are high
- Start of IIC protocol: SCL stays high, SDA jumps from high level to low level, generating a start signal
- IIC read and write data stage: including serial input and output of data and response signal issued by data receiver
- 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.
- 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 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.
Figure 11.2 EEPROM schematics of IIC device
EEPROM reads and writes data from the FPGA through the I2C_SCL clock line and the I2C_SDA data line.
11.3.3 Introduction to the Program
This experiment has two main modules, IIC 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, 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.
The second step : Create clock I2C_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; 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, 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 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 (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.
- 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;
- 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;
- 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).
- 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 )
- Register address status ADD2: Same as (3), it transfers register address to slave and jump to second response status ACK2
- 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.
- The second start state START2: it produces a start signal identical to (2) and jumps to the read register address state ADD3
- 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
- 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.
- ACK4 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
- The fourth response status ACK4: it direct jumps to stop transmission STOP1
- 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
- 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 consecutives read and write operations must not be less than 5ms.
11.4 Experiment Verification
The first step: pin assignements
See Table 11.1 for the pin assignment.
Table 11.1 IIC protocol transmission experiment pin mapping
Signal Name | Network Label | FPGA Pin | Port Description |
clk | C10_50MCLK | 91 | System clock 50 MHz |
rst | KEY3 | 11 | Reset, low by default |
sm_db[0] | SEG_PA | 132 | Segment selection signal |
sm_db [1] | SEG_PB | 137 | |
sm_db [2] | SEG_PC | 133 | |
sm_db [3] | SEG_PD | 125 | |
sm_db [4] | SEG_PE | 126 | |
sm_db [5] | SEG_PF | 138 | |
sm_db [6] | SEG_PG | 135 | |
sm_db [7] | SEG_DP | 125 | |
sm_cs1_n | SEG_3V3_D1 | 142 | Segment 1 |
sm_cs2_n | SEG_3V3_D0 | 136 | Segment 0 |
data[0] | SW0 | 80 | Switch 0 |
data[1] | SW1 | 83 | Switch 1 |
data[2] | SW2 | 86 | Switch 2 |
data[3] | SW3 | 87 | Switch 3 |
data[4] | SW4 | 74 | Switch 4 |
data[5] | SW5 | 75 | Switch 5 |
data[6] | SW6 | 76 | Switch 6 |
data[7] | SW7 | 77 | Switch 7 |
sw1 | KEY0 | 3 | Write EEPROM button |
sw2 | KEY1 | 7 | Read EEPROM button |
scl | I2C_SCL | R20 | EEPROM clock line |
sda | I2C_SDA | R21 | EEPROM data line |
The second step: board verification
After the pin assignment is completed, the compilation is performed, and the board is verified after passing.
After the board is programmed, 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 EEPROM experiment phenomenon