--
-- 4b symbol dedoder
-- 
-- Matt Warren July 2015
--
--


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

library readout130;
use readout130.pkg_star_flink.all;


entity star_flink_decoder is
  port(

    -- 160 MHz domain
    -----------------------------------------
    ser_i  : in std_logic;              -- serial input
    clk160 : in std_logic;              -- sample clock

    -- 40MHz domain
    ------------------------------------------------
    symbol_align_i : in  std_logic_vector(2 downto 0);  -- alignment control
    symbol_o       : out std_logic_vector(3 downto 0);  -- output of deser after alignment
    output_en_i    : in  std_logic;     -- enable signals after align
    isidle_o       : out std_logic;     -- might be useful for auto-align
    error_o        : out std_logic;  -- asserted if a symbol decodes as "1111" or "0000"

    -- Signals/data out
    bcr_o : out std_logic;
    ls_o  : out std_logic;

    l0id_o     : out std_logic_vector(7 downto 0);  -- single L0ID bus
    pr_valid_o : out std_logic;                     -- L0ID is for PR
    lp_valid_o : out std_logic;                     -- L0ID is for LP

    -- Infra
    clk : in std_logic;
    rst : in std_logic
    );

-- Declarations

end star_flink_decoder;

---------------------------------------------------------------------------
architecture rtl of star_flink_decoder is

  signal ser_in0 : std_logic;
  signal ser_in1 : std_logic;

  signal sr0 : std_logic_vector(7 downto 0);
  signal sr1 : std_logic_vector(7 downto 0);

  signal symbol     : std_logic_vector(3 downto 0);
  signal symb_count : std_logic_vector(1 downto 0);

  signal pr_mode : std_logic;
  signal lp_mode : std_logic;

begin

  -- Toy 160Mb Deserialiser (sim only)
  --===========================================================

  -- This demonstates using DDR as an easy way to control
  -- sample phase in the slower clock domain

  ser_in0 <= ser_i when rising_edge(clk160);
  ser_in1 <= ser_i when falling_edge(clk160);

  -- Shift in 160Mb data,
  prc_deser160 : process (clk160)
  begin
    if rising_edge(clk160) then
      if (rst = '1') then
        sr0 <= (others => '0');
        sr1 <= (others => '0');

      else
        sr0 <= sr0(6 downto 0) & ser_in0;
        sr1 <= sr1(6 downto 0) & ser_in1;

      end if;
    end if;
  end process;


  -- Alignment of 4b symbols to 40MHz clock (incl. sample phase)
  --===========================================================

  prc_align40 : process (clk)
  begin
    if rising_edge(clk) then
      case symbol_align_i is
        when "000"  => symbol <= sr0(3 downto 0);
        when "001"  => symbol <= sr1(3 downto 0);
        when "010"  => symbol <= sr0(4 downto 1);
        when "011"  => symbol <= sr1(4 downto 1);
        when "100"  => symbol <= sr0(5 downto 2);
        when "101"  => symbol <= sr1(5 downto 2);
        when "110"  => symbol <= sr0(6 downto 3);
        when "111"  => symbol <= sr1(6 downto 3);
        when others => null;

      end case;
    end if;
  end process;

  symbol_o <= symbol;


  -- Main Event: Decode symbols and assemble output signals
  --==========================================================

  prc_decode_out : process (clk)
    variable data_3b        : std_logic_vector(2 downto 0);
    variable symb_count_clr : std_logic;
    variable symb_count_inc : std_logic;

  begin

    if rising_edge(clk) then
      if (rst = '1') or (output_en_i = '0') then
        isidle_o       <= '0';
        bcr_o          <= '0';
        ls_o           <= '0';
        pr_mode        <= '0';
        lp_mode        <= '0';
        data_3b        := "000";
        l0id_o         <= x"00";
        pr_valid_o     <= '0';
        lp_valid_o     <= '0';
        symb_count     <= "00";
        symb_count_clr := '0';
        symb_count_inc := '0';
        error_o        <= '0';

      else

        -- Decode symbols
        -------------------------------------------------------

        -- defaults
        isidle_o       <= '0';
        ls_o           <= '0';
        bcr_o          <= '0';
        data_3b        := "000";
        pr_valid_o     <= '0';
        lp_valid_o     <= '0';
        symb_count_clr := '0';
        symb_count_inc := '0';
        error_o        <= '0';

        case symbol is

          when sBCR   => bcr_o <= '1';
          when sLS    => ls_o  <= '1';
          when sBCRLS => bcr_o <= '1';
                         ls_o <= '1';

          when sPR => pr_mode <= '1';
                      lp_mode        <= '0';
                      symb_count_clr := '1';
          when sLP => pr_mode <= '0';
                      lp_mode        <= '1';
                      symb_count_clr := '1';

          when sDATA0 => data_3b := "000"; symb_count_inc := '1';
          when sDATA1 => data_3b := "001"; symb_count_inc := '1';
          when sDATA2 => data_3b := "010"; symb_count_inc := '1';
          when sDATA3 => data_3b := "011"; symb_count_inc := '1';
          when sDATA4 => data_3b := "100"; symb_count_inc := '1';
          when sDATA5 => data_3b := "101"; symb_count_inc := '1';
          when sDATA6 => data_3b := "110"; symb_count_inc := '1';
          when sDATA7 => data_3b := "111"; symb_count_inc := '1';

          when sIDLE => isidle_o <= '1';

          when others => error_o <= '1';

        end case;


        -- Data symbol counter
        if (symb_count_clr = '1') then
          symb_count <= "00";

        elsif (symb_count_inc = '1') then
          symb_count <= symb_count + '1';

        end if;

        -- Assemble L0ID and Valid outputs
        -----------------------------------------------------------
        case symb_count is
          when "00" => l0id_o(7 downto 6) <= data_3b(1 downto 0);
          when "01" => l0id_o(5 downto 3) <= data_3b;
          when "10" => l0id_o(2 downto 0) <= data_3b;
                       pr_valid_o <= pr_mode;
                       lp_valid_o <= lp_mode;
          --when "11"   => l0id_o <= x"00";  -- not needed, for debug only
          when others => null;
        end case;


      end if;
    end if;
  end process;


end rtl;