library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

use work.spi_pkg.all;

entity spi_master is

  generic (
    SIZES            : natural_vector := (8, 16);
    SIZES_2LOG       : natural := 1;
    DIVISORS         : natural_vector := (2, 4, 6, 8, 16, 32, 64, 128, 256);
    DIVISORS_LOG2    : natural := 3;
    CSN_PULSE_CYCLES : natural := 1
  );

  port (
    -- IOs
    sck_o                : out std_logic;
    miso_i               : in  std_logic;
    mosi_o               : out std_logic;
    csn_o                : out std_logic;
    -- Control
    clk_i                : in  std_logic;
    rst_in               : in  std_logic;
    en_i                 : in  std_logic;
    clock_polarity_i     : in  std_logic;
    clock_phase_i        : in  std_logic;
    size_sel_i           : in  std_logic_vector(SIZES_2LOG - 1 downto 0);
    div_sel_i            : in  std_logic_vector(DIVISORS_LOG2 - 1 downto 0);
    pulse_csn_i          : in  std_logic;
    rx_block_on_full_i   : in  std_logic;
    -- Data
    -- Rx
    rx_en_i              : in  std_logic;
    rx_data_o            : out std_logic_vector(get_max_natural(SIZES) - 1 downto 0);
    rx_valid_o           : out std_logic;
    rx_ready_i           : out std_logic;
    -- Tx
    tx_en_i              : in  std_logic;
    tx_data_i            : in  std_logic_vector(get_max_natural(SIZES) - 1 downto 0);
    tx_valid_i           : out std_logic;
    tx_ready_o           : out std_logic;
    -- State
    busy_o               : out std_logic;
    err_lost_rx_data_o   : out std_logic;
    clear_lost_rx_data_i : in  std_logic);

end entity spi_master;

architecture a1 of spi_master is
  constant MAX_SIZE : natural := get_max_natural(SIZES);

  signal rst_n : std_logic;

  signal ctrl_rst : std_logic;

  signal latch_sample_data     : std_logic;
  signal counter_overflow      : std_logic;
  signal latch_change_data_out : std_logic;
  signal latch_new_tx_data     : std_logic;

  signal tx_serial_data : std_logic;

  signal sck : std_logic;
  signal csn : std_logic;
  signal mosi : std_logic_vector(0 downto 0);

  signal start_clock : std_logic;

  signal sck_mask : std_logic;
  signal sck_en : std_logic;
  signal csn_en : std_logic;
  signal mosi_en : std_logic;
  signal miso_en : std_logic;

  signal selected_size : natural;

begin  -- architecture a1
  rst_n <= rst_in and ctrl_rst;
  selected_size <= SIZES(to_integer(unsigned(size_sel_i)));

  ctrl : entity work.spi_master_ctrl
    generic map (
      SIZES            => SIZES,
      SIZES_2LOG       => SIZES_2LOG,
      DIVISORS         => DIVISORS,
      DIVISORS_LOG2    => DIVISORS_LOG2,
      CSN_PULSE_CYCLES => CSN_PULSE_CYCLES)
    port map (
      clk_i                => clk_i,
      rst_in               => rst_in,
      en_i                 => en_i,
      size_sel_i           => size_sel_i,
      div_sel_i            => div_sel_i,
      clock_phase_i        => clock_phase_i,
      pulse_csn_i          => pulse_csn_i,
      counter_overflow_i   => counter_overflow,
      rx_block_on_full_i   => rx_block_on_full_i,
      rx_en_i              => rx_en_i,
      rx_ready_i           => rx_ready_i,
      rx_valid_o           => rx_valid_o,
      tx_en_i              => tx_en_i,
      tx_valid_i           => tx_valid_i,
      tx_ready_o           => tx_ready_o,
      busy_o               => busy_o,
      err_lost_rx_data_o   => err_lost_rx_data_o,
      clear_lost_rx_data_i => clear_lost_rx_data_i,
      rst_on               => ctrl_rst,
      csn_o                => csn,
      csn_en_o             => csn_en,
      mosi_en_o            => mosi_en,
      miso_en_o            => miso_en,
      sck_mask_o           => sck_mask,
      sck_en_o             => sck_en,
      gen_clk_en_o         => start_clock,
      latch_tx_data_o      => latch_new_tx_data);

  clkgen : entity work.spi_clkgen
    generic map (
      DIVISORS      => DIVISORS,
      DIVISORS_LOG2 => DIVISORS_LOG2)
    port map (
      clk_i              => clk_i,
      rst_in             => rst_n,
      start_i            => start_clock,
      div_sel_i          => div_sel_i,
      clock_polarity_i   => clock_polarity_i,
      clock_phase_i      => clock_phase_i,
      sck_o              => sck,
      sck_mask_i         => sck_mask,
      counter_overflow_o => counter_overflow,
      sample_data_o      => latch_sample_data,
      change_data_o      => latch_change_data_out);

  shift_register: entity work.shift_register
    generic map (
      SIZE => MAX_SIZE)
    port map (
      clk_i => clk_i,
      rst_in => rst_n,
      -- Control
      shift_i => latch_sample_data,     -- sampling
      latch_i => latch_new_tx_data,     -- latching tx data
      lsbfirst_i => '0',
      -- Parallel
      data_i => tx_data_i,
      data_o => rx_data_o,
      -- Serial
      sd_i => miso_i,
      sd_o => open);

  tx_serial_data <= rx_data_o(selected_size - 1);

  mosi_reg : entity work.reg
    generic map (
      SIZE => 1)
    port map (
      -- outputting different bit on mosi
      clk_i   => clk_i,
      rst_in  => rst_n,
      d_i     => (0 => tx_serial_data),
      q_o     => mosi,
      latch_i => latch_change_data_out);

  sck_o <= sck when sck_en = '1' else 'Z';
  mosi_o <= mosi(0) when mosi_en = '1' else 'Z';
  csn_o <= csn when csn_en = '1' else 'Z';

end architecture a1;
