library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;
entity timer is
  port (
    clk                   : in STD_LOGIC := '0';
    rst                   : in STD_LOGIC := '0';
    en                    : in STD_LOGIC := '0';
    xvs_xhs               : in STD_LOGIC := '0';                     --is it allowed for xvs and xhs to be '1'?
    mode                  : in STD_LOGIC := '0';                     --0 normal mode (xvs/xhs)
    xtrig_width_axi       : in STD_LOGIC_VECTOR(31 downto 0) := (others => '0'); --xtrig pulse width
    xvs                   : out STD_LOGIC := '0';                    --timing signal to sensor
    xhs                   : out STD_LOGIC := '0';                    --timing signal to sensor
    xtrig                 : out STD_LOGIC := '0';                    --timing signal to sensor
    communication_allowed : out STD_LOGIC := '0';                    --is SPI communication allowed?
    communication_en      : in STD_LOGIC := '0';                     
    image_data_valid      : out STD_LOGIC := '0';                    --Is valid image data area read? (Recording pixel area + color processing margin)
    pure_image_data_valid : out STD_LOGIC := '0';                    --Is clean image without other data read? (Recording pixel area)
    image_data_with_sync_valid : out STD_LOGIC := '0'                --Is image data inc. sync code read? (Recording pixel area + color processing margin + sync code)
  
  );
end timer;

architecture Behavioral of timer is
  signal xhs_counter_full_sig : std_logic := '0';
  signal signals_locked_sig   : std_logic := '0'; --state of signal lock
begin
  process (clk)

  CONSTANT DELAY_NORMAL                   : integer := 1;     --how many clocks should be signal delayed after state change of other signal? (min. 1) Normal mode
  CONSTANT CHANNEL_COUNT                  : integer := 16;    --number of LVDS channels
  CONSTANT HORIZONTAL_PIXEL_COUNT         : integer := 4240;  --number of horizontal pixels (rows)
  CONSTANT VERTICAL_PIXEL_COUNT           : integer := 3062;  --number of vertical pixels (columns)
  CONSTANT XHS_PULSE_WIDTH                : integer := 16;    --XHS pulse width (active low) in clocks (min. 4)
  CONSTANT XVS_PULSE_WIDTH                : integer := HORIZONTAL_PIXEL_COUNT/CHANNEL_COUNT; --32; --XVS pulse width (active low) in clocks (min. 4)
  CONSTANT COMMUNICATION_INTERVAL_NORMAL  : integer := 4;     --allowed communication interval in normal mode (XVS/XHS) in line units
  CONSTANT COMMUNICATION_INTERVAL_TRIGGER : integer := 2;     --allowed communication interval in external trigger mode (XTRIG/XHS) in line units
  CONSTANT INITIAL_DELAY                  : integer := 1;     --initial delay in clocks (min. unspecified - just a reserve)
  variable XTRIG_WIDTH                    : integer := 10000; --how many XHS pulses does exposition last? (min. 1)
  CONSTANT DATA_VALID_H_MIN               : integer := 4;     --how many clk_counter from line start to valid image (+ color processing margin) data (start interval)
  CONSTANT DATA_VALID_H_MAX               : integer := 4116;  --how many clk_counter from line start to valid image (+ color processing margin) data (end interval)
  CONSTANT DATA_VALID_V_MIN               : integer := 53;    --how many xhs_counter from frame start to valid image (+ color processing margin) datům (start interval)
  CONSTANT DATA_VALID_V_MAX               : integer := 3057;  --how many xhs_counter from frame start to valid image (+ color processing margin) datům (end interval)
  CONSTANT DATA_PURE_VALID_H_MIN          : integer := 12;    --how many clk_counter from line start to valid image data (start interval)
  CONSTANT DATA_PURE_VALID_H_MAX          : integer := 4108;  --how many clk_counter from line start to valid image data (end interval)
  CONSTANT DATA_PURE_VALID_V_MIN          : integer := 57;    --how many xhs_counter from frame start to valid image data (start interval)
  CONSTANT DATA_PURE_VALID_V_MAX          : integer := 3057;  --how many xhs_counter from frame start to valid image data (end interval)
  CONSTANT DATA_SYNC_VALID_H_MIN          : integer := 0;     --how many clk_counter from line start to valid image (+color processing margin + sync code) data (start interval)
  CONSTANT DATA_SYNC_VALID_H_MAX          : integer := 4120;  --how many clk_counter from line start to valid image (+color processing margin + sync code) data (end interval)
  CONSTANT DATA_SYNC_VALID_V_MIN          : integer := 53;    --how many xhs_counter from frame start to valid image (+color processing margin + sync code) data (start interval)
  CONSTANT DATA_SYNC_VALID_V_MAX          : integer := 3057;  --how many xhs_counter from frame start to valid image (+color processing margin + sync code) data (end interval)
    
  variable clk_counter                    : integer range 0 to HORIZONTAL_PIXEL_COUNT := 0; --pixel counter
  variable xhs_counter                    : integer range 0 to VERTICAL_PIXEL_COUNT   := 0; --XHS counter (lines)
  variable xhs_counter_full               : integer range 0 to 30                     := 0; --second xhs counter (important for xvs signal delay)
  variable xhs_pulse_counter              : integer range 0 to XHS_PULSE_WIDTH        := 1; --XHS pulse width counter
  variable xvs_pulse_counter              : integer range 0 to XVS_PULSE_WIDTH        := 1; --XVS pulse width counter
  variable signals_locked_counter         : integer range 0 to COMMUNICATION_INTERVAL_NORMAL := 0; --signal delay length counter
  variable initial_delay_counter          : integer range 0 to 1000                   := 0; --initial delay length counter
  variable communication_interval_counter : integer range 0 to 4                      := 0; --SPI communication length counter

  variable xvs_var : std_logic := '1'; --tmp of XVS signal
  variable xhs_var : std_logic := '1'; --tmp of XHS signal

  variable xtrig_var        : std_logic := '1'; --tmp of xtrig signal
  variable xtrig_last_state : std_logic := '1'; --to identify rising_edge
  variable xtrig_counter    : integer   := 0;   --xtrig signal length counter
  begin
    if rising_edge(clk) then
      if rst = '1' or xvs_xhs = '0' then
        clk_counter := 0;
        xhs_counter := 0;
        xhs_counter_full := 0;
        xhs_pulse_counter := 1;
        xvs_pulse_counter := 1;
        signals_locked_counter := 0;
        initial_delay_counter := 0;
        communication_interval_counter := 0;
        xvs_var := '0';
        xhs_var := '0';
        xtrig_var := '1';
        xtrig_last_state := '1';
        xtrig_counter := 0;
        XTRIG_WIDTH := to_integer(unsigned(xtrig_width_axi));
        ------------------
        if(communication_en = '1')then
        communication_allowed <= '1';
        else
        communication_allowed <= '0';
        end if;
        --high (inactive) when off
        xtrig <= '1';
        xvs <= '0';
        xhs <= '0';
        image_data_valid <= '0';
      elsif (en = '1') then
      
        if (clk_counter > DATA_VALID_H_MIN AND clk_counter < DATA_VALID_H_MAX AND xhs_counter > DATA_VALID_V_MIN AND xhs_counter < DATA_VALID_V_MAX)then
          image_data_valid <= '1';
        else
          image_data_valid <= '0';
        end if;
        
        if (clk_counter > DATA_VALID_H_MIN AND clk_counter < DATA_VALID_H_MAX AND xhs_counter > DATA_VALID_V_MIN AND xhs_counter < DATA_VALID_V_MAX)then
          image_data_valid <= '1';
        else
          image_data_valid <= '0';
        end if;
                
        if (clk_counter > DATA_VALID_H_MIN AND clk_counter < DATA_VALID_H_MAX AND xhs_counter > DATA_VALID_V_MIN AND xhs_counter < DATA_VALID_V_MAX)then
          image_data_valid <= '1';
        else
          image_data_valid <= '0';
        end if;
                 
        if (mode = '0') then --what kind of signals to generate? XVS+XHS (0) or XTRIG+XHS (1)?
          --NORMAL MODE ----------------------------------------------
          --Initial delay
          if (initial_delay_counter < INITIAL_DELAY) then
            initial_delay_counter := initial_delay_counter + 1;
            if (initial_delay_counter = INITIAL_DELAY) then
              --New frame and new line
              clk_counter := (HORIZONTAL_PIXEL_COUNT/CHANNEL_COUNT) - 2;
              xhs_counter := VERTICAL_PIXEL_COUNT;
            end if;
          else
            --Ordinary run - after delay

            --Pixel counter
            clk_counter := clk_counter + 1;

            --Locks the signals for some time to delay them
            if (signals_locked_counter > 0) then
              signals_locked_counter := signals_locked_counter - 1;
            end if;
            if (signals_locked_counter = 0) then
              signals_locked_sig <= '0';
            else
              signals_locked_sig <= '1';
            end if;

            --Low pulse width
            if (xhs_var = '0') then
              if (xhs_pulse_counter < XHS_PULSE_WIDTH) then
                xhs_pulse_counter := xhs_pulse_counter + 1;
              else
                xhs_pulse_counter := 1;
                xhs_var := '1';
              end if;
            end if;

            --Low pulse width

            if (xvs_var = '0') then
              if (xvs_pulse_counter < XVS_PULSE_WIDTH) then
                xvs_pulse_counter := xvs_pulse_counter + 1;
                else
                xvs_pulse_counter := 1;
                xvs_var := '1';
              end if;
            end if;

            --Signal lock
            if (clk_counter + 1 >= HORIZONTAL_PIXEL_COUNT/CHANNEL_COUNT and xhs_counter >= VERTICAL_PIXEL_COUNT) then
              signals_locked_counter := DELAY_NORMAL;
            end if;

            --New line (XHS low)
            if (clk_counter >= HORIZONTAL_PIXEL_COUNT/CHANNEL_COUNT) then
              if (signals_locked_counter > 0) then
                xhs_counter_full := xhs_counter_full + 1;
              else
                xhs_var := '0';
                clk_counter := 0;
                xhs_counter := xhs_counter + 1;
                --Starts/ends allowed communication interval of SPI
                if (communication_interval_counter > 0) then
                  communication_allowed <= '1';
                  communication_interval_counter := communication_interval_counter - 1;
                else
                  communication_allowed <= '0';
                end if;
              end if;
            end if;

            --New frame (XVS low)
            if (xhs_counter_full > 0 or (xhs_counter >= VERTICAL_PIXEL_COUNT and DELAY_NORMAL = 0)) then
              if (xhs_counter_full > 0) then
                xhs_counter_full_sig <= '1';
              else
                xhs_counter_full_sig <= '0';
              end if;
              xvs_var := '0';
              xhs_counter := 0;
              communication_interval_counter := COMMUNICATION_INTERVAL_NORMAL;
              communication_allowed <= '1';
              xhs_counter_full := 0;
            end if;

          end if; --initial_delay_counter < INITIAL_DELAY
          --Signal actualization
          xvs <= xvs_var;
          xhs <= xhs_var;
          ---------------------------------------------------------------
        else
          --TRIGGER MODE ------------------------------------------------

          --Initial delay
          if (initial_delay_counter < INITIAL_DELAY) then
            initial_delay_counter := initial_delay_counter + 1;
            if (initial_delay_counter = INITIAL_DELAY) then
              xtrig_counter := XTRIG_WIDTH;
              xtrig_var := '1';
              xhs_var := '1';
            end if;
          else

            --Ordinary run - after delay
            clk_counter := clk_counter + 1;

            --xtrig transition 0 -> 1 allowes communication
            if (xtrig_last_state = '0' and xtrig_var = '1') then
              communication_interval_counter := COMMUNICATION_INTERVAL_TRIGGER;
            end if;

            --Low pulse width
            if (xhs_var = '0') then
              if (xhs_pulse_counter < XHS_PULSE_WIDTH) then
                xhs_pulse_counter := xhs_pulse_counter + 1;
              else
                xhs_pulse_counter := 1;
                xhs_var := '1';
              end if;
            end if;

            --New line (XHS low)
            if (clk_counter >= HORIZONTAL_PIXEL_COUNT/CHANNEL_COUNT) then
              xhs_var := '0';
              clk_counter := 0;
              xhs_counter := xhs_counter + 1;
              xtrig_counter := xtrig_counter - 1;
              --Starts/ends allowed communication interval
              if (communication_interval_counter > 0) then
                communication_allowed <= '1';
                communication_interval_counter := communication_interval_counter - 1;
              else
                communication_allowed <= '0';
              end if;
            end if;

            xtrig_last_state := xtrig_var;
            --In the middle of XHS interval starts to generate XTRIG signal
            if (clk_counter >= (HORIZONTAL_PIXEL_COUNT/CHANNEL_COUNT)/2) then
              --Keep exposition?
              if (xtrig_counter > 0) then
                xtrig_var := '0';
              else
                xtrig_var := '1';
              end if;
              --Continues exposition or starts new one
              if (xhs_counter >= VERTICAL_PIXEL_COUNT) then
                if (xtrig_counter > 0) then
                else
                  xtrig_counter := XTRIG_WIDTH;
                end if;
                xhs_counter := 0;
              end if;
            end if;

            --signals actualization
            xtrig <= xtrig_var;
            xhs <= xhs_var;
          end if;
          ---------------------------------------------------------------
        end if;
      end if; --reset and en
    end if; --rising edge(clk)
  end process;
end Behavioral;