library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

-- equal speed writer and reader
-- faster writer
-- faster reader

entity TestFIFO is
end TestFIFO;


architecture behaviour of TestFIFO is

	component FIFO is
		generic(Depth : integer := 3);
		port(
			iClk	: in std_logic;
			iReset	: in std_logic;
			-- write port
			iWrEn		: in std_logic;
			iData		: in std_logic_vector(7 downto 0);
			oHasSpace	: out std_logic;
			-- read port
			iRdEn		: in std_logic;
			oData		: out std_logic_vector(7 downto 0);
			oHasData	: out std_logic
		);
	end component;
	
	type ramType is array(0 to 255) of std_logic_vector(7 downto 0);
	
	signal clk, reset			: std_logic;
	signal wrEn, rdEn			: std_logic;
	signal inData, outData		: std_logic_vector(7 downto 0);
	signal hasSpace, hasData	: std_logic;
	
	signal rawInData		: std_logic_vector(7 downto 0);
	signal rawWrEn, rawRdEn	: std_logic;

	signal dstRAM	: ramType;
	
	signal writerData		: unsigned(8 downto 0) := (others => '0');
	signal writerWrEn		: std_logic;
	signal writeDone		: std_logic;
	signal writerClkCntInit, readerClkCntInit	: unsigned(1 downto 0) := "00";
	signal writerClkCnt, readerClkCnt			: unsigned(1 downto 0) := "00";

	signal readerCnt		: unsigned(8 downto 0) :=  (others => '0');
	signal readerRdEn		: std_logic;
	signal readDone			: std_logic;
	
	signal rawMode	: std_logic;
	

begin

	FIFO0: FIFO port map(
		iClk => clk, iReset => reset,
		iWrEn => wrEn, iData => inData, oHasSpace => hasSpace,
		iRdEn => rdEn, oData => outData, oHasData => hasData
	);
	
	wrEn <= rawWrEn when rawMode = '1' else writerWrEn;
	rdEn <= rawRdEn when rawMode = '1' else readerRdEn; 
	
	inData <= rawInData when rawMode = '1' else std_logic_vector(writerData(7 downto 0));
	writeDone <= writerData(7);	-- done when writerData == 0x100
	writerWrEn <= '1' when (writerClkCnt = "00") and (hasSpace = '1') and (writeDone = '0') else '0';
	
	fifoWriter: process(clk)
	begin
		if clk'event and clk = '1' and reset = '0' then
			if writerClkCnt = "00" then
				if hasSpace = '1' and writeDone = '0' then
					writerData <= writerData + 1;
				end if;
				report "writerClkCnt <= writerClkCntInit;";
				writerClkCnt <= writerClkCntInit;
			else
				report "writerClkCnt <= writerClkCnt - 1;";
				writerClkCnt <= writerClkCnt - 1;
			end if;
		end if;
	end process;
	
	readDone <= readerCnt(7);	-- done when readerCnt == 0x100
	readerRdEn <= '1' when readerClkCnt = "00" and hasData = '1' and readDone = '0' else '0';
	
	fifoReader: process(clk)
	begin
		if clk'event and clk = '1' and reset = '0' then
			if readerClkCnt = "00" then
				if hasData = '1' and readDone = '0' then
					dstRAM(to_integer(readerCnt)) <= outData;
					readerCnt <= readerCnt + 1;
				end if;
				readerClkCnt <= readerClkCntInit;
			else
				readerClkCnt <= readerClkCnt - 1;
			end if;
		end if;
	end process;
	
	main: process
		
		procedure nextCycle is begin
			wait for 500 ns;
			report "rise";
			clk <= '1';
			wait for 500 ns;
			clk <= '0';
		end procedure;
		
		procedure doReset is begin
			reset <= '1';
			clk <= '0';
			nextCycle;
			reset <= '0';
		end procedure;
		
		procedure testSimple is
		
			procedure write(data : std_logic_vector(7 downto 0); expHasData, expHasSpace: std_logic) is begin
				rawWrEn <= '1';
				rawInData <= data;
				nextCycle;
				assert hasData = expHasData and hasSpace = expHasSpace;
			end procedure;

		begin
			rawMode <= '1';
			rawWrEn <= '0';
			rawRdEn <= '0';
			doReset;
			assert hasData = '0' and hasSpace = '1';
			write(X"70", '1', '1');
			write(X"61", '1', '1');
			write(X"52", '1', '1');
			write(X"43", '1', '1');
			write(X"34", '1', '1');
			write(X"25", '1', '1');
			write(X"16", '1', '1');
			write(X"07", '1', '0');
			write(X"5a", '1', '0');	-- attempt to write to the full FIFO, should be ignored
		end procedure;
		
		procedure clearDst is
			variable i : integer;
		begin
			for i in 0 to 255 loop
				dstRAM(i) <= X"00";
			end loop;
		end procedure;
		
		procedure checkDst is
			variable i : integer;
		begin
			for i in 0 to 255 loop
				assert dstRAM(i) = std_logic_vector(to_unsigned(i, 8));
			end loop;
		end procedure;

		-- 0 means fastest
		procedure setWriterSpeed(div: integer) is begin
			writerClkCntInit <=  to_unsigned(div, 2);
			writerClkCnt <= to_unsigned(div, 2);
			writerData <= (others => '0');
		end procedure;
		
		procedure setReaderSpeed(div: integer) is begin
			readerClkCntInit <= to_unsigned(div, 2);
			readerClkCnt <= to_unsigned(div, 2);
		end procedure;
	
		procedure testEqualFastSpeed is begin
			setWriterSpeed(0);
			setReaderSpeed(0);
			clearDst;
			doReset;
			while readDone = '0' loop
				nextCycle;
			end loop;
			checkDst;
		end procedure;

	begin
		testSimple;
		-- testEqualFastSpeed;
		wait; -- wait forever, end simulation
	end process;
	
end behaviour;