– Pong Chu FIFO

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

entity fifo is

generic(
  B : natural := 8;                   -- number of bits
  W : natural := 4                    -- number of address bits
  );
port(
  clk, reset  : in  std_logic;
  rd, wr      : in  std_logic;
  w_data      : in  std_logic_vector (B-1 downto 0);
  empty, full : out std_logic;
  r_data      : out std_logic_vector (B-1 downto 0)
  );

end fifo;

architecture arch of fifo is

type reg_file_type is array (2**W-1 downto 0) of std_logic_vector(B-1 downto 0);
signal array_reg                                  : reg_file_type;
signal w_ptr_reg, w_ptr_next, w_ptr_succ          : std_logic_vector(W-1 downto 0);
signal r_ptr_reg, r_ptr_next, r_ptr_succ          : std_logic_vector(W-1 downto 0);
signal full_reg, empty_reg, full_next, empty_next : std_logic;
signal wr_op                                      : std_logic_vector(1 downto 0);
signal wr_en                                      : std_logic;

begin

--=================================================
-- register file
--=================================================
process(clk, reset)
begin
  if (reset = '1') then
    array_reg <= (others => (others => '0'));
  elsif (clk'event and clk = '1') then
    if wr_en = '1' then
      array_reg(to_integer(unsigned(w_ptr_reg)))
        <= w_data;
    end if;
  end if;
end process;

-- read port
r_data <= array_reg(to_integer(unsigned(r_ptr_reg)));
-- write enabled only when FIFO is not full
wr_en  <= wr and (not full_reg);

--=================================================
-- fifo control logic
--=================================================
-- register for read and write pointers
process(clk, reset)
begin
  if (reset = '1') then
    w_ptr_reg <= (others => '0');
    r_ptr_reg <= (others => '0');
    full_reg  <= '0';
    empty_reg <= '1';
  elsif (clk'event and clk = '1') then
    w_ptr_reg <= w_ptr_next;
    r_ptr_reg <= r_ptr_next;
    full_reg  <= full_next;
    empty_reg <= empty_next;
  end if;
end process;

-- successive pointer values
w_ptr_succ <= std_logic_vector(unsigned(w_ptr_reg)+1);
r_ptr_succ <= std_logic_vector(unsigned(r_ptr_reg)+1);

-- next-state logic for read and write pointers
wr_op <= wr & rd;
process(w_ptr_reg, w_ptr_succ, r_ptr_reg, r_ptr_succ, wr_op,
        empty_reg, full_reg)
begin
  w_ptr_next <= w_ptr_reg;
  r_ptr_next <= r_ptr_reg;
  full_next  <= full_reg;
  empty_next <= empty_reg;
  case wr_op is
    when "00" =>                      -- no op
    when "01" =>                      -- read
      if (empty_reg /= '1') then      -- not empty
        r_ptr_next <= r_ptr_succ;
        full_next  <= '0';
        if (r_ptr_succ = w_ptr_reg) then
          empty_next <= '1';
        end if;
      end if;
    when "10" =>                      -- write
      if (full_reg /= '1') then       -- not full
        w_ptr_next <= w_ptr_succ;
        empty_next <= '0';
        if (w_ptr_succ = r_ptr_reg) then
          full_next <= '1';
        end if;
      end if;
    when others =>                    -- write/read;
      w_ptr_next <= w_ptr_succ;
      r_ptr_next <= r_ptr_succ;
  end case;
end process;

-- output
full  <= full_reg;
empty <= empty_reg;

end arch;