⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 eeprom_interface.vhd

📁 AT24C02的应用例程
💻 VHD
字号:
-- i2c.vhd
--
-- This file implements an I2C Master interface that will read data
-- from an external NVM (AT24C02A) at power-up to initialize a 256x8
-- internal SRAM block. When external logic signals for an NVM write
-- access, the block reads data from the external source and writes
-- it to the specified I2C address.
------------------------------------------------------------------------
--
--               Copyright 2004 Actel corporation
--
------------------------------------------------------------------------
--
-- Version 1.2  06/04/04 J.Vorgert - working file
--
------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity i2c is
port(reset_n  : in    std_logic;                     -- active low reset
     clk      : in    std_logic;                     -- processor clock

     init     : out   std_logic;                     -- high during init
     ienb     : out   std_logic;                     -- low to enable write
     iaddr    : out   std_logic_vector(7 downto 0);  -- init address
     idata    : out   std_logic_vector(7 downto 0);  -- init data
     iclk     : out   std_logic;                     -- init clock

     updt     : in    std_logic;  -- high to trigger mirror image update
     uenb     : out   std_logic;                     -- low to enable fifo
     uaddr    : in    std_logic_vector(7 downto 0);  -- write address
     udata    : in    std_logic_vector(7 downto 0);  -- write data

     sdi      : in    std_logic;  -- serial input 
     sdo      : out   std_logic;  -- active low open-drain drive enable - data
     sck      : out   std_logic   -- active low open-drain drive enable - clock
);   
end i2c;

architecture rtl of i2c is

signal btck     : std_logic;
signal sten     : std_logic;
signal cstate   : std_logic_vector(3 downto 0);
signal bcnt     : std_logic_vector(3 downto 0);
signal ccnt     : std_logic_vector(7 downto 0);
signal dly      : std_logic;
signal d2i, d2  : std_logic;
signal nki      : std_logic;
signal nack     : std_logic;
signal wri      : std_logic;
signal rdi      : std_logic;
signal byte     : std_logic_vector(8 downto 0);
signal sdata    : std_logic_vector(8 downto 0);
signal ld_byte  : std_logic;
signal stsp     : std_logic;
signal ctl_val  : std_logic;

begin

-- Need to generate divide by 2 to use at the bit clock so that
-- we can generate start and stop bits and space everything right.

c1:process(reset_n, clk)
begin
  if(reset_n = '0') then
     btck <= '0';
  elsif(clk'event AND clk = '1') then
     btck <= NOT(btck) after 1 ns;
  end if;
end process;

-- INIT is set at power-up and cleared when the state machine
-- reaches state 0101.

wp:process(reset_n, clk)
begin
  if(reset_n = '0') then
     init <= '1';
  elsif(clk'event AND clk = '1') then
     if(cstate = "0101") then
        init <= '0' after 1 ns;
     end if;
  end if;
end process;

-- This state machine is set-up to read/write data to an AT24C02A 
-- serial Flash memory
--
-- At power-up, the INIT bit is set, and the state machine executes
-- a 'sequencial read' operation starting at address 0x000 and 
-- procedding until all 256 bytes have been read and forwarded into 
-- the internal memory block. The state machine then sends a
-- stop bit to the Flash and clears the INIT control bit.
--
-- The state machine then waits for updt to be set.
-- When the updt bit is set, the interface asserts u_enb low on a
-- falling-edge of clk and addr/data is latched  on the next falling edge
-- (rd_clk should be on the rising-edge).  The state machine writes
-- data to the external FLASH memory one byte at a time whenever
-- updt is asserted high.  If the FIFO remains 'not empty' then this
-- block will poll the NVM until it is ready, and then proceed with
-- a write cycle for the next byte.
--
-- State Machine:
--
-- 0000 - reset state:   generate a start bit and load 0xA0 command
-- 0001 - send byte:     then load 0x00 address
-- 0010 - send byte:     generate a start bit and load 0xA1 command
-- 0011 - send byte:     clear byte count
-- 0100 - receive byte:  if cnt /= FF: ack, cnt++, goto 0004 else: nack
-- 0101 - stop:          assert stop bit and loop until updt = 1 - then
--                       generate a start bit and load A0
-- 0110 - send byte:     send byte - if nack - goto 0101, else load Address
-- 0111 - send byte:     send data byte, load data
-- 1000 - send byte:     goto 0101
--
-- In practice, the state machine is just a counter that starts at zero
-- and counts up, then jumps back to 101 and counts up again,
-- returning to zero only when reset_n is asserted low.

sten <= '1' when ( bcnt(3) = '1' AND
                  (cstate(2) /= '1' OR cstate(2 downto 1) = "11" OR
                   cstate(3)  = '1' OR (cstate = "0100" AND ccnt = "11111111") OR
                  (cstate = "0101" AND updt  = '1'))) else  '0';

s1:process(reset_n, clk)
begin
  if(reset_n = '0') then 
     cstate <= "0000";
  elsif(clk'event and clk = '0') then
     if(sten = '1' AND btck = '0') then
       if(cstate < "0101" AND nack = '1') then
          cstate <= "0000" after 1 ns;
       elsif(cstate(3) = '1' OR nack = '1') then
          cstate <= "0101" after 1 ns;
       else
          cstate <= cstate + '1' after 1 ns;
       end if;
     end if;
  end if;
end process;

-- The bit counter (bcnt) is cleared at the state transition
-- and during the first cycle of state "0011" (for start bit).
-- incremented on the falling-edge of clk when btck is low.

bc:process(reset_n, clk)
begin
  if(reset_n = '0') then
     bcnt <= (OTHERS => '0');
     dly  <= '0';
  elsif(clk'event AND clk = '0') then
     if(btck = '0') then

       if(bcnt(3) = '1' AND cstate = "0010") then
          dly <= '1' after 1 ns;
       else
          dly <= '0';
       end if;

       if(bcnt(3) = '1' OR (cstate = "0011" AND dly = '1')) then
          bcnt <= (OTHERS => '0') after 1 ns;
       else
          bcnt <= bcnt + '1' after 1 ns;
       end if;

     end if;
  end if;
end process;

-- The byte counter (ccnt) is cleared in state 0011.
-- It is incremented during the ACK bit after each
-- byte transfer in state 0100 to count 0x00-0xFF bytes
-- as they are read from the NVM.  ccnt is used both as
-- a control signal and as the iaddr output.

d2i <= '1' when (btck = '1' AND bcnt(3) = '1' AND cstate = "0100")
           else '0';

cc:process(reset_n, clk)
begin
  if(reset_n = '0') then
     ccnt <= (OTHERS => '0');
     d2   <= '0';
  elsif(clk'event AND clk = '0') then
     d2 <= d2i after 1 ns;
     if(cstate = "0011") then
        ccnt <= (OTHERS => '0') after 1 ns;
     elsif(d2 = '1') then
        ccnt <= ccnt + '1' after 1 ns;
     end if;
  end if;
end process;

-- the following logic checks the ACK bit for all states except
-- states "0100" and "0101" and asserts NACK if the data pin is
-- high during the 9th bit of any transfer.  This is registered 
-- so that the value is present during state changes.

nki <= '1' when (bcnt(3) = '1' AND cstate /= "0100" AND cstate /= "0101"AND sdi = '1')
           else '0';

p1:process(reset_n, clk)
begin
  if(reset_n = '0') then
     nack <= '0';
  elsif(clk'event AND clk = '1') then
     if(btck = '1') then
        nack <= nki after 1 ns;
     end if;
  end if;
end process;

-- Write enables are cleared to 1 at power-up and are asserted low during
-- ACK in state 0100.

wri <= '0' when (cstate = "0100" AND bcnt(3) = '1'  AND btck = '1') else '1';

P2:process(reset_n, clk)
begin
  if(reset_n = '0') then
     ienb <= '1';
  elsif(clk'event AND clk = '0') then
     ienb <= wri after 1 ns;
  end if;
end process;

iaddr <= ccnt(7 downto 0);   -- use byte count as address
idata <= sdata(8 downto 1);  -- account for ACK bit
iclk <= NOT(btck);           -- invert BTCK and use the rising-edge of this
                             -- signal as the write clock into internal SRAM

-- u_enb is cleared to 1 at power-up and is asserted low in state 0111
-- while bcnt=7 and btck=1.  It is clocked on the falling-edge
-- of clk so rd_clk should occur on the rising-edge.

rdi <= '0' when (cstate = "0111" AND bcnt = "0111"  AND btck = '1') else '1';

P3:process(reset_n, clk)
begin
  if(reset_n = '0') then
     uenb  <= '1';
  elsif(clk'event AND clk = '0') then
     uenb  <= rdi after 1 ns;
  end if;
end process;

-- The value that gets loaded into sdata is determined
-- by which state we're exiting...

mx1:process(cstate, udata, uaddr)
begin
  case cstate is
    when "0000"  => byte <= "101000001"; -- A0
    when "0010"  => byte <= "101000011"; -- A1
    when "0101"  => byte <= "101000001"; -- A0
    when "0110"  => byte <= uaddr & '1';
    when "0111"  => byte <= udata & '1';
    when OTHERS  => byte <= "000000001"; -- 0001,0011
  end case;
end process;

-- The data register is 9 bits long (byte and ACK bit)
-- It is parallel loaded during the ACK cycle in states
-- 0000, 0001, 0010, 0011, 0101, 0110, and 0111;

ld_byte <= '1' when (bcnt(3) = '1' AND btck = '0' AND
                     cstate /= "0100" and cstate(3) = '0') else '0';

p7:process(reset_n, clk)
begin
  if(reset_n = '0') then
     sdata <= "111111111";
  elsif(clk'event AND clk = '0') then
     if(ld_byte = '1') then
        sdata <= byte after 1 ns;
     elsif((cstate /= "0101" AND cstate /= "0100" AND btck = '0' AND dly = '0') OR 
           (cstate = "0100" and btck = '1')) then
        sdata <= sdata(7 downto 0) & sdi after 1 ns;
     end if;
  end if;
end process;

-- Start bits (data falling while btck is high) are generated as
-- we exit states 0000, 0010, and 0101; stop bits (data rising
-- while btck is high) are generated as we enter state 0101.
-- This is done with the STSP signal.

p4:process(reset_n, clk)
begin
  if(reset_n = '0') then
     stsp  <= '1';
  elsif(clk'event and clk = '0') then
     if(((cstate = "0000" OR cstate = "0101") AND sten = '1' AND btck = '1') OR
         (cstate = "0011"))then
        stsp <= '0' after 1 ns;
     elsif((cstate = "0101" AND bcnt = "0000" AND btck = '1') OR
           (cstate = "010" AND bcnt(3) = '1')) then
        stsp <= '1' after 1 ns;
     end if;
  end if;
end process;

-- The serial output is driven either by stsp when
-- outen is low, or by the MSBit of the shift register
-- when oten is high.

ctl_val <= '1' when (stsp = '1' OR (cstate = "100" AND
                    (bcnt(3) /= '1' OR ccnt = "11111111"))) else '0';

sdo   <= ctl_val when (cstate = "0000" OR DLY = '1' OR cstate = "0100" OR
                       cstate = "0101")
                 else sdata(8);

sck   <= '1' when (btck = '1' OR (stsp = '1' AND (cstate = "0000" OR cstate = "0101")))
             else '0';

end rtl;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -