Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

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

Status
Not open for further replies.

SmellyNuisance

Newbie level 3
Joined
Jul 30, 2018
Messages
3
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
59
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.
 

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...
 

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?

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.

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.
 

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

Okay, I am still stuck :(

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

 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top