--
-- LCB 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_decoder is
  generic(
    ASIC_IS_ABC : integer := 1          -- else HCC
    );
  port(
    hccid_i       : in  std_logic_vector (4 downto 0);
    par4_i        : in  std_logic_vector (3 downto 0);
    locked_i      : in  std_logic;
    frame_sync_i  : in  std_logic_vector (1 downto 0);
    decoder_err_o : out std_logic;
    par4_o        : out std_logic_vector (3 downto 0);
    -- Signals/data out
    ------------------------------
    l0a_o         : out std_logic;
    l0a_tag_o     : out std_logic_vector (6 downto 0);
    --l0a_tag_valid_o : out std_logic;
    bcr_o         : out std_logic;
    cbus_data_o   : out std_logic_vector (6 downto 0);
    cbus_valid_o  : out std_logic;
    --    cmd_ignore_o : out std_logic;
    cbus_sof_o    : out std_logic;
    cbus_eof_o    : out std_logic;
    cbus_all_o    : out std_logic_vector (8 downto 0);

    fast_cmd_o : out std_logic_vector (3 downto 0);


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

-- Declarations

end lcb_decoder;

---------------------------------------------------------------------------
architecture rtl of lcb_decoder is

  --signal par4_sr : slv4_array(3 downto 0);
  signal par4_sr : slv4_array(2 downto 0);

  signal alt_hccid_insert   : std_logic;
  signal alt_hccid_insert_q : std_logic;
  signal symbol_in          : std_logic_vector(7 downto 0);
  signal symbol_sync        : std_logic;


  signal dec_6b           : std_logic_vector(5 downto 0);
  signal dec_k            : std_logic_vector(3 downto 0);
  signal dec_err          : std_logic;
  signal dec_bcr          : std_logic;
  signal dec_l0a_map      : std_logic_vector(3 downto 0);
  signal dec_tag_msb      : std_logic;
  signal dec_tag_lsbs     : std_logic_vector(5 downto 0);
  signal dec_is_k         : std_logic;
  signal dec_is_cmd       : std_logic;
  signal dec_cmd_hccid    : std_logic_vector(4 downto 0);
  signal dec_cmd_ignore   : std_logic;
  signal dec_cmd_hccid_ok : std_logic;
  signal bcr0             : std_logic;
  signal bcr_q            : std_logic_vector(5 downto 0);
  signal l0a_sr           : std_logic_vector(3 downto 0);
  signal l0a_delayed      : std_logic_vector(3 downto 0);
  signal l0a_load_go      : std_logic;


  signal tag_counter      : std_logic_vector(6 downto 0);
  --signal tag_valid        : std_logic;
  signal tag_load         : std_logic;
  signal tag_msb          : std_logic;
  signal tag_msb_store_en : std_logic;



  signal cmd_ignore     : std_logic;
  signal cmd_ignore_set : std_logic;
  signal cmd_sof_set    : std_logic;


  signal cbus_valid   : std_logic;
  --signal cbus_sof     : std_logic;
  signal cbus_eof     : std_logic;
  signal cbus_valid_q : std_logic;
  signal cbus_sof_q   : std_logic;
  signal cbus_eof_q   : std_logic;
  signal cbus_data_q  : std_logic_vector(6 downto 0);



  signal k3_bc          : std_logic_vector(1 downto 0);
  signal k3_data        : std_logic_vector(3 downto 0);
  signal k3_go          : std_logic;
  signal k3_in_progress : std_logic;
  signal k3_is_cmd_done : std_logic;


  signal dbg_symb0_sync : std_logic;
  signal dbg_symb1_sync : std_logic;
  signal dbg_symb0      : std_logic_vector(7 downto 0);
  signal dbg_symb1      : std_logic_vector(7 downto 0);
  signal dbg_dec_6b0    : std_logic_vector(5 downto 0);
  signal dbg_dec_6b1    : std_logic_vector(5 downto 0);
  signal dbg_bcr        : std_logic;
  signal dbg_l0a_map    : std_logic_vector(3 downto 0);
  signal dbg_tag        : std_logic_vector(6 downto 0);




  type states is (Idle,
                  Symbol0,
                  WaitTag,
                  WaitCmd,
                  WaitCmdStart,
                  WaitK3);

  signal state, nstate : states;

begin

  symbol_sync <= frame_sync_i(0);


  -- low latency sr, just in case 
  par4_sr(0) <= par4_i;

  prc_par4_sr : process (clk40)
  begin
    if rising_edge(clk40) then

      alt_hccid_insert_q <= alt_hccid_insert;

      if (alt_hccid_insert = '1') then
        --par4_sr(3) <= par4_sr(2);
        par4_sr(2) <= SYMB_CMD_IGNORE(7 downto 4);
        par4_sr(1) <= par4_sr(0);

      elsif (alt_hccid_insert_q = '1') then
        --par4_sr(3) <= par4_sr(2);
        par4_sr(2) <= SYMB_CMD_IGNORE(3 downto 0);
        par4_sr(1) <= par4_sr(0);

      else
        --par4_sr(3 downto 1) <= par4_sr(2 downto 0);
        par4_sr(2 downto 1) <= par4_sr(1 downto 0);

      end if;
    end if;
  end process;

  par4_o    <= par4_sr(2);
  symbol_in <= par4_sr(1) & par4_sr(0);

  -- 8b/6b symbol decoder
  -- decoder output must be strobed by frame_sync

  process(symbol_in)
  begin

    --defaults
    dec_err <= '0';
    dec_6b  <= "000000";
    dec_k   <= "0000";

    case symbol_in is

      when "01011001" => dec_6b <= "000000";
      when "01110001" => dec_6b <= "000001";
      when "01110010" => dec_6b <= "000010";
      when "11000011" => dec_6b <= "000011";
      when "01100101" => dec_6b <= "000100";
      when "11000101" => dec_6b <= "000101";
      when "11000110" => dec_6b <= "000110";
      when "10000111" => dec_6b <= "000111";
      when "01101001" => dec_6b <= "001000";
      when "11001001" => dec_6b <= "001001";
      when "11001010" => dec_6b <= "001010";
      when "10001011" => dec_6b <= "001011";
      when "11001100" => dec_6b <= "001100";
      when "10001101" => dec_6b <= "001101";
      when "10001110" => dec_6b <= "001110";
      when "01001011" => dec_6b <= "001111";
      when "01010011" => dec_6b <= "010000";
      when "11010001" => dec_6b <= "010001";
      when "11010010" => dec_6b <= "010010";
      when "10010011" => dec_6b <= "010011";
      when "11010100" => dec_6b <= "010100";
      when "10010101" => dec_6b <= "010101";
      when "10010110" => dec_6b <= "010110";
      when "00010111" => dec_6b <= "010111";
      when "11011000" => dec_6b <= "011000";
      when "10011001" => dec_6b <= "011001";
      when "10011010" => dec_6b <= "011010";
      when "00011011" => dec_6b <= "011011";
      when "10011100" => dec_6b <= "011100";
      when "00011101" => dec_6b <= "011101";
      when "00011110" => dec_6b <= "011110";
      when "01011100" => dec_6b <= "011111";
      when "01100011" => dec_6b <= "100000";
      when "11100001" => dec_6b <= "100001";
      when "11100010" => dec_6b <= "100010";
      when "10100011" => dec_6b <= "100011";
      when "11100100" => dec_6b <= "100100";
      when "10100101" => dec_6b <= "100101";
      when "10100110" => dec_6b <= "100110";
      when "00100111" => dec_6b <= "100111";
      when "11101000" => dec_6b <= "101000";
      when "10101001" => dec_6b <= "101001";
      when "10101010" => dec_6b <= "101010";
      when "00101011" => dec_6b <= "101011";
      when "10101100" => dec_6b <= "101100";
      when "00101101" => dec_6b <= "101101";
      when "00101110" => dec_6b <= "101110";
      when "01101100" => dec_6b <= "101111";
      when "01110100" => dec_6b <= "110000";
      when "10110001" => dec_6b <= "110001";
      when "10110010" => dec_6b <= "110010";
      when "00110011" => dec_6b <= "110011";
      when "10110100" => dec_6b <= "110100";
      when "00110101" => dec_6b <= "110101";
      when "00110110" => dec_6b <= "110110";
      when "01010110" => dec_6b <= "110111";
      when "10111000" => dec_6b <= "111000";
      when "00111001" => dec_6b <= "111001";
      when "00111010" => dec_6b <= "111010";
      when "01011010" => dec_6b <= "111011";
      when "00111100" => dec_6b <= "111100";
      when "01001101" => dec_6b <= "111101";
      when "01001110" => dec_6b <= "111110";
      when "01100110" => dec_6b <= "111111";

      when "01000111" => dec_k(0) <= '1';
      when "01010101" => dec_k(1) <= '1';
      when "01111000" => dec_k(2) <= '1';
      when "01101010" => dec_k(3) <= '1';

      when others => dec_err <= '1';

    end case;
  end process;

  decoder_err_o <= (dec_err and symbol_sync) when rising_edge(clk40);


  -- These mappings are not valid all the time
  --  Must use in conjunction with frame_sync

  dec_bcr          <= dec_6b(5);
  dec_l0a_map      <= dec_6b(4 downto 1);
  dec_tag_msb      <= dec_6b(0);
  dec_tag_lsbs     <= dec_6b;
  dec_is_k         <= '1' when (dec_k /= "0000")                                      else '0';
  dec_is_cmd       <= '1' when (dec_6b(5 downto 1) = "00000") and (dec_is_k = '0')    else '0';
  dec_cmd_hccid    <= dec_6b(4 downto 0);
  dec_cmd_ignore   <= dec_6b(5);
  dec_cmd_hccid_ok <= '1' when (dec_cmd_hccid = hccid_i) or (dec_cmd_hccid = "11111") else '0';


  k3_is_cmd_done <= '1' when dec_6b(3 downto 0) = "0001" else '0';



  -- lots of debug - not for synth, of course
  dbg_symb0_sync <= '1' when (frame_sync_i = FSYNC_SYMB0) else '0';
  dbg_symb1_sync <= '1' when (frame_sync_i = FSYNC_FRAME) else '0';

  dbg_symb0   <= symbol_in when rising_edge(dbg_symb0_sync);
  dbg_symb1   <= symbol_in when rising_edge(dbg_symb1_sync);
  dbg_dec_6b0 <= dec_6b    when rising_edge(dbg_symb0_sync);
  dbg_dec_6b1 <= dec_6b    when rising_edge(dbg_symb1_sync);

  dbg_bcr     <= dbg_dec_6b0(5);
  dbg_l0a_map <= dbg_dec_6b0(4 downto 1);
  dbg_tag     <= dbg_dec_6b0(0) & dbg_dec_6b1 when (frame_sync_i = FSYNC_FRAME);




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


  prc_sm_sync_part : process (clk40)
  begin
    if rising_edge(clk40) then
      if (rst = '1') then
        state <= Idle;
      else
        state <= nstate;
      end if;
    end if;
  end process;

  -----------------------------------------

  prc_sm_async : process (cmd_ignore, dec_bcr, dec_cmd_hccid_ok,
                          dec_cmd_ignore, dec_is_cmd, dec_k(2), dec_k(3),
                          dec_l0a_map, frame_sync_i, k3_is_cmd_done, locked_i,
                          state)

  begin


    -- defaults
    bcr0             <= '0';
    l0a_load_go      <= '0';
    tag_msb_store_en <= '0';
    tag_load         <= '0';
    cbus_valid       <= '0';
    cmd_ignore_set   <= '0';
    cmd_sof_set      <= '0';
    cbus_eof         <= '0';
    alt_hccid_insert <= '0';
    k3_go            <= '0';



    case state is


      -- Wait for locked and correct position if frame cycle
      when Idle =>
        nstate <= Idle;

        if (locked_i = '1') and (frame_sync_i = FSYNC_NIB0) then
          nstate <= Symbol0;
        end if;


      -- first symbol processing
      -----------------------------------------------------
      when Symbol0 =>                   -- aka FSYNC_SYMB0

        tag_msb_store_en <= '1';

        if (dec_bcr = '1') then
          bcr0 <= '1';
        end if;

        if (dec_l0a_map /= "0000") then
          l0a_load_go <= '1';
        end if;

        if (dec_k(2) = '1') then        -- CMD header
          nstate <= WaitCmdStart;

        elsif (dec_k(3) = '1') then
          nstate <= WaitK3;

        elsif (dec_is_cmd = '1') then
          nstate <= WaitCmd;

        else
          nstate <= WaitTag;

        end if;




      -- second symbol/frame processing
      -------------------------------------------------------
      when WaitTag =>
        nstate <= WaitTag;
        if (frame_sync_i = FSYNC_FRAME) then
          tag_load <= '1';
          nstate   <= Idle;
        end if;


      when WaitCmd =>
        nstate <= WaitCmd;

        if (frame_sync_i = FSYNC_FRAME) then
          tag_load <= '1';
          if (cmd_ignore = '0') then
            cbus_valid <= '1';
          end if;
          nstate <= Idle;
        end if;


      when WaitCmdStart =>
        nstate <= WaitCmdStart;

        if (frame_sync_i = FSYNC_FRAME) then

          if (ASIC_IS_ABC = 1) then
            if (dec_cmd_ignore = '1') then
              cmd_ignore_set <= '1';
            else
              cmd_sof_set <= '1';
            end if;

          else                          -- ASIC_IS_HCC
            if (dec_cmd_hccid_ok = '0') then
              alt_hccid_insert <= '1';
              cmd_ignore_set   <= '1';
            else
              cmd_sof_set <= '1';
            end if;

          end if;

          nstate <= Idle;
        end if;


      when WaitK3 =>
        nstate <= WaitK3;

        if (frame_sync_i = FSYNC_FRAME) then
          k3_go <= '1';

          if (k3_is_cmd_done = '1') then
            cbus_eof   <= '1';
            cbus_valid <= '1';
          end if;
          nstate <= Idle;
        end if;


    end case;

  end process;



-- L0A processing
----------------------------------------------------

  prc_l0a_sr : process (clk40)
  begin

    if rising_edge(clk40) then
      if (rst = '1') then
        l0a_sr      <= (others => '0');
        l0a_delayed <= (others => '0');
        tag_counter <= (others => '0');
        tag_msb     <= '0';

      else

        if (tag_msb_store_en = '1') then
          tag_msb <= dec_tag_msb;
        end if;

        if (l0a_load_go = '1') then
          l0a_sr <= dec_l0a_map;

        else
          l0a_sr <= l0a_sr(2 downto 0) & '0';

        end if;

        l0a_delayed <= l0a_delayed(2 downto 0) & l0a_sr(3);


        -- tag counter ----------------------------------
        if (tag_load = '1') then
          tag_counter <= tag_msb & dec_tag_lsbs;

        else
          if (l0a_delayed(1) = '1') then  -- *** adjust tap as needed


            if (tag_counter = "1111111") then
              tag_counter <= "0000000";
            else
              tag_counter <= tag_counter + '1';
            end if;

          end if;
        end if;

      end if;
    end if;

  end process;

  l0a_o     <= l0a_delayed(1);
  l0a_tag_o <= tag_counter;
  --l0a_tag_valid_o <= l0a_delayed(1);    -- *** adjust as per above

  cbus_data_q <= tag_counter;  -- uses same field, and is already registered;



-- BCR "processing"
----------------------------------------------------


  prc_bcr : process (clk40)
  begin

    if rising_edge(clk40) then
      if (rst = '1') then
        bcr_o <= '0';
        bcr_q <= (others => '0');

      else
        bcr_q <= bcr_q(4 downto 0) & bcr0;  -- delay to put BCR in known phase wrt frame

        bcr_o <= bcr_q(4);

      end if;
    end if;

  end process;




-- CMD "processing"
----------------------------------------------------

  prc_cmd : process (clk40)

  begin

    if rising_edge(clk40) then
      if (rst = '1') then
        cmd_ignore   <= '0';
        cbus_valid_q <= '0';
        cbus_sof_q   <= '0';
        cbus_eof_q   <= '0';

      else

        if (cmd_ignore_set = '1') then
          cmd_ignore <= '1';

        elsif (cbus_eof = '1') then
          cmd_ignore <= '0';

        end if;

        -- "latch" sof state until first cmd data sent
        if (cmd_sof_set = '1') then
          cbus_sof_q <= '1';
        elsif (cbus_valid_q = '1') then
          cbus_sof_q <= '0';
        end if;


        cbus_valid_q <= cbus_valid;
        cbus_eof_q   <= cbus_eof;


      end if;
    end if;

  end process;


  --cmd_ignore_o <= cmd_ignore;


  cbus_sof_o   <= cbus_sof_q;           -- in sync with first cmd7 word
  cbus_eof_o   <= cbus_eof_q;           -- in sync with last cmd7 word
  cbus_data_o  <= cbus_data_q;
  cbus_valid_o <= cbus_valid_q;

  cbus_all_o <= cbus_sof_q & cbus_eof_q & cbus_data_q;




-- K3 (fast_command) "processing"
----------------------------------------------------


  prc_k3 : process (clk40)
  begin

    if rising_edge(clk40) then
      if (rst = '1') then
        k3_bc          <= "00";
        k3_data        <= "0000";
        k3_in_progress <= '0';

      else

        -- default
        fast_cmd_o <= "0000";

        if (k3_go = '1') then
          k3_bc   <= dec_6b(5 downto 4);
          k3_data <= dec_6b(3 downto 0);

          -- doing first one here to save a clock
          if (dec_6b(5 downto 4) = "00") then
            fast_cmd_o <= dec_6b(3 downto 0);
          end if;

        elsif (k3_in_progress = '1') then
          if (frame_sync_i = k3_bc) then
            fast_cmd_o <= k3_data;
          end if;

          if (frame_sync_i = "11") then
            k3_in_progress <= '0';
          end if;

        end if;
      end if;
    end if;
  end process;


end rtl;