----------------------------------------------------------------------------------
-- Company: GRIIA - ETIS
-- Engineer: GAMOM, KIEGAING
-- 
-- Create Date:    01:02:10 06/17/2011 
-- Design Name: 
-- Module Name:    EX2_FSM - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
-- machine a etat qui execute la reception des packet dans le core mpi
-- Dependencies: 
--ss
-- Revision: 26/01/2012
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
library NocLib ;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.Numeric_std.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use Work.Packet_type.ALL;
use NocLib.CoreTypes.all;
---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity EX2_FSM is

   generic (
				 pid : std_logic_vector(3 downto 0) :="0001"; -- id du processeur
				 nprocs : std_logic_vector(3 downto 0):="0100"-- nombre de processeur du MPSOC - 1
			 );
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;

           Instruction_en : in std_logic;
			  
			dma_wr_grant : in  STD_LOGIC;
			dma_wr_request : out  STD_LOGIC;
			dma_rd_grant : in  STD_LOGIC;
			dma_rd_request : out  STD_LOGIC;
			ram_rd : out std_logic;
			ram_wr : out std_logic;
			ram_address : out std_logic_vector(ADRLEN-1 downto 0);
			Ram_data_in : in STD_LOGIC_VECTOR (Word-1 downto 0);
			Ram_data_out : out STD_LOGIC_VECTOR (Word-1 downto 0);
			  
			fifo_data : out  STD_LOGIC_VECTOR (Word-1 downto 0);
			fifo_wr_en : out  STD_LOGIC;
			fifo_full : in  STD_LOGIC;
			  
			AppRank : in  STD_LOGIC_VECTOR(3 downto 0);
			AppSize : in  STD_LOGIC_VECTOR(3 downto 0);
           packet_received : out  STD_LOGIC;
           packet_ack : in  STD_LOGIC;
           barrier_completed : out  STD_LOGIC;
			  Ready : Out std_logic;
			  AppInitReq :out  STD_LOGIC; -- requête d'initialisation de l'application
			  AppInitAck :in  STD_LOGIC; -- Acquitement d'initialisation
			  Initialized:in std_logic ; -- état de la Lib
			  
           switch_data_available : in  STD_LOGIC;			  
           switch_port_out_data : in  STD_LOGIC_VECTOR (Word-1 downto 0);
           switch_port_out_rd_en : out  STD_LOGIC
			  
			  
			  
			  );
end EX2_FSM;

architecture Behavioral of EX2_FSM is
-- définition du type etat de la machine à etat
type fsm_states is (fetch_packet_type, decode_packet_type, decode_packet_type2, 
fetch_addresses,execute_spawn,execute_put1,execute_put2,execute_put3 ,execute_put4,
execute_put5,execute_get1, execute_get2,execute_get3,execute_get4,
 execute_barrier1, execute_barrier2, execute_barrier3, execute_barrier4,
  execute_barrier5, execute_barrier6, execute_barrier7,execute_init1,execute_init2);
signal ex2_state_mach :fsm_states;
-- machine a etat du module
signal packet_type : std_logic_vector(3 downto 0);
signal packet_length : std_logic_vector(Word-1 downto 0);
signal barrier_counter : std_logic_vector(3 downto 0);
signal pading_data,data_to_ram :  std_logic_vector(Word-1 downto 0):=(others=>'0');
signal n : std_logic_vector(3 downto 0);
signal dest_address : std_logic_vector(ADRLEN-1 downto 0):=(others=>'Z');
signal data_to_write_fifo :  std_logic_vector(Word-1 downto 0);
signal Ex2_on : std_logic:='0';
signal rd_ok ,wr_ok:std_logic:='0';

begin
ram_address <= dest_address;
fifo_data <= data_to_write_fifo;

-- processus de transistion entre les etats 
fsm_nst_logic : process(clk)
variable delai : natural range 0 to 1:=0; --permet de détecter que l'écriture en RAM doit être décalée
variable tempval : std_logic_vector(Word-1 downto 0);
begin
 if rising_edge(clk) then
	if reset = '1' then
	ex2_state_mach <= fetch_packet_type;
	Ex2_on<='0';
	barrier_counter <= "0000";
	 else
		Ex2_on<=Instruction_en; --détermine si le module peut être activer ou non
		  case ex2_state_mach is
			 when fetch_packet_type => if switch_data_available ='1' and Ex2_on='1'  then 	--and initialized ='1'						
											      ex2_state_mach  <= decode_packet_type;
											      packet_type<=switch_port_out_data(7 downto 4);
									         else
											      ex2_state_mach <= fetch_packet_type;
								           end if;
			when decode_packet_type => if switch_data_available ='0' then
													ex2_state_mach <= decode_packet_type;
												else
													 if packet_type = MPI_PUT then
													    packet_length <= switch_port_out_data - 2;
														 n <="0000";
														 ex2_state_mach <= decode_packet_type2;
													  elsif packet_type = MPI_GET then
													    packet_length <=switch_port_out_data-2;
														 ex2_state_mach <= decode_packet_type2;
													  elsif packet_type = MPI_BARRIER_REACHED or packet_type = MPI_BARRIER_COMPLETED then
													    packet_length <= switch_port_out_data;
														 n <= "0000";
														 ex2_state_mach <= execute_barrier1;
													  elsif packet_type = MPI_INIT or packet_type =INIT_SETRANK or packet_type =INIT_SEEKMAIN then
													    ex2_state_mach <= execute_init1;
													  else
														  ex2_state_mach <= decode_packet_type;
													 end if;
												end if;
			when decode_packet_type2 => if packet_type = MPI_PUT then
													ex2_state_mach <= fetch_addresses;
												  else
												 	ex2_state_mach <= execute_get1;							  
							               end if;        									
         when fetch_addresses => if  switch_data_available = '1' and n = 0 then
												  dest_address(15 downto 8) <= switch_port_out_data;
												  n <= n + 1;
												  ex2_state_mach <= fetch_addresses;
												elsif switch_data_available = '1' and n = 1 then
												  dest_address(Word-1 downto 0) <= switch_port_out_data;
												  packet_length <= packet_length - 2;	
												  ex2_state_mach <= execute_put1;
												else
												  ex2_state_mach <= fetch_addresses;
								         end if;				
												 -- execution du mpi Init
			when execute_init1 =>if Initialized='1' then
											ex2_state_mach<=execute_init2;
										end if;
			when execute_init2=>  if AppInitAck='1' then
											ex2_state_mach<=fetch_packet_type;
										end if;
			when execute_put1 => if dma_wr_grant = '1' then
										  ex2_state_mach <= execute_put2;
										  data_to_ram<=switch_port_out_data;
										  rd_ok<='1';
										  n<="0000";
										  delai:=0;
										 else
										  ex2_state_mach <= execute_put1;
								      end if;	
			when execute_put2 =>	if unsigned( packet_length) > 1  then 
											if switch_data_available = '1' and dma_wr_grant='1'  then
													-- n<=n+1;
													 --if delai=0 then 
														 packet_length <= packet_length - 1;
														 dest_address <= dest_address + 1;
														 ex2_state_mach <= execute_put2;
														 rd_ok<='1';
														 data_to_ram<=switch_port_out_data;
													--else
													--  delai:=0;
													-- end if;
												 else
													if switch_data_available = '1' and dma_wr_grant='0'then
														if delai=0 then 
														data_to_ram<=switch_port_out_data;
														end if;
														delai:=1;--indique qu'un temps supplémentaire est
														--nécessaire pour écrire la donnée en RAM 
													end if;
													ex2_state_mach <= execute_put2;
													rd_ok<='0';
												end if;
										else 
											if switch_data_available = '1' then 
												--la dernière donnée à écrire en RAM
												data_to_ram<=switch_port_out_data;
											end if;
											rd_ok<='0';
											 
											if dma_wr_grant='1' then 

												 ex2_state_mach <= execute_put3;

												 
											 end if;
									    end if;
									
			when execute_put3 =>			if dma_rd_grant='1' then 
													dest_address<=std_logic_vector(to_unsigned(core_base_adr+4,16));
													ex2_state_mach <= execute_put4;
													n<="0000";
													rd_ok<='1';
													wr_ok<='0';
												end if;
			
			when execute_put4 => if n=0 then
											if dma_rd_grant='1' then 
												n<=n+1;
												tempval:=Ram_data_in;
												tempval(4):='1';			--SET du bit DReceived
												data_to_ram<=tempval;
												
											end if;
												rd_ok<='1';
												wr_ok<='0';
											elsif n=1 then
												rd_ok<='0';
												wr_ok<='1';
												n<=n+1;
											elsif n=2 then
												if dma_wr_grant='1' then
													rd_ok<='0';
													wr_ok<='1';
													ex2_state_mach <= execute_put5;
													n<="0000";
												end if;
											end if;
											
										 
								  
										dest_address<=std_logic_vector(to_unsigned(core_base_adr+4,16));--Adr de gest de la transaction
			when execute_put5 => 
										   ex2_state_mach <= fetch_packet_type; -- fin du mpi_put
										
										 
			when execute_get1 =>  if switch_data_available = '1' then  -- conversion du get en put en empilement dans le fifo
										     data_to_write_fifo <= MPI_PUT & switch_port_out_data(3 downto 0);
											  packet_length <= packet_length - 1;
											  ex2_state_mach <= execute_get2;
											  wr_ok<='1';
								      end if;	
			when execute_get2 => if fifo_full = '0'and switch_data_available ='1' and packet_length > 0 then
										   data_to_write_fifo <= switch_port_out_data;
											packet_length <= packet_length - 1;
										   ex2_state_mach <= execute_get2;
											wr_ok<='1';
										elsif packet_length = 0 and  switch_data_available ='1' then--
										   ex2_state_mach <= fetch_packet_type;
											wr_ok<='0';
										  else
										   ex2_state_mach <= execute_get3;
											wr_ok<='0';
								    end if;
		   when execute_get3 => if dma_rd_grant='1' then -- fin du mpi_put
										   ex2_state_mach <= execute_get4;
											n<="0000";
											--activer le bit sending du registre de transfert
										  else
											ex2_state_mach <= execute_get3;
								      end if;
											
										dest_address<=std_logic_vector(to_unsigned(core_base_adr+4,16));
			when execute_get4 => 
											if n=0 then
													if dma_rd_grant='1' then
														n<=n+1;
														tempval:=Ram_data_in;
														tempval(2):='1';  	--mise à 1 du Bit Dreceiving
														tempval(5):='0'; 		--Mise à 0 du Bit Sent
														data_to_ram<=tempval;
													end if;
													rd_ok<='1';
													wr_ok<='0';
											elsif n=1 then
												n<=n+1;
											elsif n=2 then
												n<=n+1;
												rd_ok<='0';
												wr_ok<='1';
											elsif n=3 then
											  if dma_wr_grant = '1' then
													n<="0000";
													ex2_state_mach <= fetch_packet_type; -- fin du mpi_get
											  end if;
											   rd_ok<='0';
												wr_ok<='1';
											end if;
										   
										 
										dest_address<=std_logic_vector(to_unsigned(core_base_adr+4,16));
								-- execution du barrier
			when execute_barrier1 => if switch_data_available = '1' then
												 pading_data <= switch_port_out_data;
												 ex2_state_mach <= execute_barrier2;
											  else
											    ex2_state_mach <= execute_barrier1;	
											 end if;	
			when execute_barrier2 => if packet_type = MPI_BARRIER_REACHED then
												 barrier_counter <= barrier_counter + 1;
												 ex2_state_mach <= execute_barrier4;	
											  else
											    ex2_state_mach <= execute_barrier3;	
											 end if;	
			when execute_barrier3 => if n < 10 then
												 n <= n + 1;
												 ex2_state_mach <= execute_barrier3;	
											  else
											    ex2_state_mach <= fetch_packet_type;	
											 end if;	
			when execute_barrier4 => if barrier_counter = nprocs then -- entete du packet MPI_BARRIER_COMPLETED 
												 data_to_write_fifo <= MPI_BARRIER_COMPLETED & "0000";
												 ex2_state_mach <= execute_barrier5;	
											  else
											    ex2_state_mach <= fetch_packet_type;	
											 end if;	
			when execute_barrier5 => if fifo_full = '0' then  -- taille du packet MPI_BARRIER_COMPLETED
												 data_to_write_fifo <= "00000011";
												 ex2_state_mach <= execute_barrier6;	
											  else
											    ex2_state_mach <= execute_barrier5;	
											 end if;		
			when execute_barrier6 => if fifo_full ='0' then -- troisième octet du packet MPI_BARRIER_COMPLETED
												 data_to_write_fifo <= "00000000";
												 ex2_state_mach <= execute_barrier7;	
											  else
											    ex2_state_mach <= execute_barrier6;	
											 end if;				
			when execute_barrier7 => if fifo_full = '0' then
												 barrier_counter <= "0000";
												 ex2_state_mach <= fetch_packet_type;	
											  else
											    ex2_state_mach <= execute_barrier7;	
											 end if;				
			
			when others => ex2_state_mach <= fetch_packet_type;
		   end case;
    end if;
  end if;
 end process;
 
 -- sortie de la machine à etat
--
 ex2_fsm_action : process(ex2_state_mach, fifo_full, packet_length, data_to_write_fifo, packet_type,
 switch_data_available,switch_port_out_data,Ram_data_in,rd_ok)
  variable transact : std_logic_vector(Word-1 downto 0);
  begin   
-- code fonctionnel	
	case ex2_state_mach is
	
		when fetch_packet_type => fifo_wr_en <= '0';
										  switch_port_out_rd_en <= switch_data_available;
										  packet_received <= '0'; 
										  dma_wr_request <= '0';
										  dma_rd_request <= '0';
										  barrier_completed <= '0';
										  Ram_data_out<=(others=>'Z');
										  Ram_rd<='0';
										  Ram_wr<='0';
										  Ready<='1';
										  AppInitReq<='0';
										  
								  
		when decode_packet_type => fifo_wr_en <= '0';
										  switch_port_out_rd_en <= switch_data_available;
										  packet_received <= '0'; 
										  dma_wr_request <= '0';
										  dma_rd_request <= '0';
										  Ram_rd<='0';
										  Ram_wr<='0';
										  Ram_data_out<=(others=>'Z');
										  barrier_completed <= '0';
										  AppInitReq<='0';	
											Ready<='0';
		
		when decode_packet_type2 => fifo_wr_en <= '0';
										    switch_port_out_rd_en <= '0';
										    packet_received <= '0'; 
										    dma_wr_request <= '0';
										    dma_rd_request <= '0';
										    Ram_rd<='0';
											Ram_wr<='0';
											Ram_data_out<=(others=>'Z');
										    barrier_completed <= '0';
										    AppInitReq<='0';	
											Ready<='0';
		when fetch_addresses =>  fifo_wr_en <= '0';
										  switch_port_out_rd_en <= switch_data_available;
										  packet_received <= '0'; 
										  dma_wr_request <= '0';
										  dma_rd_request <= '0';
										  Ram_rd<='0';
										  Ram_wr<='0';
										  Ram_data_out<=(others=>'Z');
										  barrier_completed <= '0';
										  AppInitReq<='0';	
											Ready<='0';
		
		when execute_put1 =>      fifo_wr_en <= '0';
										  switch_port_out_rd_en <= '0';
										  packet_received <= '0'; 
										  dma_wr_request <= '1';
										  dma_rd_request <= '0';
										  Ram_rd<='0';
										  Ram_wr<='0';
										  Ram_data_out<=(others=>'Z');
										  barrier_completed <= '0';
										  AppInitReq<='0';	
											Ready<='0';
		
		when  execute_put2 =>   Ready<='0';
										fifo_wr_en <= '0';
										switch_port_out_rd_en <=rd_ok;
									   
										if rd_ok = '1' then
										
										 Ram_data_out<=switch_port_out_data;
										else
											Ram_data_out<=data_to_ram;
										end if;
										Ram_wr<='1';
										Ram_rd<='0';
										packet_received <= '0';
										dma_rd_request <= '0'; 
										dma_wr_request <= '1';
										AppInitReq<='0';	
										barrier_completed <= '0';
		when  execute_put3 => 	 Ready<='0';
										fifo_wr_en <= '0';
									   switch_port_out_rd_en <=rd_ok;	
										--ne pas corrompre le contenu de la RAM
										Ram_data_out<=data_to_ram; 					
										Ram_wr<='0';
										Ram_rd<='1';
										packet_received <= '0';
										dma_rd_request <= '1'; 
										dma_wr_request <= '0';
										AppInitReq<='0';	
										barrier_completed <= '0';
										
		
		when execute_put4 =>    fifo_wr_en <= '0';
										Ready<='0';
										switch_port_out_rd_en <= '0';
										 packet_received <= '1'; 
										 dma_rd_request <= rd_ok;
										 dma_wr_request <=wr_ok;
										 Ram_wr<=wr_ok;
										 Ram_rd<=rd_ok;
										 AppInitReq<='0';	 
										 barrier_completed <= '0';	
										 Ram_data_out<=Ram_data_in or "00000010"; -- le résultat de l'exécution 
											
		when execute_put5 =>		  
										Ready<='0';
										switch_port_out_rd_en<='0';
										fifo_wr_en <= '0';
								        packet_received <= '1';
								        AppInitReq<='0';
								        barrier_completed <= '0';
								        dma_rd_request <= rd_ok;
										dma_wr_request <= wr_ok;
										Ram_rd<=rd_ok;
										Ram_wr<=wr_ok;
										Ram_data_out<=ram_data_in or "00000010";
										--Result <=(1=>'1',others=>'0'); --put completed								  
		
		when execute_get1=>       fifo_wr_en <= '0';
										  switch_port_out_rd_en <= switch_data_available;
										  packet_received <= '0'; 
										  dma_wr_request <= '0';
										  dma_rd_request <= '0';
										  Ram_rd<='0';
										  Ram_wr<='0';
										  Ram_data_out<=(others=>'Z');
										  barrier_completed <= '0';
										  AppInitReq<='0';	
											Ready<='0';
		
		when execute_get2 =>	     if fifo_full = '0' and switch_data_available = '1'  and packet_length > 0 then
										    
											 switch_port_out_rd_en <='1';
										  else
										    
											 switch_port_out_rd_en <='0';
										  end if;
										  
										  fifo_wr_en <= Wr_ok;
										  Ready<='0';
										  packet_received <= '0';
										  dma_rd_request <= '0'; 
										  dma_wr_request <= '0';
										  Ram_rd<='0';
										  Ram_wr<='0';
										  Ram_data_out<=(others=>'Z');
										  barrier_completed <= '0'; 
										  AppInitReq<='0';	
										    
		when execute_get3 =>      		fifo_wr_en <= '0';
										Ready<='0';
										switch_port_out_rd_en <= '0';
										 packet_received <= '1'; 
										 dma_rd_request <= '1';
										 dma_wr_request <='0';
										 Ram_wr<='0';
										 Ram_rd<='1';
										  AppInitReq<='0';	
										  barrier_completed <= '0';	
										 Ram_data_out<=Ram_data_in or "00000010"; -- activer le bit DSending
											
		when execute_get4 =>		  
										Ready<='0';
										barrier_completed <= '0';
										switch_port_out_rd_en<='0';
										fifo_wr_en <= '0';
								        packet_received <= '1';
								        AppInitReq<='0';
								        dma_rd_request <= rd_ok;
										dma_wr_request <= wr_ok;
										Ram_rd<=rd_ok;
										Ram_wr<=wr_ok;
										Ram_data_out<=ram_data_in or "00000010"; --activer le bit DSending
		
		
		when execute_barrier1 =>  fifo_wr_en <= '0';
										  switch_port_out_rd_en <= switch_data_available;
										  packet_received <= '0'; 
										  dma_wr_request <= '0';
										  dma_rd_request <= '0';
										  Ram_rd<='0';
											Ram_wr<='0';
											Ram_data_out<=(others=>'Z');
										  barrier_completed <= '0'; 
											Ready<='0';
										  AppInitReq<='0';	
										  
		when execute_barrier2 =>  fifo_wr_en <= '0';
										Ready<='0';
										  switch_port_out_rd_en <='0';
										  packet_received <= '0'; 
										  dma_wr_request <= '0';
										  dma_rd_request <= '0';
										  Ram_rd<='0';
											Ram_wr<='0';
											Ram_data_out<=(others=>'Z');
										  barrier_completed <= '0';    
										  AppInitReq<='0';	
										  
		when execute_barrier3 =>	fifo_wr_en <= '0';
										  switch_port_out_rd_en <='0';
										  Ready<='0';
										  packet_received <= '0'; 
										  dma_wr_request <= '0';
										  dma_rd_request <= '0';
										  Ram_rd<='0';
											Ram_wr<='0';
											Ram_data_out<=(others=>'Z');
										  barrier_completed <= '1';	 
											AppInitReq<='0';	
		
		when execute_barrier4 =>   fifo_wr_en <= '0';
										  switch_port_out_rd_en <='0';
										  packet_received <= '0'; 
										  dma_wr_request <= '0';
										  dma_rd_request <= '0';
										  Ram_rd<='0';
											Ram_wr<='0';
											Ram_data_out<=(others=>'Z');
										  barrier_completed <= '0';
										  AppInitReq<='0';	
										  Ready<='0';
										  
										  
		when execute_barrier5 =>  fifo_wr_en <= not(fifo_full);
										  switch_port_out_rd_en <='0';
										  packet_received <= '0'; 
										  dma_wr_request <= '0';
										  dma_rd_request <= '0';
										  Ram_rd<='0';
											Ram_wr<='0';
											Ram_data_out<=(others=>'Z');
										  barrier_completed <= '0';										  
										  AppInitReq<='0';	
										  Ready<='0';
										  
		when execute_barrier6 =>  fifo_wr_en <= not(fifo_full);										 
										  switch_port_out_rd_en <= '0';
										  packet_received <= '0'; 
										  dma_wr_request <= '0';
										  dma_rd_request <= '0';
										  Ram_rd<='0';
										  Ram_wr<='0';
										  Ram_data_out<=(others=>'Z');
										  barrier_completed <= '0';
										  AppInitReq<='0';
										  Ready<='0';		 	
		
		when execute_barrier7 =>  fifo_wr_en <= not(fifo_full); 
										  switch_port_out_rd_en <= '0';
										  packet_received <= '0'; 
										  dma_wr_request <= '0';
										  dma_rd_request <= '0';
										  Ram_rd<='0';
										  Ram_wr<='0';
										  Ram_data_out<=(others=>'Z');
										  barrier_completed <= '0';
										  AppInitReq<='0';	
										  Ready<='0';
										  	
     
	  when execute_init1 =>    	fifo_wr_en <= not(fifo_full); 
										   switch_port_out_rd_en <= '0';
										   packet_received <= '0'; 
										   dma_wr_request <= '0';
										   dma_rd_request <= '0';
										   Ram_rd<='0';
											Ram_wr<='0';
										   barrier_completed <= '0';
										   Ready<='0';
											Ram_data_out<=(others =>'Z');
											AppInitReq<='1';
											
		
		when execute_init2 =>    	fifo_wr_en <= not(fifo_full); 
										   switch_port_out_rd_en <= '0';
										   packet_received <= '0'; 
										   dma_wr_request <= '0';
										   dma_rd_request <= '0';
										   Ram_rd<='0';
											Ram_wr<='0';
										   barrier_completed <= '0';
											Ram_data_out<=(others =>'Z');
											AppInitReq<='1';
											Ready<='0';
								
		when others => 			 Ready<='1';   -- le module est à nouveau libre
									fifo_wr_en <= '0';
										  switch_port_out_rd_en <= '0';
										  packet_received <= '0'; 
										  dma_wr_request <= '0';
										  dma_rd_request <= '0';
										  barrier_completed <= '0';
										  Ram_data_out<=(others=>'Z');
										  Ram_rd<='0';
										  Ram_wr<='0';
										  Ready<='1';
										  AppInitReq<='0';
	end case;
	
 end process;

end Behavioral;

