-- The Potato Processor - A simple processor for FPGAs
-- (c) Kristian Klomsten Skordal 2014 - 2015 <kristian.skordal@wafflemail.net>
-- Report bugs and issues on <https://github.com/skordal/potato/issues>

library ieee;
use ieee.std_logic_1164.all;

--! @brief A generic FIFO module.
--! Adopted from the FIFO module in <https://github.com/skordal/smallthings>.
entity pp_fifo is
	generic(
		DEPTH : natural := 64;
		WIDTH : natural := 32
	);
	port(
		-- Control lines:
		clk   : in std_logic;
		reset : in std_logic;

		-- Status lines:
		full  : out std_logic;
		empty : out std_logic;

		-- Data in:
		data_in   : in  std_logic_vector(WIDTH - 1 downto 0);
		data_out  : out std_logic_vector(WIDTH - 1 downto 0);
		push, pop : in std_logic
	);
end entity pp_fifo;

architecture behaviour of pp_fifo is

	type memory_array is array(0 to DEPTH - 1) of std_logic_vector(WIDTH - 1 downto 0);
	shared variable memory : memory_array := (others => (others => '0'));

	subtype index_type is integer range 0 to DEPTH - 1;
	signal top, bottom : index_type;

	type fifo_op is (FIFO_POP, FIFO_PUSH);
	signal prev_op : fifo_op := FIFO_POP;

begin

	empty <= '1' when top = bottom and prev_op = FIFO_POP else '0';
	full <= '1' when top = bottom and prev_op = FIFO_PUSH else '0';

	read: process(clk)
	begin
		if rising_edge(clk) then
			if reset = '1' then
				bottom <= 0;
			else
				if pop = '1' then
					data_out <= memory(bottom);
					bottom <= (bottom + 1) mod DEPTH;
				end if;
			end if;
		end if;
	end process read;

	write: process(clk)
	begin
		if rising_edge(clk) then
			if reset = '1' then
				top <= 0;
			else
				if push = '1' then
					memory(top) := data_in;
					top <= (top + 1) mod DEPTH;
				end if;
			end if;
		end if;
	end process write;

	set_prev_op: process(clk)
	begin
		if rising_edge(clk) then
			if reset = '1' then
				prev_op <= FIFO_POP;
			else
				if push = '1' and pop = '1' then
					prev_op <= FIFO_POP;
				elsif push = '1' then
					prev_op <= FIFO_PUSH;
				elsif pop = '1' then
					prev_op <= FIFO_POP;
				end if;
			end if;
		end if;
	end process set_prev_op;

end architecture behaviour;
