-- $Id: sync_fifo.vhd,v 1.19 2006/11/02 19:13:41 tres Exp $ ------------------------------------------------------------------------------- -- LPM style synch fifo using blockram instead of registers -- For Quartus, Leo, ISE infer dp_ram and counters. -- Standard Libraries, generic address and data size -- M. Treseler -- FIFO sync inputs PUSH, POP activated by '0', '1' sequence. No -- bursting on this version. Could add a burst input to select level -- or edge triggering. Data out is held during pushes and only -- updated after a pop '0', '1' sequence. There is a one tick -- delay from pop rising to valid data_q. -- You cannot read and write the same location simultaneously with -- dual port ram. This is not a problem for a FIFO because when the -- read (head) and write (tail) addresses are the same, the FIFO -- is full or empty and access is blocked. ------------------------------------------------------------------------- -- FIFO operation: read at head, write at tail ------------------------------------------------------------------------- -- head 0123456789 tail Output Status -- init U -- rd^ U empty -- ^--------wr -- push 5: 01234 U -- rd^ ^---wr -- pop 2 : 234 1 -- rd--^ ^---wr -- push 3: 234567 1 -- rd--^ ^wr -- pop 5 7 6 -- rd-------^^wr -- pop 1 7 empty -- rd--------^ -- ^wr ------------------------------------------------------------------------- -- WAVE SIM COMMAND: -- vsim test_sync_fifo \ -- -do "add wave -radix hexadecimal \ -- -r sim:/test_sync_fifo/*;run -all" -- TEXT SIM COMMAND: -- vsim -c test_sync_fifo -do "run -all" ------------------------------------------------------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity sync_fifo is generic (dat_length : natural := 16; add_length : natural := 8 -- 7 for 127x16 8 for 255x16 ); port (clk : in std_ulogic; rst : in std_ulogic; -- powerup init of pointers, flags init : in std_ulogic; -- synch init of pointers, flags -- (but not data) push : in std_ulogic; -- data_i latched on edge after push -- ok to push and pop at same time pop : in std_ulogic; -- hold one tick, read data_q while low data_i : in std_logic_vector(dat_length-1 downto 0); data_q : out std_logic_vector(dat_length-1 downto 0); -- readback full : out std_ulogic; empty : out std_ulogic); end sync_fifo; ------------------------------------------------------------------------------- architecture synth of sync_fifo is constant mem_size : natural := 2**add_length; type mem_type is array (mem_size-1 downto 0) of std_logic_vector(dat_length-1 downto 0); subtype ptr_type is unsigned(add_length-1 downto 0); -- unsigned wraps signal mem : mem_type; signal we : std_ulogic; -- write signal to block ram signal pop_head_ptr : ptr_type; -- both _ptr must init same signal push_tail_ptr : ptr_type; -- each counts up appropriate cycles signal full_ctr : ptr_type; -- inits to FF, -- increment for write, -- decrement for read constant ptr_base : ptr_type := (ptr_type'range => '0'); signal valid : boolean; -- begin ------------------------------------------------------------------------------- -- Leo Infers ram_dq_da_inclock_out_clock_16_8_256 from this -- template. Xilinx Virtex makes a RAM_B4_S16_S16 library XCV. -- Altera uses altsyncram DPRAM_16_8_256_6_0 interface LPM library -- OPERATORS. If you add a reset to the ram_access process, you break -- the template and get lots of registers instead of RAM. ------------------------------------------------------------------------------- ram_access : process (clk) is begin if rising_edge(clk) then if we = '1' then mem(to_integer(push_tail_ptr)) <= (data_i); end if; -- raw address data_q <= mem(to_integer(pop_head_ptr)); -- mem data after pop low end if; end process ram_access; ------------------------------------------------------------------------------- fifo : process (clk, rst) is variable pushed : boolean; -- pushed last time? variable popped : boolean; -- popped last time? variable full_v : std_ulogic; -- full flag variable empty_v : std_ulogic; -- full flag ------------------------------------------------------------------------------- procedure do_flags is begin empty <= empty_v; full <= full_v; end procedure do_flags; ------------------------------------------------------------------------------- procedure init_fifo is -- purpose: Initialize fifo flags and pointers begin full_v := '0'; empty_v := '1'; pop_head_ptr <= ptr_base-1; -- both _ptr must init to same value push_tail_ptr <= ptr_base-1; -- first address is FF++ = 0 full_ctr <= ptr_base; -- must compare base +-1 we <= '0'; pushed := false; popped := false; do_flags; end procedure init_fifo; ------------------------------------------------------------------------------- impure function push_is_ok return boolean is begin return push = '1' and full_v = '0' and not pushed; end function push_is_ok; ------------------------------------------------------------------------------- impure function pop_is_ok return boolean is begin return pop = '1' and empty_v = '0' and not popped; end function pop_is_ok; ------------------------------------------------------------------------------- begin -- process fifo read_write : if rst = '1' then -- asynchronous reset (active high) init_fifo; elsif rising_edge(clk) then sync_init : if init = '1' then init_fifo; else -- we want to push/pop only on a '0' to '1' transition if pop = '0' then popped := false; end if; if push = '0' then pushed := false; end if; -- Write and Read buffer, fullness unchanged, both ptr ++ if push_is_ok and pop_is_ok then we <= '1'; pushed := true; push_tail_ptr <= push_tail_ptr + 1; -- write tail popped := true; pop_head_ptr <= pop_head_ptr + 1; -- read head -- Just write to the tail (hold last reading) -- can't get empty but might get full elsif push_is_ok then we <= '1'; pushed := true; push_tail_ptr <= push_tail_ptr + 1; -- grow tail full_ctr <= full_ctr + 1; -- getting fuller empty_v := '0'; -- just wrote, can't be empty if full_ctr = ptr_base-1 then -- last count was -1 full_v := '1'; -- this upcount will make zero end if; -- Just read from the head -- Can't get full, but might get empty elsif pop_is_ok then we <= '0'; popped := true; pop_head_ptr <= pop_head_ptr + 1; -- bite head full_ctr <= full_ctr - 1; -- getting emptier full_v := '0'; -- just read, can't be full if full_ctr = ptr_base+1 then -- last count was +1 empty_v := '1'; -- this downcount will make zero end if; else we <= '0'; end if; do_flags; -- update full, empty end if sync_init; end if read_write; end process fifo; end architecture synth;