--------------------------------------
--
-- FIFO_generic_tb.vhdl
--
-- created 9/20/26
-- tj
--
-- rev 0
----------------------------------------
--
-- Testbench for FIFO example for class notes
--
----------------------------------------
--
-- Inputs:  clk, rstb, wr_en, rd_en, data_in
-- Outputs: full, empty, rw_error, data_out
--
----------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity fifo_generic_tb is
	generic(	FIFO_DEPTH : positive := 256;
				WORD_SIZE  : positive := 16
			);
end entity fifo_generic_tb;

architecture testbench of fifo_generic_tb is

	signal CLK:			std_logic;
	signal RSTB:		std_logic;
	signal WR_EN:		std_logic;
	signal RD_EN:		std_logic;
	signal DATA_IN:	std_logic_vector((WORD_SIZE - 1) downto 0);
	
	signal DATA_OUT:	std_logic_vector((WORD_SIZE - 1) downto 0);
	signal FULL:		std_logic;
	signal EMPTY:		std_logic;
	signal RW_ERROR:	std_logic;
	
	constant PER:	time	:= 20 ns;
	
	component FIFO_generic is
		generic(
					FIFO_DEPTH : natural := 16;
					WORD_SIZE  : natural := 8
				);
		port(
				i_clk:		in  std_logic;
				i_rstb:		in  std_logic;
				i_wr_en:		in  std_logic;
				i_rd_en:		in  std_logic;
				i_DATA_IN:	in  std_logic_vector((WORD_SIZE - 1) downto 0);
			
				o_DATA_OUT:	out std_logic_vector((WORD_SIZE - 1) downto 0);
				o_FULL:		out std_logic;
				o_EMPTY:		out std_logic;
				o_rw_error:	out std_logic
			);
	end component;

begin

	DUT: FIFO_generic
		generic map(
					FIFO_DEPTH => FIFO_DEPTH,
					WORD_SIZE  => WORD_SIZE
			)
		port map(
					i_clk			=> CLK,
					i_rstb		=> RSTB,
					i_wr_en		=> WR_EN,
					i_rd_en		=> RD_EN,
					i_DATA_IN	=> DATA_IN,
					o_DATA_OUT	=> DATA_OUT,
					o_FULL		=> FULL,
					o_EMPTY		=> EMPTY,
					o_rw_error	=> RW_ERROR
				);

	-- Clock process
	clock: process		-- note - no sensitivity list allowed
      begin
			CLK <= '0';
			wait for PER/2;
			infinite: loop
				CLK <= not CLK; wait for PER/2;
			end loop;				
	end process clock;

	-- RSTB process
	reset: process		-- note - no sensitivity list allowed
	begin
		RSTB <= '0'; wait for 2*PER;
		RSTB <= '1'; wait;
	end process reset;

	-- run processes
	run: process
	begin
		
		--------------------------------------------------------------
		-- TEST 1: RSTB THE FIFO
		--------------------------------------------------------------
		report "----------------------------------------";
		report "TEST 1: Reseting FIFO";
		report "----------------------------------------";
		
		RD_EN <= '0';
		WR_EN <= '0';
		DATA_IN <= (others => '0');

		wait for PER * 4;		-- wait for rstb
		
		assert EMPTY = '1'
			report "ERROR: FIFO should be EMPTY after RSTB: " & 
					" measured " & std_logic'image(EMPTY) & 
					" expected " & integer'image(1)
			severity error;

		assert FULL = '0'
			report "ERROR: FIFO should NOT be FULL after RSTB: " & 
					" measured " & std_logic'image(FULL) & 
					" expected " & integer'image(0)
			severity error;
			
		assert RW_ERROR = '0'
			report "ERROR: No RW error after reset: " & 
					" measured " & std_logic'image(RW_ERROR) & 
					" expected " & integer'image(0)
			severity error;
			
		-- no data test

		report "*******   RSTB test complete   *******";
		
		--------------------------------------------------------------
		-- TEST 2: WRITE FIRST WORD
		--------------------------------------------------------------
		report "----------------------------------------";
		report "TEST 2: Writing first word";
		report "----------------------------------------";
		
		wait for PER;
		RD_EN <= '0';
		WR_EN <= '0';
		DATA_IN <= std_logic_vector(to_unsigned(((2** WORD_SIZE) - 1), WORD_SIZE));

		wait for PER;
		WR_EN <= '1';	
		wait for PER;
		WR_EN <= '0';
		
		assert EMPTY = '0'
			report "ERROR: FIFO should NOT be EMPTY after 1st write: " & 
					" measured " & std_logic'image(EMPTY) & 
					" expected " & integer'image(0)
			severity error;

		assert FULL = '0'
			report "ERROR: FIFO should NOT be FULL after 1st write: " & 
					" measured " & std_logic'image(FULL) & 
					" expected " & integer'image(0)
			severity error;
			
		assert RW_ERROR = '0'
			report "ERROR: No RW error after 1st write: " & 
					" measured " & std_logic'image(RW_ERROR) & 
					" expected " & integer'image(0)
			severity error;

		-- no data test
			
		report "*******   First write test comlpete    *******";
		
		--------------------------------------------------------------
		-- TEST 3: WRITE REMAINING WORDS
		--------------------------------------------------------------
		report "----------------------------------------";
		report "TEST 3: Filling FIFO";
		report "----------------------------------------";
		
		wait for PER;
		RD_EN <= '0';
		WR_EN <= '0';
		DATA_IN <= (others => '0');

		wait for PER;
		WR_EN <= '1';	
		for i in 2 to FIFO_DEPTH loop
			DATA_IN <= std_logic_vector(to_unsigned(((2** WORD_SIZE) - (i mod (2** WORD_SIZE))), WORD_SIZE));
			wait for PER;
		end loop;
		WR_EN <= '0';	
				
		assert EMPTY = '0'
			report "ERROR: FIFO should NOT be EMPTY after full write: " & 
					" measured " & std_logic'image(EMPTY) & 
					" expected " & integer'image(0)
			severity error;

		assert FULL = '1'
			report "ERROR: FIFO should be FULL after full write: " & 
					" measured " & std_logic'image(FULL) & 
					" expected " & integer'image(1)
			severity error;
			
		assert RW_ERROR = '0'
			report "ERROR: No RW error after full write: " & 
					" measured " & std_logic'image(RW_ERROR) & 
					" expected " & integer'image(0)
			severity error;

		-- no data test
			
		report "*******   FIFO FULL test complete    *******";
		
		
		--------------------------------------------------------------
		-- TEST 4: ATTEMPT TO WRITE WHILE FULL
		--------------------------------------------------------------
		report "----------------------------------------";
		report "TEST 4: Write while FIFO is FULL";
		report "----------------------------------------";
		
		wait for PER;
		RD_EN <= '0';
		WR_EN <= '0';
		DATA_IN <= std_logic_vector(to_unsigned(((2** WORD_SIZE) / 2), WORD_SIZE));
		
		wait for PER;
		WR_EN <= '1';
		wait for PER;
		
		assert EMPTY = '0'
			report "ERROR: FIFO should NOT be EMPTY during a FULL write: " & 
					" measured " & std_logic'image(EMPTY) & 
					" expected " & integer'image(0)
			severity error;

		assert FULL = '1'
			report "ERROR: FIFO should be FULL during a FULL write: " & 
					" measured " & std_logic'image(FULL) & 
					" expected " & integer'image(1)
			severity error;
			
		assert RW_ERROR = '1'
			report "ERROR: RW error should occure during a FULL write: " & 
					" measured " & std_logic'image(RW_ERROR) & 
					" expected " & integer'image(1)
			severity error;

		-- no data test
			
		report "*******   Write-while-FULL test complete    *******";
		
		
		--------------------------------------------------------------
		-- TEST 5: READ FIRST WORD
		--------------------------------------------------------------
		report "----------------------------------------";
		report "TEST 5: Reading first word";
		report "----------------------------------------";
		
		wait for PER;
		RD_EN <= '0';
		WR_EN <= '0';
		DATA_IN <= (others => '0');

		RD_EN <= '1';
		wait for PER;
		
		assert RW_ERROR = '0'
			report "ERROR: RW error should NOT occure after a during first FULL read: " & 
					" measured " & std_logic'image(RW_ERROR) & 
					" expected " & integer'image(0)
			severity error;
			
		RD_EN <= '0';
		wait for PER;
		
		assert EMPTY = '0'
			report "ERROR: FIFO should NOT be EMPTY after a first FULL read: " & 
					" measured " & std_logic'image(EMPTY) & 
					" expected " & integer'image(0)
			severity error;

		assert FULL = '0'
			report "ERROR: FIFO should NOT be FULL after a first FULL read: " & 
					" measured " & std_logic'image(FULL) & 
					" expected " & integer'image(0)
			severity error;
			
		assert RW_ERROR = '0'
			report "ERROR: RW error should NOT occure after a first FULL read: " & 
					" measured " & std_logic'image(RW_ERROR) & 
					" expected " & integer'image(0)
			severity error;
	
		assert DATA_OUT = std_logic_vector(to_unsigned(((2** WORD_SIZE) - 1), WORD_SIZE))
			report "ERROR: First FULL data read is incorrect." & 
					" measured " & integer'image(to_integer(unsigned(DATA_OUT))) & 
					" expected " & integer'image((2** WORD_SIZE) - 1)
			severity error;
			
		report "*******   First read test complete    *******";
		
		--------------------------------------------------------------
		-- TEST 6: READ REMAINING DATA
		--------------------------------------------------------------
		report "----------------------------------------";
		report "TEST 6: Reading remaining data";
		report "----------------------------------------";
		
		wait for PER;
		RD_EN <= '0';
		WR_EN <= '0';
		DATA_IN <= (others => '0');
		
		wait for PER;
		for i in 2 to (FIFO_DEPTH - 1) loop
			RD_EN <= '1';
			wait for PER;
			
			assert EMPTY = '0'
			report "ERROR: FIFO should NOT be EMPTY during a FULL read: " & 
					" measured " & std_logic'image(EMPTY) & 
					" expected " & integer'image(0)
			severity error;

			assert FULL = '0'
			report "ERROR: FIFO should NOT be FULL during a FULL read: " & 
					" measured " & std_logic'image(FULL) & 
					" expected " & integer'image(0)
			severity error;
			
			assert RW_ERROR = '0'
			report "ERROR: RW error should NOT occure during a FULL read: " & 
					" measured " & std_logic'image(RW_ERROR) & 
					" expected " & integer'image(0)
			severity error;
			
			assert DATA_OUT = std_logic_vector(to_unsigned(((2** WORD_SIZE) - (i mod (2** WORD_SIZE))), WORD_SIZE))
			report "ERROR: Expected " & integer'image((2** WORD_SIZE) - (i mod (2** WORD_SIZE)))
			severity error;
			
			assert DATA_OUT = std_logic_vector(to_unsigned(((2** WORD_SIZE) - (i mod (2** WORD_SIZE))), WORD_SIZE))
			report "ERROR: First FULL data read is incorrect." & 
					" measured " & integer'image(to_integer(unsigned(DATA_OUT))) & 
					" expected " & integer'image((2** WORD_SIZE) - (i mod (2** WORD_SIZE)))
			severity error;
			
		end loop;

		RD_EN <= '1';
		wait for PER;
		
		assert EMPTY = '1'
		report "ERROR: FIFO should  be EMPTY after a FULL read: " & 
				" measured " & std_logic'image(EMPTY) & 
				" expected " & integer'image(1)
		severity error;
			
		report "*******   FIFO read sequence complete    *******";
		
		--------------------------------------------------------------
		-- TEST 7: ATTEMPT TO READ WHILE EMPTY
		--------------------------------------------------------------
		report "----------------------------------------";
		report "TEST 7: Read while FIFO is EMPTY";
		report "----------------------------------------";
		
		wait for PER;
		RD_EN <= '0';
		WR_EN <= '0';
		DATA_IN <= (others => '0');

		RD_EN <= '0';
		wait for PER;
		RD_EN <= '1';
		wait for PER;		
		
		assert EMPTY = '1'
		report "ERROR: FIFO should be EMPTY during an empty read: " & 
				" measured " & std_logic'image(EMPTY) & 
				" expected " & integer'image(1)
		severity error;

		assert FULL = '0'
		report "ERROR: FIFO should NOT be FULL during an empty read: " & 
				" measured " & std_logic'image(FULL) & 
				" expected " & integer'image(0)
		severity error;
		
		assert RW_ERROR = '1'
		report "ERROR: RW error should occure during an empty read: " & 
				" measured " & std_logic'image(RW_ERROR) & 
				" expected " & integer'image(1)
		severity error;
			
		-- no data test

		report "*******   Read-while-EMPTY test complete    *******";
		
		
		--------------------------------------------------------------
		-- TEST 8: TEST FIFO WRAP-AROUND
		--------------------------------------------------------------	
		report "----------------------------------------";
		report "TEST 8: Testing pointer wrap-around";
		report "----------------------------------------";		
		
		wait for PER;
		RD_EN <= '0';
		WR_EN <= '0';
		DATA_IN <= (others => '0');
		
		-- fill the fifo
		wait for PER;
		WR_EN <= '1';	
		for i in 1 to FIFO_DEPTH loop
			DATA_IN <= std_logic_vector(to_unsigned(((2** WORD_SIZE) - (i mod (2** WORD_SIZE))), WORD_SIZE));
			wait for PER;
		end loop;
		WR_EN <= '0';
		
		-- remove 1st item
		wait for PER;
		RD_EN <= '1';
		wait for PER;
		RD_EN <= '0';
		
		-- write to force wrap
		wait for PER;	
		DATA_IN <= std_logic_vector(to_unsigned(((2** WORD_SIZE) - (2 mod (2** WORD_SIZE))), WORD_SIZE));
		
		WR_EN <= '1';	
		wait for PER;	
		WR_EN <= '0';
		wait for PER;	
		
		assert EMPTY = '0'
		report "ERROR: FIFO should NOT be EMPTY during a wrap: " & 
				" measured " & std_logic'image(EMPTY) & 
				" expected " & integer'image(0)
		severity error;

		assert FULL = '1'
		report "ERROR: FIFO should  be FULL during a wrap: " & 
				" measured " & std_logic'image(FULL) & 
				" expected " & integer'image(0)
		severity error;
		
		assert RW_ERROR = '0'
		report "ERROR: RW error should NOT occure during a wrap: " & 
				" measured " & std_logic'image(RW_ERROR) & 
				" expected " & integer'image(1)
		severity error;
		
		-- no data test

		report "*******   Pointer wrap-around test complete    *******";
		
		--------------------------------------------------------------	
		-- TEST 9: SIMULTANEOUS READ AND WRITE
		--------------------------------------------------------------			
		report "----------------------------------------";
		report "TEST 9: Simultaneous read and write";
		report "----------------------------------------";
		
		wait for PER;
		RD_EN <= '0';
		WR_EN <= '0';
		DATA_IN <= (others => '0');
		
		
		wait for PER;
		DATA_IN <= std_logic_vector(to_unsigned(((2** WORD_SIZE) / 2), WORD_SIZE));		
		WR_EN <= '1';
		RD_EN  <= '1';
		wait for PER;	
		WR_EN <= '0';
		RD_EN  <= '0';
		
		assert EMPTY = '0'
		report "ERROR: FIFO should NOT be EMPTY during a simultaneos r/w: " & 
				" measured " & std_logic'image(EMPTY) & 
				" expected " & integer'image(0)
		severity error;

		assert FULL = '1'
		report "ERROR: FIFO should be FULL during a simultaneos r/w: " & 
				" measured " & std_logic'image(FULL) & 
				" expected " & integer'image(1)
		severity error;
		
		assert RW_ERROR = '0'
		report "ERROR: RW error should NOT occure during a simultaneos r/w: " & 
				" measured " & std_logic'image(RW_ERROR) & 
				" expected " & integer'image(0)
		severity error;

		assert DATA_OUT = std_logic_vector(to_unsigned(((2** WORD_SIZE) - (2 mod (2** WORD_SIZE))), WORD_SIZE))
		report "ERROR: First FULL data read is incorrect." & 
				" measured " & integer'image(to_integer(unsigned(DATA_OUT))) & 
				" expected " & integer'image((2** WORD_SIZE) - (2 mod (2** WORD_SIZE)))
		severity error;
		
		report "*******   Simultaneous read/write test complete    *******";
		
		
		--============================================================
		-- END TEST
		--============================================================
		
		report "========================================";
		report "ALL FIFO TESTS COMPLETED";
		report "========================================";
		
		
		-- Wait a a few clocks and terminate the sim (independent of tool settings)
		
		wait for PER * 4;
		
		assert false
			report "FIFO TESTBENCH COMPLETE."
			severity failure;
		
	end process;

end architecture;