--345678901234567890123456789012345678901234567890123456789012345678901234567890
--       1         2         3         4         5         6         7     
-- Title:     I2C Master Controller
-- Engineer:  Evgeny Shumilov <eugene.shumilov@gmail.com>
-- Company:   For HiTechGlobal
-- Project:   
-- File name: i2c.vhd
--------------------------------------------------------------------------------
-- Purpose:   
--------------------------------------------------------------------------------
-- Simulator: Modelsim
-- Synthesis: Xilinx ISE
--------------------------------------------------------------------------------
-- Revision:  0.65
-- Modification date: 03/05/2003
-- Limitation: 
-- Notes: 
--------------------------------------------------------------------------------
-- Modifications List:
--
--
--------------------------------------------------------------------------------
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
use IEEE.std_logic_unsigned.all;
--use IEEE.std_logic_signed.all;
USE ieee.numeric_std.all;
use IEEE.std_logic_misc.all;

library unisim;
use unisim.vcomponents.all;


entity i2c is
  generic (
  count_div : integer range 0 to 1023:= 512   -- sysclk divide coafficien (2 to 1023 max)
  );  
  port (
  ADDR_IN   : in std_logic_vector (7 downto 0); -- word address
  DAT_IN    : in std_logic_vector(7 downto 0);  -- write data
  DEV_ADDR  : in std_logic_vector (6 downto 0); -- device address
  CONTINUE  : in std_logic;       -- continue read operation from ADDR_IN
  AP_EN   : in std_logic; -- Enable Address Phase During Write
  WR_OP   : in std_logic;       -- write operation WRITE ONE WORD <= '0', RD <= '1'
  WR_EN   : in std_logic;       -- enable write ADDR_IN, DAT_IN, ID_ADDR, WR_OP
  CLK   : in std_logic;       -- main clock
  RST   : in std_logic;       -- system reset
  
  I2C_RDATA : out std_logic_vector(7 downto 0); --  i2c read data
  ACK_L   : out std_logic;      --  acknowledge WR_EN (active '0')
--  SDA   : inout std_logic;      --  i2c data
--  SCL   : out std_logic;      --  i2c CLK

I_SDA_I     : in std_logic;
O_SDA_O     : out std_logic;
O_SDA_T     : out std_logic;

I_SCL_I    : in std_logic;
O_SCL_O    : out std_logic;
O_SCL_T    : out std_logic;



  I2C_RDY   : out std_logic;      --  i2c ready
  I2C_ACT   : out std_logic;      --  i2c cycle active
  ACK_ERR   : out std_logic       --  i2c(ack) error
  );
end entity i2c;

architecture translated of i2c is

component IOBUF
  port
  (
  O : out std_ulogic;
  IO  : inout std_ulogic;
  I : in std_ulogic;
  T : in std_ulogic
  );
end component;

component OBUFT
  port
  (
  O : out std_ulogic;
  I : in std_ulogic;
  T : in std_ulogic
  );
end component;

component i2c_st
  port (
  WRD_ADD   : in std_logic_vector(7 downto 0);   --  i2c address
  WRD_DAT   : in std_logic_vector(7 downto 0);
  DEV_ADDR  : in std_logic_vector (6 downto 0);
  CONTINUE  : in std_logic;
  ENAPH   : in std_logic; -- Enable Address Phase During Write
  WR_L    : in std_logic;
  RST   : in std_logic;   --  reset
  CLK   : in std_logic;   --  mpu CLK
  SCL_TICK  : in std_logic;   --  5 usec CLK tick
  I2C_GO    : in std_logic;   --  start i2c cycle
  SDA_PIN   : in std_logic;   --  i2c data muxed input
  
  I2C_RDATA : out std_logic_vector(7 downto 0);   --  i2c read data
  SDA   : out std_logic;   --  i2c data
  SCL   : out std_logic;   --  i2c CLK
  SCL_CNT_EN  : out std_logic;   --  SCL cntr enable
  I2C_RDY   : out std_logic;   --  i2c ready
  I2C_ACT   : out std_logic;   --  i2c cycle active
  ACK_ERR   : out std_logic    --  ack error
  );
end component;

---------------------------------------------------------------------------------------------------
constant sim_timescale  : time := 1 ns;

function length_ct (int_length  : integer) return positive is
variable conv_length  : std_logic_vector(9 downto 0);
variable index    : positive:= 1;
begin
  conv_length := CONV_STD_LOGIC_VECTOR(int_length, 10);
   for i in 9 downto 1 loop
    index:= i;
     exit when conv_length(i) = '1';
    end loop;
  return index;
end length_ct;
  



signal cntr   : std_logic_vector(length_ct(count_div) downto 0);
signal cntr_length  : std_logic_vector(length_ct(count_div) downto 0);
signal scl_tick   : std_logic;
signal wrd_addr   : std_logic_vector(7 downto 0);
signal wrd_dat    : std_logic_vector(7 downto 0);
signal dev_addr_int : std_logic_vector(6 downto 0);
signal ENAPH    : std_logic;
signal scl_cnt_en : std_logic;
signal i2c_go   : std_logic;
signal sda_in   : std_logic;
signal sda_o    : std_logic;
signal scl_o    : std_logic;
signal wr_l_int   : std_logic;
signal port_switch  : std_logic:= '0';
signal sda_o_spd  : std_logic;
signal scl_o_spd  : std_logic;
signal GND    : std_logic;


begin

  GND <= '0';

---------------------------------------------------------------------------------------------------
--    CLOCK DIVIDER SECTION:
---------------------------------------------------------------------------------------------------

  cntr_length <= CONV_STD_LOGIC_VECTOR(count_div, (length_ct(count_div)+1));

process (CLK,RST)
begin
  if (RST = '1') then
     cntr <= (others => '0') after 1 * sim_timescale;    
  elsif (CLK'event and CLK = '1') then
     if (scl_cnt_en = '1') then
        cntr <= cntr + 1 after 1 * sim_timescale;    
     else
        cntr <= (others => '0');
     end if;
  end if;
       end process;

process (CLK,RST)
begin
  if (RST = '1') then
     scl_tick <= '0' after 1 * sim_timescale;    
  elsif (CLK'event and CLK = '1') then
     if (cntr = cntr_length) then
        scl_tick <= '1' after 1 * sim_timescale;    
     else
        scl_tick <= '0' after 1 * sim_timescale;    
     end if;
  end if;
       end process;
---------------------------------------------------------------------------------------------------
--    ADDRESS REGISTERS SECTION:
---------------------------------------------------------------------------------------------------
process (CLK)
begin
  if (CLK'event and CLK = '1') then
   if WR_EN = '1'
     then wrd_addr <= ADDR_IN after 1 * sim_timescale;
          dev_addr_int <= DEV_ADDR after 1 * sim_timescale;
          wr_l_int <= WR_OP after 1 * sim_timescale;
          wrd_dat <= DAT_IN after 1 * sim_timescale;
          ENAPH <= AP_EN after 1 * sim_timescale;
          
          
    end if;    
  end if;
end process;

process (CLK)
begin
  if (CLK'event and CLK = '1') then
   if WR_EN = '1' 
     then port_switch <= (DEV_ADDR(3) and not DEV_ADDR(2) and DEV_ADDR(1) and not DEV_ADDR(0)) after 1 * sim_timescale; -- "1010" (SPD)
      else null;          
    end if;    
  end if;
end process;



process (CLK, RST)
begin
-- send ack right back  
  
  if (RST = '1') then
     i2c_go <= '0' after 1 * sim_timescale;    
  elsif (CLK'event and CLK = '1') then
     if WR_EN = '1'
      then
        i2c_go <= '1' after 1 * sim_timescale;    
     else
        if (scl_cnt_en = '1') then
           i2c_go <= '0' after 1 * sim_timescale;    
        end if;
     end if;
  end if;
end process;

process (CLK, RST)
begin
  if (RST = '1') then
     ACK_L <= '1' after 1 * sim_timescale;
  elsif (CLK'event and CLK = '1') then
     if WR_EN = '1'
      then ACK_L <= '0' after 1 * sim_timescale;    
     else
        ACK_L <= '1' after 1 * sim_timescale;    
     end if;
   end if;
  end process;
---------------------------------------------------------------------------------------------------
--    I2C STATE MACHINE SECTION:
---------------------------------------------------------------------------------------------------
MAIN_FSM : i2c_st 
  port map(
  WRD_ADD   => wrd_addr,
  WRD_DAT   => wrd_dat,
  DEV_ADDR  => dev_addr_int,
  CONTINUE  => CONTINUE,
  ENAPH => ENAPH,
  WR_L    => wr_l_int,
  RST   => RST,
  CLK   => CLK,
  SCL_TICK  => scl_tick,
  I2C_GO    => i2c_go,
  SDA_PIN   => sda_in,
  
  I2C_RDATA => I2C_RDATA,
  SDA   => sda_o,
  SCL   => scl_o,
  SCL_CNT_EN  => scl_cnt_en,
  I2C_RDY   => I2C_RDY,
  I2C_ACT   => I2C_ACT,
  ACK_ERR   => ACK_ERR
  );


  sda_o_spd  <= sda_o;
  scl_o_spd  <= scl_o;
  
--SDA_BUF: IOBUF
--  port map
--  (
--  O => sda_in,
--  IO  => SDA,
--  I => GND,
--  T => sda_o_spd
--  );


sda_in     <= I_SDA_I;
O_SDA_O    <= GND;
O_SDA_T    <= sda_o_spd;

--SCL_BUF: OBUFT
--  port map
--  (
--  O => SCL,
--  I => GND,
--  T => scl_o_spd
--  );

-- scl_in <= I_SCL_I
O_SCL_O     <= GND;
O_SCL_T     <= scl_o_spd;


end architecture translated;
