-------------------------------------------------------------------------
-- 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;

LIBRARY WORK;
USE WORK.FIFO_CTRL_PKG.ALL;

entity fifo_async_ctrl is
	Generic 
	(
		addr_width : integer := 3;
		almost_full_thresh : integer := 6;
		almost_empty_thresh : integer := 2
	);
	Port
	(
		rst					: in  STD_LOGIC;
		clk_w				: in  STD_LOGIC;
		clk_r				: 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_async_ctrl;

architecture Behavioral of fifo_async_ctrl is

	signal gcnt_w		: unsigned (addr_width downto 0);
	signal gcnt2_w		: unsigned (addr_width downto 0);
	signal bcnt2_aw		: unsigned (addr_width downto 0);
	signal bcnt_w		: unsigned (addr_width downto 0);
	signal gnxt_w		: unsigned (addr_width downto 0);
	signal bnxt_w		: unsigned (addr_width downto 0);
	signal gcnt_r		: unsigned (addr_width downto 0);
	signal gcnt2_r		: unsigned (addr_width downto 0);
	signal bcnt2_ar		: unsigned (addr_width downto 0);
	signal bcnt_r		: unsigned (addr_width downto 0);
	signal gnxt_r		: unsigned (addr_width downto 0);
	signal bnxt_r		: unsigned (addr_width downto 0);

	signal pre_empty, pre_full : std_logic;
	signal full, empty : std_logic;
	signal rst_w, rst_r : std_logic;

	-- synthesis translate_off
	signal diffw		: signed (addr_width downto 0);
	signal diffr		: signed (addr_width downto 0);
	-- synthesis translate_on

begin

	ptr_w			<= bcnt_w(addr_width-1 downto 0);
	ptr_r			<= bnxt_r(addr_width-1 downto 0);
	fifo_full		<= full;
	fifo_empty		<= empty;
	fifo_pre_full	<= pre_full;
	fifo_pre_empty	<= pre_empty;
	
sync_reset_w:
	process(clk_w)
	begin
		if rising_edge(clk_w) then
			rst_w <= rst;
		end if;
	end process;

sync_reset_r:
	process(clk_r)
	begin
		if rising_edge(clk_r) then
			rst_r <= rst;
		end if;
	end process;
	
proc_write_inhibit:
	process(clk_w)
	begin
		if rising_edge(clk_w) then
			if rst_w = '1' then
				full	<= '0';
			else
				full	<= pre_full;
			end if;
		end if;
	end process;
	
proc_read_inhibit:
	process(clk_r)
	begin
		if rising_edge(clk_r) then
			if rst_r = '1' then
				empty	<= '1';
			else
				empty	<= pre_empty;
			end if;
		end if;
	end process;

proc_sync_grptr:
	process(clk_w)
	variable p1, p2	: unsigned (addr_width downto 0);
	begin
		if rising_edge(clk_w) then
			gcnt2_r <= p2;
			p2 := p1;
			p1 := gcnt_r;
		end if;
	end process;

proc_ptr_gwptr:
	process(clk_r)
	variable p1, p2	: unsigned (addr_width downto 0);
	begin
		if rising_edge(clk_r) then
			gcnt2_w <= p2;
			p2 := p1;
			p1 := gcnt_w;
		end if;
	end process;
	
proc_status_full:
	process(gnxt_w, gcnt2_r)
	begin
		if (gnxt_w = (not gcnt2_r(gcnt2_r'left downto gcnt2_r'left-1) & gcnt2_r(gcnt2_r'left-2 downto 0))) then
			pre_full <= '1';
		else
			pre_full <= '0';
		end if;
	end process;

proc_status_empty:
	process(gnxt_r, gcnt2_w)
	begin
		if (gnxt_r = gcnt2_w) then
			pre_empty <= '1';
		else
			pre_empty <= '0';
		end if;
	end process;
	
proc_sync_arptr:
	process(clk_w)
	variable p1, p2	: unsigned (addr_width downto 0);
	begin
		if rising_edge(clk_w) then
			bcnt2_ar <= p2;
			p2 := p1;
			p1 := bcnt_r;
		end if;
	end process;

proc_ptr_awptr:
	process(clk_r)
	variable p1, p2	: unsigned (addr_width downto 0);
	begin
		if rising_edge(clk_r) then
			bcnt2_aw <= p2;
			p2 := p1;
			p1 := bcnt_w;
		end if;
	end process;
	
proc_status_almost_full:
	process(clk_w)
	variable diff : unsigned (addr_width downto 0);
	begin
	-- synthesis translate_off
		diffw <= signed(diff);
	-- synthesis translate_on
		if rising_edge(clk_w) then
			if rst_w = '1' then
				fifo_afull <= '0';
				diff := (others => '0');
			else
				if diff >= almost_full_thresh-1 then
					fifo_afull <= '1';
				else
					fifo_afull <= '0';
				end if;	
				diff := unsigned(abs(signed(bnxt_w) - signed(bcnt2_ar)));
			end if;
		end if;
	end process;

proc_status_almost_empty:
	process(clk_r)
	variable diff : unsigned (addr_width downto 0);
	begin
	-- synthesis translate_off
		diffr <= signed(diff);
	-- synthesis translate_on
		if rising_edge(clk_r) then
			if rst_r = '1' then
				fifo_aempty <= '1';
				diff := (others => '0');
			else
				if diff <= almost_empty_thresh+1 then
					fifo_aempty <= '1';
				else
					fifo_aempty <= '0';
				end if;	
				diff := unsigned(abs(signed(bcnt2_aw) - signed(bnxt_r)));
			end if;
		end if;
	end process;

inst_gray_counter_w : entity work.gray_counter
	generic map
	(
		width => addr_width+1,
		init_value => 0
		)
	port map
	(
		rst			=> rst_w,
		clk			=> clk_w,
		ce			=> winc, 
		bcnt		=> bcnt_w,
		bnxt		=> bnxt_w,
		gcnt		=> gcnt_w,
		gnxt		=> gnxt_w
	);

inst_gray_counter_r : entity work.gray_counter
	generic map
	(
		width => addr_width+1,
		init_value => 0
		)
	port map
	(
		rst			=> rst_r,
		clk			=> clk_r,
		ce			=> rinc, 
		bcnt		=> bcnt_r,
		bnxt		=> bnxt_r,
		gcnt		=> gcnt_r,
		gnxt		=> gnxt_r
	);

end Behavioral;

