I2C on Spartixed FPGA Learner board
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
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.
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
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.