-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- Copyright 2025 VLSI Solution Oy
-- SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
--
-- Licensed under the Solderpad Hardware License v 2.1 (the "License");
-- you may not use this file except in compliance with the License, or,
-- at your option, the Apache License version 2.0.
-- You may obtain a copy of the License at
--
--    https://solderpad.org/licenses/SHL-2.1/
--
-- Unless required by applicable law or agreed to in writing, any work
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
--  uart_rfifo.vhd
--  V1.1
--
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.uart_pkg.all;


entity  uart_rfifo is
  generic ( FIFO_WIDTH  : natural range 1 to 32 := 11;           -- FIFO size
            FIFO_DEPTH  : natural range 1 to 32 := 16;
            FIFO_PNTR_W : natural range 1 to 32 := 4 );
  port ( clk_i           : in  std_logic;
         xrst_i          : in  std_logic;
         -- control / data signals
         push_i          : in  std_logic;  -- push to fifo, active high
         pop_i           : in  std_logic;  -- pop from fifo, active high
         data_i          : in  slv_t(FIFO_WIDTH-1 downto 0);
         data_o          : out slv_t(FIFO_WIDTH-1 downto 0);
         -- status signals
         fifo_reset_i    : in  std_logic;
         reset_status_i  : in  std_logic;
         overrun_o       : out std_logic;
         count_o         : out slv_t(FIFO_PNTR_W+1-1 downto 0);  -- + 1 to pntr
         error_bit_o     : out std_logic );
end entity uart_rfifo;


architecture rtl of uart_rfifo is


  --  dual-port RAM for fifo buffer with asynchronous read
  type rfile_t is array (FIFO_DEPTH-1 downto 0) of
    slv_t(FIFO_WIDTH-3-1 downto 0);
  signal rfile_q : rfile_t;

  --  flags FIFO
  type ffifo_t is array (FIFO_DEPTH-1 downto 0) of slv_02_00;
  signal flagfifo_q   : ffifo_t;

  signal data8_out_w  : slv_t(7 downto 0);

  --  FIFO pointers
  signal top_q        : slv_t(FIFO_PNTR_W-1 downto 0);
  signal bottom_q     : slv_t(FIFO_PNTR_W-1 downto 0);

  signal top_plus_1_w : slv_t(FIFO_PNTR_W-1 downto 0);
  signal count_q      : slv_t(FIFO_PNTR_W+1-1 downto 0);         -- + 1 to pntr

begin

  count_o       <=  count_q;

  top_plus_1_w  <= inc1u(top_q);                                 -- + 1


  -- synchronous FIFO
  p_rxfifo : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        rfile_q                  <= (others => (others => '0'));
      elsif (push_i = '1') then
        rfile_q(to_uint(top_q))  <= data_i(FIFO_WIDTH-1 downto FIFO_WIDTH-8);
      end if;
    end if;
  end process p_rxfifo;


  data8_out_w  <= rfile_q(to_uint(bottom_q));

  -- data_o is only valid one clock after pop signal
  data_o       <= data8_out_w & flagfifo_q(to_uint(bottom_q));


  p_fctrl : process (clk_i)
    variable push_pop_v : slv_t(1 downto 0);
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        top_q      <= (others => '0');
        bottom_q   <= (others => '0');
        count_q    <= (others => '0');
        flagfifo_q <= (others => (others => '0'));
        push_pop_v := "00";
      elsif (fifo_reset_i = '1') then
        top_q      <= (others => '0');
        bottom_q   <= (others => '0');
        count_q    <= (others => '0');
        flagfifo_q <= (others => (others => '0'));
        push_pop_v := "00";
      else
        push_pop_v := push_i & pop_i;
        -- case unsigned'(push_i & pop_i) is
        case push_pop_v is
          when "10" =>
            if (unsigned(count_q) < FIFO_DEPTH) then              -- overrun
              flagfifo_q(to_uint(top_q))<= data_i(2 downto 0);
              top_q                     <= top_plus_1_w;
              count_q                   <= inc1u(count_q);        -- + 1
            end if;
          when "01" =>
            if (unsigned(count_q) > 0) then
              flagfifo_q(to_uint(bottom_q)) <= (others => '0');
              bottom_q                      <= inc1u(bottom_q);   -- + 1
              count_q                       <= dec1u(count_q);    -- - 1
            end if;
          when "11" =>
            flagfifo_q(to_uint(top_q)) <= data_i(2 downto 0);
            bottom_q                   <= inc1u(bottom_q);        -- + 1
            top_q                      <= top_plus_1_w;
          when others =>
            null;
        end case;
      end if;
    end if;
  end process p_fctrl;


  p_orun : process (clk_i)
    variable push_pop_v : slv_01_00;
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        overrun_o     <= '0';
      else
        if(fifo_reset_i = '1' or reset_status_i = '1') then
          overrun_o   <= '0';
        elsif (push_i = '1' and pop_i = '0' and
               (unsigned(count_q) = FIFO_DEPTH)) then
          overrun_o   <= '1';
        end if;
      end if;
    end if;
  end process p_orun;


  --  logic for detection of error conditions (parity and framing)
  --  inside the FIFO for the Line Status Register bit 7
  --  a '1' is returned if any of the error bits in the fifo is high
  p_err : process (flagfifo_q)
    variable tmp_v : slv_t(flagfifo_q(0)'range);  -- 2:0
  begin
    tmp_v := (others => '0');
    for ii in flagfifo_q'range loop
      tmp_v := tmp_v or flagfifo_q(ii);
    end loop;
    --
    error_bit_o <= '0';
    if (unsigned(tmp_v) /= 0) then
      error_bit_o <= '1';
    end if;
  end process p_err;

end rtl;
