library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;

entity delayer is
  port (
    clk               : in STD_LOGIC  := '0';
    clk_en            : in STD_LOGIC  := '0';                       --clock enable of half the frequency
    image_data_valid  : in STD_LOGIC  := '0';                       --does sensor send valid image data? (theoretically)
    ref_clock         : out STD_LOGIC := '0';                       --clock for SelectIO core   
    channel_sel       : in STD_LOGIC_VECTOR(3 downto 0)   := (others => '0');  --channel select (to be set and transferred)  
    delay_ce          : in STD_LOGIC := '0';                        --processor says if it wants to do something with the channel
    delay_inc         : in STD_LOGIC := '0';                        --procesor says what it wants to do with the channel 
    delay_ce_out      : out STD_LOGIC_VECTOR(15 downto 0) := (others => '0');  --do I want to do something with the channel?     
    delay_inc_out     : out STD_LOGIC_VECTOR(15 downto 0) := (others => '0');  --what do I want to do with the channel?
    delay_locked      : in STD_LOGIC := '0';                       --I can't change states if locked
    data_in_vector    : in STD_LOGIC_VECTOR(191 downto 0) := (others => '0');
    data_out          : out STD_LOGIC_VECTOR(11 downto 0) := (others => '0');  --sends data from selected channel of processor
    bitslip_out       : out STD_LOGIC_VECTOR(15 downto 0) := (others => '0');  --bitslips a bit in selected channel
    switch_data       : out STD_LOGIC_VECTOR(15 downto 0) := (others => '0');  --switches sextuplet of bits (111111000000) -> (000000111111)
    mode_select       : in STD_LOGIC_VECTOR(1 downto 0)   := (others => '0');  --selects align mode (0 - bit, 1 - word, 2 - channel)
    axi_data_valid    : in STD_LOGIC := '0'                        --rising_edge signalizes completed axi transfer
  );

end delayer;

architecture Behavioral of delayer is
  type data_in_array is array(0 to 15) of std_logic_Vector(11 downto 0);
  signal data_in              : data_in_array;
  signal axi_data_valid_tmp   : std_logic := '1';
  signal image_data_valid_sig : std_logic := '0';
begin
  generate1 : for i in 0 to 15 generate --is generated for each channel
    process (clk)
    begin
      if rising_edge(clk) then
        if (clk_en = '1') then
          --inserts received data to array
          data_in(i) <= data_in_vector(i * 12 + 11 downto i * 12); --data_in(0) <= data_in_vector(11 downto 0);
          --------------------------------------------
        end if;
      end if;
    end process;
    end generate generate1;

    process (clk)
    --state machine
    type delayer_state_machine is (
    idle_state,   --waits for valid image data from Timer
    align_state,  --sends signals to serdes and other cores
    wait_state    --waits until acknowledgment from processor
    );
    type int_array is array(0 to 15) of integer range 0 to 6;

    variable delayer_state_machine_var            : delayer_state_machine;
    variable delayer_state_machine_last_state_tmp : delayer_state_machine;
    variable bitslip_var                          : int_array;
    variable channel_sel_var                      : integer range 0 to 15;
    variable delay_ce_var                         : unsigned(15 downto 0);
    variable delay_ce_out_var                     : unsigned(15 downto 0);
    variable delay_inc_var                        : unsigned(15 downto 0);
    variable delay_inc_out_var                    : unsigned(15 downto 0);
    variable switch_data_var                      : unsigned(15 downto 0);
    variable bitslip_out_var                      : unsigned(15 downto 0);
    variable switch_data_sig                      : std_logic_vector(15 downto 0);
    
    begin
      if (rising_edge(clk)) then
        if (clk_en = '1') then
          --rising_edge of axi_data_valid acknowledges new data transfer
          if (axi_data_valid = '0') then
            axi_data_valid_tmp <= '0';
          end if;
          image_data_valid_sig <= image_data_valid; --asynch reg
          -----------------------------------------------------------------------------------
          if (image_data_valid_sig = '1') then --Are valid image data received? (theoretically)
            channel_sel_var := to_integer(unsigned(channel_sel)); 
            data_out <= data_in(channel_sel_var);                 --sends data from selected channel of processor

            if (delay_locked = '0') then --can I make further changes? from serdes
              case delayer_state_machine_var is
                when idle_state   => delay_ce_out <= (others => '0'); --I don't want to do anything with channels
                when align_state  => case mode_select is
                when "00"   => --bit align

                  delay_ce_var     := "000000000000000" & delay_ce; 
                  delay_inc_var    := "000000000000000" & delay_inc;

                  delay_ce_out_var := delay_ce_var sll channel_sel_var; --sets delay_ce value received from processor to bit on certain position (according to selected channel)
                  delay_ce_out <= std_logic_vector(delay_ce_out_var);   

                  delay_inc_out_var := delay_inc_var sll channel_sel_var; --na bit na určité pozici (podle vybraného kanálu) nastavím hodnotu delay_inc přijatou od procesoru
                  delay_inc_out <= std_logic_vector(delay_inc_out_var);   --sets delay_inc value received from processor to bit on certain position (according to selected channel)
                  ---------------------------
                when "01" => --word align
                  if (bitslip_var(channel_sel_var) >= 5) then                   --was bit shifted more than 5 positions? if yes, sixtuplets need to be switched and shifted again
                    switch_data_var := "0000000000000001" sll channel_sel_var;  --data switch on selected channel
                    switch_data <= std_logic_vector(switch_data_var);           --storing the variable to the signal
                    delay_inc_var   := "000000000000000" & "0";
                    bitslip_out_var := delay_inc_var sll channel_sel_var;
                    bitslip_out <= std_logic_vector(bitslip_out_var);
                    bitslip_var(channel_sel_var) := 0;

                  else --if the bit was not shifted more than 5 positions (or is already switched), tells the serdes to move the bit on selected channel
                    switch_data_var := "0000000000000000" sll channel_sel_var;  --data switch on selected channel
                    switch_data <= std_logic_vector(switch_data_var);           --storing the variable to signal
                    delay_inc_var   := "000000000000000" & "1";

                    bitslip_out_var := delay_inc_var sll channel_sel_var;
                    bitslip_out <= std_logic_vector(bitslip_out_var);
                    bitslip_var(channel_sel_var) := bitslip_var(channel_sel_var) + 1;
                  end if;
                  ---------------------------
                when "10" => --channel align
                when others     => --nothing
              end case;
              --makes clock signal for serdes
              delayer_state_machine_last_state_tmp := delayer_state_machine_var;
              ref_clock <= '1';
              delayer_state_machine_var := wait_state;
              -----------------------------------------------
              when wait_state => ref_clock <= '0'; --waits for processor acknowlegment and then returns to state from which came
              when others => 
            end case;
          end if;
        end if;
        if (axi_data_valid_tmp = '0' and axi_data_valid = '1') then --rising_edge of data_valid signal from processor
          case delayer_state_machine_var is
            when idle_state => if (image_data_valid = '1') then
              delayer_state_machine_var := align_state; --if was idle, go to align_state when rising_edge of data_valid, if not blocked by serdes, sends data (bitslips etc.) and then goes to waiting sate
          end if;
          when align_state => 
          when wait_state => delayer_state_machine_var := delayer_state_machine_last_state_tmp; --from waiting state goes to align_state, if rising_edge of data_valid was received

        end case;
        axi_data_valid_tmp <= '1';
      end if;
    end if;
    end if;
  end process;
end Behavioral;