You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
163 lines
4.4 KiB
163 lines
4.4 KiB
-- 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;
|
|
|