--
-- Readout Unit FIFO
--
-- Meant to provide a cut down version of a locallink stylee fifo
-- essentially a normal fifo with start-of-frame and end-of-frame
-- BUT only makes data available to client (asserts src_rdy) when
-- an eof has been received
--
--
--
-- 17/02/2012 - changed to block-ram fifo - for depth reporting
-- 09/05/2012 - removed fall-through from len_fifo and use SM to control read (was sof)
--            - added some more rst processes to FREE up LUTs(!!)
--            - changed len_fifo writes to be directly controlled from deser
-- 22/05/2012 - removed all to do with truncation
-- 31/07/2012 - changed to use llo type, at last.
-- 1/08/2012 - changed to use lls+lld type, inouts make tristate hell



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

library utils;
use utils.pkg_types.all;

entity ro_unit_fifo is
   port(
      -- input interface
      wren_i                : in     std_logic;
      data_i                : in     std_logic_vector (15 downto 0);
      sof_i                 : in     std_logic;
      eof_i                 : in     std_logic;
      data_truncd_i         : in     std_logic;
      data_len_i            : in     std_logic_vector (10 downto 0);
      data_len_wr_i         : in     std_logic;
      -- locallink tx interface
      lls_o                 : out    t_llsrc;
      lld_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;
      data_count_o          : out    std_logic_vector (1 downto 0);
      len_fifo_full_o       : out    std_logic;
      len_fifo_near_full_o  : out    std_logic;
      len_fifo_data_count_o : out    std_logic_vector (1 downto 0);
      -- infrastructure
      en                    : in     std_logic;
      clk                   : in     std_logic;
      rst                   : in     std_logic
   );

-- Declarations

end ro_unit_fifo ;


architecture rtl of ro_unit_fifo is


  component cg_ipfifo_1kx18
    port (
      clk       : in  std_logic;
      rst       : 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;
      overflow  : out std_logic;
      empty     : out std_logic;
      underflow : out std_logic;
      prog_full : out std_logic
      );
  end component;

  component cg_brfifo_1kx18
    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;
      overflow   : out std_logic;
      empty      : out std_logic;
      underflow  : out std_logic;
      data_count : out std_logic_vector(1 downto 0);
      prog_full  : out std_logic
      );
  end component;

  component cg_dfifo_32x12
    port (
      clk         : in  std_logic;
      srst        : in  std_logic;
      din         : in  std_logic_vector(11 downto 0);
      wr_en       : in  std_logic;
      rd_en       : in  std_logic;
      dout        : out std_logic_vector(11 downto 0);
      full        : out std_logic;
      almost_full : out std_logic;
      empty       : out std_logic;
      data_count  : out std_logic_vector(1 downto 0)
      );
  end component;

  component cg_dfifo_16x12
    port (
      clk         : in  std_logic;
      srst        : in  std_logic;
      din         : in  std_logic_vector (11 downto 0);
      wr_en       : in  std_logic;
      rd_en       : in  std_logic;
      dout        : out std_logic_vector (11 downto 0);
      full        : out std_logic;
      almost_full : out std_logic;
      empty       : out std_logic;
      data_count  : out std_logic_vector(1 downto 0)
      );
  end component;


  signal fifo_empty  : 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_pre_rd : std_logic;
  signal fifo_wr     : std_logic;

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


  constant EOFIN_DELAY : integer := 2;  -- *** was 7 (just a big number for now
                                        -- now smaller for better utilisation

  signal eofin   : std_logic;
  signal eofin_q : std_logic_vector(EOFIN_DELAY downto 0) := (others => '0');

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



  type states is (
    FlufferSOF,
                  WaitStateSOF,
                  OpcodeSOF,
                  SeqID,
                  Len,
                  WaitEOF,
                  DecDeltaEOF,
                  Delay,
                  Idle
                  );

  signal state, nstate : states;

  signal len_fifo_din         : std_logic_vector(11 downto 0);
  signal len_fifo_rd          : std_logic;
  signal len_fifo_wr          : std_logic;
  signal len_fifo_dout        : std_logic_vector(11 downto 0);
  signal len_fifo_data        : std_logic_vector(10 downto 0);
--  signal len_fifo_data_truncd : std_logic;

  signal insert_length       : std_logic;
--  signal insert_error_opcode : std_logic;


begin


--------------------------------------------------
-- EOF manager - sync to 40/80 and count in vs. out
--------------------------------------------------


-- by pipelining the write section we ensure there
-- is enough time for the len fifo to propagate data

  eofin <= (eof_i and wren_i);

  prc_eofin_q : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') then
        eofin_q <= (others => '0');
      else
        eofin_q <= eofin_q(EOFIN_DELAY-1 downto 0) & eofin;
      end if;
    end if;
  end process;


  eof_coded <= (eofin_q(EOFIN_DELAY) & 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;

  --delta_eof_o   <= conv_std_logic_vector(delta_eof, 8);

------------------------------------------------------------------
-- fifo in, locallink out
------------------------------------------------------------------

  -- datasheet says don't toggle wr during rst
  fifo_wr <= wren_i;                    -- and not(rst); *** this simply shouldn't happen ;-)



  fifo0 : cg_brfifo_1kx18
    port map (
      clk        => clk,
      srst       => rst,
      din        => fifo_din,
      wr_en      => fifo_wr,
      rd_en      => fifo_rd,
      dout       => fifo_dout,
      full       => full_o,
      overflow   => overflow_o,
      empty      => fifo_empty,
      underflow  => underflow_o,
      data_count => data_count_o,
      prog_full  => near_full_o
      );


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

  lls_o.data <= --x"E" & fifo_dout(11 downto 0) when (insert_error_opcode = '1') else
            ("00000" & len_fifo_data)     when (insert_length = '1')       else
            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, lld_i, fifo_eof, en)--, len_fifo_data_truncd)
  begin

    -- defaults
    nstate              <= Idle;
    lls_o.src_rdy           <= '0';
    lls_o.sof               <= '0';
    lls_o.eof               <= '0';
    fifo_rd             <= '0';
    insert_length       <= '0';
    dec_delta_eof       <= '0';
--    insert_error_opcode <= '0';
    len_fifo_rd         <= '0';

    case state is

      when Idle =>
        nstate   <= Idle;
        if (delta_eof > 0) and (en = '1') then
          nstate <= FlufferSOF;
        end if;


      when FlufferSOF =>
        nstate      <= FlufferSOF;
        fifo_rd     <= '1';
        if (fifo_sof = '1') then
          fifo_rd   <= '0';
          lls_o.src_rdy <= '1';
          if (lld_i = '1') then
            nstate  <= WaitStateSOF;
          end if;
        end if;


      when WaitStateSOF =>
        lls_o.src_rdy   <= '1';
        len_fifo_rd <= '1';
        nstate      <= OpcodeSOF;


      when OpcodeSOF =>
        nstate                  <= OpcodeSOF;
        lls_o.src_rdy               <= '1';
        lls_o.sof                   <= '1';
        if (lld_i = '1') then
          fifo_rd               <= '1';
--          if (len_fifo_data_truncd = '1') then
--            insert_error_opcode <= '1';
--          end if;
          nstate                <= SeqID;
        end if;


      when SeqID =>
        nstate    <= SeqID;
        lls_o.src_rdy <= '1';
        if (lld_i = '1') then
          fifo_rd <= '1';
          nstate  <= Len;
        end if;


      when Len =>
        nstate        <= Len;
        lls_o.src_rdy     <= '1';
        insert_length <= '1';
        if (lld_i = '1') then
          fifo_rd     <= '1';
          nstate      <= WaitEOF;
        end if;


      when WaitEOF =>
        nstate      <= WaitEOF;
        lls_o.src_rdy   <= '1';
        lls_o.eof       <= fifo_eof;
        if (lld_i = '1') then
          fifo_rd   <= '1';
          if (fifo_eof = '1') then
            fifo_rd <= '0';
            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;



------------------------------------------------------------
-- store lengths in parallel with data
-------------------------------------------------------------

  len_fifo_wr  <= data_len_wr_i;
  --len_fifo_din <= data_truncd_i & data_len_i;
  len_fifo_din <= '0' & data_len_i;


  inst_len_fifo : cg_dfifo_16x12
    port map (
      clk         => clk,
      srst        => rst,
      din         => len_fifo_din,
      wr_en       => len_fifo_wr,
      rd_en       => len_fifo_rd,
      dout        => len_fifo_dout,
      full        => len_fifo_full_o,
      empty       => open,
      data_count  => len_fifo_data_count_o,
      almost_full => len_fifo_near_full_o
      );


  --len_fifo_data_truncd <= len_fifo_dout(11);
  len_fifo_data        <= len_fifo_dout(10 downto 0);

end architecture;