library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; -- First-word fall-through (FWFT) FIFO. -- dout always presents the current head combinatorially (no read latency). -- rd_en advances the read pointer; the next head is visible on the same cycle. -- DEPTH must be a power of 2; ADDR_BITS = log2(DEPTH). entity fifo is generic ( DEPTH : positive := 16; ADDR_BITS : positive := 4; WIDTH : positive := 8 ); port ( clk : in std_logic; wr_en : in std_logic; rd_en : in std_logic; din : in std_logic_vector(WIDTH - 1 downto 0); dout : out std_logic_vector(WIDTH - 1 downto 0); full : out std_logic; empty : out std_logic ); end entity fifo; architecture rtl of fifo is -- MSB pointer trick: pointers are ADDR_BITS+1 wide. -- Empty: read_ptr = write_ptr -- Full: (read_ptr XOR write_ptr) = DEPTH (MSBs differ, lower bits equal) signal read_ptr : unsigned(ADDR_BITS downto 0) := (others => '0'); signal write_ptr : unsigned(ADDR_BITS downto 0) := (others => '0'); type mem_t is array (0 to DEPTH - 1) of std_logic_vector(WIDTH - 1 downto 0); signal mem : mem_t := (others => (others => '0')); signal empty_i : std_logic; signal full_i : std_logic; begin empty_i <= '1' when read_ptr = write_ptr else '0'; full_i <= '1' when (read_ptr xor write_ptr) = to_unsigned(DEPTH, ADDR_BITS + 1) else '0'; empty <= empty_i; full <= full_i; -- FWFT: head is always visible without asserting rd_en dout <= mem(to_integer(read_ptr(ADDR_BITS - 1 downto 0))); enqueue : process(clk) begin if rising_edge(clk) then if wr_en = '1' and full_i = '0' then mem(to_integer(write_ptr(ADDR_BITS - 1 downto 0))) <= din; write_ptr <= write_ptr + 1; end if; end if; end process enqueue; dequeue : process(clk) begin if rising_edge(clk) then if rd_en = '1' and empty_i = '0' then read_ptr <= read_ptr + 1; end if; end if; end process dequeue; end architecture rtl;