library ieee;
use ieee.std_logic_1164.all;
use IEEE.STD_LOGIC_ARITH.all;
use ieee.STD_LOGIC_UNSIGNED.all;

library FACT_FAD_lib;
use FACT_FAD_lib.fad_definitions.all;

-- library UNISIM;
-- use UNISIM.VComponents.all;
-- USE IEEE.NUMERIC_STD.all;

-- RAM_ADDR_WIDTH_64B is used for 
-- output ram_start_addr

-- RAM_ADDR_WIDTH_16B is used for
-- output wiz_ram_start_addr 


ENTITY memory_manager_2 IS
generic(
	RAM_ADDR_WIDTH_64B : integer := 12;
	RAM_ADDR_WIDTH_16B : integer := 14 
);
PORT( 
	state          : OUT    std_logic_vector (3 DOWNTO 0);  -- state is encoded here ... useful for debugging.

	clk : IN std_logic;
	
	-- CONFIG handshake:
	config_start : IN std_logic;
	config_ready : OUT std_logic := '1';
		-- output of CONFIG states
	roi_array : IN roi_array_type;
	roi_max : OUT roi_max_type := (others => conv_std_logic_vector (0, 11));
	package_length : OUT std_logic_vector (15 downto 0) := (others => '0');
	
	wiz_number_of_channels : OUT std_logic_vector (3 downto 0) := (others => '0');
	
	-- interface to DG:
	dg_start_config : OUT std_logic := '0';
	dg_config_done : IN std_logic;
	ram_write_ready : IN std_logic;
	ram_write_ea : OUT std_logic := '0';
	ram_start_addr : OUT std_logic_vector (RAM_ADDR_WIDTH_64B-1 DOWNTO 0) := (others => '0');
	
	-- interface to W5300:
	wiz_read_done : IN std_logic;
	wiz_write_ea : OUT std_logic := '0';
	wiz_write_length : OUT std_logic_vector (16 downto 0) := (others => '0');
	wiz_ram_start_addr : OUT std_logic_vector (RAM_ADDR_WIDTH_16B-1 downto 0) := (others => '0');
	wiz_write_header : OUT std_logic := '0';
	wiz_write_end : OUT std_logic := '0';
	
	
	data_ram_empty : out std_logic			-- stupid signal, I need to get rid of it. DN 23.05.2011
);

-- Declarations

END memory_manager_2 ;

--
ARCHITECTURE beha OF memory_manager_2 IS

type state_mm_type is (	MM_CONFIG, MAX_ROI, MAX_ROI1, MAX_ROI2, FIFO_CALC, RAM_CALC, RAM_CALC1, RAM_CALC2, 
						CONFIG_DG, WAIT_FOR_CONFIG_DG,
						MM_MAIN,
						WRITE_FIN, READ_FIN, UPDATE_OUTPUT, UPDATE_OUTPUT_2);
signal state_mm : state_mm_type := MM_CONFIG;

--type roi_array_type is array (0 to 35) of integer range 0 to 1024;
type roi_max_array_type is array (0 to 8) of integer range 0 to 1024;
type channel_size_type is array (0 to 8) of integer range 0 to W5300_TX_FIFO_SIZE;
type fifo_write_length_type is array (0 to 8) of integer range 0 to W5300_TX_FIFO_SIZE;
type fifo_channels_array_type is array (0 to 8) of integer range 0 to 9;
type fifo_package_size_ram_type is array (0 to 8) of integer range 0 to RAM_SIZE_16B;

signal roi_max_array : roi_max_array_type := (others => 0);

-- size of channel groups (16 bit)
signal channel_size : channel_size_type := (others => 0);
-- write length of packages (16 bit)
signal fifo_write_length : fifo_write_length_type := (others => 0);
-- number of channels per package
signal fifo_channels_array : fifo_channels_array_type := (others => 0);
-- size of packages in ram (16 bit)
signal fifo_package_size_ram : fifo_package_size_ram_type := (others => 0);
--
signal event_size_ram : integer range 0 to RAM_SIZE_16B := 0;
signal event_size_ram_64b : integer range 0 to RAM_SIZE_64B := 0;
signal event_size : integer range 0 to RAM_SIZE_16B := 0;

signal drs_id : integer range 0 to 4 := 0;
signal channel_id : integer range 0 to 9 := 0;
signal channel_index : integer range 0 to 9 := 0;
signal package_index : integer range 0 to 9 := 0;
signal number_of_packages : integer range 0 to 9 := 0;
signal max_events_ram : integer range 0 to 2048;
signal events_in_ram : integer range 0 to 2048;
signal event_start_addr : integer range 0 to (RAM_SIZE_64B - 1);
signal write_start_addr : integer range 0 to (RAM_SIZE_16B - 1);


signal roi_index : integer range 0 to 45 := 0;
signal temp_roi : integer range 0 to 1024 := 0;

-- SYNCH IN INPUT SIGNALS -----------------------------------------

	signal config_start_sr		: std_logic_vector(1 downto 0) := "00";
	signal ram_write_ready_sr	: std_logic_vector(1 downto 0) := "00";
	signal wiz_read_ready_sr 			: std_logic_vector(1 downto 0) := "00";
	signal dg_config_done_sr : std_logic_vector(1 downto 0) := "00";
		-- no shift register, but local copy.
	signal roi_array_local : roi_array_type;
	
	
	signal state_sig : std_logic_vector( 3 downto 0 ) := "0000";

BEGIN
	state <= state_sig;

	data_ram_empty <= '1' when events_in_ram = 0 else '0'; 
  
	mm : process (clk)
	begin
		if rising_edge (clk) then
		-- here: the synchin in of asynchronous input signals takes place. 
		config_start_sr		<= config_start_sr(0) & config_start;
		ram_write_ready_sr	<= ram_write_ready_sr(0) & ram_write_ready;
		wiz_read_ready_sr 			<= wiz_read_ready_sr(0) & wiz_read_done;
		dg_config_done_sr <= dg_config_done_sr(0) & dg_config_done;
		roi_array_local 	<= roi_array;
		
		case state_mm is
		
		
		
    
        when MM_CONFIG =>
			state_sig <= X"1";

			
            config_ready <= '0';
            roi_max_array <= (others => 0);
            channel_size <= (others => 0);
            fifo_write_length <= (others => 0);
            fifo_channels_array <= (others => 0);
            event_size <= 0;
            ram_write_ea <= '0';
			wiz_write_ea <= '0';
            state_mm <= MAX_ROI;
          
        
        -- calculate max ROIs and channel sizes
        when MAX_ROI =>
			state_sig <= X"2";
          roi_index <= (drs_id * 9) + channel_id;
          state_mm <= MAX_ROI1;
        when MAX_ROI1 =>
			state_sig <= X"3";
          temp_roi <= roi_array_local (roi_index);
          state_mm <= MAX_ROI2;
        when MAX_ROI2 =>
			state_sig <= X"4";
          if (channel_id < 9) then
            if ( temp_roi > roi_max_array (channel_id)) then
              roi_max_array (channel_id) <= temp_roi;
            end if;
            channel_size (channel_id) <= channel_size (channel_id) + temp_roi + CHANNEL_HEADER_SIZE;
            drs_id <= drs_id + 1;
            state_mm <= MAX_ROI;
            if (drs_id = 3) then
              drs_id <= 0;
              channel_id <= channel_id + 1;
            end if;
          else
            drs_id <= 0;
            channel_id <= 0;
            channel_size (0) <= channel_size (0) + PACKAGE_HEADER_LENGTH;
            channel_size (8) <= channel_size (8) + PACKAGE_END_LENGTH;
            state_mm <= FIFO_CALC;
          end if;
        
        -- calculate number of channels that fit in FIFO
        when FIFO_CALC =>
			state_sig <= X"5";
          if (channel_id < 9) then
            if ((fifo_write_length (package_index) + channel_size (channel_id)) <= W5300_TX_FIFO_SIZE) then
              fifo_write_length (package_index) <= fifo_write_length (package_index) + channel_size (channel_id);
              fifo_channels_array (package_index) <= fifo_channels_array (package_index) + 1;
              channel_id <= channel_id + 1;
              event_size <= event_size + channel_size (channel_id);
            else
              package_index <= package_index + 1;
            end if;
          else
            number_of_packages <= package_index + 1;
            package_index <= 0;
            channel_index <= 0;
            channel_id <= 0;
            fifo_package_size_ram <= (others => 0);
            fifo_package_size_ram (0) <= PACKAGE_HEADER_LENGTH + PACKAGE_HEADER_ZEROS; 
            event_size_ram <= 0;
            event_size_ram_64b <= 0;
            max_events_ram <= 0;           
            state_mm <= RAM_CALC;
          end if;
          
        when RAM_CALC =>
			state_sig <= X"6";
          if (package_index < number_of_packages) then
            if (channel_index < fifo_channels_array (package_index)) then
              fifo_package_size_ram (package_index) <= 
			  fifo_package_size_ram (package_index) + ((roi_max_array (channel_id) + CHANNEL_HEADER_SIZE) * NUMBER_OF_DRS);
              channel_index <= channel_index + 1;
              channel_id <= channel_id + 1;
            else
              package_index <= package_index + 1;
              event_size_ram <= event_size_ram + fifo_package_size_ram (package_index);
              channel_index <= 0;
            end if;
          else
            fifo_package_size_ram (package_index - 1) <= fifo_package_size_ram (package_index - 1) + PACKAGE_END_LENGTH + PACKAGE_END_ZEROS;
            event_size_ram <= event_size_ram + PACKAGE_END_LENGTH + PACKAGE_END_ZEROS;
            state_mm <= RAM_CALC1;
          end if;
        when RAM_CALC1 =>
			state_sig <= X"7";
          max_events_ram <= max_events_ram + 1;
          if ((max_events_ram * event_size_ram) <= RAM_SIZE_16B) then
            state_mm <= RAM_CALC1;
          else
            max_events_ram <= max_events_ram - 1;
            state_mm <= RAM_CALC2;
          end if;
        when RAM_CALC2 =>
			state_sig <= X"8";
          event_size_ram_64b <= (event_size_ram / 4);
          events_in_ram <= 0;
          event_start_addr <= 0;
          write_start_addr <= 0;
          package_index <= 0;
          channel_id <= 0;
          ram_start_addr <= (others => '0');
          
          package_length <= conv_std_logic_vector (event_size, 16);
          for i in 0 to 8 loop
            roi_max(i) <= conv_std_logic_vector(roi_max_array(i), 11);
          end loop;
          state_mm <= CONFIG_DG;
		  
		 when CONFIG_DG =>
			state_sig <= X"9";
			dg_start_config <= '1';
			state_mm <= CONFIG_DG;
			if (dg_config_done_sr(1) = '0') then
				dg_start_config <= '0';
				state_mm <= WAIT_FOR_CONFIG_DG;
			end if;
		 
		 when WAIT_FOR_CONFIG_DG =>
			state_sig <= X"E";
			state_mm <= WAIT_FOR_CONFIG_DG;
			if (dg_config_done_sr(1) = '1') then
				state_mm <= UPDATE_OUTPUT;
			end if;
		  
		  
		when MM_MAIN =>
			state_sig <= X"A";
			state_mm <= MM_MAIN;
			
			config_ready <= '1';
		
			if (config_start_sr(1) = '1') then
				state_mm <= MM_CONFIG;
				
			elsif (ram_write_ready_sr(1) = '1') then
				-- this is part of the DG - MM handshake. 
				-- pulling write_ea low shows that MM understood DG is ready
				ram_write_ea <= '0';

				events_in_ram <= events_in_ram + 1;
				if ((event_start_addr + event_size_ram_64b) < (RAM_SIZE_64B - event_size_ram_64b)) then
				  event_start_addr <= event_start_addr + event_size_ram_64b;
				else
				  event_start_addr <= 0;
				end if;
				state_mm <= WRITE_FIN;
				
			elsif (wiz_read_ready_sr(1) = '1') then
				wiz_write_ea <= '0';
				
				if (package_index = (number_of_packages - 1)) then
				
					-- next address 
					if ((write_start_addr + fifo_package_size_ram (package_index)) < (RAM_SIZE_16B - event_size_ram)) then
						write_start_addr <= write_start_addr + fifo_package_size_ram (package_index);
					else
						write_start_addr <= 0;
					end if;
				else
					write_start_addr <= write_start_addr + fifo_package_size_ram (package_index);
				end if;
				
				-- increase pack_index
				-- the package_index goes from 0..NumOfPack-1
				
				if ( package_index < (number_of_packages - 1)) then
					package_index <= package_index + 1;
				else
					package_index <= 0;
					events_in_ram <= events_in_ram - 1;
				end if;
				
				
				
				
				state_mm <= READ_FIN;
			
			end if;
		  
		------------------------- WRITING to MEM was finished ------------------------------------
		------------------------------------------------------------------------------------------		  
		when WRITE_FIN =>
			state_sig <= X"B";
			ram_start_addr <= conv_std_logic_vector(event_start_addr, RAM_ADDR_WIDTH_64B);
			-- 2nd part of hand shake is, that DG pulls low ram_write_ready
			if (ram_write_ready_sr(1) = '0') then
				state_mm <= UPDATE_OUTPUT;
			end if;
			

		------------------------- READING from MEM was finished ----------------------------------
		------------------------------------------------------------------------------------------
			
		when READ_FIN =>
			state_sig <= X"C";
			if (wiz_read_ready_sr(1) = '0') then 
				
			
				state_mm <= UPDATE_OUTPUT;
			end if;
			
						
			
		 
		when UPDATE_OUTPUT =>
				
			   
				wiz_ram_start_addr <= conv_std_logic_vector(write_start_addr, RAM_ADDR_WIDTH_16B);
				wiz_write_length <= conv_std_logic_vector(fifo_write_length (package_index), 17);
				wiz_number_of_channels <= conv_std_logic_vector(fifo_channels_array (package_index), 4);
				
				if (package_index = 0) then
					-- first package -> write header
					wiz_write_header <= '1';
				else
					wiz_write_header <= '0';
				end if;
				
				if (package_index = (number_of_packages - 1)) then
					-- last package -> write end-flag
					wiz_write_end <= '1';
				else
					wiz_write_end <= '0';
				end if;
			
			state_mm <= UPDATE_OUTPUT_2;
		
		
		when UPDATE_OUTPUT_2 =>
			state_sig <= X"D";
			state_mm <= MM_MAIN;
			
			if (events_in_ram > 0) then
				wiz_write_ea <= '1';
			else 
				wiz_write_ea <= '0';
			end if;  
			
			if (events_in_ram < max_events_ram) then
				ram_write_ea <= '1';              
			else 
				ram_write_ea <= '0';
			end if;
			
			
		when others =>
			state_sig <= X"F";
			state_mm <= MM_CONFIG;
       
      end case; -- state_mm
    end if;
  end process mm; 
  
   
  
END ARCHITECTURE beha;

