-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- 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_tfifo.vhd
--  V1.1
--
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

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


entity uart_tfifo is
  generic ( FIFO_WIDTH  : natural range 1 to 32 := 8;
            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 signals
         push_i          : in  std_logic;  -- push to fifo, active high
         pop_i           : in  std_logic;  -- pop from fifo, active high
         --  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);
         --
         data_i          : in  slv_t(FIFO_WIDTH-1 downto 0);
         data_o          : out slv_t(FIFO_WIDTH-1 downto 0) );
end entity uart_tfifo;


architecture rtl of uart_tfifo 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-1 downto 0);
  signal rfile_q : rfile_t;

  --  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(count_o'range);

begin

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

  count_o <= count_q;

  p_txfifo : 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;
      end if;
    end if;
  end process p_txfifo;

  data_o  <= rfile_q(to_uint(bottom_q));


  p_fctrl : process (clk_i)
    variable push_pop_v : slv_01_00;
  begin
    if (rising_edge(clk_i)) then                          -- synchronous FIFO
      if (xrst_i = '0') then
        top_q      <= (others => '0');
        bottom_q   <= (others => '0');
        count_q    <= (others => '0');
        push_pop_v := "00";
      elsif (fifo_reset_i = '1') then
        top_q      <= (others => '0');
        bottom_q   <= (others => '0');
        count_q    <= (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 condition
              top_q     <= top_plus_1_w;
              count_q   <= inc1u(count_q);                -- + 1
            end if;
          when "01" =>
            if (unsigned(count_q) > 0) then
              bottom_q <= inc1u(bottom_q);                -- + 1
              count_q  <= dec1u(count_q);                 -- - 1
            end if;
          when "11" =>
            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                          -- synchronous FIFO
      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 (unsigned(count_q) = FIFO_DEPTH)) then
          overrun_o   <= '1';
        end if;
      end if;
    end if;
  end process p_orun;


end rtl;
