----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    16:23:33 04/19/2009 
-- Design Name: 
-- Module Name:    snoopy - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity snoopy is
	Generic
	(
		NUM_MICROS : integer := 2;
		NUM_DFG_NODES : integer := 8;
		DFG_NODE_WIDTH : integer := 8;
		PROC_ADDR_WIDTH : integer := 4;
		INFO_ADDR_WIDTH : integer := 4;
		DATA_WIDTH : integer := 32
	);
	Port 
	( 
		clk : in  STD_LOGIC;
      reset : in  STD_LOGIC;
		mem_requests : in  STD_LOGIC_VECTOR (NUM_MICROS-1 downto 0);		
		executing_task : in  STD_LOGIC_VECTOR (NUM_MICROS-1 downto 0);
		start_task_control : in std_logic; --this is active low
		proc_tasks_done : in  STD_LOGIC_VECTOR (NUM_MICROS-1 downto 0);
		all_tasks_done : in std_logic;
		dfg_node : in STD_LOGIC_VECTOR ((NUM_MICROS*DFG_NODE_WIDTH)-1 downto 0);
		
		proc_task_addr : in std_logic_vector(PROC_ADDR_WIDTH-1 downto 0);
		info_addr : in std_logic_vector(INFO_ADDR_WIDTH-1 downto 0);
		data : out std_logic_vector(DATA_WIDTH-1 downto 0)		
	);
end snoopy;

architecture Behavioral of snoopy is
	type PROC_COUNT_ARRAY is array (NUM_MICROS-1 downto 0) of std_logic_vector(DATA_WIDTH-1 downto 0);
	type LONG_PROC_COUNT_ARRAY is array (NUM_MICROS-1 downto 0) of std_logic_vector((DATA_WIDTH*2)-1 downto 0);
	type TASK_COUNT_ARRAY is array (NUM_DFG_NODES-1 downto 0) of std_logic_vector(DATA_WIDTH-1 downto 0);
	type LONG_TASK_COUNT_ARRAY is array (NUM_DFG_NODES-1 downto 0) of std_logic_vector((DATA_WIDTH*2)-1 downto 0);

	signal last_mem_requeset_state : STD_LOGIC_VECTOR(NUM_MICROS-1 downto 0) := (others => '0');
	signal total_execution_time : STD_LOGIC_VECTOR((DATA_WIDTH*2)-1 downto 0) := (others => '0');
	signal mem_wait_times : LONG_PROC_COUNT_ARRAY := (others => (others => '0'));
	signal num_mem_accesses : TASK_COUNT_ARRAY := (others => (others => '0'));	
	signal total_task_exec_time : LONG_TASK_COUNT_ARRAY := (others => (others => '0'));
	signal processor_idle_time : LONG_PROC_COUNT_ARRAY := (others => (others => '0'));

begin

	execution_time_process : process( clk ) is			
	begin
		if (clk'event and clk = '1') then
			if (reset = '1') then
				total_execution_time <= (others => '0');
				processor_idle_time <= (others => (others => '0'));				
				total_task_exec_time <= (others => (others => '0'));	
			else	
			    --if the tasks are still being executed, then increment the counter
				if ((start_task_control = '0') and (all_tasks_done = '0'))  then
					total_execution_time <= total_execution_time + 1;
					for i in 0 to NUM_MICROS-1 loop
						if (executing_task(i) = '0') then
							processor_idle_time(i) <= processor_idle_time(i) + 1; 
						else
							if (conv_integer(unsigned(dfg_node((i+1)*DFG_NODE_WIDTH-1 downto i*DFG_NODE_WIDTH))) < NUM_DFG_NODES) then
								total_task_exec_time(conv_integer(unsigned(dfg_node((i+1)*DFG_NODE_WIDTH-1 downto i*DFG_NODE_WIDTH)))) <= total_task_exec_time(conv_integer(unsigned(dfg_node((i+1)*DFG_NODE_WIDTH-1 downto i*DFG_NODE_WIDTH)))) + 1;
							end if;
						end if;												
					end loop;	
				end if;				
			end if;		
		end if;
	end process execution_time_process;
	
	mem_wait_time_process : process( clk ) is			
	begin
		if (clk'event and clk = '1') then
			if (reset = '1') then
				mem_wait_times <= (others => (others => '0')); 
			else	
			    --if the tasks are being executed
				if ((start_task_control = '0') and (all_tasks_done = '0'))  then
					for i in 0 to NUM_MICROS-1 loop
						if (mem_requests(i) = '1') then
							mem_wait_times(i) <= mem_wait_times(i) + 1; 
						end if;
					end loop;
				end if;				
			end if;
		end if;
	end process mem_wait_time_process;
	
	num_mem_access_process : process( clk ) is			
	begin
		if (clk'event and clk = '1') then
			if (reset = '1') then
				last_mem_requeset_state <= (others => '0');
				num_mem_accesses <= (others => (others => '0')); 
			else	
			    --if the tasks are being executed
				if ((start_task_control = '0') and (all_tasks_done = '0'))  then				
					for i in 0 to NUM_MICROS-1 loop
						
						if ((last_mem_requeset_state(i) = '0') and (mem_requests(i) = '1')) then
							if (conv_integer(unsigned(dfg_node((i+1)*DFG_NODE_WIDTH-1 downto i*DFG_NODE_WIDTH))) < NUM_DFG_NODES) then
								num_mem_accesses(conv_integer(unsigned(dfg_node((i+1)*DFG_NODE_WIDTH-1 downto i*DFG_NODE_WIDTH)))) <= num_mem_accesses(conv_integer(unsigned(dfg_node((i+1)*DFG_NODE_WIDTH-1 downto i*DFG_NODE_WIDTH)))) + 1;
							end if;							
						end if;
						
						last_mem_requeset_state(i) <= mem_requests(i);
					end loop;
				end if;				
			end if;	
		end if;
	end process num_mem_access_process;
	
	data_out_process : process (clk)
	begin
		if (clk'event and clk = '1') then
			if (reset = '1') then
				data <= (others => '0');
			else	
				case (conv_integer(unsigned(info_addr))) is
					when 0 => -- this is addressing the lower 32-bits of the total execution time
						data <= total_execution_time(DATA_WIDTH-1 downto 0);
					when 1 => -- this is addressing the upper 32-bits of the total execution time		
						data <= total_execution_time((2*DATA_WIDTH)-1 downto DATA_WIDTH);
			
					when 2 => -- this is addressing the low 32-bits of the mem_wait_times
						if (conv_integer(unsigned(proc_task_addr)) < NUM_MICROS) then
							data <= mem_wait_times(conv_integer(unsigned(proc_task_addr)))(DATA_WIDTH-1 downto 0);
						else
							data <= (others => '0');
						end if;
					when 3 => -- this is addressing the upper 32-bits of the mem_wait_times
						if (conv_integer(unsigned(proc_task_addr)) < NUM_MICROS) then
							data <= mem_wait_times(conv_integer(unsigned(proc_task_addr)))((2*DATA_WIDTH)-1 downto DATA_WIDTH);
						else
							data <= (others => '0');
						end if;
			
					when 4 => -- this is addressing the number of memory accesses for a particular dfg node
						if (conv_integer(unsigned(proc_task_addr)) < NUM_DFG_NODES) then
							data <= num_mem_accesses(conv_integer(unsigned(proc_task_addr)));
						else
							data <= (others => '0');
						end if;				
				
					when 6 => --this is addressing the lower 32-bits of the total task execution time for a particular task
						if (conv_integer(unsigned(proc_task_addr)) < NUM_DFG_NODES) then
							data <= total_task_exec_time(conv_integer(unsigned(proc_task_addr)))(DATA_WIDTH-1 downto 0);
						else
							data <= (others => '0');
						end if;
					when 7 => -- this is addressing the upper 32-bits of the total task execution time for a particular task
						if (conv_integer(unsigned(proc_task_addr)) < NUM_DFG_NODES) then
							data <= total_task_exec_time(conv_integer(unsigned(proc_task_addr)))((2*DATA_WIDTH)-1 downto DATA_WIDTH);
						else
							data <= (others => '0');
						end if;
			
					when 8 => --this is addressing the lower 32-bits of the processor idle time for a particular processor
						if (conv_integer(unsigned(proc_task_addr)) < NUM_MICROS) then
							data <= processor_idle_time(conv_integer(unsigned(proc_task_addr)))(DATA_WIDTH-1 downto 0);
						else
							data <= (others => '0');
						end if;			
					when 9 => --this is addressing the upper 32-bits of the processor idle time for a particular processor
						if (conv_integer(unsigned(proc_task_addr)) < NUM_MICROS) then
							data <= processor_idle_time(conv_integer(unsigned(proc_task_addr)))((2*DATA_WIDTH)-1 downto DATA_WIDTH);
						else
							data <= (others => '0');
						end if;
					when others =>
						data <= (others => '0');			
				end case;
			end if;
		end if;
	end process data_out_process;

end Behavioral;

