--
-- Multi channel IODELAY2 delay programme
--
-- Matt Warren 2014
--
--

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

library utils;
use utils.pkg_types.all;

library hsio;
use hsio.pkg_core_globals.all;


entity iodelay2_prog is
  port(
    reg_i         : in  std_logic_vector (15 downto 0);
    reg_wr_i      : in  std_logic;
    idelay_ctl_o  : out t_idelay_ctl;
    idelay_cal_o  : out std_logic;
    idelay_busy_i : in  std_logic;
    rst           : in  std_logic;
    clk           : in  std_logic
    );

-- Declarations

end iodelay2_prog;

--

architecture rtl of iodelay2_prog is

  signal selected_hs : integer range 0 to 71;
  signal address     : std_logic_vector(6 downto 0);
  signal delayval    : std_logic_vector(7 downto 0);
  signal counter     : std_logic_vector(7 downto 0);
  signal count_en    : std_logic;

  signal idelay_cal : std_logic;

  --signal inc  : std_logic;
  signal ce   : std_logic_vector(71 downto 0);
  signal zero : std_logic_vector(71 downto 0);

  signal strm_swap : std_logic_vector (71 downto 0);
  signal strm_loop : std_logic_vector (71 downto 0);

  type states is (Reset, IssueCal0, IssueCal1, CalWaitBusy,
                  ZeroDelay, ZDWaitBusy,
                  CheckCounter, IncDelay, WaitBusy,
                  Idle);

  signal state, nstate : states;


begin


  idelay_ctl_o.ce        <= ce(71 downto 0);
  idelay_ctl_o.zero      <= zero(71 downto 0);
  idelay_ctl_o.inc       <= '1';        -- we always inc
  idelay_ctl_o.strm_loop <= strm_loop(71 downto 0);
  idelay_ctl_o.strm_swap <= strm_swap(71 downto 0);

  selected_hs <= conv_integer(address);

  address  <= reg_i(14 downto 8);
  delayval <= reg_i(7 downto 0);



  prc_store : process (clk)
  begin
    if rising_edge(clk) then
-- if (rst = '1') then
-- counter <= (others => '0');

-- else
      -- defaults
      strm_swap <= (others => '0');
      strm_loop <= (others => '0');

      --strm_swap (conv_integer(reg_i(14 downto 8))) <= reg_i();
      strm_loop (conv_integer(reg_i(14 downto 8))) <= reg_i(15);

      if (reg_wr_i = '1') then
        counter <= delayval;

      else
        if (count_en = '1') then
          counter <= counter - '1';
        end if;
      end if;

-- end if;
    end if;
  end process;




  -- State Machine
  --------------------------------------------------------

  prc_sm_clocked : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') then
        state <= Reset;
      else
        state <= nstate;
      end if;
    end if;
  end process;



  prc_sm_async : process (state, reg_wr_i, counter, selected_hs, idelay_busy_i)
  begin

    --defaults
    nstate     <= Idle;
    ce         <= (others => '0');
    zero       <= (others => '0');
    count_en   <= '0';
    idelay_cal <= '0';

    case state is


      when Reset =>
        nstate   <= Reset;
        if (idelay_busy_i = '0') then
          nstate <= IssueCal0;
        end if;

      when IssueCal0 =>
        nstate <= IssueCal1;

      when IssueCal1 =>
        idelay_cal <= '1';
        nstate       <= CalWaitBusy;


      when CalWaitBusy =>
        nstate   <= CalWaitBusy;
        if (idelay_busy_i = '0') then
          nstate <= Idle;
        end if;

      -----------------------------------------------------------
      when Idle =>
        nstate     <= Idle;
        if (idelay_busy_i = '0') then
          if (reg_wr_i = '1') then
            nstate <= ZeroDelay;
          end if;
        end if;


      when ZeroDelay =>
        zero(selected_hs) <= '1';
        nstate            <= ZDWaitBusy;

      when ZDWaitBusy =>
        nstate   <= ZDWaitBusy;
        if (idelay_busy_i = '0') then
          nstate <= CheckCounter;
        end if;

      ----------------------------------------------------------------
      when CheckCounter =>
        if (counter = "00000000") then
          nstate <= Idle;
        else
          nstate <= IncDelay;
        end if;


      when IncDelay =>
        ce(selected_hs) <= '1';
        count_en        <= '1';
        nstate          <= WaitBusy;


      when WaitBusy =>
        nstate   <= WaitBusy;
        if (idelay_busy_i = '0') then
          nstate <= CheckCounter;
        end if;


    end case;

  end process;



  idelay_cal_o <= idelay_cal when rising_edge(clk);


end architecture rtl;