----------------------------------------------------------------------------------
-- Company:        CERN, BE-CO-HT 
-- Engineer:       Carlos Gil Soriano
-- 
-- Create Date:    11:29:56 10/25/2011 
-- Design Name:    I2C Slave to Wishbone bridge 
-- Module Name:    i2c_slave_core - Behavioral 
-- Project Name:   Level Conversion Circuits
-- Target Devices: Spartan 6
-- Tool versions: 
-- Description:    This is a bridge from I2C to Wishbone.
--                 The module consist on the following interfaces:
--                    - I2C interface
--                    - Wishbone master
--                    - Wishbone slave
--                    - Interruption lines
--                 Each time a valid instruction is set in the I2C interface, this
--                 is reported either:
--                    A.- Via Wishbone: acting the module as a Wishbone master.
--                    B.- Interrupts:   the module offers some interrupt pins that
--                                      allows an external processor interact with
--                                      the module via the Wishbone slave
--                                      interface.
--                 The FSM of this I2C core is able to detect indirect I2C
--                 addressing. Thus reads, writes and prefetching of data
--                 through Wishbone master interface will be issued.
--                 ~ SUBCOMPONENTS ~
--                    This component evaluates the data at the byte level, upon
--                 the previous analysis of the SCL and SDA pins at a bit level.
--                 The bit level analysis is carried out in:
--                       --> ./i2c_bit.vhd
--                    Two internal FIFOs (one for RX and another TX) are combined
--                 with this module to store the received and ready-to-be sent
--                 frames. The files are:
--                       --> ./FIFO_stack.vhd
--                       --> ./FIFO_dispatcher.vhd
--                    Both the Wishbone master and slave interface access control
--                 and all configuration registers can be found in:
--                       --> ./i2c_regs.vhd
--                    
-- Dependencies:   i2c_bit.vhd, FIFO_stack.vhd, FIFO_dispatcher.vhd, gc_ff.vhd
--
-- Revision: 
-- Revision 0.01 - File Created
--          0.1  - Initial submission. I2C instructions for Read and Write work.
--          1.0  - Code completely revamped. Code tested with a code coverage of
--                 81.5% with the testbench provided in:
--                       ../test/i2c_slave_test_top_tb.vhd
--
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
library work;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use work.ctdah_pkg.ALL;

entity i2c_slave_core is
   port (
      -- Wishbone reset and clock, used as and main clock source
      wb_clk        : in  STD_LOGIC;
      wb_rst_i      : in  STD_LOGIC;
      -- I2C pins
      sda_oen       : out  STD_LOGIC;
      sda_i         : in   STD_LOGIC;
      sda_o         : out  STD_LOGIC;
      scl_oen       : out  STD_LOGIC;
      scl_i         : in   STD_LOGIC;
      scl_o         : out  STD_LOGIC;
      -- Registers directly accesible
      I2C_MASK_i    : in   STD_LOGIC_VECTOR (6 downto 0);
      CTR0_i        : in   STD_LOGIC_VECTOR (15 downto 0);
      CTR1_i        : in   STD_LOGIC_VECTOR (15 downto 0);
      STA_o         : out  STD_LOGIC_VECTOR (15 downto 0);
      DRX0_o        : out  STD_LOGIC_VECTOR (7  downto 0);
      DRX1_o        : out  STD_LOGIC_VECTOR (7  downto 0);
      DRX2_o        : out  STD_LOGIC_VECTOR (7  downto 0);
      DRX3_o        : out  STD_LOGIC_VECTOR (7  downto 0);
      DRX4_o        : out  STD_LOGIC_VECTOR (7  downto 0);
      DRX5_o        : out  STD_LOGIC_VECTOR (7  downto 0);
      DTX0_i        : in   STD_LOGIC_VECTOR (7  downto 0);
      DTX1_i        : in   STD_LOGIC_VECTOR (7  downto 0);
      DTX2_i        : in   STD_LOGIC_VECTOR (7  downto 0);
      DTX3_i        : in   STD_LOGIC_VECTOR (7  downto 0);
      load_TX       : in   STD_LOGIC;
      -- Alarms for controlling the i2c states
      pf_wb_addr_o  : out  STD_LOGIC;
      rd_done_o     : out  STD_LOGIC;
      wr_done_o     : out  STD_LOGIC 
);
end i2c_slave_core;

architecture Behavioral of i2c_slave_core is


-------------------------------------------------------------------------------
-- This is the declaration area of the constants used in other components:
-- c_COUNTER_WIDTH    bit length of the gc_counter
-- c_DATA_WIDTH       width of the data to be stored in FIFO_stack 
--                    and FIFO_dispatchers.
-- c_RX_STACK_DEPTH   depth of FIFO_stack
-- c_TX_STACK_DEPTH   depth of FIFO_dispatch 
-------------------------------------------------------------------------------

constant c_COUNTER_WIDTH     : NATURAL := 8;
constant c_DATA_WIDTH        : NATURAL := 8;
constant c_RX_STACK_DEPTH    : NATURAL := 6;
constant c_TX_STACK_DEPTH    : NATURAL := 4;
-- Broadcast address as specified in I2C standard
constant c_i2c_general_addr  : STD_LOGIC_VECTOR (6 downto 0)  := "0000000";

-- fsm states of the i2c slave
type SLA_fsm is ( S0_IDLE, S1_START_DETECT, S2_FIRST_BYTE, S3A_WRITE_ACK,
                  S3B_READ_ACK, S4_WISHBONE_ADDR, S5A_WRITE_SDA, S5B_READ_SDA,
                  S6_PAUSE_DETECT, S7_WAIT_START_PAUSE, Q0_OK, Q1_ERROR);

type I2C_inst_fsm is (S0_READ_SDA, S1_WRITE_SDA);


-------------------------------------------------------------------------------
-- FSM signals for the I2C module
-------------------------------------------------------------------------------
-- Four signals are employed to govern the FSM behaviour and its outputs.
--
-- These signals are updated in done_comb_proc process, dependant of the done
-- signal coming from i2c_bit.vhd
-- Every time a deglitched falling edge of the SCL line is detected in the
-- aforementioned i2c_bit.vhd, the signals done, start_o, pause_o, rcved_o are
-- considered valid to be studied in i2c_slave_core.vhd processes.
--
-- DESCRIPTION OF i2c_SLA_fsm signals:
-- i2c_SLA_fsm_next           i2c_SLA_fsm value to be updated to in the next
--                            wb_clk rising edge
-- i2c_SLA_fsm                current I2C fsm state
-- i2c_SLA_fsm_previous       previous i2c_SLA_fsm state, excluding the
--                            S3A_WRITE_ACK, S3B_READ_ACK. Used to keep track
--                            of the fsm state of the last byte.
-- i2c_SLA_fsm_previous_next  When a read operation is issued from an I2C master
--                            to this I2C slave, a restart condition must be set
--                            after receiving the bytes of indirect addressing.
--                            Then, in the first byte upon the reception of the
--                            restart condition, the I2C rd_n/wr flag
--                            (last bit) will branch the fsm into write to SDA
--                            or read from it.
--                            By keeping track of the fsm state before of the
--                            restart condition, we can branch properly and
--                            safely either into S5A_WRITE_SDA or S5B_READ_SDA. 
-------------------------------------------------------------------------------

signal i2c_SLA_fsm_next           : SLA_fsm;
signal i2c_SLA_fsm                : SLA_fsm;
signal i2c_SLA_fsm_previous       : SLA_fsm;
signal i2c_SLA_fsm_previous_start : SLA_fsm;

signal rcv_I2C_inst_fsm : I2C_inst_fsm;

type RX_FIFO_REG is array(0 to c_RX_STACK_DEPTH -1) of STD_LOGIC_VECTOR(c_DATA_WIDTH -1 downto 0);
type TX_FIFO_REG is array(0 to c_TX_STACK_DEPTH -1) of STD_LOGIC_VECTOR(c_DATA_WIDTH -1 downto 0);
 
-------------------------------------------------------------------------------
-- i2c signals 
-------------------------------------------------------------------------------
signal s_sda_o       : STD_LOGIC;

-- This corresponds to the six DRXi registers
signal s_rx_fifo_dout  : RX_FIFO_REG;

-- done signal received from i2c_bit.vhd and delayed versions of it.
signal s_done        : STD_LOGIC;
signal s_done_d1     : STD_LOGIC;
signal s_done_d2     : STD_LOGIC;

-- Bit counter signals
signal s_bit_cnt_rst_i      : STD_LOGIC; 
signal s_bit_cnt_rst        : STD_LOGIC;
signal s_bit_cnt            : STD_LOGIC_VECTOR(c_COUNTER_WIDTH - 1 downto 0);

-- Byte counter signals
signal s_byte_tick          : STD_LOGIC;
signal s_byte_cnt_rst       : STD_LOGIC;
signal s_byte_cnt           : STD_LOGIC_VECTOR(c_COUNTER_WIDTH - 1 downto 0);


-- 8 bit load register for RX FIFO (FIFO_stack) signals
signal s_rx_fifo_din        : STD_LOGIC_VECTOR (c_DATA_WIDTH-1 downto 0);
signal s_rx_fifo_din_rst    : STD_LOGIC;
signal s_rx_fifo_din_rst_i  : STD_LOGIC;

-- RX FIFO (FIFO_stack) signals
signal s_rx_fifo_push         : STD_LOGIC;
signal s_rx_fifo_addr_pushed  : STD_LOGIC;
signal s_rx_fifo_flush        : STD_LOGIC;

-- TX FIFO (FIFO_dispatcher) signals
signal s_tx_fifo_flush    : STD_LOGIC;
signal s_tx_fifo_oen      : STD_LOGIC;
signal s_tx_fifo_reg_o    : STD_LOGIC_VECTOR (c_DATA_WIDTH -1 downto 0);    

-- Received i2c addr
signal s_ff_bit0_C        : STD_LOGIC;
signal s_ff_i2c_byte_C    : STD_LOGIC;

-- Interrupting signals (i2c_bit.vhd)
signal s_start_o          : STD_LOGIC;
signal s_pause_o          : STD_LOGIC;
signal s_rcved_o          : STD_LOGIC;

signal s_DRX0_o           : STD_LOGIC_VECTOR(7 downto 0);
signal s_i2c_addr_masked  : STD_LOGIC_VECTOR(6 downto 0);

   component i2c_bit
      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 component;

begin

STA_o(0)              <= CTR0_i (0);
STA_o(1)              <= CTR0_i (1);
STA_o(8  downto 2)    <= (others => '0');
STA_o(15 downto 9)    <= "0000000";--This is the replacement of i2c_SLA_fsm;

-- Added to solve VHDL 2008 compatibility
s_bit_cnt_rst_i       <= s_bit_cnt_rst or wb_rst_i; 
s_rx_fifo_din_rst_i   <= wb_rst_i or s_rx_fifo_din_rst;

sda_o               <= s_sda_o;

   bit_counter_8: gc_counter
	generic map (g_data_width => c_DATA_WIDTH)
   port map (
      clk_i  => s_done_d1,
      rst_i  => s_bit_cnt_rst_i,
      en_i   => '1',
      cnt_o  => s_bit_cnt
   );

   byte_counter_8: gc_counter
   generic map (g_data_width => c_DATA_WIDTH)
   port map (
      clk_i  => s_byte_tick,
      rst_i  => s_byte_cnt_rst,
      en_i   => '1',
      cnt_o  => s_byte_cnt
   );

   inst_i2c_bit  : i2c_bit
   port map(
     rst_i     => wb_rst_i,
     wb_clk_i  => wb_clk,  -- Targeted at 20 MHz
     done      => s_done,
     sda_i     => sda_i,
     scl_i     => scl_i,
     start_o   => s_start_o,
     pause_o   => s_pause_o,
     rcved_o   => s_rcved_o
   );

   -- We left half a wishbone cycle for setting the data before
   -- passing it to the FIFO
   ff_i2c_bit0: gc_ff
   port map(
      Q     => s_rx_fifo_din(0),
      C     => s_done_d1,
      CLR   => s_rx_fifo_din_rst_i,
      D     => s_rcved_o
   );

   ff_i2c_byte: for i in 1 to 7 generate
      ff_i2c_byte: gc_ff
      port map(
         Q     => s_rx_fifo_din(i),
         C     => s_done_d1,
         CLR   => s_rx_fifo_din_rst_i,
         D     => s_rx_fifo_din(i - 1)
      );
   end generate ff_i2c_byte;

   inst_ff_done_d1: gc_ff
      port map(
         Q     => s_done_d1,
         C     => wb_clk,
         CLR   => wb_rst_i,
         D     => s_done
      );

   inst_ff_done_d2: gc_ff
      port map(
         Q     => s_done_d2,
         C     => wb_clk,
         CLR   => wb_rst_i,
         D     => s_done_d1
      );

      rx_fifo_i2c: FIFO_stack 
      generic map( g_data_width => c_DATA_WIDTH,
                   g_stack_depth => c_RX_STACK_DEPTH)
      port map(
      reg_i   => s_rx_fifo_din,
      clk     => wb_clk,
      push    => s_rx_fifo_push,
      flush   => s_rx_fifo_flush,
      reg_o(7 downto 0)    => s_DRX0_o,
      reg_o(15 downto 8)   => DRX1_o, 
      reg_o(23 downto 16)  => DRX2_o, 
      reg_o(31 downto 24)  => DRX3_o, 
      reg_o(39 downto 32)  => DRX4_o, 
      reg_o(47 downto 40)  => DRX5_o  
   );

   DRX0_o <= s_DRX0_o;

   tx_fifo_i2c: FIFO_dispatcher
   generic map( g_data_width => c_DATA_WIDTH,
                g_dispatcher_depth => c_TX_STACK_DEPTH)
   port map(
      reg_i(7  downto  0)  => DTX3_i,
      reg_i(15 downto  8)  => DTX2_i,
      reg_i(23 downto 16)  => DTX1_i,
      reg_i(31 downto 24)  => DTX0_i,
      clk     => wb_clk,
      load    => load_TX,
      flush   => s_tx_fifo_flush,
      oen_i   => s_tx_fifo_oen,
      reg_o   => s_tx_fifo_reg_o
   );


-------------------------------------------------------------------------------
-- Process for output the signals depending in the current fsm state
-------------------------------------------------------------------------------
   reg_proc: process(wb_clk)

      variable caseCond : STD_LOGIC_VECTOR(2 downto 0);
   begin

      if rising_edge(wb_clk) then
         if wb_rst_i = '1' then
            i2c_SLA_fsm           <= S0_IDLE;
            s_bit_cnt_rst         <= '1'; 
            s_byte_tick           <= '0';
            s_byte_cnt_rst        <= '1'; 
            s_rx_fifo_flush       <= '1';
            s_rx_fifo_push        <= '0';
            s_rx_fifo_addr_pushed <= '0';
            s_tx_fifo_flush       <= '1';
            s_rx_fifo_din_rst     <= '1';
            rd_done_o             <= '0';
            wr_done_o             <= '0';
            pf_wb_addr_o          <= '0';
            scl_oen               <= '0';
            sda_oen               <= '0';
            s_sda_o               <= '1';
            scl_o                 <= 'Z';
            s_i2c_addr_masked     <= (others => '0');
         else
            i2c_SLA_fsm       <= i2c_SLA_fsm_next;
            if i2c_SLA_fsm = i2c_SLA_fsm_next then
               s_rx_fifo_push         <= '0';
               s_rx_fifo_flush        <= '0';
               s_tx_fifo_flush        <= '0';
               s_tx_fifo_oen          <= '0';
               s_byte_tick            <= '0';
               s_bit_cnt_rst          <= '0';
               s_byte_cnt_rst         <= '0';
               pf_wb_addr_o           <= '0';
               case i2c_SLA_fsm is
                  when S2_FIRST_BYTE       =>
                     -- TIP: we left SDA line to default high, as it is
                     -- for both I2C lines.
                     sda_oen             <= '0';
                     s_sda_o             <= '1';
                     if s_bit_cnt = STD_LOGIC_VECTOR(TO_UNSIGNED(7,8)) then
                        if s_rx_fifo_addr_pushed = '0' then
                           s_rx_fifo_push        <= '1';
                           s_rx_fifo_addr_pushed <= '1';
                        else
                           s_i2c_addr_masked <= s_DRX0_o(6 downto 0) and I2C_MASK_i; 
                        end if;
                     else
                        s_rx_fifo_addr_pushed <= '0';
                     end if;
                  when S3A_WRITE_ACK              =>
                     case i2c_SLA_fsm_previous is
                        when S2_FIRST_BYTE       =>
                           s_rx_fifo_din_rst   <= '1';
                           sda_oen             <= '1';
                        when S4_WISHBONE_ADDR    =>
                           sda_oen             <= '1';
                        when S5B_READ_SDA        =>
                           sda_oen             <= '1';
                        when others              =>
                           sda_oen             <= '0';
                           s_sda_o             <= '1';
                     end case;
                  when S4_WISHBONE_ADDR    =>
                     sda_oen             <= '0';
                     s_sda_o             <= '1';
                     s_rx_fifo_din_rst   <= '0';
                  when S5A_WRITE_SDA       =>
                     sda_oen             <= '1';
                     s_sda_o    <= s_tx_fifo_reg_o(TO_INTEGER(UNSIGNED(s_bit_cnt)));
                  when S7_WAIT_START_PAUSE =>
                     sda_oen             <= '0';
                     s_sda_o             <= '1';
                     if s_done_d2 = '1' then
                        case i2c_SLA_fsm_previous is
                           when S5A_WRITE_SDA =>
                              wr_done_o     <= '1';
                           when S5B_READ_SDA  =>
                              rd_done_o     <= '1';
                           when others        =>
                        end case;
                     else
                        wr_done_o     <= '0';
                        rd_done_o     <= '0';
                     end if;
                  when others              =>
                     sda_oen               <= '0';
                     s_sda_o               <= '1';
                     s_rx_fifo_addr_pushed <= '0';
               end case;
            else
               case i2c_SLA_fsm_next is
                  when S0_IDLE             =>
                  when S1_START_DETECT     =>
                     s_bit_cnt_rst       <= '1';
                     s_rx_fifo_din_rst   <= '0';
                  when S2_FIRST_BYTE       =>
                  when S3A_WRITE_ACK              =>
                     s_rx_fifo_din_rst   <= '0';
                     case i2c_SLA_fsm is
                        when S2_FIRST_BYTE      =>
                           s_tx_fifo_oen      <= '1';
                           case i2c_SLA_fsm_previous is
                              when S2_FIRST_BYTE  =>
                                 if (CTR0_i(14 downto 8) = s_i2c_addr_masked) then
                                    s_sda_o               <= '0';
                                    if i2c_SLA_fsm_previous_start = S4_WISHBONE_ADDR  
                                    and s_rcved_o = '1' then
                                       pf_wb_addr_o <= '1';
                                    else
                                       pf_wb_addr_o <= '0';
                                    end if;
                                 else
                                    s_sda_o         <= '1';
                                 end if;
                              when others =>
                                 s_rx_fifo_flush    <= '1';
                           end case;
                        when S4_WISHBONE_ADDR   =>
                           s_rx_fifo_push     <= '1';
                           s_byte_tick        <= '1';
                           s_sda_o            <= '0';
                        when S5A_WRITE_SDA      =>
                           s_rx_fifo_push     <= '1';
                           s_tx_fifo_oen      <= '1';
                           s_byte_tick        <= '1';
                           s_sda_o            <= '0';
                        when S5B_READ_SDA       =>
                           s_rx_fifo_push     <= '1';
                           s_byte_tick        <= '1';
                           s_sda_o            <= '0';
                        when others           =>
                           null;
                     end case;
                  when S3B_READ_ACK        =>
                     s_tx_fifo_oen       <= '1';
                     s_byte_tick         <= '1';
                  when S4_WISHBONE_ADDR    =>
                     s_rx_fifo_din_rst   <= '0';
                     sda_oen             <= '0';
                     case i2c_SLA_fsm_previous is
                        when S2_FIRST_BYTE      =>
                           s_bit_cnt_rst      <= '1';
                           s_byte_cnt_rst     <= '1';
                        when S4_WISHBONE_ADDR   =>
                           s_bit_cnt_rst      <= '1';
                        when others           =>
                     end case;
                  when S5A_WRITE_SDA       =>
                     case i2c_SLA_fsm_previous is
                        when S2_FIRST_BYTE   =>
                           s_bit_cnt_rst      <= '1';
                           s_byte_cnt_rst     <= '1';
                        when S5A_WRITE_SDA      =>
                           s_bit_cnt_rst      <= '1';
                           s_tx_fifo_oen      <= '1';
                        when others             =>
                     end case;
                  when S5B_READ_SDA        =>
                     sda_oen             <= '0';
                     case i2c_SLA_fsm_previous is
                        when S4_WISHBONE_ADDR   =>
                           s_bit_cnt_rst      <= '1';
                           s_byte_cnt_rst     <= '1';
                        when S5B_READ_SDA       =>
                           s_bit_cnt_rst      <= '1';
                        when others             =>
                           null;
                     end case;
                  when S7_WAIT_START_PAUSE  =>
                  when others               =>
                     s_rx_fifo_flush      <= '1';
                     s_tx_fifo_flush      <= '1';
               end case;
            end if;
         end if;
      else
      end if;
   end process;

   ----------------------------------------------------------------------------
   -- Process to set the next fsm
   ----------------------------------------------------------------------------


   fsm_proc: process(wb_clk)
      variable caseCond : STD_LOGIC_VECTOR(1 downto 0);
   begin
      caseCond := s_start_o & s_pause_o;

      if rising_edge(wb_clk) then
         if s_done = '1' then
            case i2c_SLA_fsm is
               when S0_IDLE =>
                  i2c_SLA_fsm_previous   <= S0_IDLE;
                  case caseCond is
                     when "00"    =>
                        i2c_SLA_fsm_next <= S0_IDLE;
                     when "01"    =>
                        i2c_SLA_fsm_next <= S6_PAUSE_DETECT;
                     when "10"    =>
                        i2c_SLA_fsm_next <= S1_START_DETECT;
                     when others  =>
                        i2c_SLA_fsm_next <= Q1_ERROR;
                  end case;
               when S1_START_DETECT =>
                  i2c_SLA_fsm_previous   <= S0_IDLE;
                  case caseCond is
                     when "00"    =>
                        i2c_SLA_fsm_next <= S2_FIRST_BYTE;
                     when "01"    =>
                        i2c_SLA_fsm_next <= S6_PAUSE_DETECT;
                     when "10"    =>
                        i2c_SLA_fsm_next <= S1_START_DETECT;
                     when others  =>
                        i2c_SLA_fsm_next <= Q1_ERROR;
                  end case;
               when S2_FIRST_BYTE         =>
                  case caseCond is
                     when "00"    =>
                        if s_bit_cnt = STD_LOGIC_VECTOR(TO_UNSIGNED(7, 8)) then
                           i2c_SLA_fsm_next     <= S3A_WRITE_ACK;
                           i2c_SLA_fsm_previous <= S2_FIRST_BYTE;
                        -- A zero in the last received bit means that we should
                        -- read from I2C line. Otherwise we must write into it.
                           if (s_rcved_o = '0') then
                              rcv_I2C_inst_fsm  <= S0_READ_SDA;
                           elsif (i2c_SLA_fsm_previous_start = S4_WISHBONE_ADDR) then
                           -- This situation is reached when the I2C master
                           -- wants to read from the internal I2C slave map.
                           -- For achieving this, the I2C master has
                           -- previously set a read I2C line instruction, 
                           -- following by the indirect addressing bytes.
                           -- After the corresponding ACK, I2C master has
                           -- set an start condition and he has sent
                           -- another instruction byte but with a write
                           -- from I2C line order for the slave.
                           -- In that case, the indirect address should be
                           -- read through the wishbone master at the same
                           -- time the I2C byte ACK is reported.
                           -- Then, as the data to be send is already in
                           -- DTX register, we are ready to send the bytes
                           -- though I2C.
                              rcv_I2C_inst_fsm  <= S1_WRITE_SDA;
                           else
                           -- This state is reached if it was not set an
                           -- indirect address to read data from.
                              i2c_SLA_fsm_next  <= Q1_ERROR;
                           end if;
                        else
                           i2c_SLA_fsm_next       <= S2_FIRST_BYTE;
                           i2c_SLA_fsm_previous   <= S2_FIRST_BYTE;
                        end if;
                     when "01"    =>
                        i2c_SLA_fsm_next       <= S6_PAUSE_DETECT;
                        i2c_SLA_fsm_previous   <= S0_IDLE;
                     when "10"    =>
                        i2c_SLA_fsm_next       <= S1_START_DETECT;
                        i2c_SLA_fsm_previous   <= S0_IDLE;
                     when others  =>
                        i2c_SLA_fsm_next       <= Q1_ERROR;
                        i2c_SLA_fsm_previous   <= S0_IDLE;
                  end case;
               when S3A_WRITE_ACK                =>
               -- The ACK condition is treated differently. As we can
               -- experience bit flips in start or pause signals from
               -- i2c_bit.vhd, we do not evaluat that bits cause we are
               -- not reading from SDA line (we are writing, indeed).
                  case caseCond is
                     when others    =>
                        case i2c_SLA_fsm_previous is
                           when S2_FIRST_BYTE      =>
                              if s_sda_o = '1' then
                                 i2c_SLA_fsm_previous        <= S0_IDLE;
                                 i2c_SLA_fsm_previous_start  <= S0_IDLE;
                                 i2c_SLA_fsm_next            <= Q1_ERROR;
                              else
                                 i2c_SLA_fsm_previous        <= S2_FIRST_BYTE;
                                 i2c_SLA_fsm_previous_start  <= S0_IDLE;
                                 case i2c_SLA_fsm_previous_start is
                                    when S4_WISHBONE_ADDR =>
                                       i2c_SLA_fsm_next <= S5A_WRITE_SDA;
                                    when others      => 
                                       i2c_SLA_fsm_next <= S4_WISHBONE_ADDR;
                                 end case;
                              end if;
                           when S4_WISHBONE_ADDR   =>
                              i2c_SLA_fsm_previous        <= S4_WISHBONE_ADDR;
                           -- We check if we have counted all the indirect address
                           -- bytes. Otherwise we stay in the same state.
                              if s_byte_cnt = (X"0" & "00" & CTR1_i(15 downto 14)) then
                                 case rcv_I2C_inst_fsm is
                                    when S0_READ_SDA       =>
                                       i2c_SLA_fsm_next            <= S5B_READ_SDA;
                                       i2c_SLA_fsm_previous_start  <= S4_WISHBONE_ADDR;
                                    when others            =>
                                       i2c_SLA_fsm_next  <= S5A_WRITE_SDA;
                                 end case;
                              else
                                 i2c_SLA_fsm_next <= S4_WISHBONE_ADDR;
                              end if;
                           when S5B_READ_SDA     =>
                              i2c_SLA_fsm_previous      <= S5B_READ_SDA;
                           -- We chack if we have counted all the bytes to be
                           -- read from SDA line. Otherwise we stay in the same
                           -- state.
                              if s_byte_cnt = ('0' & CTR1_i(13 downto 7)) then
                                 i2c_SLA_fsm_next <= S7_WAIT_START_PAUSE;
                              else
                                 i2c_SLA_fsm_next <= S5B_READ_SDA;
                              end if;
                           when others =>
                              i2c_SLA_fsm_next     <= Q1_ERROR;
                              i2c_SLA_fsm_previous <= S0_IDLE;
                        end case;
                  end case;
               when S3B_READ_ACK         =>
               -- This is the kind of ACK state employed when an I2C slave
               -- module has written a byte into the SDA line. It should be
               -- kept in mind that THE LAST BYTE to be acknowledge should
               -- be a NACK (receive a '1' from the I2C master) and not a
               -- ACK ('0').
                  i2c_SLA_fsm_previous     <= S5A_WRITE_SDA;
                  case caseCond is
                     when "00" =>
                        if s_byte_cnt = ('0' & CTR1_i(6  downto 0)) then
                           if s_rcved_o = '1' then
                              i2c_SLA_fsm_next <= S7_WAIT_START_PAUSE;
                           else
                              i2c_SLA_fsm_next <= Q1_ERROR;
                           end if;
                        else
                           i2c_SLA_fsm_next <= S5A_WRITE_SDA;
                        end if;
                     when others  =>
                        i2c_SLA_fsm_next <= Q1_ERROR;
                  end case;
               when S4_WISHBONE_ADDR     =>
                  i2c_SLA_fsm_previous       <= S4_WISHBONE_ADDR;
                  case caseCond is
                     when "00"    =>
                        if s_bit_cnt = STD_LOGIC_VECTOR(TO_UNSIGNED(7, 8)) then
                           i2c_SLA_fsm_next     <= S3A_WRITE_ACK;
                           i2c_SLA_fsm_previous <= i2c_SLA_fsm;
                        else
                           i2c_SLA_fsm_next     <= S4_WISHBONE_ADDR;
                        end if;
                     when "01"    =>
                        i2c_SLA_fsm_next  <= S6_PAUSE_DETECT; 
                     when "10"    =>
                        i2c_SLA_fsm_next  <= S1_START_DETECT;
                     when others  =>
                        i2c_SLA_fsm_next  <= Q1_ERROR;
                  end case;
               when S5A_WRITE_SDA        =>
                  i2c_SLA_fsm_previous <= S5A_WRITE_SDA;
                  if s_bit_cnt = STD_LOGIC_VECTOR(TO_UNSIGNED(7,8)) then
                     i2c_SLA_fsm_next     <= S3B_READ_ACK;
                  else
                     i2c_SLA_fsm_next     <= S5A_WRITE_SDA;
                  end if;
               when S5B_READ_SDA         =>
                  i2c_SLA_fsm_previous <= S5B_READ_SDA;
                  case caseCond is
                     when "00"    =>
                        if s_bit_cnt = STD_LOGIC_VECTOR(TO_UNSIGNED(7, 8)) then
                           i2c_SLA_fsm_next      <= S3A_WRITE_ACK;
                           i2c_SLA_fsm_previous  <= i2c_SLA_fsm;
                        else
                           i2c_SLA_fsm_next      <= S5B_READ_SDA;
                        end if;
                     when "01"    =>
                        i2c_SLA_fsm_next <= S6_PAUSE_DETECT;
                     when "10"    =>
                        i2c_SLA_fsm_next <= S1_START_DETECT;
                     when others  =>
                        i2c_SLA_fsm_next <= Q1_ERROR;
                  end case;
               when S6_PAUSE_DETECT             =>
                  i2c_SLA_fsm_next            <= S0_IDLE;
                  i2c_SLA_fsm_previous        <= S0_IDLE;
                  i2c_SLA_fsm_previous_start  <= S0_IDLE;
                  rcv_I2C_inst_fsm            <= S0_READ_SDA;
               when S7_WAIT_START_PAUSE  =>
                  i2c_SLA_fsm_previous        <= S0_IDLE;
                  case caseCond is
                     when "00"    =>
                        i2c_SLA_fsm_next <= Q1_ERROR;
                     when "01"    =>
                        i2c_SLA_fsm_next <= S6_PAUSE_DETECT;
                     when "10"    =>
                        i2c_SLA_fsm_next <= S1_START_DETECT;
                     when others  =>
                        i2c_SLA_fsm_next <= Q1_ERROR;
                  end case;
               when Q1_ERROR                    => 
                  i2c_SLA_fsm_next            <= S0_IDLE;
                  i2c_SLA_fsm_previous        <= S0_IDLE;
                  i2c_SLA_fsm_previous_start  <= S0_IDLE;
                  rcv_I2C_inst_fsm            <= S0_READ_SDA;
               when others           =>
                  i2c_SLA_fsm_next            <= Q1_ERROR;
                  i2c_SLA_fsm_previous        <= S0_IDLE;
            end case;
         elsif s_done_d1 = '1' then
            case i2c_SLA_fsm_next is
               when S6_PAUSE_DETECT      =>
                  i2c_SLA_fsm_next            <= S0_IDLE;
                  i2c_SLA_fsm_previous        <= S0_IDLE;
                  i2c_SLA_fsm_previous_start  <= S0_IDLE;
                  rcv_I2C_inst_fsm            <= S0_READ_SDA;
               when Q1_ERROR             =>
                  i2c_SLA_fsm_next            <= S0_IDLE;
                  i2c_SLA_fsm_previous        <= S0_IDLE;
                  i2c_SLA_fsm_previous_start  <= S0_IDLE;
                  rcv_I2C_inst_fsm            <= S0_READ_SDA;
               when others               =>
            end case;
         end if;
      else
      end if;
   end process;




end Behavioral;
