-------------------------------------------------------------------------
-- 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;
		winc			: in  STD_LOGIC;
		rinc			: 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 diff : unsigned (addr_width downto 0);
	
begin

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


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

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

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

proc_full_reg:
	process(clk)
	begin
		if rising_edge(clk) then
			if rst = '1' then
				full <= '0';
			else
				full <= pre_full;
			end if;
		end if;
	end process;
	
proc_empty_reg:
	process(clk)
	begin
		if rising_edge(clk) then
			if rst = '1' then
				empty <= '1';
			else
				empty <= pre_empty;
			end if;
		end if;
	end process;
	
proc_status_w:
	process(was_write, pW_next, pR_next, pW, pR, winc, rinc)
	begin
		if (pW_next = pR) and (winc = '1' or was_write = '1') then
			pre_full <= '1';
		else
			pre_full <= '0';
		end if;
		if (pR_next = pW) and (rinc = '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 rising_edge(clk) then
			if rst = '1' then
				was_write <= '0';
			elsif winc = '1' then
				was_write <= '1';
			elsif rinc = '1' then
				was_write <= '0';
			end if;
		end if;
	end process;
	
	pW_next <= (pW + 1) when winc = '1' else pW;
	ptr_w		<= pW;

proc_ptr_w:
	process(clk)
	begin
		if rising_edge(clk) then
			if rst = '1' then
				pW <= to_unsigned(0, addr_width);
			else
				pW <= pW_next;
			end if;
		end if;
	end process;

	pR_next	<= (pR + 1) when rinc = '1' else pR;
	ptr_r		<= pR_next;

proc_ptr_r:
	process(clk)
	begin
		if rising_edge(clk) then
			if rst = '1' then
				pR <= to_unsigned(0, addr_width);
			else
				pR <= pR_next;
			end if;	
		end if;
	end process;

end Behavioral;

