You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			164 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			VHDL
		
	
			
		
		
	
	
			164 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			VHDL
		
	
-- Synchronous FIFO with a protocol similar to AXI
 | 
						|
--
 | 
						|
-- The outputs are generated combinationally from the inputs
 | 
						|
-- in order to allow for back-to-back transfers with the type
 | 
						|
-- of flow control used by busses lite AXI, pipelined WB or
 | 
						|
-- LiteDRAM native port when the FIFO is full.
 | 
						|
--
 | 
						|
-- That means that care needs to be taken by the user not to
 | 
						|
-- generate the inputs combinationally from the outputs otherwise
 | 
						|
-- it would create a logic loop.
 | 
						|
--
 | 
						|
-- If breaking that loop is required, a stash buffer could be
 | 
						|
-- added to break the flow control "loop" between the read and
 | 
						|
-- the write port.
 | 
						|
--
 | 
						|
library ieee;
 | 
						|
use ieee.std_logic_1164.all;
 | 
						|
 | 
						|
library work;
 | 
						|
use work.utils.all;
 | 
						|
 | 
						|
entity sync_fifo is
 | 
						|
    generic(
 | 
						|
        -- Fifo depth in entries
 | 
						|
        DEPTH     : natural := 64;
 | 
						|
 | 
						|
        -- Fifo width in bits
 | 
						|
        WIDTH     : natural := 32;
 | 
						|
 | 
						|
        -- When INIT_ZERO is set, the memory is pre-initialized to 0's
 | 
						|
        INIT_ZERO : boolean := false
 | 
						|
        );
 | 
						|
    port(
 | 
						|
        -- Control lines:
 | 
						|
        clk      : in std_ulogic;
 | 
						|
        reset    : in std_ulogic;
 | 
						|
 | 
						|
        -- Write port
 | 
						|
        wr_ready : out std_ulogic;
 | 
						|
        wr_valid : in std_ulogic;
 | 
						|
        wr_data  : in std_ulogic_vector(WIDTH - 1 downto 0);
 | 
						|
 | 
						|
        -- Read port
 | 
						|
        rd_ready : in std_ulogic;
 | 
						|
        rd_valid : out std_ulogic;
 | 
						|
        rd_data  : out std_ulogic_vector(WIDTH - 1 downto 0)
 | 
						|
        );
 | 
						|
end entity sync_fifo;
 | 
						|
 | 
						|
architecture behaviour of sync_fifo is
 | 
						|
 | 
						|
    subtype data_t is std_ulogic_vector(WIDTH - 1 downto 0);    
 | 
						|
    type memory_t is array(0 to DEPTH - 1) of data_t;
 | 
						|
 | 
						|
    function init_mem return memory_t is
 | 
						|
        variable m : memory_t;
 | 
						|
    begin
 | 
						|
        if INIT_ZERO then
 | 
						|
            for i in 0 to DEPTH - 1 loop
 | 
						|
                m(i) := (others => '0');
 | 
						|
            end loop;
 | 
						|
        end if;
 | 
						|
        return m;
 | 
						|
    end function;
 | 
						|
 | 
						|
    signal memory : memory_t := init_mem;
 | 
						|
 | 
						|
    subtype index_t is integer range 0 to DEPTH - 1;
 | 
						|
    signal rd_idx  : index_t;
 | 
						|
    signal rd_next : index_t;
 | 
						|
    signal wr_idx  : index_t;
 | 
						|
    signal wr_next : index_t;
 | 
						|
 | 
						|
    function next_index(idx : index_t) return index_t is
 | 
						|
        variable r : index_t;
 | 
						|
    begin
 | 
						|
        if ispow2(DEPTH) then
 | 
						|
            r := (idx + 1) mod DEPTH;
 | 
						|
        else
 | 
						|
            r := idx + 1;
 | 
						|
            if r = DEPTH then
 | 
						|
                r := 0;
 | 
						|
            end if;
 | 
						|
        end if;
 | 
						|
        return r;
 | 
						|
    end function;
 | 
						|
    
 | 
						|
    type op_t is (OP_POP, OP_PUSH);
 | 
						|
    signal op_prev : op_t := OP_POP;
 | 
						|
    signal op_next : op_t;
 | 
						|
 | 
						|
    signal full, empty : std_ulogic;
 | 
						|
    signal push, pop   : std_ulogic;
 | 
						|
begin
 | 
						|
 | 
						|
    -- Current state at last clock edge
 | 
						|
    empty <= '1' when rd_idx = wr_idx and op_prev = OP_POP  else '0';
 | 
						|
    full  <= '1' when rd_idx = wr_idx and op_prev = OP_PUSH else '0';
 | 
						|
 | 
						|
    -- We can accept new data if we aren't full or we are but
 | 
						|
    -- the read port is going to accept data this cycle    
 | 
						|
    wr_ready <= rd_ready or not full;
 | 
						|
 | 
						|
    -- We can provide data if we aren't empty or we are but
 | 
						|
    -- the write port is going to provide data this cycle
 | 
						|
    rd_valid <= wr_valid or not empty;
 | 
						|
 | 
						|
    -- Internal control signals
 | 
						|
    push <= wr_ready and wr_valid;
 | 
						|
    pop  <= rd_ready and rd_valid;
 | 
						|
 | 
						|
    -- Next state
 | 
						|
    rd_next <= next_index(rd_idx) when pop  = '1' else rd_idx;
 | 
						|
    wr_next <= next_index(wr_idx) when push = '1' else wr_idx;
 | 
						|
    with push & pop select op_next <=
 | 
						|
        OP_PUSH when "10",
 | 
						|
        OP_POP  when "01",
 | 
						|
        op_prev when others;
 | 
						|
 | 
						|
    -- Read port output
 | 
						|
    rd_data <= memory(rd_idx) when empty = '0' else wr_data;
 | 
						|
 | 
						|
    -- Read counter
 | 
						|
    reader: process(clk)
 | 
						|
    begin
 | 
						|
        if rising_edge(clk) then
 | 
						|
            if reset = '1' then
 | 
						|
                rd_idx <= 0;
 | 
						|
            else
 | 
						|
                rd_idx <= rd_next;
 | 
						|
            end if;
 | 
						|
        end if;
 | 
						|
    end process;
 | 
						|
 | 
						|
    -- Write counter and memory write
 | 
						|
    producer: process(clk)
 | 
						|
    begin
 | 
						|
        if rising_edge(clk) then
 | 
						|
            if reset = '1' then
 | 
						|
                wr_idx <= 0;
 | 
						|
            else
 | 
						|
                wr_idx <= wr_next;
 | 
						|
 | 
						|
                if push = '1' then
 | 
						|
                    memory(wr_idx) <= wr_data;
 | 
						|
                end if;
 | 
						|
            end if;
 | 
						|
        end if;
 | 
						|
    end process;
 | 
						|
 | 
						|
    -- Previous op latch used for generating empty/full
 | 
						|
    op: process(clk)
 | 
						|
    begin
 | 
						|
        if rising_edge(clk) then
 | 
						|
            if reset = '1' then
 | 
						|
                op_prev <= OP_POP;
 | 
						|
            else
 | 
						|
                op_prev <= op_next;
 | 
						|
            end if;
 | 
						|
        end if;
 | 
						|
    end process;
 | 
						|
 | 
						|
end architecture behaviour;
 |