--
-- 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_16_axi8 is
  port(
    -- output interface (LL 16b)
    data_i    : in  std_logic_vector (15 downto 0);
    sof_i     : in  std_logic;
    eof_i     : in  std_logic;
    dst_rdy_o : out std_logic;
    src_rdy_i : in  std_logic;

    -- output interface (AXI-S 8b)
    axis_tdata_o  : out std_logic_vector(7 downto 0);
    axis_tvalid_o : out std_logic;
    axis_tlast_o  : out std_logic;
    axis_tready_i : in  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_16_axi8;


architecture rtl of ll_pkt_fifo_16_axi8 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;

  component cg_brfifo_2kx18_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 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_half_full   : std_logic;
  --signal fifo_almost_full : std_logic;
  signal fifo_data_count  : std_logic_vector(3 downto 0);

  signal fifo_dout_eof    : std_logic;

  --signal src_fifo_rdy : std_logic;
  signal dst_rdy : 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 (--wStart,
                   wWaitEOF, wIPG,
                   wIdle
                   );

  signal wstate, nwstate : wstates;

  type states is (LowByte, HighByte,
                  DecDeltaEOF, Delay,
                  Idle
                  );

  signal state, nstate : states;


begin


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

  eofin     <= src_rdy_i and eof_i and dst_rdy;
  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;

  --src_fifo_rdy <= src_rdy_i and not(fifo_almost_full);
  --dst_rdy <= not(fifo_almost_full) and not(rst);
  dst_rdy_o <= dst_rdy;


  prc_sm_wr_async : process (wstate, fifo_empty, sof_i, eof_i)
  begin

    -- defaults
    fifo_we <= '0';
    dst_rdy <= '0';

    case wstate is

      when wIdle =>
        nwstate     <= wIdle;
        if (fifo_half_full = '1') then  -- only go if there is room for a whole packet
          dst_rdy <= '0';
        else
          dst_rdy <= '1';
          if (src_rdy_i = '1') and (sof_i = '1') then
            fifo_we <= '1';
            nwstate <= wWaitEOF;
          end if;
        end if;


--       when wIdle =>
--         nwstate     <= wIdle;
--         dst_rdy <= '1';
--         if (src_rdy_i = '1') and (fifo_half_full = '0') then  -- only go if there is room for a whole packet
--           --dst_rdy <= '0';  -- we should not need this!
--           nwstate <= wStart;
--         end if;


--       when wStart =>
--         nwstate     <= wStart;
--         dst_rdy <= '1';
--         if (src_rdy_i = '1') then
--           if (sof_i = '1') then
--             fifo_we <= '1';
--             nwstate <= wWaitEOF;
--           end if;
--         end if;



      when wWaitEOF =>
        nwstate     <= wWaitEOF;
        dst_rdy <= '1';
        if (src_rdy_i = '1') then
          fifo_we   <= '1';
          if (eof_i = '1') then
            nwstate <= wIPG;
          end if;
        end if;


      when wIPG =>
        nwstate     <= wIdle;


    end case;
  end process;


  fifo_din(15 downto 0) <= data_i;
  fifo_din(16)          <= eof_i;
  fifo_din(17)          <= sof_i;


  fifo0 : cg_brfifo_2kx18_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
      );


  fifo_dout_eof   <= fifo_dout(16);
  -- <= fifo_dout(17);                  --unused, was sof

  fifo_half_full <= fifo_data_count(1);


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

  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, axis_tready_i, fifo_dout_eof, fifo_dout )
  begin

-- defaults
    nstate        <= Idle;
    axis_tdata_o  <= fifo_dout(7 downto 0);
    axis_tlast_o  <= '0';
    axis_tvalid_o <= '0';
    fifo_rd       <= '0';
    dec_delta_eof <= '0';

    case state is

      when Idle =>
        nstate        <= Idle;
        if (delta_eof > 0) then
          nstate      <= HighByte;
        end if;


      when HighByte =>
        nstate       <= HighByte;
        axis_tvalid_o <= '1';
        axis_tdata_o <= fifo_dout(15 downto 8);
        if (axis_tready_i = '1') then
          nstate   <= LowByte;
        end if;

      when LowByte =>
        nstate   <= LowByte;
        axis_tvalid_o <= '1';
        axis_tdata_o <= fifo_dout(7 downto 0);
        axis_tlast_o <= fifo_dout_eof;

        if (axis_tready_i = '1') then
          nstate <= HighByte;
          fifo_rd    <= '1';
          if (fifo_dout_eof = '1') then
            nstate   <= DecDeltaEOF;
          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;