mor1kx-bemicrocv/ip/intercon/rtl/fifo.vhd

151 lines
4.5 KiB
VHDL

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.rwp_ram_package.all;
entity fifo is
generic(
sync_depth : natural := 3;
gray_code : boolean := true;
addr_width : natural := 4;
data_width : natural := 32);
port(
rst : in std_logic;
-- write port, only set w_en when w_rdy
w_clk : in std_logic;
w_rdy : out std_logic;
w_en : in std_logic;
w_data : in std_logic_vector(data_width-1 downto 0);
-- (pre)alloc port, can be unused
a_clk : in std_logic;
a_rdy : out std_logic;
a_en : in std_logic;
-- read port, only set r_en when r_rdy
-- data is valid the cycle after r_en raised
r_clk : in std_logic;
r_rdy : out std_logic;
r_en : in std_logic;
r_data : out std_logic_vector(data_width-1 downto 0));
end fifo;
architecture rtl of fifo is
subtype counter is unsigned(addr_width downto 0);
type counter_shift is array(sync_depth downto 0) of counter;
signal r_idx_bnry : counter;
signal r_idx_gray : counter;
signal w_idx_bnry : counter;
signal w_idx_gray : counter;
signal a_idx_bnry : counter;
signal a_idx_gray : counter;
signal r_idx_shift_w : counter_shift; -- r_idx_gray in w_clk
signal r_idx_shift_a : counter_shift; -- r_idx_gray in a_clk
signal w_idx_shift_r : counter_shift; -- w_idx_gray in r_clk
function bin2gray(a : unsigned) return unsigned is
variable o : unsigned(a'length downto 0);
begin
if gray_code then
o := (a & '0') xor ('0' & a);
else
o := (a & '0');
end if;
return o(a'length downto 1);
end bin2gray;
function index(a : counter) return std_logic_vector is
begin
return std_logic_vector(a(addr_width-1 downto 0));
end index;
function empty(a, b : counter) return std_logic is
begin
if a = b then
return '1';
else
return '0';
end if;
end empty;
function full(a, b : counter) return std_logic is
variable mask : counter := (others => '0');
begin
-- In binary a full FIFO has indexes (a XOR 1000...00) = b
-- bin2gray is a linear function, thus:
-- a XOR 1000..00 = b iff
-- bin2gray(a XOR 1000...00) = bin2gray(b) iff
-- bin2gray(a) XOR bin2gray(1000...00) = bin2gray(b) iif
-- bin2gray(a) XOR 1100..00 = bin2gray(b)
mask(addr_width) := '1';
mask := bin2gray(mask);
if (a xor mask) = b then
return '1';
else
return '0';
end if;
end full;
begin
ram : rwp_ram
generic map(addr_width => addr_width, data_width => data_width)
port map(w_clk => w_clk, w_en => w_en, w_addr => index(w_idx_bnry), w_data => w_data,
r_clk => r_clk, r_en => r_en, r_addr => index(r_idx_bnry), r_data => r_data);
read : process(r_clk)
variable idx : counter;
begin
if rising_edge(r_clk) then
if rst = '1' then
idx := (others => '0');
elsif r_en = '1' then
idx := r_idx_bnry + 1;
else
idx := r_idx_bnry;
end if;
r_idx_bnry <= idx;
r_idx_gray <= bin2gray(idx);
w_idx_shift_r(sync_depth downto 1) <= w_idx_shift_r(sync_depth-1 downto 0);
end if;
end process;
w_idx_shift_r(0) <= w_idx_gray;
r_rdy <= not empty(r_idx_gray, w_idx_shift_r(sync_depth));
write : process(w_clk)
variable idx : counter;
begin
if rising_edge(w_clk) then
if rst = '1' then
idx := (others => '0');
elsif w_en = '1' then
idx := w_idx_bnry + 1;
else
idx := w_idx_bnry;
end if;
w_idx_bnry <= idx;
w_idx_gray <= bin2gray(idx);
r_idx_shift_w(sync_depth downto 1) <= r_idx_shift_w(sync_depth-1 downto 0);
end if;
end process;
r_idx_shift_w(0) <= r_idx_gray;
w_rdy <= not full(w_idx_gray, r_idx_shift_w(sync_depth));
alloc : process(a_clk)
variable idx : counter;
begin
if rising_edge(a_clk) then
if rst = '1' then
idx := (others => '0');
elsif a_en = '1' then
idx := a_idx_bnry + 1;
else
idx := a_idx_bnry;
end if;
a_idx_bnry <= idx;
a_idx_gray <= bin2gray(idx);
r_idx_shift_a(sync_depth downto 1) <= r_idx_shift_a(sync_depth-1 downto 0);
end if;
end process;
r_idx_shift_a(0) <= r_idx_gray;
a_rdy <= not full(a_idx_gray, r_idx_shift_a(sync_depth));
end rtl;