Verilog Code for I2C Protocol


I2C PROTOCOL

Hola Amigos
I2C devices have been around us from a long time. If you have done any arduino projects with any peripherals such as Bluetooh (HC-05) or Gyroscope (MPU6050) or Barometer etc you might be surprised you have already used I2C devices. Yes

An I2C basically consists of a master micro controller and a slave device which responds to the requests of the master. A slave cannot operate on its own. It can't even communicate with other slave without having nay permission from the master.

      You may have come across multi master schematic but it become much more complex to handle such situation because of data leakage and also it requires more than 1 micro controllers. So if you are using an I2C you cannot use any other non-I2C device on the same bus as bothe SDA and SCL lines are in conjunction with the I2C module. If you find this facility somewhere you are being fooled seriously !!!
 I2C works on 2 signals as SCL and SDA
                                           SCL - Serial Clock
                                           SDA- Serial Data
When SDA is having negedge and SCL is positive level triggered then we have start signal and with every SCL clock a bit is transferred. Combining up to eight bit the slave receives an address. Then come the R/W signal means whether the slave has to read or write from/to address. AT the very moment after R/W bit the last bit known as acknowledgement bit is sent. Then the slave sends bit by bit data and finalizing by the acknowledge bit and the process comes to a STOP.

Do remember that when SDA changes the SCL lines must remain stable hence SDA doesn't change at posedge or nedge of SCL and only on the level of SCL i.e. either 1 or 0.

Here is a demonstration


Pic Credit- Google
 You can easily see the working as I have explained in comparison to the diagrams.
 Ok Coming down to the code

Starting with Master module and with its inputs



clk = Normal clock
sda = serial data
scl = serial clock
data_wr = data that has to be written if rw = 0;
address = address of slave
register = address of register which has to be read
rw = read or write pin

Next we move to declaration of internal variables


temp = to copy address incoming
register2 = to copy register value
scl2x = clock with which sda works to change sda while scl is 0
i = internal counter
n = single counter for start and stop conditions

One must note that initially we have sda and scl = 1 
After 5ns we turn sda to 0 to introduce start bit condition


From Line 28 to 30 :- we use n as a flag to start scl and scl2x to start bit transmitting
At Line 33 :- Temp stores the concated value of address of slave then rw bit and                                                acknowledge bit
At Line 34:- Incoming register address is stored in register2 internal variable because                                     further we will be using shift operators which doesn't work on wires and always                       does on reg data type.
At Line 36 :- If n==1 means start condition and if rw=1 means we have to read register thus                        scl will run upto 50 times
                       Similarly for rw=1 means we have to write scl will run 64 times.
                       The value 50 and 64 can be obtained by self coding 




This piece of code is for stop bit condition.

At Line 55 :- Till value of i reaches 9 we will grab bit by bit from temp
                       Here temp is having 6 bit slave address and 2 bits of rw and ack. Thus each bit                          is being read by shifting temp one by one and reading its MSB
                       Same is happening after Line 60 to get address of register
At Line 65 :- if rw ==0 we will recieve data which has to be written hence same process is                              followed too.

Here is the full code -:

Master Code-:
module master(data,address,clk,rw,sda,scl,register,data_wr);
output reg sda;
input [7:0] data;
input [7:0] data_wr;
reg [7:0]data_wr_dup;
input clk;
input rw;
output reg scl;
input [6:0] address;
input [7:0] register;
reg [8:0] temp;
reg [7:0] register2;
reg pstate;
reg scl2x;
reg ack;
reg a;
integer i;
integer n;
initial begin
i = 0;
n = 0;
scl2x = 0;
ack = 1'b1;
sda = 1;
scl = 1;
#5 sda = 0;  //START BIT condition starts here
end
                                                always @(negedge sda)
                                                if(scl==1)
                                                n=1;
always @(posedge clk)begin
ack = 0;
temp = {address,rw,ack};
register2 = register;
data_wr_dup = data_wr;
if(n==1 && rw==1)
repeat(50)begin
#2 scl <= !scl;n=0;
#1 scl2x <= !scl2x;n=0;
end
else if(n==1 && rw==0)
repeat(64) begin
#2 scl = !scl;
#1 scl2x = !scl2x;n=0;
end
end
always @(posedge clk)begin
if(i==25 && rw==1)
repeat(2)
#1 scl2x = !scl2x;
else if(i==32 && rw==0)
repeat(2)
#1 scl2x = !scl2x;end
always @(posedge scl2x)begin
if(i<=9)begin
sda = temp[8];
temp = temp<<1;
end
else if(i==12 || i==13)
sda = 1'b0;
else if(i>=14)begin
sda = register2[7];
register2 = register2<<1;
end
if(rw==0 && i>=23)begin
sda = data_wr_dup[7];
data_wr_dup = data_wr_dup<<1;
end
i = i + 1;
if(i>32 && rw ==0)
sda= 1;
else if(i>25 && rw==1)
sda = 1;
end
slave slv(data,sda,scl);
endmodule


And here is the code for Slave-:

module slave(out,sda,scl);
input sda;
input scl;
output reg [7:0]out;
integer j = 0;
reg [6:0]temp;
reg [7:0]add;
reg rw;
reg [7:0]register_address;
reg bitin;
reg [7:0]storage[0:38];
initial
storage[37]=16;
parameter address = 7'b1101001;
always @(posedge scl)begin
//if({sda,scl}==2'b01)begin
bitin = sda;
if(j<8)
temp = {temp,bitin};
if(j==8)
            if(bitin==0)
                        rw = 0;
            else
                        rw = 1;
j = j +1 ;
if(temp==address && (j>15 && j<24) && rw==1)begin
            add = {add,bitin};
end
if(temp==address && rw == 0 && j>15 && j!=24 && j<33)begin
            add = {add,bitin};
end
else if(j==24)
            register_address = add;
if(j==33 && rw==0)
storage[register_address]=add;
out = storage[add];
end
endmodule

And here is the testbench _-:

module tbmast;

            // Inputs
            reg [6:0] address;
            reg [7:0] register;
            reg [7:0] data;
            reg [7:0] data_wr;
            reg clk;
            reg rw;

            // Outputs
            wire sda;
            wire scl;

            // Instantiate the Unit Under Test (UUT)
            master uut (
                        .address(address),
                        .register(register),
                        .clk(clk),
                        .rw(rw), 
                        .sda(sda),
                        .scl(scl),
                        .data(data),
                        .data_wr(data_wr)
            );

            initial begin
                        // Initialize Inputs
                        address = 105;
                        register = 7'b0100101;
                        clk = 0;
                        rw = 0;
                        data_wr = 20;

                        // Wait 100 ns for global reset to finish
                        #100;
       
                        // Add stimulus here

            end
      always
                        #1 clk = !clk;
endmodule

Here is the waveform for rw==0 means write a data to register
Better ZOOM it




Master Model
 Slave Model

So Long

28 comments:

  1. Replies
    1. Indeed just put sda=0 in test bench but after certain time

      Delete
  2. Hello,
    J is used here as a counter to initiate operations at approprite timings.

    ReplyDelete
  3. /* input data */ in the master program where it is used???

    ReplyDelete
  4. why input data is declared in master code?

    ReplyDelete
  5. Hello @Ajisha, @anonymous,
    Slave devices never have any third connection (out[7:0]) as I have used here. They transfer data back on the SDA line which is read by the master. This would make the code very complicated. Thus for simplicity, we used a separate out[7:0] register to output the data.

    The input[7:0] data in the master is a used line. It will receive data back from the slave if the rw bit is meant to read data. It won't work if data is meant to be written. It is connected with the out[7:0] from slave so that the data can be read back in the master. This is how real I2C devices work.The data that will be sent to the slave by the master is processed by data_wr wire which is incorporated into test bench.

    ThankYou,
    Have a nice day
    Eva

    ReplyDelete
  6. Awesome article. Worked perfectly with Xilinx. Had issues with ModelSim. Did you try on Model Sim ?

    ReplyDelete
    Replies
    1. Hi Tesla,
      Thanks for the compliment. Well yes, Shashi had many issues regarding ModelSim while he was working on I2C. Also the RTL viewer in Xilinx provided a bonus to work upon.

      Delete
  7. If the transmission of the slave address is successful and recognized by the device, it should send the ACK as 0. But in the snippet of the simulation that you have given, the 9th bit, i.e the ACK bit is high. Does that mean it is a NACK? If so, why is it not re-transmitting the slave address and instead sending the register address? Also why do we have a stream of 0's after the 9th bit? And why is 'data' declared as an input in the master module and not used anywhere except in the port declaration of the slave?

    ReplyDelete
    Replies
    1. Q Why do we have a stream of 0's after the 9th bit?

      A: For a Naive Reason. To make it visually easy and understandable I used a stream of 0's. It can be modified.

      Q: Why is 'data' declared as an input in the master module and not used anywhere except in the port declaration of the slave?

      A: Because I have used a separate line as out from slave that connects to the input data in master. To avoid complexity and reading data back from slave on the same SDA line increased the complexity. It is just a wire connected between master and slave to read the data.

      Q: If the transmission of the slave address is successful and recognized by the device, it should send the ACK as 0. But in the snippet of the simulation that you have given, the 9th bit, i.e the ACK bit is high. Does that mean it is a NACK?

      A: No it is an ACK. Actually I hardcoded ack bit in the code because while my team was implementing this code on Basys2 FPGA, we had to face bit conflict issue i.e different bit at the same when Master tried to resend the address. That is why we used hard coded ACK in the code. Also its a mistake here. ACK = 0 and not 1. Thanks for pointing out.

      Have a nice day.

      Delete
    2. You are welcome. Do follow us :)

      Delete
  8. Thanks for the article!
    Can you tell me how to communicate with slave? How do we check the read operation? Since, the test bench is written for the master, how do we know if the value sent from the master is being written to the slave or not?
    Also, how do we read from the slave?
    Thank you.

    ReplyDelete
    Replies
    1. Hi There HiThere,
      Nice username by the way.

      You will have to select open the slave file on the left panel of simulator. Then right click on any of the wires and select "add to wave". This will instantaneously appear on the wave. Restart the simulation. You will certainly see the data stored inside the array of registers inside slave.

      To communicate with slave just pull the SDA line to zero in the test bench after a desired time.

      To read operation, firstly, you will have to send the RW bit as 1. Then mention the address of the register. The slave will send the data stored at that address on the out[7:0] line. However you will have to add that line to the wave by the same methid I described above.

      Delete
    2. Thanks a lot for the quick reply.
      I meant, How do you include the slave instantiation inside the master's testbench and connect it to master and what are the connections that are to be made while doing that?

      Delete
    3. Slave is not instantiated inside test bench. It is instantiated at line no.75
      It is a basic connection. Output from one module is input for another module. SDA and SCL are output in master. These are joined with slave as inputs. Similarly output from slave is output which is connected with master as input.

      Do check carefully while you are instantiating a module for correct order.
      Ex- Slave connections are Slave (output, input, input). The order inside is very important or else you may connect wrong wires.
      So instantiating slave in master at line no75 is Slave slv(data, sda, scl)

      Master__________________________Slave
      data(input)<---------------------- out(output)
      SDA (output)-----------------------> SDA (input)
      SCL (output) -----------------------> SCL (input)

      Delete
    4. Thanks a lot! That was really useful!!

      Delete
  9. I am new to verilog. I have a few doubts in the code of slve module. Could you pleas help me

    1. What is the significance of bitin in the slave module. Why are we using it and what is it doing

    reg bitin;

    2. What does the following expression does. Could you give a few examples with the input and output
    temp = {temp,bitin};

    ReplyDelete
    Replies
    1. Hi,
      First
      temp = {temp,bitin} This line will concatenate the bitin bit to the end of temp.
      Example if temp = 101011 and bitin = 0, then {temp,bitin} = 010110 and this will be stored in the temp itself.

      bitin helps to gather the data or address on SDA line which is to be stored for useful purposes.
      Example for(i<7) temp will store every bitin values repeatedly by concatenating it at the end of temp. This will be the address sent by the master. Similarly at i==8 SDA value is again stored in bitin which tells us whether rw is enabled or not.

      Actually I was having difficulties working with SDA wire hence I had to store the wire value locally to perform operations.

      Regards

      Delete
  10. How to communicate multiple slave with the same logic as you mentioned

    ReplyDelete
    Replies
    1. Hola,
      You will have to copy the code of slave and recreate a module named slave1 or anything different from previous one.
      Then you will have to change the line that says
      parameter address = **Unique Address**; in that particular slave code.
      Finally in the test bench set address = **Unique Address**.
      The if statement in each slave will first match the incoming address and only then it will start communication.
      Do remember to instantiate all slaves in master.

      Delete
  11. Hello Thanks for the reply and if i want to communicate with one slave at a time and stop the operation of others how can i do it using the same logic even if the address does not match as you mentioned

    ReplyDelete
    Replies
    1. This code will work with only one slave at a time ONLY if you have assigned different address to each slave . If the address doesn't match then the variable in Slave module named "Register_address" will be equal to XXXXXX.

      Since the address won't match therefore, this if loop

      if(temp==address && (j>15 && j<24) && rw==1)begin

      will not execute.
      No need to worry. As long as Slave address is not matched, it will remain in a stop condition.
      Just remember to give different address to each slave and you are good to go.

      Delete
  12. Hi i have a small error like
    ERROR:Xst:871 - "step 1.v" line 73: Invalid use of input signal as target.
    This is while instaniating slave code in master in Xilinx 9.2 version so say me how to fix that error.

    ReplyDelete
  13. Hi the error is i m using data while instating slave as output in master code in the same way as you mentioned like Slave slv(data, sda, scl) but here the xilionx is saying that invalid use of data as target.

    ReplyDelete
    Replies
    1. You can't use data terminal for target. After contacting with slave, slave will send a data. Instead of keeping this data on SDA line, I have kept this information on *DATA* line for ease of use and visibility. So whenever communicated, the slave will send the data stored in it's arrays on the data line by *ITSELF*. If you want to see any particular data then store your data in the arrays inside the slave code.
      This is the line in slave that consists of an array to store data. -->
      reg [7:0]storage[0:38];
      storage[5] = "Your Data"

      Delete