library ieee;
use ieee.std_logic_1164.all;

library utils;

entity master is
  generic (
    SCL_FALLING_DELAY : natural;
    SCL_MIN_STABLE_CYCLES : natural);

  port (
    clk_i               : in  std_logic;  -- Synchronous clock
    rst_in              : in  std_logic;  -- Synchronous reset (active low)
    -- address
    slave_address_i     : in  std_logic_vector(6 downto 0);  -- Address of the
    -- ack control
    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
    -- 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_clear_buffer_i   : in  std_logic;
    -- errors
    err_noack_data_o    : out std_logic;
    err_noack_address_o : out std_logic;
    err_arbitration_o   : out std_logic;
    err_general_o       : out std_logic;
    -- state
    stop_i              : in  std_logic;
    start_i             : in  std_logic;
    run_i               : in  std_logic;
    rw_i                : in  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

    -- i2c
    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 master;

architecture a1 of master 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 tx_done : std_logic;

  signal rx_sda_enable : std_logic;
  signal rx_scl_stretch : std_logic;
  signal rx_done : std_logic;

  signal bus_busy : std_logic;

  signal adr_gen : std_logic;
  signal adr_sda_enable : std_logic;
  signal adr_noack : std_logic;
  signal adr_unexpected_sda : std_logic;
  signal adr_done : std_logic;
  signal adr_gen_store : std_logic;
  signal adr_gen_start : std_logic;

  signal cond_gen : std_logic;
  signal cond_gen_sda_enable : std_logic;
  signal cond_gen_start : std_logic;
  signal cond_gen_stop : std_logic;
  signal cond_gen_req_scl_fall : std_logic;
  signal cond_gen_req_scl_rise : std_logic;
  signal cond_gen_done : std_logic;
  signal cond_gen_early : std_logic;

  signal scl_gen_scl_enable : std_logic;
  signal scl_gen_req_continuous : std_logic;

  signal rst_i2c : std_logic;

  signal scl_falling_delayed : std_logic;
  signal waiting_for_data : std_logic;
  signal scl_gen_falling : std_logic;
begin  -- architecture a1
  bus_busy_o <= bus_busy;
  waiting_for_data <= tx_scl_stretch or rx_scl_stretch;
  waiting_o <= waiting_for_data;

  scl_enable_o <= scl_gen_scl_enable;

  scl_gen_falling <= cond_gen_req_scl_fall when cond_gen = '1' else
                     tx_scl_stretch when transmitting = '1' else
                     rx_scl_stretch when transmitting = '1' else
                     '0';

  sda_enable_o <= tx_sda_enable when transmitting = '1' else
                  rx_sda_enable when receiving = '1' else
                  cond_gen_sda_enable when cond_gen = '1' else
                  adr_sda_enable when adr_gen = '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);

  condition_generator : entity work.startstop_condition_generator
    generic map (
      DELAY => SCL_FALLING_DELAY)
    port map (
      clk_i                 => clk_i,
      rst_in                => rst_in,
      sda_i                 => sync_sda,
      scl_rising_i          => scl_rising,
      scl_falling_i         => scl_falling,
      scl_falling_delayed_i => scl_falling_delayed,
      sda_enable_o          => cond_gen_sda_enable,
      start_condition_i     => start_condition,
      stop_condition_i      => stop_condition,
      gen_start_i           => cond_gen_start,
      gen_stop_i            => cond_gen_stop,
      req_scl_fall_o        => cond_gen_req_scl_fall,
      req_scl_rise_o        => cond_gen_req_scl_rise,
      early_condition_o     => cond_gen_early,
      done_o                => cond_gen_done);

  scl_generator : entity work.scl_generator
    generic map (
      MIN_STABLE_CYCLES => SCL_MIN_STABLE_CYCLES)
    port map (
      clk_i            => clk_i,
      rst_in           => rst_in,
      scl_i            => sync_scl,
      scl_rising_i     => scl_rising,
      scl_falling_i    => scl_falling,
      gen_continuous_i => scl_gen_req_continuous,
      gen_rising_i     => cond_gen_req_scl_rise,
      gen_falling_i    => scl_gen_falling,
      scl_enable_o     => scl_gen_scl_enable,
      cannot_comply_o  => open);

  -- 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,
      done_o                => rx_done,
      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,
      done_o                => tx_done,
      write_data_i          => tx_data_i);

  address_generator : entity work.address_generator
    port map (
      clk_i                 => clk_i,
      rst_in                => rst_in,
      address_i             => slave_address_i,
      store_address_rw_i    => adr_gen_store,
      scl_rising_i          => scl_rising,
      scl_falling_delayed_i => scl_falling_delayed,
      sda_enable_o          => adr_sda_enable,
      sda_i                 => sync_sda,
      noack_o               => adr_noack,
      unexpected_sda_o      => adr_unexpected_sda,
      done_o                => adr_done,
      start_i               => adr_gen_start,
      rw_i                  => rw_i);

  state_machine : entity work.master_state
    port map (
      clk_i                    => clk_i,
      rst_in                   => rst_in,
      rst_i2c_o                => rst_i2c,
--
      start_i                  => start_i,
      stop_i                   => stop_i,
      run_i                    => run_i,
      rw_i                     => rw_i,
--
      expect_ack_i             => expect_ack_i,
      noack_address_i          => adr_noack,
      noack_data_i             => tx_noack,
      unexpected_sda_address_i => adr_unexpected_sda,
      unexpected_sda_data_i    => tx_unexpected_sda,
      condition_early_i        => cond_gen_early,
--
      err_noack_address_o      => err_noack_address_o,
      err_noack_data_o         => err_noack_data_o,
      err_arbitration_o        => err_arbitration_o,
      err_general_o            => err_general_o,
--
      start_condition_i        => start_condition,
      stop_condition_i         => stop_condition,
--
      waiting_for_data_i       => waiting_for_data,
      rx_done_i                => rx_done,
      tx_done_i                => tx_done,
--
      address_gen_start_o      => adr_gen_start,
      address_gen_done_i       => adr_done,
      address_gen_store_o      => adr_gen_store,
--
      req_start_o              => cond_gen_start,
      req_stop_o               => cond_gen_stop,
      req_cond_done_i          => cond_gen_done,
--
      req_scl_continuous_o     => scl_gen_req_continuous,
--
      cond_gen_o               => cond_gen,
      address_gen_o            => adr_gen,
      receive_o                => receiving,
      transmit_o               => transmitting,
      dev_busy_o               => dev_busy_o,
      bus_busy_o               => bus_busy);

end architecture a1;
