library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity FWFT_FIFO is
    generic(
        FIFO_DEPTH  : natural := 2;
        DATA_WIDTH  : natural := 32
    );
    port
    (
        reset               : in std_logic;
        clk                 : in std_logic;
        data_in             : in std_logic_vector(DATA_WIDTH-1 downto 0);
        write               : in std_logic;
        read                : in std_logic;
        data_out            : out std_logic_vector(DATA_WIDTH-1 downto 0);
        empty               : out std_logic;
        full                : out std_logic;
        free                : out natural range 0 to FIFO_DEPTH
    );
end entity;

architecture arch of FWFT_FIFO is
    
    -- *TYPE DECLARATIONS*
    type FIFO_DATA_ARRAY is array (FIFO_DEPTH-1 downto 0) of std_logic_vector(DATA_WIDTH-1 downto 0);

    -- *SIGNAL DECLARATIONS*
    signal fifo_data : FIFO_DATA_ARRAY := (others => (others => '0'));
    signal free_sig : natural range 0 to FIFO_DEPTH := FIFO_DEPTH;
    
begin
    data_out    <= fifo_data(0);

    free    <= free_sig;
    empty   <= '1' when (free_sig = FIFO_DEPTH) else '0';
    full    <= '1' when (free_sig = 0) else '0';
    
    sync : process(clk, reset)
        variable free_var : integer range 0 to FIFO_DEPTH;
    begin
        if(rising_edge(clk)) then
            if(reset = '1') then
                fifo_data   <= (others => (others => '0'));
                free_sig    <= FIFO_DEPTH;
            else
                free_var := free_sig;
                if(read = '1' and free_var < FIFO_DEPTH ) then
                    for i in 1 to (FIFO_DEPTH-1) loop
                        fifo_data(i-1)  <= fifo_data(i);
                    end loop;
                    fifo_data(FIFO_DEPTH-1) <= (others => '0');
                    free_var                := free_var + 1;
                end if;
                if(write = '1') then
                    if(free_var > 0) then
                        fifo_data(FIFO_DEPTH-free_var)  <= data_in;
                        free_var    := free_var - 1;
                    end if;
                end if;
                free_sig <= free_var;
            end if;
        end if;
    end process;
    
end architecture;