0% found this document useful (0 votes)
7 views17 pages

I2c Protocol

The document details the design and verification of the Inter-Integrated Circuit (I2C) protocol using SystemVerilog, focusing on a Master-Slave communication system. It outlines the I2C protocol's features, communication process, and the implementation of both Master and Slave modules, including their state machines and operations. Additionally, it includes a testbench for simulation and verification of data transfer correctness between the Master and Slave devices.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views17 pages

I2c Protocol

The document details the design and verification of the Inter-Integrated Circuit (I2C) protocol using SystemVerilog, focusing on a Master-Slave communication system. It outlines the I2C protocol's features, communication process, and the implementation of both Master and Slave modules, including their state machines and operations. Additionally, it includes a testbench for simulation and verification of data transfer correctness between the Master and Slave devices.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 17

Design and Verification of

Inter Integrated Circuit


(I2C) protocol using
SystemVerilog
(by Ch. Anirudh)
1. Introduction:
The Inter-Integrated Circuit (I2C) protocol is a widely used serial
communication protocol developed by Philips (now NXP Semiconductors). It is
primarily used for short-distance, low-speed communication between multiple
devices within an embedded system. The protocol supports multiple masters
and slaves, using only two wires: SCL (Serial Clock Line) and SDA (Serial Data
Line), making it a highly efficient and cost-effective communication standard.
I2C operates in three main speed modes: Standard Mode (100 kbps), Fast
Mode (400 kbps), and High-Speed Mode (3.4 Mbps). It employs synchronous
communication, meaning data is transmitted and received with respect to the
clock signal generated by the master. The master controls communication by
initiating data transfers, while slaves respond accordingly based on their
assigned 7-bit or 10-bit addresses.
This project focuses on implementing an I2C Master-Slave communication
system using System Verilog. The design includes both the Master and Slave
modules, capable of handling read and write operations. The simulation is
performed using Vivado , and the results are verified using waveform analysis.
The implementation demonstrates data transfer between an I2C Master and
multiple Slave devices, ensuring correct protocol behavior.

2. Overview of I2C Protocol:


I2C is a multi-master, multi-slave, half-duplex communication protocol where
multiple devices can share the same bus. It operates using two bidirectional
lines:
 SCL (Serial Clock Line): Carries the clock signal generated by the master.
 SDA (Serial Data Line): Transfers data between master and slaves.
Key Features of I2C:
 Simple two-wire interface for connecting multiple devices.
 Supports multi-master and multi-slave configurations.
 Provides synchronous communication with clock-based control.
 Uses ACK/NACK mechanisms for data verification.
 Supports multiple speed modes (Standard, Fast, and High-Speed)
Fig(a): I2C Protocol Master & Slave

3. Basic I2C Communication Process:


 Master initiates communication by sending a Start Condition (SDA goes
LOW while SCL is HIGH).
 Master sends the 7-bit Slave Address + Read/Write bit.
 Slave acknowledges (ACK) if it recognizes its address.
 Master sends or receives data based on the operation.
 Communication ends with a Stop Condition (SDA goes HIGH while SCL is
High)

Fig(b): Working of I2C Protocol

4. Design and Implementation:


The I2C Master and Slave modules are implemented in SystemVerilog using
Finite State Machines (FSMs) to manage communication.
Design Components:
I2C Master Module
o Generates Start & Stop conditions.
o Controls SCL clock signal.
o Handles addressing and data transfer.
o Implements ACK/NACK handling.
I2C Slave Module
o Detects Start & Stop conditions.
o Recognizes its assigned address.
o Responds with ACK/NACK.
o Reads/writes data based on master commands.
Testbench for Simulation
o Generates clock (SCL) and data (SDA) signals.
o Stimulates Master-Slave communication.
o Verifies data transfer correctness.

5. Operations of I2C protocol:


1. Write Operation (Master to Slave)
 Master sends START condition.
 Sends slave address + write bit (0).
 Slave acknowledges (ACK).
 Master sends data byte-by-byte.
 Slave acknowledges each byte.
 Master sends STOP condition.
2. Read Operation (Slave to Master)
 Master sends START condition.
 Sends slave address + read bit (1).
 Slave acknowledges and sends data.
 Master acknowledges received bytes.
 Master sends STOP condition.

6. RTL Code:
I2C Master:
module I2C_master(

input wire clk, //clock signal

input wire rst_n, // Active-low reset signal

output reg scl, // I2C Serial clock line

inout wire sda, // I2C serial data line (bidirectional)

input wire start, // start signal to initiate I2C transaction

input wire [6:0] addr, // 7-bit slave address

input wire rw, // read / write bit (0: write, 1: read)

input wire [7:0] data_wr, // data to write to the slave

output reg [7:0] data_rd, // data read from the slave

output reg busy, // busy signal (1: busy, 0: idle)

output reg ack // acknowledge signal

);
//I2C state machine for internal states

typedef enum logic [2:0] {

IDLE, // Idle state

START, // we need to start condition state

ADDR, // address transmission state

WRITE, // write state for data

READ, // data read store

STOP // at last we need to stop condition state

} state_t;
state_t state;

//I2C state machine current state

reg [7:0] shift_reg; // for data transmission / reception

reg [2:0] bit_cnt; // bit counter for tracking bits in a byte

reg sda_out; // internal SDA output signal

reg sda_oe; // SDA output enable(1: drive SDA, 0: Tri-state)

// Tri-state buuffer for SDA

assign sda = sda_oe ? sda_out : 1'bz;

// clock divider for generating SCL

reg [7:0] clk_div; // used to divide the system clock to generate the SCL signal

always @(posedge clk or negedge rst_n) begin

if (!rst_n) begin

clk_div <= 8'b0;

scl <= 1'b1;

end else begin

if (clk_div == 8'd199) begin // assuming a 100MHz clock, divide by 200 for 400khz SCL

clk_div <= 8'b0;

scl <= ~scl; // counters reaches 199 reset to '0' and toogle SCL

end else begin

clk_div <= clk_div + 8'd1;

end

end

end

// I2C state machine logic

always @(posedge clk or negedge rst_n) begin

if (!rst_n) begin

state <= IDLE;

busy <= 1'b0;

ack <= 1'b0;

sda_out <= 1'b1; // it is high because in idle state

sda_oe <= 1'b0;


bit_cnt <= 3'd0;

shift_reg <= 8'd0;

data_rd <= 8'd0;

end else begin

case (state)

IDLE: begin

busy <= 1'b0; // master is not busy

sda_out <= 1'b1; // SDA is high

sda_oe <= 1'b0; // SDA is tri-stated beacuse not driven by master

if (start) begin

state <= START;

busy <= 1'b1; // when start signal start master is busy

end

end

START: begin

sda_out <= 1'b0; // it is low (start condition)

sda_oe <= 1'b1; // drive SDA

if (scl == 1'b1) begin // wait for SCl high

state <= ADDR;

shift_reg <= {addr, rw};

bit_cnt <= 3'd7; // initialize bit counter to 7 MSB first

end

end

ADDR: begin

sda_out <= shift_reg[bit_cnt]; // need to transmit current bit of address

sda_oe <= 1'b1; // drive SDA by master

if (scl == 1'b0) begin // wait for SCL to be low

if (bit_cnt == 3'd0) begin // all bits are transmitted (7-bit address + R/W bit)

state <= WRITE; // transition to write state

bit_cnt <= 3'd7; // reset bit counter for data byte

end else begin

bit_cnt <= bit_cnt - 3'd1; // decrement bit counter

end
end

end

WRITE: begin

sda_out <= data_wr[bit_cnt]; // transmit current bit of data

sda_oe <= 1'b1; // drive SDA

if (scl == 1'b0) begin

if (bit_cnt == 3'd0) begin // if all bits are transmitted go to STOP state

state <= STOP;

ack <= sda; // wew need to capture ACK / NACK

end else begin

bit_cnt <= bit_cnt - 3'd1;

end

end

end

STOP: begin

sda_out <= 1'b0; // pull SDA to low

sda_oe <= 1'b1; //drive SDA

if (scl == 1'b1) begin // wait for SCL to begin high

sda_out <= 1'b1; // pill SDA to high

state <= IDLE; // transition to idle state

end

end

default: state <= IDLE;

endcase

end

end

endmodule

I2C Slave:
module I2C_slave(

input wire clk, //system clock

input wire rst_n, // active-low reset

input wire scl, // I2C serial clock line

inout wire sda, // I2C serial data line


input wire [6:0] addr, // 7-bit slave address

output reg [7:0] data_rd, // data read from master

output reg ack // Acknowledgement signal

);

// internal states for I2C slave state machine

typedef enum logic[2:0] {IDLE, ADDR, READ, WRITE, ACK} state_t;

state_t state; //current state of the I2C slave state machine

reg [7:0] shift_reg; //shift register for data transmission / reception

reg [2:0] bit_cnt; //bit counter for tracking bits in a byte

reg sda_out; // internal SDA output signal

reg sda_oe; // SDA output enable (1: drive SDA, 0: Tri-state)

// Tri-state buffer for SDA

assign sda = sda_oe ? sda_out : 1'bz;

// I2C slave state machine

always @(posedge clk or negedge rst_n) begin

if (!rst_n) begin

state <= IDLE;

data_rd <= 8'b0;

ack <= 1'b0;

sda_out <= 1'b1;

sda_oe <= 1'b0;

bit_cnt <= 3'd0;

shift_reg <= 8'b0;

end else begin

case (state)

IDLE: begin

sda_out <= 1'b1;

sda_oe <= 1'b0;

if (scl == 1'b1 && sda == 1'b0) begin // detect start condition


state <= ADDR;

bit_cnt <= 3'd7;

end

end

ADDR: begin

if (scl == 1'b1) begin

shift_reg[bit_cnt] <= sda; //sample address bit

if(bit_cnt == 3'b0) begin

if (shift_reg[7:1] == addr) begin // check address match

state <= ACK;

ack <= 1'b1; // acknowledge address match

end else begin

state <= IDLE; // adress mismatch, return to idle

end

end else begin

bit_cnt <= bit_cnt - 3'd1;

end

end

end

READ: begin

if (scl == 1'b1) begin

data_rd[bit_cnt] <= sda; // sample data bit

if (bit_cnt == 3'b0) begin

state <= ACK;

ack <= 1'b1; // acknowledge data reception

end else begin

bit_cnt <= bit_cnt - 3'd1;

end

end

end

WRITE: begin

if (scl == 1'b1) begin

sda_out <= shift_reg[bit_cnt]; // transmit the data bit


sda_oe <= 1'b1;

if (bit_cnt == 3'd0) begin

state <= ACK;

end else begin

bit_cnt <= bit_cnt - 3'd1;

end

end

end

ACK: begin

if (scl == 1'b1) begin

sda_out <= 1'b0; //drive sda low for ACk

sda_oe <= 1'b1;

if (shift_reg[0] == 1'b0) begin //check R / W bit

state <= READ; // master wants to read

end else begin

state <= WRITE; // master wants to write

end

end

end

default: state <= IDLE;

endcase

end

end

endmodule

I2C Top:
module I2C_top(

input wire clk, //system clock

input wire rst_n, // active-low reset

input wire start, // start signal from the master

input wire [6:0] addr, // 7-bit slave address

input wire rw, // read / write bit (0: write, 1: read)

input wire [7:0] data_wr, // data to write master to slave

output wire [7:0] data_rd_master, // data read by master to slave


output wire [7:0] data_rd_slave, // data read by slave to master

output wire ack_master, // acknowledge signal from master

output wire ack_slave // acknowledge signal from slave

);

// internal signals from I2C communication

wire scl; // I2C serial clock line

wire sda; // I2C serial data line

// instantiate the I2C master

I2C_master master(

.clk(clk),

.rst_n(rst_n),

.start(start),

.addr(addr),

.rw(rw),

.data_wr(data_wr),

.scl(scl),

.sda(sda),

.busy(),

.ack(ack_master),

.data_rd(data_rd_master)

);

// instantiate I2C slave

I2C_slave slave (

.clk(clk),

.rst_n(rst_n),

.scl(scl),

.sda(sda),

.addr(addr),

.data_rd(data_rd_slave),

.ack(ack_slave)
);

endmodule

7. Testbench Simulation:

I2C_top_tb:

module I2C_top_tb;
// Inputs
reg clk;
reg rst_n;
reg start;
reg [6:0] addr;
reg rw;
reg [7:0] data_wr;

// Outputs
wire [7:0] data_rd_master;
wire [7:0] data_rd_slave;
wire ack_master;
wire ack_slave;

// Instantiate the top-level module


I2C_top uut (
.clk(clk),
.rst_n(rst_n),
.start(start),
.addr(addr),
.rw(rw),
.data_wr(data_wr),
.data_rd_master(data_rd_master),
.data_rd_slave(data_rd_slave),
.ack_master(ack_master),
.ack_slave(ack_slave)
);

// Clock generation
always #5 clk = ~clk; // 100 MHz clock

// Testbench logic
initial begin
// Initialize inputs
clk = 0;
rst_n = 0;
start = 0;
addr = 7'b0;
rw = 0;
data_wr = 8'b0;
// Apply reset
#10 rst_n = 1;

// Test 1: Write data from master to slave


$display("Test 1: Write data from master to slave");
addr = 7'b1010101; // Slave address
rw = 0; // Write operation
data_wr = 8'b11001100; // Data to write
start = 1; // Start transaction
#20 start = 0; // Deassert start
wait (ack_master); // Wait for acknowledgment
$display("Data written by master: %b", data_wr);
$display("Data received by slave: %b", data_rd_slave);
#100;

// Test 2: Read data from slave to master


$display("Test 2: Read data from slave to master");
addr = 7'b1010101; // Slave address
rw = 1; // Read operation
start = 1; // Start transaction
#20 start = 0; // Deassert start
wait (ack_master); // Wait for acknowledgment
$display("Data read by master: %b", data_rd_master);
#100;

// Test 3: Randomized test with error injection


$display("Test 3: Randomized test with error injection");
repeat (10) begin
addr = $random; // Random slave address
rw = $random; // Random read/write operation
data_wr = $random; // Random data
start = 1; // Start transaction
#20 start = 0; // Deassert start
wait (ack_master); // Wait for acknowledgment
if (addr == 7'b1010101) begin
$display("Valid transaction: Addr = %b, R/W = %b, Data = %b", addr, rw, data_wr);
if (rw == 0) begin
$display("Data written by master: %b", data_wr);
$display("Data received by slave: %b", data_rd_slave);
end else begin
$display("Data read by master: %b", data_rd_master);
end
end else begin
$display("Invalid transaction: Addr = %b (no acknowledgment)", addr);
end
#100;
end

// End simulation
$display("Simulation completed.");
$finish;
end
// Monitor signals
initial begin
$monitor("Time = %0t: SCL = %b, SDA = %b, Ack Master = %b, Ack Slave = %b",
$time, uut.scl, uut.sda, ack_master, ack_slave);
end
endmodule

8. Elaborated Design:

Fig(c): I2C Elaborated design


9. Simulation:

Fig(d): I2C Simulation


10.Synthesis:

Fig(e): I2C Synthesized Design

11.Applications:
I2C is widely used in embedded systems and digital electronics for interfacing
low-speed peripherals.
 Microcontrollers & Sensors: Temperature sensors, accelerometers, and
EEPROMs.
 Real-Time Clocks (RTC): Timekeeping modules in embedded systems.
 Display Interfaces: OLED, LCD, and LED drivers.
 Power Management ICs: Battery monitoring and power regulation.
 FPGA Communication: Connecting multiple ICs within FPGA designs.

12. Advantages & Disadvantages:


1. Uses only two wires (SCL & SDA), reducing pin count.
2. Multi-master and multi-slave support allows flexible system design.
3. Efficient communication with error detection (ACK/NACK).
4. Variable data transfer rates (100 kbps to 3.4 Mbps).
Disadv:
 Slower than SPI due to clock-based control.
 Requires pull-up resistors, adding hardware complexity.
 Limited data transfer rate for high-speed applications.

13.Conclusion:
This project successfully demonstrates the I2C protocol implementation using
SystemVerilog. The Master and Slave modules perform correct read/write
operations, verified through functional simulation. The results confirm the
proper working of I2C communication, making it suitable for sensor
interfacing, memory communication, and embedded system designs.

------The End-----

You might also like