LIBRARY ieee;
use IEEE.STD_LOGIC_1164.ALL;
USE ieee.numeric_std.ALL;

library work;
use work.mips_types.all;
use work.mips_util_pkg.all;

ENTITY tb_cam2 IS
END tb_cam2;

ARCHITECTURE behavior OF tb_cam2 IS 

	constant CLK_PERIOD			: time := 10 ns;
	constant DATA_WIDTH			: integer := 20;
	constant CAM_NUM_ENTRIES	: integer := 256;
	constant ENTRY_WIDTH		: integer := 32;

	signal CLK_O				: std_logic := '1';
	signal RST_O				: std_logic := '1';
	signal cam_we				: std_logic := '0';
	signal cam_re				: std_logic := '0';
	signal cam_vld				: std_logic := '0';
	signal cam_rdy				: std_logic := '-';

	signal cam_addr				: unsigned(lg2(CAM_NUM_ENTRIES)-1 downto 0) := (others => '0');
	signal cam_rtag				: unsigned(DATA_WIDTH-1 downto 0) := (others => '0');
	signal cam_wtag				: unsigned(DATA_WIDTH-1 downto 0) := (others => '0');
	signal cam_entry_din		: unsigned(ENTRY_WIDTH-1 downto 0) := (others => '0');
	signal cam_entry_dout		: unsigned(ENTRY_WIDTH-1 downto 0) := (others => '0');

	signal cam_hit_vld			: std_logic := '-';


BEGIN

inst_cam2: entity work.cam2
	GENERIC MAP
	(
		NUM_ENTRIES			=>	CAM_NUM_ENTRIES,
		ENTRY_WIDTH			=>	ENTRY_WIDTH,
		DATA_WIDTH			=>	DATA_WIDTH,
		CAM_RAM_MAX_WIDTH	=>	8
	)
	PORT MAP
	(
		rst			=>	RST_O,
		clk			=>	CLK_O,
		
		rdy			=>	cam_rdy,
		
		re			=>	cam_re,
		we			=>	cam_we,
		vld			=>	cam_vld,
		addr		=>	cam_addr,
		tag_rd		=>	cam_rtag,
		tag_wr		=>	cam_wtag,
		entry_din	=>	cam_entry_din,
		entry_dout	=>	cam_entry_dout,

		hit_vld		=>	cam_hit_vld

	);

CLK_GEN: process
	begin
		wait for CLK_PERIOD/2;
		CLK_O <= not CLK_O;
	end process;


STIMULUS: process

	variable tag				: unsigned(DATA_WIDTH-1 downto 0) := (others => '0');
	variable entry_din			: unsigned(ENTRY_WIDTH-1 downto 0) := (others => '0');

	procedure cam_entry_write(index : integer; data : unsigned; valid : std_logic) is
	begin
	
	cam_entry_din		<= to_unsigned(index, ENTRY_WIDTH);
	cam_wtag			<= data;
	cam_addr			<= to_unsigned(index, lg2(CAM_NUM_ENTRIES));
	cam_vld				<= valid;
	cam_we				<= '1';
	wait until rising_edge(CLK_O) and cam_rdy = '1';
	cam_we				<= '0';
	
	end procedure cam_entry_write;

	procedure cam_lookup(data : unsigned) is
	begin
	
	cam_rtag			<= data;
	cam_re				<= '1';
	wait until rising_edge(CLK_O) and cam_rdy = '1';
	cam_re				<= '0';

	end procedure cam_lookup;
	

	begin

		wait for 6*CLK_PERIOD;
		RST_O <= '0';
		wait for 60*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';

		-----------------------
		tag	:= X"CAF0_0";
		for i in 0 to CAM_NUM_ENTRIES-1 loop
			cam_entry_write(i, tag, '1');
			tag	:= tag + X"0100_0";
		end loop;
		wait for 60*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';
		tag	:= X"CAF0_0";
		for i in 0 to CAM_NUM_ENTRIES-1 loop
			cam_lookup(tag);
			tag	:= tag + X"0100_0";
		end loop;

		wait for 60*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';

		RST_O <= '1';
		wait for 6*CLK_PERIOD;
		wait until rising_edge(CLK_O);
		RST_O <= '0';
		wait until rising_edge(CLK_O) and cam_rdy = '1';
		-----------------------
		tag	:= X"CAF0_0";
		for i in 0 to CAM_NUM_ENTRIES-1 loop
			cam_entry_write(i, tag, '1');
			tag	:= tag + X"0010_0";
		end loop;
		wait for 60*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';
		tag	:= X"CAF0_0";
		for i in 0 to CAM_NUM_ENTRIES-1 loop
			cam_lookup(tag);
			tag	:= tag + X"0010_0";
		end loop;

		wait for 60*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';

		RST_O <= '1';
		wait for 6*CLK_PERIOD;
		wait until rising_edge(CLK_O);
		RST_O <= '0';
		wait until rising_edge(CLK_O) and cam_rdy = '1';
		-----------------------
		tag	:= X"CAF0_0";
		for i in 0 to CAM_NUM_ENTRIES-1 loop
			cam_entry_write(i, tag, '1');
			tag	:= tag + X"0001_0";
		end loop;
		wait for 60*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';
		tag	:= X"CAF0_0";
		for i in 0 to CAM_NUM_ENTRIES-1 loop
			cam_lookup(tag);
			tag	:= tag + X"0001_0";
		end loop;

		wait for 60*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';

		RST_O <= '1';
		wait for 6*CLK_PERIOD;
		wait until rising_edge(CLK_O);
		RST_O <= '0';
		wait until rising_edge(CLK_O) and cam_rdy = '1';
		-----------------------
		tag	:= X"CAF0_0";
		for i in 0 to CAM_NUM_ENTRIES-1 loop
			cam_entry_write(i, tag, '1');
			tag	:= tag + X"0000_1";
		end loop;
		wait for 60*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';
		tag	:= X"CAF0_0";
		for i in 0 to CAM_NUM_ENTRIES-1 loop
			cam_lookup(tag);
			tag	:= tag + X"0000_1";
		end loop;

		wait for 60*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';

		RST_O <= '1';
		wait for 6*CLK_PERIOD;
		wait until rising_edge(CLK_O);
		RST_O <= '0';
		wait until rising_edge(CLK_O) and cam_rdy = '1';
		-----------------------
		tag	:= X"CAF0_0";
		for i in 0 to CAM_NUM_ENTRIES-1 loop
			cam_entry_write(i, tag, '1');
			tag	:= tag + X"0102_3";
		end loop;
		
		wait for 60*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';
		tag	:= X"CAF0_0";
		for i in 0 to CAM_NUM_ENTRIES-1 loop
			cam_lookup(tag);
			tag	:= tag + X"0102_3";
		end loop;

		wait for 60*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';

		RST_O <= '1';
		wait for 6*CLK_PERIOD;
		wait until rising_edge(CLK_O);
		RST_O <= '0';
		wait until rising_edge(CLK_O) and cam_rdy = '1';
		-----------------------
		-- overwrite
		cam_entry_write(2, X"BEEF_2", '1');
		wait for 8*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';
		
		-- overwrite entry
		cam_entry_write(2, X"CAFE_2", '1');
		-- new entry
		cam_entry_write(3, X"CAFE_3", '1');
		cam_entry_write(4, X"CAFE_4", '1');

		wait for 8*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';
		cam_entry_write(1, X"CAFE_1", '0');
		cam_entry_write(2, X"CAFE_2", '0');
		cam_entry_write(3, X"CAFE_3", '0');
		cam_entry_write(4, X"CAFE_4", '0');

		wait for 8*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';
		cam_lookup(X"BEEF_2");
		cam_lookup(X"CAFE_1");
		cam_lookup(X"CAFE_2");
		cam_lookup(X"CAFE_3");
		cam_lookup(X"CAFE_4");
		
		wait for 8*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';
		cam_entry_write(1, X"CAFE_1", '1');
		cam_entry_write(2, X"CAFE_2", '1');
		cam_entry_write(3, X"CAFE_3", '1');
		cam_entry_write(4, X"CAFE_4", '1');

		wait for 8*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';
		cam_lookup(X"CAFE_1");
		cam_lookup(X"CAFE_2");
		cam_lookup(X"CAFE_3");
		cam_lookup(X"CAFE_4");

		wait for 8*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';
		cam_entry_write(7, X"CAFE_7", '0');

		wait for 8*CLK_PERIOD;
		wait until rising_edge(CLK_O) and cam_rdy = '1';
	wait;
	
	end process;
	
END;
