--
-- Downlink Decoder
--
-- This attempts to be fast and minimise FF usage
-- May not have succeeded ...
--
-- Matt Warren Aug 2016
--
--


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

library downlink;
use downlink.lcb_pkg_globals.all;


entity lcb_regblock_if is
  port(

    abcid_i : in std_logic_vector(3 downto 0);

    -- RegBlock interface
    rb_addr_o : out std_logic_vector(7 downto 0);
    rb_rnw_o  : out std_logic;
    rb_init_o : out std_logic;
    rb_load_o : out std_logic;
    rb_sdat_o : out std_logic;
    rb_shen_o : out std_logic;
    rb_busy_i : in  std_logic;


    -- CmdFIFO
    cfifo_all_i   : in  std_logic_vector(8 downto 0);
    cfifo_empty_i : in  std_logic;
    cfifo_re_o    : out std_logic;

    -- Infra
    clk40 : in std_logic;               --40MHz BCO
    rstb  : in std_logic
    );


end lcb_regblock_if;

---------------------------------------------------------------------------
architecture rtl of lcb_regblock_if is

  constant C_RST_REG_ADDR : std_logic_vector(7 downto 0) := x"00";

  signal cfifo_data : std_logic_vector(6 downto 0);
  signal cfifo_sof  : std_logic;
  signal cfifo_eof  : std_logic;

  signal cfifo_data_abcid : std_logic_vector(3 downto 0);

  signal cfifo_re : std_logic;


--  signal rb_addr : std_logic_vector(7 downto 0);
--  signal rb_rnw  : std_logic;
  signal rb_init : std_logic;
  signal rb_sdat : std_logic;
  signal rb_shen : std_logic;
  signal rb_load : std_logic;

  signal addr_store0 : std_logic;
  signal addr_store1 : std_logic;

  type states is (
    Header0, Header1,
    CheckSendDat6, SendDat5, SendDat4, SendDat3, SendDat2, SendDat1, SendDat0,
    WaitNextWord, WaitEoF,
    Idle
    );

  signal state, nstate : states;


-------------------------------------------------------------------------------
begin

  -- Mappings

  cfifo_re_o <= cfifo_re;
  cfifo_sof  <= cfifo_all_i(8);
  cfifo_eof  <= cfifo_all_i(7);
  cfifo_data <= cfifo_all_i(6 downto 0);

  cfifo_data_abcid <= cfifo_data(5 downto 2);


  -- cmd frame decoder/sequencer
  --=============================================================================

  prc_ser_sm_sync_part : process (clk40, rstb)
  begin

    if (rstb = '0') then
      state <= Idle;

    elsif rising_edge(clk40) then
      state <= nstate;

    end if;
  end process;


  prc_ser_sm_async_part : process (abcid_i, cfifo_data,
                                   cfifo_data_abcid, cfifo_empty_i, cfifo_eof,
                                   cfifo_sof, rb_busy_i, state)
  begin

    -- defaults
    cfifo_re    <= '0';
    addr_store0 <= '0';
    addr_store1 <= '0';
    rb_init     <= '0';
    rb_load     <= '0';
    rb_sdat     <= '0';
    rb_shen     <= '0';


    case state is

      when Idle =>
        nstate <= Idle;
        if (cfifo_empty_i = '0') then
          if (cfifo_sof = '1') then
            nstate <= Header0;
          else
            cfifo_re <= '1';
          end if;
        end if;


      when Header0 =>                   -- check abcid
        nstate <= Header0;
        if (cfifo_data_abcid = abcid_i) or (cfifo_data_abcid = "1111") then
          addr_store0 <= '1';
          if (cfifo_empty_i = '0') then
            cfifo_re <= '1';
            nstate   <= Header1;
          end if;
        else
          nstate <= WaitEoF;          -- not for me wait 'til done; 
        end if;


      when Header1 =>
        nstate <= Header1;
        if (cfifo_empty_i = '0') and (rb_busy_i = '0') then
          addr_store1 <= '1';
          rb_init     <= '1';
          cfifo_re    <= '1';
          nstate      <= SendDat3;      -- first chunk has only 4b data
        end if;

      ----------------------------------------------------
      when CheckSendDat6 =>
        nstate  <= CheckSendDat6;
        rb_sdat <= cfifo_data(6);
        if (rb_busy_i = '0') then
          if (cfifo_eof = '1') then
            rb_load <= '1';
            nstate  <= Idle;
          else
            rb_shen <= '1';
            nstate  <= SendDat5;
          end if;
        end if;


      when SendDat5 =>
        nstate  <= SendDat5;
        rb_sdat <= cfifo_data(5);
        if (rb_busy_i = '0') then
          rb_shen <= '1';
          nstate  <= SendDat4;
        end if;


      when SendDat4 =>
        nstate  <= SendDat4;
        rb_sdat <= cfifo_data(4);
        if (rb_busy_i = '0') then
          rb_shen <= '1';
          nstate  <= SendDat3;
        end if;


      when SendDat3 =>
        nstate  <= SendDat3;
        rb_sdat <= cfifo_data(3);
        if (rb_busy_i = '0') then
          rb_shen <= '1';
          nstate  <= SendDat2;
        end if;


      when SendDat2 =>
        nstate  <= SendDat2;
        rb_sdat <= cfifo_data(2);
        if (rb_busy_i = '0') then
          rb_shen <= '1';
          nstate  <= SendDat1;
        end if;

      when SendDat1 =>
        nstate  <= SendDat1;
        rb_sdat <= cfifo_data(1);
        if (rb_busy_i = '0') then
          rb_shen <= '1';
          nstate  <= SendDat0;
        end if;

      when SendDat0 =>
        nstate  <= SendDat0;
        rb_sdat <= cfifo_data(0);
        if (rb_busy_i = '0') then
          rb_shen <= '1';
          if (cfifo_empty_i = '1') then
            nstate <= WaitNextWord;
          else
            cfifo_re <= '1';
            nstate   <= CheckSendDat6;
          end if;
        end if;

      ----------------------------
      when WaitNextWord =>
        nstate <= WaitNextWord;
        if (cfifo_empty_i = '0') then
          --cfifo_re <= '1';
          nstate <= CheckSendDat6;

        end if;


      ----------------------------
      when WaitEoF =>
        nstate   <= WaitEoF;
        if (cfifo_empty_i = '0') then
          cfifo_re <= '1';
        end if;
        if (cfifo_eof = '1') then
          nstate <= Idle;
        end if;


    end case;

  end process;






  prc_stores : process (clk40, rstb)
  begin
    if (rstb = '0') then
      rb_rnw_o  <= '0';
      rb_addr_o <= x"00";

    elsif rising_edge(clk40) then

      if (addr_store0 = '1') then
        rb_rnw_o              <= cfifo_data(6);
        rb_addr_o(7 downto 6) <= cfifo_data(1 downto 0);
      end if;

      if (addr_store1 = '1') then
        rb_addr_o(5 downto 0) <= cfifo_data(6 downto 1);
      end if;



      -- clock these to make things tidy
      rb_init_o <= rb_init;
      rb_load_o <= rb_load;
      rb_sdat_o <= rb_sdat;
      rb_shen_o <= rb_shen;

    end if;

  end process;



end rtl;