-------------------------------------------------------------------------
-- Project: FIFO, generic FIFOs written in VHDL
-- Release 1
--
-- Copyright (C) 2007 J. Ahrensfeld
--
-- This library is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public
-- License as published by the Free Software Foundation; either
-- version 2.1 of the License, or (at your option) any later version.
--
-- This library is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-- Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public
-- License along with this library; if not, write to the Free Software
-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
--
-- For questions and ideas, please contact the author at jens@jayfield.org
--
-----------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.ALL;

entity fifo_sync_ctrl is
	Generic
	(
		addr_width : integer := 3;
		almost_full_thresh : integer := 6;
		almost_empty_thresh : integer := 2
	);
	Port
	(
		rst				: in  STD_LOGIC;
		clk				: in  STD_LOGIC;
		we				: in  STD_LOGIC;
		re				: in  STD_LOGIC;
		ptr_w			: out unsigned (addr_width-1 downto 0);
		ptr_r			: out unsigned (addr_width-1 downto 0);
		fifo_pre_full	: out STD_LOGIC;
		fifo_pre_empty	: out STD_LOGIC;
		fifo_full		: out STD_LOGIC;
		fifo_empty		: out STD_LOGIC;
		fifo_afull		: out STD_LOGIC;
		fifo_aempty		: out STD_LOGIC
	);
end fifo_sync_ctrl;

architecture Behavioral of fifo_sync_ctrl is

	signal pW, pW_cnt, pW_next : unsigned (addr_width-1 downto 0);
	signal pR, pR_cnt, pR_next : unsigned (addr_width-1 downto 0);
	signal empty, full : std_logic;
	signal pre_empty, pre_full : std_logic;
	signal was_write : std_logic;
	signal write, read : std_logic;
	signal diff : unsigned (addr_width downto 0);
	
begin

	read	<= re and not empty;
	write	<= we and not full;

	fifo_full		<= full;
	fifo_empty		<= empty;
	fifo_pre_full	<= pre_full;
	fifo_pre_empty	<= pre_empty;


proc_diff_gen:
	process(rst, clk)
	begin
		if rst = '1' then
			diff <= (others => '0');
		elsif rising_edge(clk) then
			if write = '1' and read = '0' then
				diff <= diff + 1;
			elsif write = '0' and read = '1' then
				diff <= diff - 1;
			end if;
		end if;
	end process;

proc_status_almost_full:
	process(rst, clk)
	begin
		if rst = '1' then
			fifo_afull <= '0';
		elsif rising_edge(clk) then
			if diff >= almost_full_thresh-1 then
				fifo_afull <= '1';
			else
				fifo_afull <= '0';
			end if;	
		end if;
	end process;

proc_status_almost_empty:
	process(rst, clk)
	begin
		if rst = '1' then
			fifo_aempty <= '1';
		elsif rising_edge(clk) then
			if diff <= almost_empty_thresh+1 then
				fifo_aempty <= '1';
			else
				fifo_aempty <= '0';
			end if;	
		end if;
	end process;

proc_full_reg:
	process(rst, clk)
	begin
		if rst = '1' then
			full <= '0';
		elsif rising_edge(clk) then
			full <= pre_full;
		end if;
	end process;
	
proc_empty_reg:
	process(rst, clk)
	begin
		if rst = '1' then
			empty <= '1';
		elsif rising_edge(clk) then
			empty <= pre_empty;
		end if;
	end process;
	
proc_status_w:
	process(was_write, pW_next, pR_next, pW, pR, write, read)
	begin
		if (pW_next = pR) and (write = '1' or was_write = '1') then
			pre_full <= '1';
		else
			pre_full <= '0';
		end if;
		if (pR_next = pW) and (read = '1' or was_write = '0') then
			pre_empty <= '1';
		else
			pre_empty <= '0';
		end if;
	end process;

proc_flag_write:
	process(rst, clk)
	begin
		if rst = '1' then
			was_write <= '0';
		elsif rising_edge(clk) then
			if write = '1' then
				was_write <= '1';
			elsif read = '1' then
				was_write <= '0';
			end if;
		end if;
	end process;
	
proc_ptr_w:
	process(rst, clk, pW, write, full)
	begin
		if rst = '1' then
			pW <= to_unsigned(0, addr_width);
			pW_next <= to_unsigned(0, addr_width);
		elsif rising_edge(clk) then
			if write = '1' then 
				pW <= pW_next;
			end if;
		end if;
		pW_next <= pW;
		if write = '1' then 
			pW_next <= pW + 1;
		end if;
		ptr_w		<= pW;
	end process;

proc_ptr_r:
	process(rst, clk, pR_next, pR, read, empty)
	begin
		if rst = '1' then
			pR <= to_unsigned(0, addr_width);
			pR_next <= to_unsigned(0, addr_width);
		elsif rising_edge(clk) then
			if read = '1' then
				pR <= pR_next;
			end if;	
		end if;
		pR_next	<= pR;
		if read = '1' then
			pR_next	<= pR + 1;
		end if;
		ptr_r		<= pR_next;
	end process;

end Behavioral;

