library ieee;
use ieee.std_logic_1164.all;

library utils;

entity slave is
  generic (
    SCL_FALLING_DELAY : natural := 5);

  port (
    clk_i        : in std_logic;        -- Synchronous clock
    rst_in       : in std_logic;        -- Synchronous reset (active low)

    -- address
    address_i      : in std_logic_vector(6 downto 0);  -- Address of the slave
    generate_ack_i : in std_logic;
    expect_ack_i   : in std_logic;

    -- rx
    rx_valid_o   : out std_logic;       -- Data in rx_data are valid
    rx_data_o    : out std_logic_vector(7 downto 0);  -- Received data
    rx_confirm_i : in std_logic;        -- Confirm data from rx_data are read
    rx_stretch_i : in std_logic;

    -- tx
    tx_ready_o        : out std_logic;  -- Transmitter ready for new data
    tx_valid_i        : in  std_logic;  -- Are data in tx_data valid? Should be
                                        -- a pulse for one cycle only
    tx_data_i         : in  std_logic_vector(7 downto 0);  -- Data to transmit
    tx_stretch_i      : in  std_logic;
    tx_clear_buffer_i : in  std_logic;

    -- errors
    err_noack_o  : out std_logic;
    err_sda_o  : out std_logic;

    -- state
    rw_o         : out std_logic;       -- 1 - read, 0 - write
    dev_busy_o   : out std_logic;       -- Communicating with master
    bus_busy_o   : out std_logic;       -- Bus is busy, someone else is communicating
    waiting_o    : out std_logic;       -- Waiting for data or read data

    sda_i        : in std_logic;        -- I2C SDA line
    scl_i        : in std_logic;        -- I2C SCL line

    sda_enable_o : out std_logic;       -- Pull down sda
    scl_enable_o : out std_logic);      -- Pull down scl

end entity slave;

architecture a1 of slave is
  signal sync_sda, sync_scl                  : std_logic;
  signal start_condition, stop_condition     : std_logic;
  signal scl_rising, scl_falling : std_logic;

  signal transmitting, receiving : std_logic;

  signal tx_sda_enable : std_logic;
  signal tx_scl_stretch : std_logic;
  signal tx_noack, tx_unexpected_sda : std_logic;

  signal rx_sda_enable : std_logic;
  signal rx_scl_stretch : std_logic;

  signal address_detect_sda_enable : std_logic;

  signal bus_busy : std_logic;

  signal address_detect_activate : std_logic;
  signal address_detect_success : std_logic;
  signal address_detect_fail : std_logic;
  signal address_detect_store : std_logic;
  signal address_detection : std_logic;

  signal rw : std_logic;
  signal rst_i2c : std_logic;

  signal scl_falling_delayed : std_logic;
begin  -- architecture a1
  rw_o <= rw;
  dev_busy_o <= transmitting or receiving;
  bus_busy_o <= bus_busy;
  waiting_o <= tx_scl_stretch or rx_scl_stretch;

  scl_enable_o <= tx_scl_stretch when transmitting = '1' and tx_stretch_i = '1' and scl_i = '0' else
                  rx_scl_stretch when receiving = '1' and rx_stretch_i = '1' and scl_i = '0' else
                  '0';

  sda_enable_o <= tx_sda_enable when transmitting = '1' else
                  rx_sda_enable when receiving = '1' else
                  address_detect_sda_enable when address_detection = '1' else
                  '0';

  scl_falling_delayer: entity utils.delay
    generic map (
      DELAY => SCL_FALLING_DELAY)
    port map (
      clk_i    => clk_i,
      rst_in   => rst_in,
      signal_i => scl_falling,
      signal_o => scl_falling_delayed);

  scl_edge_detector: entity utils.sync_edge_detector
    port map (
      clk_i          => clk_i,
      signal_i       => sync_scl,
      rising_edge_o  => scl_rising,
      falling_edge_o => scl_falling);

  sda_sync: entity utils.metastability_filter
    port map (
      clk_i    => clk_i,
      signal_i => sda_i,
      signal_o => sync_sda);

  scl_sync: entity utils.metastability_filter
    port map (
      clk_i    => clk_i,
      signal_i => scl_i,
      signal_o => sync_scl);

  condition_detector: entity work.startstop_condition_detector
    port map (
      clk_i  => clk_i,
      sda_i   => sync_sda,
      scl_i   => sync_scl,
      start_o => start_condition,
      stop_o  => stop_condition);

  -- rx
  rx : entity work.rx
    port map (
      clk_i                 => clk_i,
      rst_in                => rst_in,
      start_read_i          => receiving,
      generate_ack_i        => generate_ack_i,
      rst_i2c_i             => rst_i2c,
      scl_rising           => scl_rising,
      scl_falling_delayed_i => scl_falling_delayed,
      scl_stretch_o         => rx_scl_stretch,
      sda_i                 => sync_sda,
      sda_enable_o          => rx_sda_enable,
      read_valid_o          => rx_valid_o,
      read_ready_o          => open,
      read_data_o           => rx_data_o,
      confirm_read_i        => rx_confirm_i);

  -- tx
  tx : entity work.tx
    port map (
      clk_i                 => clk_i,
      rst_in                => rst_in,
      clear_buffer_i        => tx_clear_buffer_i,
      start_write_i         => transmitting,
      rst_i2c_i             => rst_i2c,
      scl_rising_i    => scl_rising,
      scl_falling_delayed_i => scl_falling_delayed,
      scl_stretch_o         => tx_scl_stretch,
      sda_i                 => sync_sda,
      sda_enable_o          => tx_sda_enable,
      unexpected_sda_o      => tx_unexpected_sda,
      noack_o               => tx_noack,
      ready_o               => tx_ready_o,
      valid_i               => tx_valid_i,
      write_data_i          => tx_data_i);

  address_detector : entity work.address_detector
    port map (
      clk_i                 => clk_i,
      rst_in                => rst_in,
      address_i             => address_i,
      store_address_i       => address_detect_store,
      scl_rising            => scl_rising,
      scl_falling_delayed_i => scl_falling_delayed,
      sda_enable_o          => address_detect_sda_enable,
      sda_i                 => sync_sda,
      start_i               => address_detect_activate,
      rw_o                  => rw,
      success_o             => address_detect_success,
      fail_o                => address_detect_fail);

  state_machine : entity work.i2c_slave_state
    port map (
      clk_i                    => clk_i,
      rst_in                   => rst_in,
      rst_i2c_o                => rst_i2c,
      start_condition_i        => start_condition,
      stop_condition_i         => stop_condition,
      err_noack_o              => err_noack_o,
      err_sda_o                => err_sda_o,
      unexpected_sda_i         => tx_unexpected_sda,
      expect_ack_i             => expect_ack_i,
      noack_i                  => tx_noack,
      rw_i                     => rw,
      address_detect_success_i => address_detect_success,
      address_detect_fail_i    => address_detect_fail,
      address_detect_start_o   => address_detect_activate,
      address_detect_store_o   => address_detect_store,
      address_detect_o         => address_detection,
      receive_o                => receiving,
      transmit_o               => transmitting,
      bus_busy_o               => bus_busy);

end architecture a1;
