--=====================================================
-- Проект 23. Кольцевой буфер FIFO
-- Очередь «первым пришёл — первым вышел» на двух
-- указателях с признаками пустоты и заполнения.
--=====================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity fifo is
    generic (
        ADDR_W : integer := 4;                          -- глубина 2^ADDR_W
        DATA_W : integer := 8
    );
    port (
        clk   : in  std_logic;
        rst   : in  std_logic;
        wr_en : in  std_logic;
        rd_en : in  std_logic;
        din   : in  std_logic_vector(DATA_W-1 downto 0);
        dout  : out std_logic_vector(DATA_W-1 downto 0);
        empty : out std_logic;
        full  : out std_logic
    );
end entity fifo;

architecture rtl of fifo is
    type mem_t is array (0 to 2**ADDR_W - 1) of std_logic_vector(DATA_W-1 downto 0);
    signal mem    : mem_t := (others => (others => '0'));
    signal wr_ptr : unsigned(ADDR_W-1 downto 0) := (others => '0');
    signal rd_ptr : unsigned(ADDR_W-1 downto 0) := (others => '0');
    signal count  : unsigned(ADDR_W downto 0) := (others => '0');   -- на разряд шире
begin
    process (clk, rst)
    begin
        if rst = '1' then
            wr_ptr <= (others => '0');
            rd_ptr <= (others => '0');
            count  <= (others => '0');
        elsif rising_edge(clk) then
            -- запись, если есть место
            if wr_en = '1' and count < 2**ADDR_W then
                mem(to_integer(wr_ptr)) <= din;
                wr_ptr <= wr_ptr + 1;
            end if;

            -- чтение, если есть данные
            if rd_en = '1' and count > 0 then
                dout   <= mem(to_integer(rd_ptr));
                rd_ptr <= rd_ptr + 1;
            end if;

            -- счётчик заполнения меняется только при несовпадении операций
            if wr_en = '1' and count < 2**ADDR_W and not (rd_en = '1' and count > 0) then
                count <= count + 1;
            elsif rd_en = '1' and count > 0 and not (wr_en = '1' and count < 2**ADDR_W) then
                count <= count - 1;
            end if;
        end if;
    end process;

    empty <= '1' when count = 0 else '0';
    full  <= '1' when count = 2**ADDR_W else '0';
end architecture rtl;
