----------------------------------------------------------------------------------
-- Company:        CERN, BE-CO-HT 
-- Engineer:       Carlos Gil Soriano
-- 
-- Create Date:    00:47:34 10/26/2011 
-- Design Name:    i2c bit recognition fsm
-- Module Name:    i2c_bit - Behavioral 
-- Project Name:   Level Conversion Circuits
-- Target Devices: Spartan 6
-- Tool versions:  
-- Description:    This is the fsm for the recognition of a bit. It offers some
--                 interrupting lines to the upper level (i2c_slave_core.vhd)
--                 which is responsible for managing byte level transactions
--
-- Dependencies:   The inputs should be debounced: i2c_debouncer.vhd
--
-- Revision: 
-- Revision 0.01 - File Created
--          0.1  - Module works with no reported issues.
--          1.0  - Code revamped. Now is clearer and easier to read.
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
library work;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use work.ctdah_pkg.ALL;

entity i2c_bit is
   port (
           rst_i     : in   STD_LOGIC;
           wb_clk_i  : in   STD_LOGIC;

           done      : out  STD_LOGIC;
           sda_i     : in   STD_LOGIC;
           scl_i     : in   STD_LOGIC;
           start_o   : out  STD_LOGIC;
           pause_o   : out  STD_LOGIC;
           rcved_o   : out  STD_LOGIC
        );
end i2c_bit;

architecture Behavioral of i2c_bit is


   type bit_fsm is (S0_IDLE, S1A_HIGH_TMP, S1B_LOW_TMP, S2A_START_TMP, S2B_STOP_DETECT, Q1_ERROR);

-- It specifies the maximum number of stages that will be employed for
-- deglitching. Clocked with wb_clk_i
   constant c_MAX_GLITCH_DELAY :  UNSIGNED (3 downto 0) := "0110";
-- Three delay stages out of six
   constant c_GLITCH_MASK      : STD_LOGIC_VECTOR (5 downto 0) := "000111";

   component i2c_debouncer
      generic (
      g_LENGTH    : UNSIGNED(3 downto 0) := c_MAX_GLITCH_DELAY;
      g_outputONrise : BOOLEAN := TRUE
   );
   port (
           rst         : IN   std_logic;
           clk         : IN   std_logic;                                
           input       : IN   std_logic;
           output      : OUT  std_logic;
           glitch_mask : IN   std_logic_vector(to_integer(g_LENGTH)-1 downto 0)
        );
   end component;

   signal s_sda_dGLITCH     : STD_LOGIC;
   signal s_sda_dGLITCH_d1  : STD_LOGIC;
   signal s_scl_dGLITCH     : STD_LOGIC;
   signal s_scl_dGLITCH_d1  : STD_LOGIC;

   signal i2c_bit_fsm       : bit_fsm;

   signal s_scl_rising      : STD_LOGIC;
   signal s_scl_falling     : STD_LOGIC;

   signal s_start           : STD_LOGIC;
   signal s_pause           : STD_LOGIC;
   signal s_rcved           : STD_LOGIC;
   signal s_done            : STD_LOGIC;

begin

   start_o    <= s_start;
   pause_o    <= s_pause;
   rcved_o    <= s_rcved;
   done       <= s_done;


   debouncer_scl_i: i2c_debouncer
   generic map(
                 g_outputONrise => TRUE
              )
   port map(
              rst          => rst_i,
              clk          => wb_clk_i,
              input        => scl_i,
              output       => s_scl_dGLITCH,
              glitch_mask  => c_GLITCH_MASK
           );

   ff1_scl : gc_ff
   port map(
              Q   => s_scl_dGLITCH_d1,
              C   => wb_clk_i,
              CLR => rst_i,
              D   => s_scl_dGLITCH
           );

   debouncer_sda_i: i2c_debouncer
   generic map(
                 g_outputONrise => TRUE
              )
   port map(
              rst          => rst_i,
              clk          => wb_clk_i,
              input        => sda_i,
              output       => s_sda_dGLITCH,
              glitch_mask  => c_GLITCH_MASK
           );

   ff1_sda : gc_ff
   port map(
              Q   => s_sda_dGLITCH_d1,
              C   => wb_clk_i,
              CLR => rst_i,
              D   => s_sda_dGLITCH
           );

-------------------------------------------------------------------------------
-- This is the process that samples the scl for detecting
-- rise and falling edges
-------------------------------------------------------------------------------

   reg_proc: process (wb_clk_i)
   begin
      if rising_edge(wb_clk_i) then
         if (s_scl_dGLITCH xor s_scl_dGLITCH_d1) = '1' then
            if s_scl_dGLITCH = '0' then
               s_scl_falling    <= '1';
            else
               s_scl_rising   <= '1';
            end if;
         else
            s_scl_rising       <= '0'; 
            s_scl_falling      <= '0'; 
         end if;
      else
      end if;
   end process;


------------------------------------------------------------------
-- The fsm of this module, later on the sda sampled line is
-- validated in the falling edge of scl.
------------------------------------------------------------------

   comb_proc: process(wb_clk_i)
   begin
      if rising_edge(wb_clk_i) then
         if rst_i = '1' then
            s_start     <= '0';
            s_pause     <= '0';
            s_rcved     <= '0';
            s_done      <= '0';
            i2c_bit_fsm <= S0_IDLE;
         elsif s_scl_falling = '1' then
            case i2c_bit_fsm is
               when S1A_HIGH_TMP  =>
                  s_start     <= '0';
                  s_pause     <= '0';
                  s_rcved     <= '1';
                  s_done      <= '1';
                  i2c_bit_fsm <= S0_IDLE;
               when S1B_LOW_TMP   =>
                  s_start     <= '0';
                  s_pause     <= '0';
                  s_rcved     <= '0';
                  s_done      <= '1';
                  i2c_bit_fsm <= S0_IDLE;
               when S2A_START_TMP =>
                  s_start     <= '1';
                  s_pause     <= '0';
                  s_rcved     <= '0';
                  s_done      <= '1';
                  i2c_bit_fsm <= S0_IDLE;
               when others =>
                  s_start     <= '0';
                  s_pause     <= '0';
                  s_rcved     <= '0';
                  s_done      <= '0';
                  i2c_bit_fsm <= S0_IDLE;
            end case;

         elsif s_scl_rising = '1' then
            s_start     <= '0';
            s_pause     <= '0';
            s_rcved     <= '0';
            s_done      <= '0';
            if s_sda_dGLITCH_d1 = '1' then
               i2c_bit_fsm <= S1A_HIGH_TMP;
            else
               i2c_bit_fsm <= S1B_LOW_TMP;
            end if;
         else
            if s_scl_dGLITCH = '1' then
               case i2c_bit_fsm is
                  when S0_IDLE       =>
                     if s_sda_dGLITCH = '1' then
                        i2c_bit_fsm <= S1A_HIGH_TMP;
                     else
                        i2c_bit_fsm <= S1B_LOW_TMP;
                     end if;
                  when S1A_HIGH_TMP  =>
                     if s_sda_dGLITCH = '0' then
                     -- The detection of the start condition will be reported
                     -- in the next SCL rising edge.
                        s_done      <= '0';
                        i2c_bit_fsm <= S2A_START_TMP;
                     else
                     end if;
                  when S1B_LOW_TMP   =>
                     if s_sda_dGLITCH = '1' then
                     -- The detection of the pause condition MUST be
                     -- reported immediately.
                        s_start     <= '0';
                        s_pause     <= '1';
                        s_rcved     <= '0';
                        s_done      <= '1';
                        i2c_bit_fsm <= S2B_STOP_DETECT;
                     else
                     end if;
                  when S2A_START_TMP =>
                     if s_sda_dGLITCH = '1' then
                        s_start     <= '0';
                        s_pause     <= '0';
                        s_rcved     <= '0';
                        s_done      <= '0';
                        i2c_bit_fsm <= Q1_ERROR;
                     else
                     end if;
                  when others =>
                     s_start     <= '0';
                     s_pause     <= '0';
                     s_rcved     <= '0';
                     s_done      <= '0';
                     i2c_bit_fsm <= S0_IDLE;
               end case;
            else
               if s_scl_dGLITCH_d1 = '0' then
                  s_start     <= '0';
                  s_pause     <= '0';
                  s_rcved     <= '0';
                  s_done      <= '0';
                  i2c_bit_fsm <= S0_IDLE;
               else
               end if;
            end if;
         end if;
      else
      end if;
   end process;

end Behavioral;
