+ Post New Thread
Results 1 to 6 of 6
  1. #1
    Newbie level 3
    Points: 26, Level: 1

    Join Date
    Jul 2018
    Posts
    3
    Helped
    0 / 0
    Points
    26
    Level
    1

    FPGA I2C Can write to EEPROM but not read from EEPROM DE0 Nano Board

    Hello

    I am learning FPGA's using a DE0-Nano Cyclone IV board from Terasic.

    Using verilog and I2C, I can write to the boards onboard 24LC02B I2C 2K EEPROM, but I cannot read the EEPROM. So I can read but not write.

    The reason I know I can write to the EEPROM, is because after writing, I use the DE0_Nano_ControlPanel program included in the kit CDROM to view the EEPROM memory.

    The way I have been trying to read the EEPROM was by trying to fill a 8 bit data register with the I2C SDA line, then assigning it to the LED's.

    I got the EEPROM writing code from watching a tutorial on Terasics youtube channel.

    From what I have seen, the write code takes the DE0-Nano's board 50 mhz clock, and uses the bit number 9 of an internal counter, as the i2c clock, which seems to operate at just below 25 khz.

    Then it looks like using this slow clock, a counter for the i2c operations is created.

    Then this counter is used to sequence the individual i2c operations.

    Here is the EEPROM write code.

    Code:
    module i2c_test (
    
    	CLOCK_50,
    	LED,
    	KEY,
    	I2C_SCLK,
    	I2C_SDAT,
    	COUNT,
    	SD_COUNTER_OUT
    );
    
    input 		CLOCK_50;
    output  	[7:0] LED;
    input 	[1:0] KEY;
    output 		 I2C_SCLK;
    inout  		 I2C_SDAT;
    output       [6:0] SD_COUNTER_OUT;
    output       [9:0] COUNT;
    
    wire 				reset_n;
    
    reg 				GO;
    reg	[6:0]		        SD_COUNTER;
    reg				SDI;
    reg 				SCLK;
    reg	[9:0] 	        COUNT;
    
    assign			reset_n = KEY[0];
    
    always@(posedge CLOCK_50) COUNT <= COUNT + 1;
    
    always @ (posedge COUNT[9] or negedge reset_n)
    begin
    	if(!reset_n)
    		GO <= 0;
    	else
    		if(!KEY[1])
    			GO <= 1;
    end
    
    // 12C COUNTER
    
    always @(posedge COUNT[9] or negedge reset_n)
    begin
    	if(!reset_n)
    		SD_COUNTER <= 6'b0;
    	else
    	begin
    		if(!GO)
    			SD_COUNTER <= 0;
    		else
    			if(SD_COUNTER < 33)
    				SD_COUNTER <= SD_COUNTER + 1;
    	end
    end
    
    
    // I2C OPERATION
    
    always @(posedge COUNT[9] or negedge reset_n)
    begin
    
    	if(!reset_n)
    	begin
    		SCLK <= 1; 
    		SDI	<= 1;
    	end
    	
    	else
    	case (SD_COUNTER)
    		
    		6'd0	: begin 
                              SDI <= 1; 
                              SCLK <= 1; 
                              end
    		
    		// START
    		
    		6'd1	: SDI <= 0;
    		6'd2	: SCLK <= 0;
    
    		// SLAVE ADDRESS
    		
    		6'd3	: SDI <= 1;
    		6'd4	: SDI <= 0;
    		6'd5	: SDI <= 1;
    		6'd6	: SDI <= 0;
    		6'd7	: SDI <= 0;
    		6'd8	: SDI <= 0;
    		6'd9	: SDI <= 0;
    		6'd10 : SDI <= 0;
    		6'd11 : SDI <= 1'bz;
    		
    		// SUB ADDRESS
    		
    		6'd12	: SDI <= 0;
    		6'd13	: SDI <= 0;
    		6'd14	: SDI <= 0;
    		6'd15 : SDI <= 0;
    		6'd16	: SDI <= 0;
    		6'd17	: SDI <= 0;
    		6'd18	: SDI <= 0;
    		6'd19	: SDI <= 0;
    		6'd20	: SDI <= 1'bz;
    		
    		// DATA
    		
    		6'd21	: SDI <= 0;
    		6'd22	: SDI <= 1;
    		6'd23	: SDI <= 0;
    		6'd24        : SDI <= 1;
    		6'd25	: SDI <= 0;
    		6'd26	: SDI <= 1;
    		6'd27	: SDI <= 0;
    		6'd28	: SDI <= 1;
    		6'd29	: SDI <= 1'bz;
    
    		6'd30	: begin SDI <= 1'b0; SCLK <= 1'b1; end
    		6'd31	: SDI <= 1'b1;
    	endcase
    end
    
    assign I2C_SCLK = ((SD_COUNTER >= 4) & (SD_COUNTER <= 31))? ~COUNT[9] : SCLK;
    assign I2C_SDAT = SDI;
    
    assign SD_COUNTER_OUT = SD_COUNTER;
    
    endmodule
    The above works, now, here is the read code, which does not work.

    I dont know what I am doing wrong, there are a few possibilities.
    1) For the random read, it appears, that you send a control byte, then the address, then you restart the i2c communications again, then you send a control byte, then you read. I dont know if I am doing this sequence correctly.

    2) Reading the data from the SDA line, to the DATA register, then assigning it to the LEDs.

    For example, should I be assigning the I2C_SDAT variable to DATA[n] during the read.

    Also, should the assign I2C_SDAT = SDI be disabled during the above read operations?

    3) Not using the bi directional pins properly.

    Code:
    module i2c_test (
    	CLOCK_50,
    	LED,
    	KEY,
    	I2C_SCLK,
    	I2C_SDAT,
    	COUNT,
    	SD_COUNTER_OUT
    );
    
    input 			CLOCK_50;
    output  	        [7:0] LED;
    input 	        [1:0] KEY;
    output 			I2C_SCLK;
    inout  			I2C_SDAT;
    output        [6:0] 	SD_COUNTER_OUT;
    output        [9:0] 	COUNT;
    
    wire 				reset_n;
    
    reg 				GO;
    reg	  [6:0]		SD_COUNTER;
    reg				SDI;
    reg 				SCLK;
    reg	        [9:0] 	COUNT;
    
    reg	  [7:0]		DATA = 8'b00000000;
    
    assign			reset_n = KEY[0];
    
    always@(posedge CLOCK_50) COUNT <= COUNT + 1;
    
    always @ (posedge COUNT[9] or negedge reset_n)
    begin
    	if(!reset_n)
    		GO <= 0;
    	else
    		if(!KEY[1])
    			GO <= 1;
    end
    
    
    always @(posedge COUNT[9] or negedge reset_n)
    begin
    	if(!reset_n)
    		SD_COUNTER <= 6'b0;
    	else
    	begin
    		if(!GO)
    			SD_COUNTER <= 0;
    		else
    			if(SD_COUNTER < 45)
    				SD_COUNTER <= SD_COUNTER + 1;
    	end
    end
    
    // I2C OPERATION
    
    always @(posedge COUNT[9] or negedge reset_n)
    begin
    
    	if(!reset_n)
    	begin
    		SCLK <= 1; 
    		SDI	<= 1;
    	end
    	
    	else
    	case (SD_COUNTER)
    		
    		6'd0	: begin SDI <= 1; SCLK <= 1; end
    		
    		// START
    		
    		6'd1	: SDI <= 0;
    		6'd2	: SCLK <= 0;
    
    		// SLAVE ADDRESS
    		
    		6'd3	: SDI <= 1;
    		6'd4	: SDI <= 0;
    		6'd5	: SDI <= 1;
    		6'd6	: SDI <= 0;
    		6'd7	: SDI <= 0;
    		6'd8	: SDI <= 0;
    		6'd9	: SDI <= 0;
    		6'd10	: SDI <= 0;
    		6'd11	: SDI <= 1'bz;
    		
    		// SUB ADDRESS
    		
    		6'd12	: SDI <= 0;
    		6'd13	: SDI <= 0;
    		6'd14	: SDI <= 0;
    		6'd15 : SDI <= 0;
    		6'd16	: SDI <= 0;
    		6'd17	: SDI <= 0;
    		6'd18	: SDI <= 0;
    		6'd19	: SDI <= 0;
    		6'd20	: SDI <= 1'bz;
    		
    		// START
    		
    		6'd21	: begin SDI <= 1; SCLK <= 1; end
    		
    		6'd22	: SDI <= 0;
    		6'd23	: SCLK <= 0;
    		
    		// SLAVE ADDRESS
    		
    		6'd24	: SDI <= 1;
    		6'd25	: SDI <= 0;
    		6'd26	: SDI <= 1;
    		6'd27	: SDI <= 0;
    		6'd28	: SDI <= 0;
    		6'd29	: SDI <= 0;
    		6'd30	: SDI <= 0;
    		6'd31	: SDI <= 1;
    		6'd32	: SDI <= 1'bz;
    		
    		// DATA is this the right way to read data from the 
    			
    		6'd33	: DATA[7] <= SDI;
    		6'd34	: DATA[6] <= SDI;
    		6'd35	: DATA[5] <= SDI;
    		6'd36       : DATA[4] <= SDI;
    		6'd37	: DATA[3] <= SDI;
    		6'd38	: DATA[2] <= SDI;
    		6'd39	: DATA[1] <= SDI;
    		6'd40	: DATA[0] <= SDI;
    		//6'd41	: SDI <= 1'bz;  // The datasheet says there is now acknowledge after read.
    		6'd41	: SDI <= 1;     
    		// END
    	
    		6'd42	: begin SDI <= 1'b0; SCLK <= 1'b1; end
    		6'd43	: SDI <= 1'b1;
    	endcase
    end
    
    assign I2C_SCLK = ((SD_COUNTER >= 4) & (SD_COUNTER <= 45))? ~COUNT[9] : SCLK;
    assign I2C_SDAT = SDI;
    
    assign SD_COUNTER_OUT = SD_COUNTER;
    assign LED = DATA;
    
    endmodule
    Any help is appreciated.

    •   AltAdvertisment

        
       

  2. #2
    Member level 5
    Points: 598, Level: 5
    Achievements:
    Created Blog entry

    Join Date
    Apr 2018
    Location
    Gdańsk, Poland
    Posts
    90
    Helped
    19 / 19
    Points
    598
    Level
    5
    Blog Entries
    3

    Re: FPGA I2C Can write to EEPROM but not read from EEPROM DE0 Nano Board

    I see a very nasty project.

    First of all use only CLOCK_50 in the processes sensitivity list, i.e. always@(posedge CLOCK_50) and visualize your design as clocked by CLOCK_50.

    Then I see that you want to check COUNT[9] bit almost everywhere. May I ask why? Do you mean to divide CLOCK_50 by 2^10?
    I suggest to set up a flag in always@(posedge CLOCK_50) COUNT <= COUNT + 1; when COUNT[9] = '1' then reset a flag and COUNT.
    Check that flag inside always@(posedge CLOCK_50) processes and do something when flag='1', otherwise do nothing.

    This is a very nasty hardware description:
    assign I2C_SCLK = ((SD_COUNTER >= 4) & (SD_COUNTER <= 45))? ~COUNT[9] : SCLK;
    Think about what logics must be utilized to workout upper assignment...
    To encourage me to help more, click on "Helpful Post" if you feel that I've helped you.



    •   AltAdvertisment

        
       

  3. #3
    Newbie level 3
    Points: 26, Level: 1

    Join Date
    Jul 2018
    Posts
    3
    Helped
    0 / 0
    Points
    26
    Level
    1

    Re: FPGA I2C Can write to EEPROM but not read from EEPROM DE0 Nano Board

    Quote Originally Posted by niciki View Post
    Then I see that you want to check COUNT[9] bit almost everywhere. May I ask why? Do you mean to divide CLOCK_50 by 2^10?
    Correct, the turns a 50 MHz clock into the I2C clock SCL clock and operations counter.

    Should I change the example to make a slow clock outside of the modules? Is it better style?

    Quote Originally Posted by niciki View Post

    I suggest to set up a flag in always@(posedge CLOCK_50) COUNT <= COUNT + 1; when COUNT[9] = '1' then reset a flag and COUNT.
    Check that flag inside always@(posedge CLOCK_50) processes and do something when flag='1', otherwise do nothing.
    I will give this a try.

    Quote Originally Posted by niciki View Post

    This is a very nasty hardware description:
    assign I2C_SCLK = ((SD_COUNTER >= 4) & (SD_COUNTER <= 45))? ~COUNT[9] : SCLK;
    Think about what logics must be utilized to workout upper assignment...
    I agree.

    Its just this write sample is the only one I could find thats a complete working sample which demonstrates how to use the i2c bus.



    •   AltAdvertisment

        
       

  4. #4
    Super Moderator
    Points: 249,046, Level: 100
    Awards:
    1st Helpful Member

    Join Date
    Jan 2008
    Location
    Bochum, Germany
    Posts
    43,337
    Helped
    13176 / 13176
    Points
    249,046
    Level
    100

    Re: FPGA I2C Can write to EEPROM but not read from EEPROM DE0 Nano Board

    You are reading the data from reg SDI instead of I2C_DAT which can't work.



  5. #5
    Newbie level 3
    Points: 26, Level: 1

    Join Date
    Jul 2018
    Posts
    3
    Helped
    0 / 0
    Points
    26
    Level
    1

    Re: FPGA I2C Can write to EEPROM but not read from EEPROM DE0 Nano Board

    Okay, I am still stuck :(

    Quote Originally Posted by FvM View Post
    You are reading the data from reg SDI instead of I2C_DAT which can't work.
    That was on my mind.

    So in the read module I have changed this part

    Code Verilog - [expand]
    1
    2
    3
    4
    5
    6
    7
    8
    
    6'd33   : DATA[7] <= SDI;
            6'd34   : DATA[6] <= SDI;
            6'd35   : DATA[5] <= SDI;
            6'd36        : DATA[4] <= SDI;
            6'd37   : DATA[3] <= SDI;
            6'd38   : DATA[2] <= SDI;
            6'd39   : DATA[1] <= SDI;
            6'd40   : DATA[0] <= SDI;

    To

    Code Verilog - [expand]
    1
    2
    3
    4
    5
    6
    7
    8
    
    6'd33   : DATA[7] <= I2C_SDAT;
            6'd34   : DATA[6] <= I2C_SDAT;
            6'd35   : DATA[5] <= I2C_SDAT;
            6'd36        : DATA[4] <= I2C_SDAT;
            6'd37   : DATA[3] <= I2C_SDAT;
            6'd38   : DATA[2] <= I2C_SDAT;
            6'd39   : DATA[1] <= I2C_SDAT;
            6'd40   : DATA[0] <= I2C_SDAT;;

    Also, I changed it so the I2C_SDAT input output goes to Z when trying to read the values.

    This got changed

    Code Verilog - [expand]
    1
    
    assign I2C_SDAT = SDI;

    to

    Code Verilog - [expand]
    1
    
    assign I2C_SDAT = ((SD_COUNTER >= 33) & (SD_COUNTER <= 40)) ? 1'bz : SDI;

    So my module now looks like this, I also changed my design so the I2C clock gets made outside of the module, so it looks a bit neater than CLOCK_50[9].

    Code Verilog - [expand]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    
    module i2c_test (
        CLOCK,
        LED,
        KEY,
        I2C_SCLK,
        I2C_SDAT,
        COUNT,
        SD_COUNTER_OUT
    );
     
    input           CLOCK;
    output      [7:0] LED;
    input   [1:0] KEY;
    output          I2C_SCLK;
    inout           I2C_SDAT;
    output [6:0]    SD_COUNTER_OUT;
    output [9:0]    COUNT;
     
    wire                reset_n;
     
    reg                 GO;
    reg [6:0]       SD_COUNTER = 6'b0;
    reg             SDI     = 1;
    reg                 SCLK    = 1;
    reg [9:0]   COUNT;
     
    reg [7:0]       DATA = 8'b00000000;
     
    assign          reset_n = KEY[0];
     
    always @ (posedge CLOCK or negedge reset_n)
    begin
        if(!reset_n)
            GO <= 0;
        else
            if(!KEY[1])
                GO <= 1;
    end
     
     
    always @(posedge CLOCK or negedge reset_n)
    begin
        if(!reset_n)
            SD_COUNTER <= 6'b0;
        else
        begin
            if(!GO)
                SD_COUNTER <= 0;
            else
                if(SD_COUNTER < 45)
                    SD_COUNTER <= SD_COUNTER + 1;
        end
    end
     
    // I2C OPERATION
     
    always @(posedge CLOCK or negedge reset_n)
    begin
     
        if(!reset_n)
        begin
            SCLK <= 1; 
            SDI <= 1;
        end
        
        else
        case (SD_COUNTER)
            
            6'd0    : begin SDI <= 1; SCLK <= 1; end
            
            // START
            
            6'd1    : SDI <= 0;
            6'd2    : SCLK <= 0;
     
            // SLAVE ADDRESS
            
            6'd3    : SDI <= 1;
            6'd4    : SDI <= 0;
            6'd5    : SDI <= 1;
            6'd6    : SDI <= 0;
            6'd7    : SDI <= 0;
            6'd8    : SDI <= 0;
            6'd9    : SDI <= 0;
            6'd10   : SDI <= 0;
            6'd11   : SDI <= 1'bz;
            
            // SUB ADDRESS
            
            6'd12   : SDI <= 0;
            6'd13   : SDI <= 0;
            6'd14   : SDI <= 0;
            6'd15 : SDI <= 0;
            6'd16   : SDI <= 0;
            6'd17   : SDI <= 0;
            6'd18   : SDI <= 0;
            6'd19   : SDI <= 0;
            6'd20   : SDI <= 1'bz;
            
            // START
            
            6'd21   : begin SDI <= 1; SCLK <= 1; end
            
            6'd22   : SDI <= 0;
            6'd23   : SCLK <= 0;
            
            // Read 
            
            6'd24   : SDI <= 1;
            6'd25   : SDI <= 0;
            6'd26   : SDI <= 1;
            6'd27   : SDI <= 0;
            6'd28   : SDI <= 0;
            6'd29   : SDI <= 0;
            6'd30   : SDI <= 0;
            6'd31   : SDI <= 1;
            6'd32   : SDI <= 1'bz;
            
            // DATA
            6'd33   : DATA[7] <= I2C_SDAT;
            6'd34   : DATA[6] <= I2C_SDAT;
            6'd35   : DATA[5] <= I2C_SDAT;
            6'd36 : DATA[4] <= I2C_SDAT;
            6'd37   : DATA[3] <= I2C_SDAT;
            6'd38   : DATA[2] <= I2C_SDAT;
            6'd39   : DATA[1] <= I2C_SDAT;
            6'd40   : DATA[0] <= I2C_SDAT;
        
            6'd41   : SDI <= 1'bz;
            
            6'd42   : begin SDI <= 1'b0; SCLK <= 1'b1; end
            6'd43   : SDI <= 1'b1;
        endcase
    end
     
    assign I2C_SCLK = ((SD_COUNTER >= 4) & (SD_COUNTER <= 43))? ~CLOCK : SCLK;
    assign I2C_SDAT = ((SD_COUNTER >= 33) & (SD_COUNTER <= 40)) ? 1'bz : SDI;
     
    assign SD_COUNTER_OUT = SD_COUNTER;
    assign LED = DATA;
     
    endmodule



    •   AltAdvertisment

        
       

  6. #6
    Member level 5
    Points: 598, Level: 5
    Achievements:
    Created Blog entry

    Join Date
    Apr 2018
    Location
    Gdańsk, Poland
    Posts
    90
    Helped
    19 / 19
    Points
    598
    Level
    5
    Blog Entries
    3

    Re: FPGA I2C Can write to EEPROM but not read from EEPROM DE0 Nano Board

    What is the problem after changes?
    To encourage me to help more, click on "Helpful Post" if you feel that I've helped you.



--[[ ]]--