Home > Uncategorized > I2C on Spartixed FPGA Learner board

I2C on Spartixed FPGA Learner board

December 13th, 2015

By Vikas Shukla

Update 12/28/2015
It turned out that the start condition was not properly generated. There was a small fix and the updated program is posted here .

Update 12/27/2015

Today I finally tried to download the working code into Spartixed. We attempted to glow an LED when we get an Acknowledgement from an I2C IC. First the code

module i2c
(input clk, reset, button_sw3 , output scl, led2,i2c_wp , inout sda );

reg start;
reg [7:0] data;

reg led_output;

reg clk_track;
// For simulation use below
//reg [2:0] counter; 
// Actual for  division by 1024
reg [9:0] counter; 

always @(posedge clk, negedge reset )
//always @(posedge clk )
begin

  if (~reset)
     begin
        counter <= 0;
     end
 
  else
  begin
  
  counter <= counter +1;
  // Use this for simulation
  // clk_track<= counter[2]; // Division by 8
  clk_track <= counter[9]; // Division by 1024
  end 

end  

  
 //wire serclock = clk_track;
 wire serclock = (counter ==0);
 
 // Use this for simulation	
 // wire sernegclock =(counter ==5);
 wire sernegclock =(counter ==512);
 
 assign scl = ~clk_track; 
 assign i2c_wp= 1'b0; 
 
reg [3:0] state;

always @(posedge clk)
begin
   case (state)
	
        4'b0000: if (start)  state <= 4'b0001;
		  4'b0001: if (serclock) state <= 4'b0010;    // Start bit
		  
        4'b0010: if (sernegclock) state <= 4'b0011;    // Bit 0
        4'b0011: if (sernegclock) state <= 4'b0100;    // Bit 1
        4'b0100: if (sernegclock) state <= 4'b0101;    // Bit 2
        4'b0101: if (sernegclock) state <= 4'b0110;    // Bit 3
        4'b0110: if (sernegclock) state <= 4'b0111;    // Bit 4
        4'b0111: if (sernegclock) state <= 4'b1000;    // Bit 5
        4'b1000: if (sernegclock) state <= 4'b1001;    // Bit 6
         4'b1001: if (sernegclock) state <= 4'b1010;   // Bit 7
        4'b1010: if (serclock) state <= 4'b1011;    // Release Bus
		4'b1011: if (sernegclock) state <= 4'b0000;    // Read Acknowledgement
		
        default: state <= 4'b0000;                  // Undefined, skip to stop
		  
		  
		  
    endcase
end
 
 
reg outbit;
 
always @(posedge clk)
begin
    case (state)
	   
	
	 
         4'b0000: outbit <= 1'bz;              // idle - May be assign like this outbit <= 1'bz; 
			
         4'b0001: outbit <= 0;              // Start bit
         4'b0010: outbit <= data[7];        // Bit 7 , MAB
         4'b0011: outbit <= data[6];        // Bit 6
         4'b0100: outbit <= data[5];        // Bit 5
         4'b0101: outbit <= data[4];        // Bit 4
         4'b0110: outbit <= data[3];        // Bit 3
         4'b0111: outbit <= data[2];        // Bit 2
         4'b1000: outbit <= data[1];        // Bit 1
         4'b1001: outbit <= data[0];        // Bit 0
         4'b1010: outbit <= 1'bz;          // Release Bus// May be assign like outbit <= 1'bz; 
		 4'b1011:  led_output<= sda;              // Read the SDA line 
		 
		 
             default: outbit <= 1'bz;          // Bad state output idle better outbit <= 1'bz;
				 
				 
    endcase
end
 
// Output register to pin
assign sda = outbit;
assign led2 = ~led_output;
 
 
    reg  delay_reg  ;
     
     always @(posedge  clk,  negedge  reset)
	  //always @(posedge  clk_track,  negedge  reset)
    if  ( reset == 0)
     begin
    // Correct Address for the I2C EEPROM
	 data <= 8'b10100001; // 1010 is device address, last is 0 means write operation, 1 means read operation
   
	
	// Wrong Address to test 
  // data <= 8'b10101101; // 1010 is device address, last is 0 means write operation, 1 means read operation

	delay_reg  <=  0;
     //start <=0;
	  start <=1;
     end
      
		
    else
     begin
	 
    delay_reg  <=  button_sw3 ;
     start  <=  (delay_reg)  &  (~button_sw3);
	  
	  //start <= ~button_sw3;
     end
	  
	  
	 
endmodule

[verlog]
NET "scl" LOC = P78;
NET "sda" LOC = P79;
NET "led2" LOC = P131;
NET "reset" LOC = P88; // Moved to new Pin as the original not working
NET "clk" LOC = P55;
NET "button_sw3" LOC = P126;
NET "i2c_wp" LOC = P75;
[/verilog]

Basically we are trying to communicate with the I2C EEPROM CAT24C08YI-GT3. Everytime, I press the SW3 button the LED glows indicating the presence of the Acknowdgement signal. If I Change the device address

data <= 8'b10100001; to something like data <= 8'b10101001; the LED does not glow, indicating that the program is functioning as expected. A typical Data and Clock line is observed as follows. However, not everytime the program functioned as expected. A typical screen shot when it did not work will look like this i2c

When it does not work, it has to do with the start signal. It looks like, it is related to the push button ( may be key debounce or something we do not know about).

Stay tuned for more updates.
=============================

The last week I was trying to get some code for I2C working for the FPGA Learner's board, that I have nicknamed Spartixed. The name is derived from Spartan Six Educational - and indicates and Spartan 6 board for Education and learning.

The following is a section of the Schematics governing the I2C Section that uses CAT24C08YI-GT3 EEPROM.

I2C_Bus

It turned out that the Write protect is active high and therefore the WP pin needs to be driven low by the FPGA.

The first thing that I wished to do was to check if we the acknowledgement, or not ( and glow the LED based upon that). This is a first attempt to do verilog.

module i2c
(input clk, reset, button_sw3 , output scl, led2,i2c_wp , inout sda );

reg start;
reg [7:0] data;

reg led_output;

reg clk_track;
reg [2:0] counter; 
// Actual for  division by 1024
//reg [9:0] counter; 
always @(posedge clk )
 
begin
  if (reset == 0)
     begin
        counter <= 0;
     end
 
  else
  begin
  counter <= counter +1;
   clk_track<= counter[2]; // Division by 8
 //  state <= counter[9]; // Division by 1024
  end 
  
 

  
  
 end 
  
 //wire serclock = clk_track;
 wire serclock = (counter ==0);
 wire sernegclock =(counter ==5);
 
 assign scl = ~clk_track; 
 assign i2c_wp= 1'b0; 
 
reg [3:0] state;
 
always @(posedge clk)
begin
   case (state)
        4'b0000: if (start) state <= 4'b0001;
        4'b0001: if (sernegclock) state <= 4'b0010;    // Start bit
        4'b0010: if (sernegclock) state <= 4'b0011;    // Bit 0
        4'b0011: if (sernegclock) state <= 4'b0100;    // Bit 1
        4'b0100: if (sernegclock) state <= 4'b0101;    // Bit 2
        4'b0101: if (sernegclock) state <= 4'b0110;    // Bit 3
        4'b0110: if (sernegclock) state <= 4'b0111;    // Bit 4
        4'b0111: if (sernegclock) state <= 4'b1000;    // Bit 5
        4'b1000: if (sernegclock) state <= 4'b1001;    // Bit 6
         4'b1001: if (sernegclock) state <= 4'b1010;   // Bit 7
        4'b1010: if (sernegclock) state <= 4'b1011;    // Release Bus
		4'b1011: if (sernegclock) state <= 4'b0000;    // Read Acknowledgement
		
        default: state <= 4'b0000;                  // Undefined, skip to stop
    endcase
end
 
 
reg outbit;
 
always @(posedge clk)
begin
    case (state)
         4'b0000: outbit <= 1'bz;              // idle - May be assign like this outbit <= 1'bz; 
         4'b0001: outbit <= 0;              // Start bit
         4'b0010: outbit <= data[7];        // Bit 7 , MAB
         4'b0011: outbit <= data[6];        // Bit 6
         4'b0100: outbit <= data[5];        // Bit 5
         4'b0101: outbit <= data[4];        // Bit 4
         4'b0110: outbit <= data[3];        // Bit 3
         4'b0111: outbit <= data[2];        // Bit 2
         4'b1000: outbit <= data[1];        // Bit 1
         4'b1001: outbit <= data[0];        // Bit 0
         4'b1010: outbit <= 1'bz;          // Release Bus// May be assign like outbit <= 1'bz; 
		 4'b1011:  led_output<= sda;              // Read the SDA line 
		 
             default: outbit <= 1'bz;          // Bad state output idle better outbit <= 1'bz;
    endcase
end
 
// Output register to pin
assign sda = outbit;
assign led2 = ~led_output;
 

    reg  delay_reg  ;
     
     always @(posedge  clk,  negedge  reset)
    if  ( reset == 0)
     begin
     data <= 8'b10100001; // 1010 is device address, last is 0 means write operation, 1 means read operation
    delay_reg  <=  0;
     start <=0;
     end
      
    else
     begin
    delay_reg  <=  button_sw3 ;
     start  <=  (delay_reg)  &  (~button_sw3);
     end
	 
endmodule
  

And a testbench for this ( remove the i2c_wp in the code above for it to work)

`timescale 1 us / 1 us
 
 
module i2c_tb();
    reg clk;
    reg reset;
    reg button_sw3;
    wire out;
    wire sda;	
		
	
 
    i2c UUT (
        .clk(clk), 
        .reset(reset),
		.button_sw3(button_sw3), 	
        .scl(scl),
		.sda(sda)
		
        );
 
 reg OE =0;
 reg SDA_reg;
 
 assign sda = (OE == 1) ? SDA_reg : 1'bz;
 
    initial begin
            clk = 1'b0;
			OE = 0;
			reset = 1'b0;
			#15 reset = 1'b1;
			button_sw3 = 1'b1;
			#150 button_sw3 = 1'b0;
			#1450 SDA_reg = 1'b0; 
			OE =1;
			 
			#1700 OE = 0;
			 #1000
			            $finish;
    end
    always
            #10 clk = ~clk;  // 20 mirco seconds = 50 KHz Clock cycle   
 
initial
      begin
      $dumpfile ("i2c.vcd");
      $dumpvars (0,i2c_tb);
      end
endmodule

This is the simulation result

I2C_sim

This is the ucf file used.

NET "scl" LOC = P78;
NET "sda" LOC = P79;
NET "led2" LOC = P131;
NET "reset" LOC = P124;
NET "clk" LOC = P55;
NET "button_sw3" LOC = P126;
NET "i2c_wp" LOC = P75;

I have still not downloaded the code in the Spartixed. Something for the next week. I do expect some errors, but should be debuggable with a scope.

Uncategorized

  1. No comments yet.
  1. No trackbacks yet.