----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date: 02/06/2020 05:37:02 PM
-- Design Name: 
-- Module Name: i2c_master_logic - Behavioral
-- Project Name: 
-- Target Devices: 
-- Tool Versions: 
-- Description: 
-- 
-- Dependencies: 
-- 
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
-- 
----------------------------------------------------------------------------------


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity i2c_master_logic is
    Port ( clk                              : in STD_LOGIC;
           reset                            : in std_logic;
           number_to_recieve                : out STD_LOGIC_VECTOR (7 downto 0);
           sda                              : inout STD_LOGIC;
           scl                              : inout STD_LOGIC);
end i2c_master_logic;


architecture Behavioral of i2c_master_logic is

    --**********************************************************************************************
    --      CONSTANTS FOR CLOCK DIVIDER                                                            *
    --********************************************************************************************** 
    constant c_cnt_1Hz                      : natural := 100000000;
    constant c_cnt_10Hz                     : natural := 10000000;
    constant c_cnt_50Hz                     : natural := 4000000;
    constant c_cnt_100Hz                    : natural := 1000000;
    constant c_cnt_1kHz                     : natural := 100000;
    constant c_cnt_10kHz                    : natural := 10000;
    constant c_cnt_100kHz                   : natural := 1000;
    constant c_cnt_1MHz                     : natural := 100;
    constant c_cnt_10MHz                    : natural := 10;

    type FSM_state_type is(idle, get_data, process_data);
    signal FSM_state                        : FSM_state_type;
    --Signaly namapovane na I2C komponentu
    signal s_m_i2c_enable                     : std_logic                       := '0';               
    signal s_m_i2c_addr                       : std_logic_vector(6 downto 0)    := "0000000";              
    signal s_m_i2c_rw                         : std_logic                       := '0';
    signal s_m_i2c_data_wr                    : std_logic_vector(7 downto 0)    := "00000000";
    signal s_m_i2c_busy                       : std_logic                       := '1';
    signal s_m_i2c_data_rd                    : std_logic_vector(7 downto 0)    := "00000000";
    signal s_m_i2c_ack_error                  : std_logic                       := '0';
    
    --Signaly pro stav get_data
    signal s_busy_prev                      : std_logic;
    
    --Signaly drzici adresy a data
    signal s_slave_addr                     : std_logic_vector(6  downto 0) := "0111100";
    signal s_data_to_write                  : std_logic_vector(7  downto 0) := "00110010";
    signal s_new_data_to_write              : std_logic_vector(7  downto 0);
    signal s_recieved_data                  : std_logic_vector(15 downto 0);
    
     signal s_transmit_trigger              : std_logic;
    
    --**********************************************************************************************
    --      DIVIDER FOR TRIGERRING THE TRANSMITTER COMPONENT                                       *
    --**********************************************************************************************
    component clk_divider
	   generic (MAX : positive); 
	   Port(   --INPUTS
                   clk                       : in std_logic;
	           divider_clock_multiplier      : in std_logic_vector(7 downto 0);
	           --OUTPUTS
               divider_clock_output          : out std_logic; 
               divider_pulse_output          : out std_logic);
	end component;
    
    --**********************************************************************************************
    --      I2C MASTER COMPONENT                                                                   *
    --**********************************************************************************************
    component i2c_master
      GENERIC(
        input_clk                            : INTEGER := 50_000_000; --input clock speed from user logic in Hz
        bus_clk                              : INTEGER := 400_000);   --speed the i2c bus (scl) will run at in Hz
      PORT(
          clk              : IN     STD_LOGIC;                    --system clock
          reset_n          : IN     STD_LOGIC;                    --active low reset
          in_ena           : IN     STD_LOGIC;                    --latch in command
          in_addr          : IN     STD_LOGIC_VECTOR(6 DOWNTO 0); --address of target slave
          in_rw            : IN     STD_LOGIC;                    --'0' is write, '1' is read
          in_data_wr       : IN     STD_LOGIC_VECTOR(7 DOWNTO 0); --data to write to slave
          out_busy         : OUT    STD_LOGIC;                    --indicates transaction in progress
          out_data_rd      : OUT    STD_LOGIC_VECTOR(7 DOWNTO 0); --data read from slave
          buffer_ack_error : BUFFER STD_LOGIC;                    --flag if improper acknowledge from slave
          inout_sda        : INOUT  STD_LOGIC;                    --serial data output of i2c bus
          inout_scl        : INOUT  STD_LOGIC);                   --serial clock output of i2c bus
    END component;

    attribute DONT_TOUCH : string;
    attribute DONT_TOUCH of i2c_master_instance : label is "true";

begin

    process(clk)
        variable s_busy_cnt                  : integer := 0;
    begin
       
        if rising_edge(clk) then
            case FSM_state is
                when idle =>
                    if (s_transmit_trigger = '1') then
                        FSM_state <= get_data;
                    end if;
                ------------------------------------------------------------------------------        
                when get_data =>                                          --state for conducting this transaction
                  s_busy_prev <= s_m_i2c_busy;                            --capture the value of the previous i2c busy signal
                  IF(s_busy_prev = '0' AND s_m_i2c_busy = '1') THEN       --i2c busy just went high
                    s_busy_cnt := s_busy_cnt + 1;                         --counts the times busy has gone from low to high during transaction
                  END IF;
                  CASE s_busy_cnt IS                                      --busy_cnt keeps track of which command we are on
                    when 0 =>                                             --no command latched in yet
                      s_m_i2c_enable <= '1';                              --initiate the transaction
                      s_m_i2c_addr <= s_slave_addr;                       --set the address of the slave
                      s_m_i2c_rw <= '0';                                  --command 1 is a write
                      s_m_i2c_data_wr <= s_data_to_write;                 --data to be written
                    when 1 =>                                             --1st busy high: command 1 latched, okay to issue command 2
                      s_m_i2c_rw <= '1';                                  --command 2 is a read (addr stays the same)
                    when 2 =>                                             --2nd busy high: command 2 latched, okay to issue command 3
                      s_m_i2c_rw <= '0';                                  --command 3 is a write
                      s_m_i2c_data_wr <= s_new_data_to_write;             --data to be written
                      IF(s_m_i2c_busy = '0') THEN                         --indicates data read in command 2 is ready
                        s_recieved_data(15 DOWNTO 8) <= s_m_i2c_data_rd;  --retrieve data from command 2
                      END IF;
                    when 3 =>                                             --3rd busy high: command 3 latched, okay to issue command 4
                      s_m_i2c_rw <= '1';                                  --command 4 is read (addr stays the same)
                    when 4 =>                                             --4th busy high: command 4 latched, ready to stop
                      s_m_i2c_enable <= '0';                              --deassert enable to stop transaction after command 4
                      IF(s_m_i2c_busy = '0') THEN                         --indicates data read in command 4 is ready
                        s_recieved_data(7 DOWNTO 0) <= s_m_i2c_data_rd;   --retrieve data from command 4
                        s_busy_cnt := 0;                                  --reset busy_cnt for next transaction
                        FSM_state <= process_data;                        --transaction complete, go to next state in design
                      END IF;
                    WHEN OTHERS => NULL;
                  END CASE;
                ------------------------------------------------------------------------------  
                when process_data =>
                    number_to_recieve <= s_recieved_data(7 downto 0);
                    FSM_state <= idle;
            end case;
        end if;
    end process;

divider_module_1 : clk_divider
		GENERIC MAP (max => c_cnt_100kHz) 
		PORT MAP( clk                                   => clk,
		          divider_clock_multiplier              => "00000001",
		          divider_clock_output                  => OPEN,
		          divider_pulse_output                  => s_transmit_trigger); 

 i2c_master_instance : i2c_master
  GENERIC MAP(
    input_clk                   => 100_000_000, --input clock speed from user logic in Hz
    bus_clk                     => 400_000)   --speed the i2c bus (scl) will run at in Hz
  PORT MAP(
    clk                                 => clk,
    reset_n                             => '1',
    in_ena                              => s_m_i2c_enable,
    in_addr                             => s_m_i2c_addr,
    in_rw                               => s_m_i2c_rw,
    in_data_wr                          => s_m_i2c_data_wr,
    out_busy                            => s_m_i2c_busy,
    out_data_rd                         => s_m_i2c_data_rd,
    buffer_ack_error                    => s_m_i2c_ack_error,
    inout_sda                           => sda,
    inout_scl                           => scl);

end Behavioral;
