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.

VHDL code for I2C master write/read

Status
Not open for further replies.

pulkit.vlsi

Junior Member level 1
Junior Member level 1
Joined
Jun 11, 2012
Messages
16
Helped
1
Reputation
2
Reaction score
0
Trophy points
1,281
Visit site
Activity points
1,475
Can anyone help me to write a simple working vhdl code for I2C master write and read interface with FPGA ?
I am doing this project for interfacing AT24C01A eprom with FPGA.

Thanks in advance.
 

Thanks for your reply TrickyDicky.

Here is my I2C master code. I am writing "11110000" on "00000001"th location in eprom. but when i go to read this location, i am getting only "11111111".

Can you have a look n help me to find mistake in this code.

Thanks


Code VHDL - [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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
--------------------------------------------------------------------------------
--
--   FileName:         i2c_master.vhd
    
--------------------------------------------------------------------------------
 
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;
 
ENTITY i2c_master IS
 
  PORT(
    clk         : IN     STD_LOGIC;                    --system clock
    rst         : IN     STD_LOGIC;                    --active HIGH reset
    ena         : IN     STD_LOGIC;                    --latch in command
--    addr      : IN     STD_LOGIC_VECTOR(6 DOWNTO 0); --address of target slave
    rw          : IN     STD_LOGIC;                    --'0' is write, '1' is read
--    data_wr   : IN     STD_LOGIC_VECTOR(7 DOWNTO 0); --data to write to slave
        CNT_ST  : out std_logic_vector(3 downto 0);
--      clk_out : out std_logic;
    data_rd     : OUT    STD_LOGIC_VECTOR(7 DOWNTO 0); --data read from slave
    
    sda         : INOUT  STD_LOGIC;                    --serial data output of i2c bus
    scl         : INOUT  STD_LOGIC);                   --serial clock output of i2c bus
END i2c_master;
 
ARCHITECTURE logic OF i2c_master IS
 
 
  TYPE machine IS(ready, start, command, slv_ack1, wr, wr2, rd, slv_ack2, mstr_ack, stop,slv_ack3); --needed states
  SIGNAL  ps,ns     :  machine;                          --state machine
  SIGNAL  data_clk  :  STD_LOGIC;                        --clock edges for sda
  SIGNAL  scl_clk   :  STD_LOGIC;                        --constantly running internal scl
  SIGNAL  scl_ena   :  STD_LOGIC := '0';                 --enables internal scl to output
  SIGNAL  sda_int   :  STD_LOGIC := '1';                 --internal sda
  SIGNAL  sda_ena_n :  STD_LOGIC;                        --enables internal sda to output
  SIGNAL  addr_rw   :  STD_LOGIC_VECTOR(7 DOWNTO 0);     --latched in address and read/write
  SIGNAL  data_tx   :  STD_LOGIC_VECTOR(7 DOWNTO 0);     --latched in data to write to slave
  SIGNAL  data_rx   :  STD_LOGIC_VECTOR(7 DOWNTO 0);     --data received from slave
  SIGNAL  bit_cnt   :  INTEGER RANGE 0 TO 7 := 7;        --tracks bit number in transaction
  
--XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
 
 
    SIGNAL addr         : STD_LOGIC_VECTOR(6 DOWNTO 0):= "1010000";     --address of target slave
--  SIGNAL rw           : STD_LOGIC:= '0';                                  --'0' is write, '1' is read
    SIGNAL data_wr      : STD_LOGIC_VECTOR(7 DOWNTO 0):= "11110000"; --data to write to slave
    SIGNAL WR_ADR       : STD_LOGIC_VECTOR(7 DOWNTO 0):= "00000001";
 
 
    signal delay : std_logic_vector(30 downto 0):= (others => '0');
    signal count : INTEGER RANGE 0 TO 200;--divider*4; --timing for clock generation
--XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX  
  
  
  
  
BEGIN
 
process(clk,rst)
begin
    if(rst = '1')then
        count <= 0;
    elsif rising_edge(clk)then
        IF(count = 199)then       --end of timing cycle
        count <= 0;                      --reset timer
      ELSE                --clock stretching from slave not detected
        count <= count + 1;              --continue clock generation timing
      END IF;
    end if;
end process;
 
 
                                --generate the timing for the bus clock (scl_clk) and the data clock (data_clk)
  PROCESS(clk,rst,count)
                                --    VARIABLE count : INTEGER RANGE 0 TO divider*4; --timing for clock generation
  BEGIN
    IF(rst = '1') THEN               --reset asserted
            scl_clk <= '0';
         data_clk <= '0';
    
     ELSIF RISING_EDGE(clk) THEN
      
    
      CASE count IS
        WHEN 0 TO 49 =>           --first 1/4 cycle of clocking
          scl_clk <= '0';
          data_clk <= '0';
       
         WHEN 50 to 99 =>    --second 1/4 cycle of clocking
          scl_clk <= '0';
          data_clk <= '1';
        
          WHEN 100 to 149 =>  --third 1/4 cycle of clocking
          scl_clk <= '1';                --release scl
 
 
          data_clk <= '1';
        
          WHEN 150 to 199 =>                   --last 1/4 cycle of clocking
          scl_clk <= '1';
          data_clk <= '0';
        
            WHEN others => null ;
             
      END CASE;
    END IF;
  END PROCESS;
 
process(clk,rst)
begin
    if(rst = '1')then
        ps <= ready;
    elsif rising_edge(clk)then
        ps <= ns ;
    end if;
end process;
 
  --state machine and writing to sda during scl low (data_clk rising edge)
  PROCESS(data_clk, rst)
  BEGIN
    IF(rst = '1') THEN                  --reset asserted
      ns <= ready;                       --return to initial state
      
      scl_ena <= '0';                       --sets scl high impedance
      sda_int <= '1';                       --sets sda high impedance
      bit_cnt <= 7;                         --restarts data bit counter
      data_rd <= "00000000";                --clear data read port
        CNT_ST <= "1111";
    ELSIF RISING_EDGE(data_clk) THEN
      CASE ps IS
        
          WHEN ready =>                       --idle state
          CNT_ST <= "0001";
             
             IF(ena = '1') THEN                --transaction requested
           
            addr_rw <= addr & rw;           --collect requested slave address and command
            data_tx <= WR_ADR;--data_wr;             --collect requested data to write
            ns <= start;                 --go to start bit
          ELSE                              --remain idle
            
            ns <= ready;                 --remain idle
          END IF;
        
          
          WHEN start =>                       --start bit of transaction
          CNT_ST <= "0010";          
          
             scl_ena <= '1';                   --enable scl output
          sda_int <= addr_rw(bit_cnt);      --set first address bit to bus
          ns <= command;                 --go to command
        
          
          WHEN command =>                     --address and command byte of transaction
                CNT_ST <= "0011";
          
          IF(bit_cnt = 0) THEN              --command transmit finished
            sda_int <= '1';                 --release sda for slave acknowledge
            bit_cnt <= 7;                   --reset bit counter for "byte" states
            ns <= slv_ack1;              --go to slave acknowledge (command)
          ELSE                              --next clock cycle of command state
            bit_cnt <= bit_cnt - 1;         --keep track of transaction bits
            sda_int <= addr_rw(bit_cnt-1);  --write address/command bit to bus
            ns <= command;               --continue with command
          END IF;
       
 
         WHEN slv_ack1 =>                    --slave acknowledge bit (command)
                CNT_ST <= "0100";
         
          IF(addr_rw(0) = '0') THEN         --write command
            sda_int <= data_tx(bit_cnt);    --write first bit of data
            ns <= wr;                    --go to write byte
          ELSE                              --read command
            sda_int <= '1';                 --release sda from incoming data
            ns <= rd;                    --go to read byte
          END IF;
       
 
         WHEN wr =>                          --write byte of transaction
                CNT_ST <= "0101";
             
          IF(bit_cnt = 0) THEN              --write byte transmit finished
            sda_int <= '1';                 --release sda for slave acknowledge
            bit_cnt <= 7;                   --reset bit counter for "byte" states
            ns <= slv_ack2;              --go to slave acknowledge (write)
 
          ELSE                              --next clock cycle of write state
            bit_cnt <= bit_cnt - 1;         --keep track of transaction bits
            sda_int <= data_tx(bit_cnt-1);  --write next bit to bus
            ns <= wr;                    --continue writing
          END IF;
        
         WHEN slv_ack2 =>                    --slave acknowledge bit (write)
                CNT_ST <= "0110";
            
          IF(ena = '1') THEN                --continue transaction
           
--            addr_rw <= addr & rw;           --collect requested slave address and command
            data_tx <= data_wr;             --collect requested data to write
--            IF(rw = '1') THEN               --continue transaction with a read
--              state <= start;               --go to repeated start
--            ELSE                            --continue transaction with another write
              sda_int <= data_wr(bit_cnt);  --write first bit of data
              ns <= wr2;                  --go to write byte
--            END IF;
          ELSE                              --complete transaction
            scl_ena <= '0';                 --disable scl
            ns <= stop;                  --go to stop bit
          END IF;
        
          WHEN wr2 =>                          --write byte of transaction
                CNT_ST <= "0111";
             
          IF(bit_cnt = 0) THEN              --write byte transmit finished
            sda_int <= '1';                 --release sda for slave acknowledge
            bit_cnt <= 7;                   --reset bit counter for "byte" states
            ns <= slv_ack3;              --go to slave acknowledge (write)
                 
          ELSE                              --next clock cycle of write state
            bit_cnt <= bit_cnt - 1;         --keep track of transaction bits
            sda_int <= data_tx(bit_cnt-1);  --write next bit to bus
            ns <= wr2;                    --continue writing
          END IF;
 
        
          WHEN slv_ack3 =>                      --slave acknowledge bit (write)
                CNT_ST <= "1000";
                scl_ena <= '0';                 --disable scl
            ns <= stop;                  --go to stop bit
        
          
          WHEN stop =>                        --stop bit of transaction
          
          CNT_ST <= "1001";
             
             if(rw = '1')then
                    ns <= ready;
                else
                    ns <= stop;
                end if;
             
            
        
          WHEN rd =>                          --read byte of transaction
          
                CNT_ST <= "1010";
          
          IF(bit_cnt = 0) THEN              --read byte receive finished
            IF(ena = '1' AND rw = '1') THEN --continuing with another read
              sda_int <= '0';               --acknowledge the byte has been received
            ELSE                            --stopping or continuing with a write
              sda_int <= '1';               --send a no-acknowledge (before stop or repeated start)
            END IF;
            data_rd(0) <= sda;
                data_rd(7 downto 1) <= data_rx(7 downto 1);             --output received data
                bit_cnt <= 7;                   --reset bit counter for "byte" states
            
            ns <= stop;--mstr_ack;              --go to master acknowledge
          ELSE                              --next clock cycle of read state
                data_rx(bit_cnt) <= sda;            
              bit_cnt <= bit_cnt - 1;         --keep track of transaction bits
            ns <= rd;                    --continue reading
                
          END IF;
            
            
             WHEN mstr_ack =>                    --master acknowledge bit after a read
                
                CNT_ST <= "1011";
             
             IF(ena = '1') THEN                --continue transaction
            
            addr_rw <= addr & rw;           --collect requested slave address and command
            data_tx <= data_wr;             --collect requested data to write
            IF(rw = '0') THEN               --continue transaction with a write
              ns <= start;               --repeated start
            ELSE                            --continue transaction with another read
              sda_int <= '1';               --release sda from incoming data
              ns <= rd;                  --go to read byte
            END IF;
          ELSE                              --complete transaction
            scl_ena <= '0';                 --disable scl
            ns <= stop;                  --go to stop bit
          END IF;
             
            WHEN others => null;
 
 END CASE;    
    END IF;
 
    --reading from sda during scl high (falling edge of data_clk)
 
    
  END PROCESS;  
 
  --set sda output
  WITH ps SELECT
    sda_ena_n <=   data_clk WHEN start, --generate start condition
              NOT data_clk WHEN stop,   --generate stop condition
              sda_int WHEN OTHERS;      --set to internal sda signal    
      
  --set scl and sda outputs
  scl <= scl_clk ;--WHEN scl_ena = '1' ELSE 'Z';
  sda <= '0' WHEN sda_ena_n = '0' ELSE sda_ena_n;--'Z';
  
--clk_out <= data_clk;
  
END logic;

 
Last edited by a moderator:

    V

    Points: 2
    Helpful Answer Positive Rating
Did you run a testbench and checked the waveforms against required I2C protocol? You would normally write a simple emulation module that generates ACK of the external EEPROM, but if I understand right, ACK isn't checked in your design.

Did you hear about code tags?
 

Additionally, how much time elapses between your write cycle and your attempt to read? EEPROMs take a certain amount of time to complete a write operation, and attempting to access the page where the write is in progress may yield invalid bits. I agree that you should run a testbench to simulate your code, and check the timing against the datasheet for the eeprom part.
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top