--
-- LL Packet FIFO
--
-- Simple 2kB FIFO that can buffer a whole packet LL stylee
-- * Does not push use dst_rdy to stop incoming data * 
-- 
-- 

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

entity ll_pkt_fifo_axi8_16 is
  port(
    -- input interface (AXI-S 8b)
    axis_tdata_i  : in  std_logic_vector(7 downto 0);
    axis_tvalid_i : in  std_logic;
    axis_tlast_i  : in  std_logic;
    axis_tready_o : out std_logic;

    -- output interface (LL 16b)
    data_o    : out std_logic_vector (15 downto 0);
    sof_o     : out std_logic;
    eof_o     : out std_logic;
    dst_rdy_i : in  std_logic;
    src_rdy_o : out std_logic;

    -- fifo status
    --full_o          : out std_logic;
    --near_full_o     : out std_logic;
    --overflow_o      : out std_logic;
    --underflow_o     : out std_logic;
    --wr_data_count_o : out std_logic_vector (3 downto 0) := "0000";
    clk : in std_logic;
    rst : in std_logic
    );

-- Declarations

end ll_pkt_fifo_axi8_16;


architecture rtl of ll_pkt_fifo_axi8_16 is




  component cg_brfifo_1kx18_ft
    port (
      clk         : in  std_logic;
      srst        : in  std_logic;
      din         : in  std_logic_vector(17 downto 0);
      wr_en       : in  std_logic;
      rd_en       : in  std_logic;
      dout        : out std_logic_vector(17 downto 0);
      full        : out std_logic;
      almost_full : out std_logic;
      empty       : out std_logic;
      data_count  : out std_logic_vector(3 downto 0)
      );
  end component;

  signal tdata16      : std_logic_vector(15 downto 0);
  signal tdata16hi_we : std_logic;
  signal tlast        : std_logic;


  signal fifo_we          : std_logic;
  signal fifo_din         : std_logic_vector(17 downto 0);
  signal fifo_dout        : std_logic_vector(17 downto 0);
  signal fifo_rd          : std_logic;
  signal fifo_empty       : std_logic;
  signal fifo_full        : std_logic;
  signal fifo_almost_full : std_logic;
  signal fifo_data_count  : std_logic_vector(3 downto 0);


  -- signal fifo_pre_rd : std_logic;

  signal fifo_sof   : std_logic;
  signal fifo_sof_q : std_logic := '0';
  signal fifo_eof   : std_logic;


  signal dst_rdy_out : std_logic;


  signal eofin : std_logic;

  signal eof_coded     : std_logic_vector(1 downto 0);
  signal delta_eof     : integer range 0 to 255;
  signal dec_delta_eof : std_logic;



  type wstates is (wLowByte, wHighByte, wLastLowByte,
                   wIdle
                   );

  signal wstate, nwstate : wstates;

  type states is (SrcRdySOF, WaitEOF,
                  DecDeltaEOF, Delay,
                  Idle
                  );

  signal state, nstate : states;


begin


  -- eof manager - only allow whole packets
  -------------------------------------------------------------------

  eofin     <= (axis_tvalid_i and axis_tlast_i);
  eof_coded <= (eofin & dec_delta_eof);

  prc_count_delta_eof : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') then
        delta_eof <= 0;

      else
        case eof_coded is
          when "00"   => null;
          when "01"   => delta_eof <= delta_eof - 1;
          when "10"   => delta_eof <= delta_eof + 1;
          when "11"   => null;
          when others => null;

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


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



  prc_sm_wr_sync : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') then
        wstate <= wIdle;
      else
        wstate <= nwstate;

      end if;
    end if;
  end process;


  prc_sm_wr_async : process (wstate, axis_tvalid_i, axis_tlast_i)
  begin

    -- defaults
    axis_tready_o        <= '1';
    tdata16hi_we         <= '0';
    tdata16(7 downto 0) <= axis_tdata_i;
    tlast                <= '0';
    fifo_we              <= '0';

    case wstate is

      when wIdle =>
        nwstate       <= wIdle;
        axis_tready_o <= '0';
        if (axis_tvalid_i = '1') then
          nwstate     <= wHighByte;
        end if;


      when wHighByte =>
        tdata16hi_we <= '1';
        if (axis_tvalid_i = '1') then
          if (axis_tlast_i = '1') then
            nwstate  <= wLastLowByte;
          else
            nwstate  <= wLowByte;
          end if;
        end if;


      when wLowByte =>
        nwstate   <= wLowByte;
        if (axis_tvalid_i = '1') then
          nwstate <= wHighByte;
          fifo_we <= '1';
          if (axis_tlast_i = '1') then
            tlast <= '1';
            nwstate <= wIdle;
          end if;

        end if;


      when wLastLowByte =>
        tdata16(7 downto 0) <= x"00";
        tlast                <= '1';
        fifo_we              <= '1';
        nwstate              <= wIdle;


    end case;
  end process;


  prc_tdatalo : process (clk)
  begin
    if rising_edge(clk) then
      if (tdata16hi_we = '1') then
        tdata16(15 downto 8) <= axis_tdata_i;
      end if;
    end if;
  end process;



  fifo_din(15 downto 0) <= tdata16;
  fifo_din(16)          <= tlast;       --eof
  fifo_din(17)          <= '0';         --sof


  fifo0 : cg_brfifo_1kx18_ft
    port map (
      clk         => clk,
      srst        => rst,
      din         => fifo_din,
      wr_en       => fifo_we,
      rd_en       => fifo_rd,
      dout        => fifo_dout,
      full        => fifo_full,
      almost_full => fifo_almost_full,
      empty       => fifo_empty,
      data_count  => fifo_data_count
      );


  data_o   <= fifo_dout(15 downto 0);
  fifo_eof <= fifo_dout(16);
  --fifo_sof <= fifo_dout(17);


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

  prc_sm_sync : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') then
        state <= Idle;
      else
        state <= nstate;

      end if;
    end if;
  end process;


  prc_sm_async : process (state, delta_eof, fifo_sof, dst_rdy_i, fifo_eof )
  begin

-- defaults
    nstate        <= Idle;
    src_rdy_o     <= '0';
    sof_o         <= '0';
    eof_o         <= '0';
    fifo_rd       <= '0';
    dec_delta_eof <= '0';

    case state is

      when Idle =>
        nstate <= Idle;

        if (delta_eof /= 0) and (dst_rdy_i = '1') then
          nstate <= SrcRdySOF;
        end if;


      when SrcRdySOF =>
        nstate    <= SrcRdySOF;
        src_rdy_o <= '1';
        sof_o     <= '1';
        if (dst_rdy_i = '1') then
          fifo_rd <= '1';
          nstate  <= WaitEOF;
        end if;

      when WaitEOF =>
        nstate     <= WaitEOF;
        src_rdy_o  <= '1';
        eof_o      <= fifo_eof;
        if (dst_rdy_i = '1') then
          fifo_rd  <= '1';
          if (fifo_eof = '1') then
            nstate <= DecDeltaEOF;      -- no more reads needed now
          end if;
        end if;


      when DecDeltaEOF =>
        dec_delta_eof <= '1';
        nstate        <= Delay;


      when Delay =>                     -- need to wait for the delta_eof to update
        nstate <= Idle;


    end case;

  end process;

end architecture;