--345678901234567890123456789012345678901234567890123456789012345678901234567890
--       1         2         3         4         5         6         7     
-- Title:     I2C Master Controller Top level
-- Engineer:  Evgeny Shumilov <eugene.shumilov@gmail.com>
-- Company:   For HiTechGlobal
-- Project:   
-- File name: i2c_st.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_st is
	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 entity i2c_st;

architecture translated of i2c_st is

constant sim_timescale	: time := 1 ns;
---------------------------------------------------------------------
type st_type is (idle, en_clk, start1, dev_id1, ack_id1, w_add, w_dat, ack_wr, ack_add, wait1,
                 dis_clk1, start2, dev_id2, ack_id2, data, ack_rd, stop1, ack_rd_mult);

signal i2c_state	: st_type:= idle;
signal bit_cntr		: std_logic_vector(2 downto 0);   
signal scl_en		: std_logic;   
signal en_cntr		: std_logic;   
signal cntr_done	: std_logic;
signal sda_int		: std_logic;   
signal scl_int		: std_logic;   
signal scl_cnt_en_int	: std_logic;   
signal i2c_rdy_int	: std_logic;   
signal i2c_act_int	: std_logic;   
signal i2c_rdata_int	: std_logic_vector(7 downto 0);   
signal ack_err_int	: std_logic;
signal id_sel		: std_logic_vector(7 downto 0);
signal id_code_op	: std_logic_vector(7 downto 0);

attribute syn_useioff	: boolean;
attribute syn_useioff of translated : architecture is true;
---------------------------------------------------------------------


begin

	id_sel <= DEV_ADDR & '0';
	id_code_op <= DEV_ADDR & '1';


   
   SRL16E_inst : SRL16E
   generic map (
      INIT => X"1111")
   port map (
      Q => SDA,       -- SRL data output
      A0 => '1',     -- Select[0] input
      A1 => '1',     -- Select[1] input
      A2 => '1',     -- Select[2] input
      A3 => '1',     -- Select[3] input
      CE => '1',     -- Clock enable input
      CLK => CLK,   -- Clock input
      D => sda_int        -- SRL data input
   );
--	SDA <= sda_int;
	SCL <= scl_int;
	SCL_CNT_EN <= scl_cnt_en_int;
	I2C_RDY <= i2c_rdy_int;
	I2C_ACT <= i2c_act_int;
	I2C_RDATA <= i2c_rdata_int;
	ACK_ERR <= ack_err_int;


   ---------------------------------------------------------------------
-- state machine

process (CLK, RST)
begin
   if (RST = '1') then
      i2c_state <= idle after 1 * sim_timescale;    
   elsif (CLK'event and CLK = '1') then
      case i2c_state is
         when idle =>
                  if (I2C_GO = '1') then
                     i2c_state <= en_clk after 1 * sim_timescale;
                     else i2c_state <= idle;
                  end if;
         when en_clk =>
                  if (SCL_TICK = '1') then
                     i2c_state <= start1 after 1 * sim_timescale;
                     else i2c_state <= en_clk;    
                  end if;
         when start1 =>
                  if (SCL_TICK = '1') then
                     i2c_state <= dev_id1 after 1 * sim_timescale;
                     else i2c_state <= start1;
                  end if;
         when dev_id1 =>
                  if ((cntr_done and SCL_TICK) = '1') then
                    i2c_state <= ack_id1 after 1 * sim_timescale;
                     else i2c_state <= dev_id1;
                  end if;
         when ack_id1 =>
                  if ((SCL_TICK and scl_int) = '1') then
                      if ENAPH = '1' then
                        i2c_state <= w_add after 1 * sim_timescale;
                        else
                          i2c_state <= w_dat after 1 * sim_timescale;
                      end if;
                     else i2c_state <= ack_id1;    
                  end if;
         when w_add =>
                  if ((cntr_done and SCL_TICK) = '1') then
                     i2c_state <= ack_add after 1 * sim_timescale; 
                     else i2c_state <= w_add;       
                  end if;
         when ack_add =>
                  if ((SCL_TICK and scl_int) = '1') then
                   case WR_L is
                     when '1'    => i2c_state <= dis_clk1 after 1 * sim_timescale;
                     when others => i2c_state <= w_dat after 1 * sim_timescale;
                   end case;
                   else i2c_state <= ack_add;
                  end if;
         when w_dat =>
                  if ((cntr_done and SCL_TICK) = '1') then
                   i2c_state <= ack_wr after 1 * sim_timescale;
                   else i2c_state <= w_dat;
                  end if;
         when ack_wr =>
                  if ((SCL_TICK and scl_int) = '1') then
                   i2c_state <= stop1 after 1 * sim_timescale;
                   else i2c_state <= ack_wr; 
                  end if;
         when dis_clk1 =>
                  if ((SCL_TICK and scl_int) = '1') then
                     i2c_state <= wait1 after 1 * sim_timescale;
                     else i2c_state <= dis_clk1;     
                  end if;
         when wait1 =>
                  if (SCL_TICK = '1') then
                     i2c_state <= start2 after 1 * sim_timescale;
                     else i2c_state <= wait1;    
                  end if;
         when start2 =>
                  if (SCL_TICK = '1') then
                     i2c_state <= dev_id2 after 1 * sim_timescale;
                     else i2c_state <= start2;    
                  end if;
         when dev_id2 =>
                  if ((cntr_done and SCL_TICK) = '1') then
                     i2c_state <= ack_id2 after 1 * sim_timescale;
                     else i2c_state <= dev_id2;    
                  end if;
         when ack_id2 =>
                  if ((SCL_TICK and scl_int) = '1') then
                     i2c_state <= data after 1 * sim_timescale;
                     else i2c_state <= ack_id2;
                  end if;
         when data =>
                  if ((cntr_done and SCL_TICK) = '1') then
                   case CONTINUE is
                    when '1'    => i2c_state <= ack_rd_mult after 1 * sim_timescale; --
                    when others => i2c_state <= ack_rd after 1 * sim_timescale;--
                   end case;
                   else i2c_state <= data;
                  end if;
         when ack_rd =>
                  if ((SCL_TICK and scl_int) = '1') then
                     i2c_state <= stop1 after 1 * sim_timescale; 
                     else i2c_state <= ack_rd;   
                  end if;
         when ack_rd_mult =>
                  if ((SCL_TICK and scl_int) = '1')
                    then i2c_state <= data after 1 * sim_timescale;
                    else i2c_state <= ack_rd_mult;
                  end if;
         when stop1 =>
                  if ((SCL_TICK and scl_int) = '1') then
                     i2c_state <= idle after 1 * sim_timescale;
                     else i2c_state <= stop1;
                  end if;
         when others  =>
                  i2c_state <= idle after 1 * sim_timescale;    
         
      end case;
   end if;
end process;

---------------------------------------------------------------------
-- bit counter

process (CLK, RST)
begin
   if (RST = '1') then
      bit_cntr <= "111" after 1 * sim_timescale;    
   elsif (CLK'event and CLK = '1') then
      if (((en_cntr and scl_int) and SCL_TICK) = '1') then
         bit_cntr <= bit_cntr - "001" after 1 * sim_timescale;    
      end if;
   end if;
end process;

process (CLK, RST)
begin
   if (RST = '1') then
      cntr_done <= '0' after 1 * sim_timescale;    
   elsif (CLK'event and CLK = '1') then
      if ((bit_cntr = "000") and (scl_int = '1')) then
         cntr_done <= '1' after 1 * sim_timescale;    
      else
         cntr_done <= '0' after 1 * sim_timescale;    
      end if;
   end if;
end process;

process (CLK, RST)
begin
   ---------------------------------------------------------------------
   -- SCL generation
   -- enable CLK divider
   
   if (RST = '1') then
      en_cntr <= '0' after 1 * sim_timescale;    
   elsif (CLK'event and CLK = '1') then
      if ((i2c_state = dev_id1) or (i2c_state = w_add) or (i2c_state = 
      dev_id2) or (i2c_state = data) or (i2c_state = w_dat)) then
         en_cntr <= '1' after 1 * sim_timescale;    
      else
         if (cntr_done = '1') then
            en_cntr <= '0' after 1 * sim_timescale;    
         end if;
      end if;
   end if;
end process;

process (CLK, RST)
begin
   if (RST = '1') then
      scl_cnt_en_int <= '0' after 1 * sim_timescale;    
   elsif (CLK'event and CLK = '1') then
      if (i2c_state = en_clk) then
         scl_cnt_en_int <= '1' after 1 * sim_timescale;    
      else
         if (i2c_state = idle) then
            scl_cnt_en_int <= '0' after 1 * sim_timescale;    
         end if;
      end if;
   end if;
end process;

-- enables tick divider        

--process (CLK, RST)
--begin
--   if (RST = '1') then
--      scl_en <= '0' after 1 * sim_timescale;    
--   elsif (CLK'event and CLK = '1') then
--      if (i2c_state = start1) then
--         scl_en <= '1' after 1 * sim_timescale;    
--      else
--         if (i2c_state = dis_clk1) and (SCL_TICK = '1') then
--            scl_en <= '0' after 1 * sim_timescale;    
--         else
--            if (i2c_state = start2) then
--               scl_en <= '1' after 1 * sim_timescale;    
--            else
--               if (i2c_state = stop1) and (scl_int = '1') 
--               then
--                  scl_en <= '0' after 1 * sim_timescale;    
--               end if;
--            end if;
--         end if;
--      end if;
--   end if;
--end process;


process (CLK, RST)
begin
   if (RST = '1') then
      scl_en <= '0' after 1 * sim_timescale;    
   elsif (CLK'event and CLK = '1') then
      if (i2c_state = start1) or (i2c_state = start2) then
         scl_en <= '1' after 1 * sim_timescale;    
      else
         if ((i2c_state = dis_clk1) and (SCL_TICK = '1')) or ((i2c_state = stop1) and (scl_int = '1')) then
            scl_en <= '0' after 1 * sim_timescale;    
         else null;
         end if;
      end if;
   end if;
end process;







-- tick divider        

process (CLK, RST)
begin
   if (RST = '1') then
      scl_int <= '1' after 1 * sim_timescale;    
   elsif (CLK'event and CLK = '1') then
      if ((scl_en and SCL_TICK) = '1') then
         scl_int <= not scl_int after 1 * sim_timescale;    
      end if;
   end if;
end process;

---------------------------------------------------------------------

process (CLK, RST)
begin
   if (RST = '1') then
      sda_int <= '1' after 1 * sim_timescale;    
   elsif (CLK'event and CLK = '1') then
      if ((i2c_state = start1) or (i2c_state = start2) or (i2c_state = stop1) or (i2c_state = ack_rd_mult))
       then sda_int <= '0' after 1 * sim_timescale;    
      else
         if (i2c_state = dev_id1) then
            sda_int <= id_sel(conv_integer(bit_cntr)) after 1 * 
            sim_timescale;
         elsif (i2c_state = dev_id2) then
            sda_int <= id_code_op(conv_integer(bit_cntr)) after 1 * 
            sim_timescale;
         elsif (i2c_state = w_add) then
            sda_int <= WRD_ADD(conv_integer(bit_cntr)) after 1 * 
            sim_timescale;
         elsif (i2c_state = w_dat) then
            sda_int <= WRD_DAT(conv_integer(bit_cntr)) after 1 * 
            sim_timescale;
         else
            sda_int <= '1' after 1 * sim_timescale;    
         end if;
      end if;
   end if;
end process;


process (CLK, RST)
begin
   if (RST = '1') then
      i2c_rdata_int <= "00000000" after 1 * sim_timescale;    
   elsif (CLK'event and CLK = '1') then
      if (i2c_state = data) and (scl_int = '1') then
         i2c_rdata_int(conv_integer(bit_cntr)) <= SDA_PIN after 1 * 
         sim_timescale;    
      else
         if (I2C_GO = '1') then
            i2c_rdata_int <= "00000000" after 1 * sim_timescale;    
         end if;
      end if;
   end if;
end process;

process (CLK, RST)
begin
   if (RST = '1') then
      i2c_rdy_int <= '0' after 1 * sim_timescale;    
   elsif (CLK'event and CLK = '1') then
      if (((i2c_state = stop1) and (scl_int = '1')) or
       ((i2c_state = ack_rd_mult) and (scl_int and not SDA_PIN and WR_L) = '1')) then
         i2c_rdy_int <= '1' after 1 * sim_timescale;    
      else
--         if (I2C_GO = '1') then
            i2c_rdy_int <= '0' after 1 * sim_timescale;    
--         end if;
      end if;
   end if;
end process;

process (CLK, RST)
begin
   if (RST = '1') then
      ack_err_int <= '0' after 1 * sim_timescale;    
   elsif (CLK'event and CLK = '1') then
      if (((i2c_state = ack_id1) or (i2c_state = ack_add) or (i2c_state = ack_id2) or (i2c_state = ack_rd_mult) or (i2c_state = ack_wr))
       and (scl_int and SDA_PIN) = '1') then
         ack_err_int <= '1' after 1 * sim_timescale;    
      else
         if (I2C_GO = '1') then
            ack_err_int <= '0' after 1 * sim_timescale;    
         end if;
      end if;
   end if;
end process;

	i2c_act_int <= '0' when i2c_state = idle else '1';

end architecture translated;
