----------------------------------------------------------------------------------
-- Company: CNRS
-- Engineer: CC
-- 
-- Create Date:    16:06:08 09/09/2013
-- Design Name: 
-- Module Name:    udp_tx_fsm - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: send bunch of data
--
-- Dependencies: 
--
-- Revision: 0.07
-- Revision 0.01 - File Created
-- Revision 0.03 - add bunch counter & busy counter 
-- Revision 0.04 - modify time out triggerring, start counter on tx last data 
-- Revision 0.05 - change FIFO & data_in 128bits
-- Revision 0.06 - add double FIFO mux & supress timeout (moved to fifomux)
-- Revision 0.07 - add UDP commnand acknowledgment
-- Additional Comments: 
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;
use work.ipv4_types.all;
use work.data_IO.all;
use work.genram_pkg.all;

entity udp_tx_fsm is
	generic(
		 g_data_tx_width  	: natural := 16;	-- minimum data width for WR MAC
		 g_nb_data_packed 	: natural := 20;	-- number max of data packed in one UDP frame 
		 g_add_data_tx_width : natural := 16;	-- tailer width
		 g_udp_cmd_width 		: natural := 16	-- UDP command width
		 );
    Port (
		clk_i				: in std_logic;
		rst_i				: in std_logic;
		--- configuration
		port_dstsrc_i 		: in std_logic_vector (15 downto 0);
		cfg_ip_target_i 	: in std_logic_vector(31 downto 0) := x"C0A80010";  			--IP dst 192.168.0.10
		-- Destination MAC address for data
		cfg_mac_target_i : in std_logic_vector(47 downto 0);
		-- Destination MAC address for ack
		mac_target_ack_i : in std_logic_vector(47 downto 0);
		-- Destination MAC address
		mac_target_o : out std_logic_vector(47 downto 0);
		-- UDP TX 
		udp_tx_start				: out std_logic;								-- indicates req to tx UDP
		udp_tx_o						: out udp_tx_type;							-- UDP tx cxns
		udp_tx_result				: in std_logic_vector (1 downto 0);		-- tx status (changes during transmission)
		udp_tx_data_out_ready	: in std_logic;								-- indicates udp_tx is ready to take data		
		busy_o						: out std_logic;	
		-- UDP RX header 
		udp_rx_hdr_i				: in udp_rx_header_type;		--udp_rx_type;
		--additionnal information sent once with bunch
		add_tx_data_i				: in  std_logic_vector (g_add_data_tx_width-1 downto 0);	
		-- UDP command ack
		udp_ack_i					: in std_logic :='0';
		udp_cmd_ack_i				: in std_logic_vector (g_udp_cmd_width-1 downto 0);
		--addr_ack_i 					: in src_addr_type;
		udp_ack_done_o				: out std_logic;
		--- FIFO interface	
		data4tx_i			: in std_logic_vector(g_data_tx_width-1 downto 0);     -- data for tx
		rd_en_o				: out std_logic;	
		--fifo_empty_i 		: in std_logic;
		data_cnt_i  		: in std_logic_vector(f_log2_size(2*g_nb_data_packed)-1 downto 0); --6bits for 40 events
		almost_full_i  	: in std_logic;
		tx_timeout_hit_i	: in std_logic;
		fifo_mux_o 			: out std_logic
--		TRIG0 : out std_logic_vector(31 downto 0);
--		TRIG1 : out std_logic_vector(31 downto 0)		
	 );
end udp_tx_fsm;

architecture Behavioral of udp_tx_fsm is

	type STATE_TYPE is (IDLE, WAIT_FOR_SEND_ACK, WAIT_FOR_SEND, SEND_ACK, SEND, ADD_INFO);
	signal current_state		: STATE_TYPE := IDLE;
	
	signal data4tx 			: std_logic_vector(15 downto 0);
	signal data_tx_buf		: std_logic_vector(g_data_tx_width-1 downto 0);
	signal cmd_buf				: std_logic_vector(g_udp_cmd_width-1 downto 0);
	signal nb_tx_word, nb_ack_word			: unsigned (3 downto 0); --16 => word max size 256 bits
	signal nb_tx_octet, nb_ack_octet 		: unsigned (4 downto 0); --32 => word max size 256 bits
	signal cnt_16bwords		: unsigned (3 downto 0); --16 => word max size 256 bits
	signal bunch_octet_size : unsigned (4 downto 0); --32 => bunch max size 256 bits
	signal bunch_tail_size 	: natural range 0 to 255;
	signal cmd_ack_size		: unsigned (15 downto 0);
--	signal tx_data_nb			: unsigned (9 downto 0);
	signal bunch_cnt			: unsigned (31 downto 0);
	signal bunch_tail			: std_logic_vector((g_add_data_tx_width + 32-1) downto 0);
	signal cnt_tx_data		: unsigned (f_log2_size(2*g_nb_data_packed)-1 downto 0);
	signal data_valid_out 	: std_logic := '0';
	
	signal fifo_mux 			: std_logic := '0';
	
begin

-- sizes computation 
bunch_tail_size 			<= g_add_data_tx_width + 32; 		--width of additionnal info + width of bunch counter
nb_tx_word					<= to_unsigned((g_data_tx_width/16),4);			--nb of 16bits word to send for one event
nb_tx_octet					<= nb_tx_word & '0';
nb_ack_word					<= to_unsigned((g_udp_cmd_width/16),4);			--nb of 16bits word to send for ack command (64 bits)
nb_ack_octet				<= nb_ack_word & '0';
bunch_octet_size 			<= to_unsigned((bunch_tail_size/8),5);
cmd_ack_size				<= x"00" & "000" & (nb_ack_octet + bunch_octet_size);	

--udp_tx_o.hdr.dst_port 	<=	Port_dstsrc;			
--udp_tx_o.hdr.src_port 	<= Port_dstsrc;
--udp_tx_o.hdr.dst_ip_addr <= cfg_ip_target_i;
udp_tx_o.hdr.checksum 	<= x"0000";
  
udp_tx_o.data.data_out 			<= data4tx;
udp_tx_o.data.data_out_valid 	<= data_valid_out;

-- change FIFO
fifo_mux_o <= fifo_mux;

process (clk_i)
	begin
	if rising_edge(clk_i)then
		if (rst_i = '1') then			
			udp_tx_o.hdr.data_length 		<= std_logic_vector(to_unsigned(((g_data_tx_width/16)*g_nb_data_packed),16));			
			data_valid_out 					<= '0';
			udp_tx_o.data.data_out_last 	<= '0';
			data4tx								<=  x"FFFF";	--(others => '0');	
			udp_tx_start						<=	'0';
			cnt_tx_data							<=(others => 'X');	
			cnt_16bwords						<=(others => 'X');
			bunch_cnt							<=(others => '0');
			bunch_tail 							<=(others => '0');
			fifo_mux 							<= '0';
			current_state						<=	IDLE;	

		else
			case current_state is
				when IDLE =>
					rd_en_o	<= '0';
					fifo_mux <= '0';
				-- send ack when udp command (64bits) received, only when in reset mode => no data are sent
				-- send back 64bits word command received
					if udp_ack_i = '1' then 
						mac_target_o					<= mac_target_ack_i;								--addr_ack_i.mac_addr;	
						udp_tx_o.hdr.dst_port 		<=	udp_rx_hdr_i.src_port;						--addr_ack_i.port_nb;			
						udp_tx_o.hdr.src_port 		<= udp_rx_hdr_i.dst_port;						--addr_ack_i.port_nb;
						udp_tx_o.hdr.dst_ip_addr 	<= udp_rx_hdr_i.src_ip_addr;					--addr_ack_i.ip_addr;
						udp_tx_o.hdr.data_length		<= std_logic_vector(cmd_ack_size);	-- x"001C";		
						cnt_16bwords					<= nb_ack_word;
						data_valid_out 				<= '0';
						udp_tx_o.data.data_out_last <= '0';
						data4tx							<= x"FFFF";
						udp_tx_start					<=	'1';				
						bunch_cnt						<= bunch_cnt + 1;
						udp_ack_done_o					<= '0';
						bunch_tail(g_add_data_tx_width-1 downto 0) <= add_tx_data_i;
						current_state					<= WAIT_FOR_SEND_ACK;  --SEND_ACK;
				--- start Tx when FIFO has g_nb_data_packed number of data OR time out is reached 
					elsif (almost_full_i = '1') or (tx_timeout_hit_i = '1') then 
						mac_target_o					<= cfg_mac_target_i;
						udp_tx_o.hdr.dst_port 		<=	port_dstsrc_i;			
						udp_tx_o.hdr.src_port 		<= port_dstsrc_i;
						udp_tx_o.hdr.dst_ip_addr   <= cfg_ip_target_i;
						udp_tx_o.hdr.data_length 	<= "00000" & std_logic_vector(((unsigned(data_cnt_i))* nb_tx_octet) + bunch_octet_size);
						cnt_tx_data						<=	unsigned(data_cnt_i); 	-- number of FIFO words
						cnt_16bwords					<= nb_tx_word -1;
						data_valid_out 				<= '0';
						udp_tx_o.data.data_out_last <= '0';
						data4tx							<= x"FFFF";
						udp_tx_start					<=	'1';	
						bunch_cnt						<= bunch_cnt + 1;
						udp_ack_done_o					<= '0';
						bunch_tail(g_add_data_tx_width-1 downto 0) <= add_tx_data_i;
						current_state					<= WAIT_FOR_SEND;						
					else
						data4tx			   <= (others => 'X');
						udp_tx_start		<=	'0';
						cnt_tx_data			<=(others => 'X');
						current_state		<= IDLE;
					end if;	
					
				when WAIT_FOR_SEND_ACK =>
					udp_ack_done_o	<= '0';
					udp_tx_start	<=	'0';
					busy_o			<= '1';	
					rd_en_o			<= '0';
					if (udp_tx_data_out_ready = '1') then					
						cmd_buf			<= udp_cmd_ack_i;	
						bunch_tail(bunch_tail_size-1 downto (bunch_tail_size-bunch_cnt'length)) <= std_logic_vector(bunch_cnt);
						current_state	<=	SEND_ACK;	
					else 
						current_state 	<= WAIT_FOR_SEND_ACK;	
					end if;
					
				when WAIT_FOR_SEND =>
					udp_ack_done_o	<= '0';
					udp_tx_start	<=	'0';
					busy_o			<= '1';	
					if (udp_tx_data_out_ready = '1') then
						rd_en_o			<= '1';
						data_tx_buf		<= data4tx_i;
						bunch_tail(bunch_tail_size-1 downto (bunch_tail_size-bunch_cnt'length)) <= std_logic_vector(bunch_cnt);
						current_state	<=	SEND;	
					else 
						rd_en_o			<= '0';
						current_state 	<= WAIT_FOR_SEND;	
					end if;
				
				when SEND_ACK =>
					udp_ack_done_o						<= '1';
					udp_tx_start						<=	'0';
					udp_tx_o.data.data_out_last 	<= '0';
					fifo_mux 							<= '0';
					busy_o								<= '1';
					rd_en_o								<= '0';
					data_valid_out 					<= '1';
					data4tx 								<= cmd_buf(g_udp_cmd_width-1 downto g_udp_cmd_width-16);
					if cnt_16bwords = 0 then				
						data_valid_out 	<= '0';
						cnt_16bwords		<= to_unsigned(bunch_tail_size/16,4) -1;
						current_state		<=	ADD_INFO;
					else
						cnt_16bwords	<= cnt_16bwords -1;
						cmd_buf(g_udp_cmd_width-1 downto 16) <= cmd_buf((g_udp_cmd_width-16-1) downto 0);
						current_state	<=	SEND_ACK;
					end if;
				
				when SEND =>
					udp_ack_done_o						<= '1';
					udp_tx_start						<=	'0';
					udp_tx_o.data.data_out_last 	<= '0';
					fifo_mux 							<= '0';
					busy_o								<= '1';
					if cnt_tx_data = 0 then 				-- data FIFO is empty, add bunch tailer
						rd_en_o				<= '0';
						data_valid_out 	<= '0';
						cnt_16bwords		<= to_unsigned(bunch_tail_size/16,4) -1;
						current_state		<=	ADD_INFO;
					else
						data_valid_out 	<= '1';
						data4tx 				<= data_tx_buf(g_data_tx_width-1 downto g_data_tx_width-16);
						if cnt_16bwords = 0 then
							rd_en_o			<= '1';
							data_tx_buf		<= data4tx_i;
							cnt_tx_data 	<= cnt_tx_data -1;
							cnt_16bwords	<= nb_tx_word -1;
							current_state 	<= SEND;
						else
							rd_en_o			<= '0';
							cnt_16bwords	<= cnt_16bwords -1;
							data_tx_buf(g_data_tx_width-1 downto 16) <= data_tx_buf((g_data_tx_width-16-1) downto 0);
							current_state	<=	SEND;
						end if;
					end if;
					
				when ADD_INFO =>
					udp_ack_done_o		<= '0';
					data4tx 				<= bunch_tail((bunch_tail_size-1) downto bunch_tail_size-16);
					data_valid_out 	<= '1';
					if (cnt_16bwords = 0) then 			--last data 
						fifo_mux 						<= '1';			-- ready to send next FIFO	data
						udp_tx_o.data.data_out_last <= '1';	
						current_state					<=	IDLE;
					else 
						fifo_mux 											<= '0';
						cnt_16bwords										<= cnt_16bwords -1;
						udp_tx_o.data.data_out_last 					<= '0';	
						bunch_tail((bunch_tail_size-1) downto 16) <= bunch_tail((bunch_tail_size-16-1) downto 0);
						current_state										<=	ADD_INFO;
					end if;
						
				when others => 
					current_state	<=	IDLE;			
			end case;
			end if;
	end if;
	end process;
	
  
	-----DEBUG-----
--	TRIG0(31 downto 16)	<= data4tx;
--	TRIG0(12)				<= timeout_cnt_en;
--	TRIG0(11)				<= tx_timeout_hit;
--	TRIG0(10)				<= data_valid_out;
--	TRIG0(9 downto 5)		<= std_logic_vector(cnt_16bwords);
--	TRIG0(4 downto 0)		<= std_logic_vector(cnt_tx_data(4 downto 0));	
--
--	TRIG1(27 downto 22)	<= data_cnt_i;
	--TRIG1(21 downto 0)	<= std_logic_vector(timeout_counter); 	
				

end Behavioral;



