123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- -- 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;
|