----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    14:50:41 10/11/2013 
-- Design Name: 
-- Module Name:    packet_parser - 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;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.ALL;

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


entity packet_parser is
   port (
		clk									: in  std_logic;
		reset_enet_counters				: in  std_logic;
      rx_clk								: in  std_logic;
      rx_data_in							: in  std_logic_vector(3 downto 0);
      rx_data_valid						: in  std_logic;
		tx_clk								: in  std_logic;
      tx_data_out							: out std_logic_vector(3 downto 0);
      tx_data_enable						: out std_logic;
		message_fifo_in_wr_en			: out std_logic;
		message_fifo_in_din				: out std_logic_vector(8 downto 0);
		num_good_pkts_counter			: out std_logic_vector(31 downto 0);
		num_bad_pkts_counter				: out std_logic_vector(31 downto 0);
		board_id								: in  std_logic_vector(2 downto 0);
		message_fifo_out_rd_en			: out std_logic;
		message_fifo_out_dout			: in  std_logic_vector(63 downto 0);
		message_fifo_out_empty			: in  std_logic;
		send_reply							: in  std_logic;
		reply_busy							: out std_logic;
		pkt_rx_busy							: out std_logic;
		pkt_tx_arp_busy					: out std_logic;
		pkt_tx_udp_busy					: out std_logic;
		debug									: out std_logic_vector(1 downto 0)
	);
end packet_parser;



architecture Behavioral of packet_parser is

	component rx_fifo
	port (
		wr_clk			: in std_logic;
		rd_clk			: in std_logic;
		din				: in std_logic_vector(9 downto 0);
		wr_en				: in std_logic;
		rd_en				: in std_logic;
		dout				: out std_logic_vector(9 downto 0);
		full				: out std_logic;
		empty				: out std_logic;
		valid				: out std_logic
	);
	end component;
	
	signal rx_data_fifo_din						: std_logic_vector(9 downto 0) := (others => '0');
	signal rx_data_fifo_wr_en					: std_logic := '0';
	signal rx_data_fifo_rd_en					: std_logic := '0';
	signal rx_data_fifo_dout					: std_logic_vector(9 downto 0);
	signal rx_data									: std_logic_vector(7 downto 0);
	signal rx_data_fifo_full					: std_logic;
	signal rx_data_fifo_empty					: std_logic;
	signal rx_data_fifo_valid					: std_logic;
		
--	signal rx_last_data_valid					: std_logic := '0';
	signal rx_last_data_in						: std_logic_vector(7 downto 0) := (others => '0');
	signal rx_data_in_start						: std_logic := '0';
	
	type   rx_packet_in_type is (idle, buildPacket);
	signal rx_packet_in_state					: rx_packet_in_type := idle;
	signal rx_data_in_new_pkt					: std_logic;
	signal rx_data_in_new						: std_logic;
	signal rx_data_in_end						: std_logic := '0';
	signal rx_data_in_nibble					: std_logic := '0';
	signal rx_data_in_nibble_0					: std_logic_vector(3 downto 0) := (others => '0');
	signal rx_data_in_nibble_1					: std_logic_vector(3 downto 0) := (others => '0');
	
	signal rx_data_pkt_start					: std_logic;
	signal rx_data_pkt_end						: std_logic;


	component pkt_crc_32
	port (
		clock			: in std_logic;
		reset			: in std_logic;
		data			: in std_logic_vector(7 downto 0);
		load_init	: in std_logic;
		calc			: in std_logic;
		d_valid		: in std_logic;          
		crc			: out std_logic_vector(7 downto 0);
		crc_reg		: out std_logic_vector(31 downto 0);
		crc_valid	: out std_logic
	);
	end component;

	signal rx_crc_clear							: std_logic := '0';
	signal rx_crc_calc							: std_logic := '0';
	signal rx_crc_read							: std_logic := '0';
	signal rx_crc_byte							: std_logic_vector(7 downto 0);
	signal rx_crc_valid							: std_logic;
		
	signal num_good_pkts_counter_i			: unsigned(31 downto 0) := (others => '0');
	signal num_bad_pkts_counter_i				: unsigned(31 downto 0) := (others => '0');

	type   pkt_rx_type is (idle, CheckPacketStart, readPreamble, readRxHdrHdwDst_0, readRxHdrHdwDst_1,
										readRxHdrHdwDst_2, readRxHdrHdwDst_3, readRxHdrHdwDst_4, readRxHdrHdwDst_5,
											readRxHdrHdwSrc_0, readRxHdrHdwSrc_1, readRxHdrHdwSrc_2, readRxHdrHdwSrc_3,
												readRxHdrHdwSrc_4, readRxHdrHdwSrc_5, readRxHdrEthertype_0, readRxHdrEthertype_1,
													waitPacketEnd, checkFCS_0, checkFCS_1, checkFCS_2, checkFCS_3);
	signal pkt_rx_state							: pkt_rx_type := idle;
--	signal pkt_rx_busy							: std_logic := '0';
	signal rx_pkt_counter						: unsigned(10 downto 0) := (others => '0');
--	signal rx_dst_mac_0							: std_logic_vector(7 downto 0) := (others => '0');
--	signal rx_dst_mac_1							: std_logic_vector(7 downto 0) := (others => '0');
--	signal rx_dst_mac_2							: std_logic_vector(7 downto 0) := (others => '0');
--	signal rx_dst_mac_3							: std_logic_vector(7 downto 0) := (others => '0');
--	signal rx_dst_mac_4							: std_logic_vector(7 downto 0) := (others => '0');
--	signal rx_dst_mac_5							: std_logic_vector(7 downto 0) := (others => '0');
	signal rx_src_mac_0							: std_logic_vector(7 downto 0) := (others => '0');
	signal rx_src_mac_1							: std_logic_vector(7 downto 0) := (others => '0');
	signal rx_src_mac_2							: std_logic_vector(7 downto 0) := (others => '0');
	signal rx_src_mac_3							: std_logic_vector(7 downto 0) := (others => '0');
	signal rx_src_mac_4							: std_logic_vector(7 downto 0) := (others => '0');
	signal rx_src_mac_5							: std_logic_vector(7 downto 0) := (others => '0');
	signal rx_etype_0								: std_logic_vector(7 downto 0) := (others => '0');
--	signal rx_etype_1								: std_logic_vector(7 downto 0) := (others => '0');
	signal rx_pkt_isIP							: std_logic := '0';
	signal rx_pkt_isARP							: std_logic := '0';
	signal rx_pkt_checksum_error_0			: std_logic := '0';
	signal rx_pkt_checksum_error_1			: std_logic := '0';
	signal rx_pkt_checksum_error_2			: std_logic := '0';
	signal rx_pkt_good_checksum				: std_logic := '0';
	signal rx_pkt_bad_checksum					: std_logic := '0';
	signal rx_reset_pkt_rx_counter			: std_logic := '0';
	signal rx_inc_pkt_rx_counter				: std_logic := '0';
		
	signal arp_parser_data_counter			: unsigned(5 downto 0) := (others => '0');
	
	type   arp_parser_type is
				(idle, readARPHeader_0, readARPHeader_1, readARPHeader_2, readARPHeader_3, readARPHeader_4,
					readARPHeader_5, readARPHeader_6, readARPHeader_7, readARPSrcHdwAddr_0, readARPSrcHdwAddr_1,
						readARPSrcHdwAddr_2, readARPSrcHdwAddr_3, readARPSrcHdwAddr_4, readARPSrcHdwAddr_5,
							readARPSrcPrtclAddr_0, readARPSrcPrtclAddr_1, readARPSrcPrtclAddr_2, readARPSrcPrtclAddr_3,
								readARPDstHdwAddr_0, readARPDstHdwAddr_1, readARPDstHdwAddr_2, readARPDstHdwAddr_3,
									readARPDstHdwAddr_4, readARPDstHdwAddr_5, readARPDstPrtclAddr_0, readARPDstPrtclAddr_1,
										readARPDstPrtclAddr_2, readARPDstPrtclAddr_3, readPadding);
	signal arp_parser_state						: arp_parser_type := idle;
	signal arp_msg_received						: std_logic := '0';
--	signal arp_hdr_counter						: unsigned(3 downto 0) := (others => '0');
--	signal arp_addr_counter						: unsigned(2 downto 0) := (others => '0');
--	signal arp_addr_counter_max				: unsigned(2 downto 0) := (others => '0');
--	signal arp_hdw_type_0						: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_hdw_type_1						: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_prtcl_type_0						: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_prtcl_type_1						: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_hdw_addr_len						: unsigned(2 downto 0) := (others => '0');
--	signal arp_prtcl_addr_len					: unsigned(2 downto 0) := (others => '0');
	signal arp_opcode_0							: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_opcode_1							: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_src_hdw_addr_0					: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_src_hdw_addr_1					: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_src_hdw_addr_2					: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_src_hdw_addr_3					: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_src_hdw_addr_4					: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_src_hdw_addr_5					: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_src_hdw_addr_6					: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_src_hdw_addr_7					: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_src_prtcl_addr_0				: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_src_prtcl_addr_1				: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_src_prtcl_addr_2				: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_src_prtcl_addr_3				: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_src_prtcl_addr_4				: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_src_prtcl_addr_5				: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_src_prtcl_addr_6				: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_src_prtcl_addr_7				: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_dst_hdw_addr_0					: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_dst_hdw_addr_1					: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_dst_hdw_addr_2					: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_dst_hdw_addr_3					: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_dst_hdw_addr_4					: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_dst_hdw_addr_5					: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_dst_hdw_addr_6					: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_dst_hdw_addr_7					: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_dst_prtcl_addr_0				: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_dst_prtcl_addr_1				: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_dst_prtcl_addr_2				: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_dst_prtcl_addr_3				: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_dst_prtcl_addr_4				: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_dst_prtcl_addr_5				: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_dst_prtcl_addr_6				: std_logic_vector(7 downto 0) := (others => '0');
--	signal arp_dst_prtcl_addr_7				: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_parser_rst_data_counter		: std_logic := '0';
	signal arp_parser_inc_data_counter		: std_logic := '0';
	signal arp_rx_stop_crc						: std_logic := '0';


	component arp_reply_fifo
	port (
		wr_clk			: in std_logic;
		rd_clk			: in std_logic;
		din				: in std_logic_vector(7 downto 0);
		wr_en				: in std_logic;
		rd_en				: in std_logic;
		dout				: out std_logic_vector(7 downto 0);
		full				: out std_logic;
		empty				: out std_logic
	);
	end component;

	signal arp_reply_fifo_din					: std_logic_vector(7 downto 0) := (others => '0');
	signal arp_reply_fifo_wr_en				: std_logic := '0';
	signal arp_reply_fifo_rd_en				: std_logic := '0';
	signal arp_reply_fifo_dout					: std_logic_vector(7 downto 0);
	signal arp_reply_fifo_empty				: std_logic;
	
	type   arp_reply_type is (idle, checkIfToReply, waitARPReplyFifoEmpty, SetDestHdwAddr_0,
												SetDestHdwAddr_1, SetDestHdwAddr_2, SetDestHdwAddr_3, SetDestHdwAddr_4, SetDestHdwAddr_5,
													SetSrcHdwAddr_0, SetSrcHdwAddr_1, SetSrcHdwAddr_2, SetSrcHdwAddr_3, SetSrcHdwAddr_4, SetSrcHdwAddr_5,
														SetEthertype_0, SetEthertype_1, SetArpHdwType_0, SetArpHdwType_1, SetArpPrtclType_0,
															SetArpPrtclType_1, SetArpHdwAddrLen, SetArpPrtclAddrLen, SetArpOpcode_0, SetArpOpcode_1,
																SetArpSrcHdwAddr_0, SetArpSrcHdwAddr_1, SetArpSrcHdwAddr_2, SetArpSrcHdwAddr_3,
																	SetArpSrcHdwAddr_4, SetArpSrcHdwAddr_5, SetArpSrcPrtclAddr_0, SetArpSrcPrtclAddr_1,
																		SetArpSrcPrtclAddr_2, SetArpSrcPrtclAddr_3, SetArpDstHdwAddr_0, SetArpDstHdwAddr_1,
																			SetArpDstHdwAddr_2, SetArpDstHdwAddr_3, SetArpDstHdwAddr_4, SetArpDstHdwAddr_5,
																				SetArpDstPrtclAddr_0, SetArpDstPrtclAddr_1, SetArpDstPrtclAddr_2, SetArpDstPrtclAddr_3);
	signal arp_reply_state						: arp_reply_type := idle;


	signal ip_rx_src_mac_0						: std_logic_vector(7 downto 0) := (others => '0');
	signal ip_rx_src_mac_1						: std_logic_vector(7 downto 0) := (others => '0');
	signal ip_rx_src_mac_2						: std_logic_vector(7 downto 0) := (others => '0');
	signal ip_rx_src_mac_3						: std_logic_vector(7 downto 0) := (others => '0');
	signal ip_rx_src_mac_4						: std_logic_vector(7 downto 0) := (others => '0');
	signal ip_rx_src_mac_5						: std_logic_vector(7 downto 0) := (others => '0');
	
	type   ip_hdr_parser_type is (idle, readIPHeader_0, readIPHeader_1, readIPHeader_2, readIPHeader_3,
												readIPHeader_4, readIPHeader_5, readIPHeader_6, readIPHeader_7,
													readIPHeader_8, readIPHeader_9, readIPHeader_10, readIPHeader_11,
														readIPHeader_12, readIPHeader_13, readIPHeader_14, readIPHeader_15,
															readIPHeader_16, readIPHeader_17, readIPHeader_18, readIPHeader_19);
	signal ip_hdr_parser_state					: ip_hdr_parser_type := idle;
	signal rx_pkt_isUDP							: std_logic := '0';
	signal rx_pkt_isICMP							: std_logic := '0';
--	signal ip_hdr_counter						: unsigned(5 downto 0) := (others => '0');
	signal ip_hdr_version						: std_logic_vector(3 downto 0) := (others => '0');
	signal ip_hdr_length							: std_logic_vector(3 downto 0) := (others => '0');
	signal ip_hdr_diff_services				: std_logic_vector(7 downto 0) := (others => '0');
	signal ip_hdr_total_length					: std_logic_vector(10 downto 0) := (others => '0');
	signal ip_hdr_pktid							: std_logic_vector(15 downto 0) := (others => '0');
	signal ip_hdr_flags							: std_logic_vector(2 downto 0) := (others => '0');
	signal ip_hdr_frag_offset					: std_logic_vector(12 downto 0) := (others => '0');
	signal ip_hdr_TTL								: std_logic_vector(7 downto 0) := (others => '0');
	signal ip_hdr_protocol						: std_logic_vector(7 downto 0) := (others => '0');
--	signal ip_hdr_checksum						: std_logic_vector(15 downto 0) := (others => '0');
	signal ip_hdr_src_addr_0					: std_logic_vector(7 downto 0) := (others => '0');
	signal ip_hdr_src_addr_1					: std_logic_vector(7 downto 0) := (others => '0');
	signal ip_hdr_src_addr_2					: std_logic_vector(7 downto 0) := (others => '0');
	signal ip_hdr_src_addr_3					: std_logic_vector(7 downto 0) := (others => '0');
	signal ip_hdr_dst_addr_0					: std_logic_vector(7 downto 0) := (others => '0');
	signal ip_hdr_dst_addr_1					: std_logic_vector(7 downto 0) := (others => '0');
	signal ip_hdr_dst_addr_2					: std_logic_vector(7 downto 0) := (others => '0');
	signal ip_hdr_dst_addr_3					: std_logic_vector(7 downto 0) := (others => '0');
	
	
	component ip_checksum
	port (
		clk									: in  std_logic;
		calc_ip_reply_checksum			: in  std_logic;
		ip_reply_hdr_version				: in  std_logic_vector(3 downto 0);
		ip_reply_hdr_length				: in  std_logic_vector(3 downto 0);
		ip_reply_hdr_diff_services		: in  std_logic_vector(7 downto 0);
		ip_reply_hdr_total_length		: in  std_logic_vector(10 downto 0);
		ip_reply_hdr_pktid				: in  std_logic_vector(15 downto 0);
		ip_reply_hdr_flags				: in  std_logic_vector(2 downto 0);
		ip_reply_hdr_frag_offset		: in  std_logic_vector(12 downto 0);
		ip_reply_hdr_TTL					: in  std_logic_vector(7 downto 0);
		ip_reply_hdr_protocol			: in  std_logic_vector(7 downto 0);
		ip_reply_addr_0					: in  std_logic_vector(7 downto 0);
		ip_reply_addr_1					: in  std_logic_vector(7 downto 0);
		ip_reply_addr_2					: in  std_logic_vector(7 downto 0);
		ip_reply_addr_3					: in  std_logic_vector(7 downto 0);
		ip_reply_hdr_dst_addr_0			: in  std_logic_vector(7 downto 0);
		ip_reply_hdr_dst_addr_1			: in  std_logic_vector(7 downto 0);
		ip_reply_hdr_dst_addr_2			: in  std_logic_vector(7 downto 0);
		ip_reply_hdr_dst_addr_3			: in  std_logic_vector(7 downto 0);
		ip_reply_checksum					: out std_logic_vector(15 downto 0);
		ip_reply_checksum_valid			: out std_logic
	);
	end component;

	
	component icmp_fifo
	port (
		clk			: in std_logic;
		din			: in std_logic_vector(7 downto 0);
		wr_en			: in std_logic;
		rd_en			: in std_logic;
		dout			: out std_logic_vector(7 downto 0);
		full			: out std_logic;
		empty			: out std_logic
	);
	end component;

	signal icmp_data_fifo_din						: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_data_fifo_wr_en					: std_logic := '0';
	signal icmp_data_fifo_rd_en					: std_logic := '0';
	signal icmp_data_fifo_dout						: std_logic_vector(7 downto 0);
	signal icmp_data_fifo_empty					: std_logic;
			
	signal icmp_rx_address_good					: std_logic := '0';
			
	signal icmp_rx_data_counter					: unsigned(10 downto 0) := (others => '0');
	
	type   icmp_receive_type is (idle, readType, readCode, readChecksum_1,
												readChecksum_2, readData, waitChecksumResult);
	signal icmp_receive_state						: icmp_receive_type := idle;
	signal icmp_echo_request_received			: std_logic := '0';
	signal icmp_rx_type								: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_code								: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_checksum							: std_logic_vector(15 downto 0) := (others => '0');
	signal icmp_rx_stop_crc							: std_logic := '0';
	signal icmp_rx_set_counter						: std_logic := '0';
	signal icmp_rx_dec_counter						: std_logic := '0';
	signal icmp_rx_copy_IP_header					: std_logic := '0';

	
	component icmp_reply_fifo
	port (
		wr_clk		: in std_logic;
		rd_clk		: in std_logic;
		din			: in std_logic_vector(7 downto 0);
		wr_en			: in std_logic;
		rd_en			: in std_logic;
		dout			: out std_logic_vector(7 downto 0);
		full			: out std_logic;
		empty			: out std_logic
	);
	end component;
			
	signal icmp_reply_fifo_din						: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_reply_fifo_wr_en					: std_logic := '0';
	signal icmp_reply_fifo_rd_en					: std_logic := '0';
	signal icmp_reply_fifo_dout					: std_logic_vector(7 downto 0);
	signal icmp_reply_fifo_empty					: std_logic;
			
	signal icmp_reply_checksum_long				: unsigned(31 downto 0) := (others => '0');
	
	type   icmp_checksum_type is (idle, step_1, step_2, step_3, step_4, step_5);
	signal icmp_checksum_state						: icmp_checksum_type := idle;
	signal icmp_reply_checksum						: std_logic_vector(15 downto 0) := (others => '0');
	signal icmp_reply_checksum_valid				: std_logic := '0';
	signal icmp_reply_checksum_sub				: std_logic := '0';
	signal icmp_reply_checksum_add				: std_logic := '0';
	signal icmp_reply_checksum_a					: std_logic_vector(15 downto 0) := (others => '0');
	signal icmp_reply_checksum_b					: std_logic_vector(15 downto 0) := (others => '0');
			
	signal icmp_rx_ip_hdr_version					: std_logic_vector(3 downto 0) := (others => '0');
	signal icmp_rx_ip_hdr_length					: std_logic_vector(3 downto 0) := (others => '0');
	signal icmp_rx_diff_services					: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_hdw_src_addr_0					: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_hdw_src_addr_1					: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_hdw_src_addr_2					: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_hdw_src_addr_3					: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_hdw_src_addr_4					: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_hdw_src_addr_5					: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_length							: std_logic_vector(10 downto 0) := (others => '0');
	signal icmp_rx_pkt_id							: std_logic_vector(15 downto 0) := (others => '0');
	signal icmp_rx_flags								: std_logic_vector(2 downto 0) := (others => '0');
	signal icmp_rx_frag_offset						: std_logic_vector(12 downto 0) := (others => '0');
	signal icmp_rx_TTL								: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_protocol							: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_src_ip_addr_0					: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_src_ip_addr_1					: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_src_ip_addr_2					: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_src_ip_addr_3					: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_dst_ip_addr_0					: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_dst_ip_addr_1					: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_dst_ip_addr_2					: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_rx_dst_ip_addr_3					: std_logic_vector(7 downto 0) := (others => '0');
		
	signal icmp_reply_ip_hdr_version				: std_logic_vector(3 downto 0) := (others => '0');
	signal icmp_reply_ip_hdr_length				: std_logic_vector(3 downto 0) := (others => '0');
	signal icmp_reply_ip_hdr_diff_services		: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_reply_ip_hdr_total_length		: std_logic_vector(10 downto 0) := (others => '0');
	signal icmp_reply_ip_hdr_pktid				: std_logic_vector(15 downto 0) := (others => '0');
	signal icmp_reply_ip_hdr_flags				: std_logic_vector(2 downto 0) := (others => '0');
	signal icmp_reply_ip_hdr_frag_offset		: std_logic_vector(12 downto 0) := (others => '0');
	signal icmp_reply_ip_hdr_TTL					: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_reply_ip_hdr_protocol			: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_reply_ip_addr_3					: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_reply_ip_hdr_dst_addr_0			: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_reply_ip_hdr_dst_addr_1			: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_reply_ip_hdr_dst_addr_2			: std_logic_vector(7 downto 0) := (others => '0');
	signal icmp_reply_ip_hdr_dst_addr_3			: std_logic_vector(7 downto 0) := (others => '0');
	
	signal icmp_reply_ip_hdr_checksum_valid	: std_logic := '0';
	signal icmp_reply_ip_hdr_checksum			: std_logic_vector(15 downto 0) := (others => '0');

	
	type   icmp_reply_type is (idle, waitIPHeaderSet, waitChecksumsValid, setDestHdwAddr_1, setDestHdwAddr_2, setDestHdwAddr_3,
												setDestHdwAddr_4, setDestHdwAddr_5, setDestHdwAddr_6, setSrcHdwAddr_1, setSrcHdwAddr_2, setSrcHdwAddr_3,
													setSrcHdwAddr_4, setSrcHdwAddr_5, setSrcHdwAddr_6, setEthertype_1, setEthertype_2,
														setIPHdr_1, setIPHdr_2, setIPHdrLen_1, setIPHdrLen_2, setIPHdrPktID_1, setIPHdrPktID_2,
															setIPHdrFlagsFrag_1, setIPHdrFlagsFrag_2, setIPHdr_TTL, setIPHdr_Protocol, setIPHdr_Checksum_1,
																setIPHdr_Checksum_2, setIPHdr_SrcAddr_1, setIPHdr_SrcAddr_2, setIPHdr_SrcAddr_3, setIPHdr_SrcAddr_4,
																	setIPHdr_DstAddr_1, setIPHdr_DstAddr_2, setIPHdr_DstAddr_3, setIPHdr_DstAddr_4,
																		setICMP_Type, setICMP_Code, setICMP_Checksum_1, setICMP_Checksum_2, setICMP_Data);
	signal icmp_reply_state							: icmp_reply_type := idle;
	signal icmp_reply_calc_checksum				: std_logic := '0';
	signal icmp_calc_ip_reply_checksum			: std_logic := '0';
	signal icmp_reply_set_ip_header				: std_logic := '0';

	
	component udp_data_fifo
	port (
		clk			: in std_logic;
		din			: in std_logic_vector(8 downto 0);
		wr_en			: in std_logic;
		rd_en			: in std_logic;
		dout			: out std_logic_vector(8 downto 0);
		full			: out std_logic;
		empty			: out std_logic
	);
	end component;

	signal udp_data_fifo_din				: std_logic_vector(8 downto 0) := (others => '0');
	signal udp_data_fifo_wr_en				: std_logic := '0';
	signal udp_data_fifo_rd_en				: std_logic := '0';
	signal udp_data_fifo_dout				: std_logic_vector(8 downto 0);
	signal udp_data_fifo_empty				: std_logic;

--	signal udp_rx_ip_hdr_version			: std_logic_vector(3 downto 0) := (others => '0');
	signal udp_rx_hdw_src_addr_0			: std_logic_vector(7 downto 0) := (others => '0');
	signal udp_rx_hdw_src_addr_1			: std_logic_vector(7 downto 0) := (others => '0');
	signal udp_rx_hdw_src_addr_2			: std_logic_vector(7 downto 0) := (others => '0');
	signal udp_rx_hdw_src_addr_3			: std_logic_vector(7 downto 0) := (others => '0');
	signal udp_rx_hdw_src_addr_4			: std_logic_vector(7 downto 0) := (others => '0');
	signal udp_rx_hdw_src_addr_5			: std_logic_vector(7 downto 0) := (others => '0');
--	signal udp_rx_length						: std_logic_vector(10 downto 0) := (others => '0');
	signal udp_rx_pkt_id						: std_logic_vector(15 downto 0) := (others => '0');
--	signal udp_rx_flags						: std_logic_vector(2 downto 0) := (others => '0');
	signal udp_rx_frag_offset				: std_logic_vector(12 downto 0) := (others => '0');
--	signal udp_rx_TTL							: std_logic_vector(7 downto 0) := (others => '0');
--	signal udp_rx_protocol					: std_logic_vector(7 downto 0) := (others => '0');
	signal udp_rx_src_ip_addr_0			: std_logic_vector(7 downto 0) := (others => '0');
	signal udp_rx_src_ip_addr_1			: std_logic_vector(7 downto 0) := (others => '0');
	signal udp_rx_src_ip_addr_2			: std_logic_vector(7 downto 0) := (others => '0');
	signal udp_rx_src_ip_addr_3			: std_logic_vector(7 downto 0) := (others => '0');
--	signal udp_rx_dst_ip_addr_0			: std_logic_vector(7 downto 0) := (others => '0');
--	signal udp_rx_dst_ip_addr_1			: std_logic_vector(7 downto 0) := (others => '0');
--	signal udp_rx_dst_ip_addr_2			: std_logic_vector(7 downto 0) := (others => '0');
--	signal udp_rx_dst_ip_addr_3			: std_logic_vector(7 downto 0) := (others => '0');
	
	signal udp_rx_packet_good				: std_logic := '0';
	
	signal udp_rx_total_counter			: unsigned(10 downto 0) := (others => '0');
	
	signal udp_rx_data_counter				: unsigned(10 downto 0) := (others => '0');

	type   udp_receive_type is (idle, readUDPHdrSrcPort_0, readUDPHdrSrcPort_1, readUDPHdrDstPort_0, readUDPHdrDstPort_1,
											readUDPHdrLen_0, readUDPHdrLen_1, readUDPHdrChkSum_0, readUDPHdrChkSum_1,
												readUDPData, readPadding, waitChecksumResult);
	signal udp_receive_state				: udp_receive_type := idle;
--	signal udp_rx_header_valid				: std_logic;
	signal udp_rx_pass_message				: std_logic := '0';
	signal udp_rx_flush_message			: std_logic := '0';
	signal udp_rx_stop_crc					: std_logic := '0';
--	signal udp_rx_src_port					: std_logic_vector(15 downto 0) := (others => '0');
	signal udp_rx_dst_port					: std_logic_vector(15 downto 0) := (others => '0');
	signal udp_rx_data_length				: std_logic_vector(10 downto 0) := (others => '0');
--	signal udp_rx_checksum					: std_logic_vector(15 downto 0) := (others => '0');
--	signal udp_rx_copy_IP_header			: std_logic := '0';
	signal udp_rx_set_total_counter		: std_logic := '0';
	signal udp_rx_dec_total_counter		: std_logic := '0';
	signal udp_rx_set_data_counter		: std_logic := '0';
	signal udp_rx_dec_data_counter		: std_logic := '0';
	signal reset_udp_rx_packet_good		: std_logic := '0';
	signal check_udp_rx_packet_good		: std_logic := '0';
	
	type   message_transfer_type is (idle, passMessage, flushMessage);
	signal message_transfer_state			: message_transfer_type := idle;
	signal message_fifo_in_wr_en_i		: std_logic := '0';
	signal message_fifo_in_din_i			: std_logic_vector(8 downto 0) := (others => '0');
	signal message_first						: std_logic := '0';
	



	component reply_fifo
	port (
		clk			: in std_logic;
		din			: in std_logic_vector(8 downto 0);
		wr_en			: in std_logic;
		rd_en			: in std_logic;
		dout			: out std_logic_vector(8 downto 0);
		full			: out std_logic;
		empty			: out std_logic;
		valid			: out std_logic
	);
	end component;

	signal reply_fifo_rd_en						: std_logic;
	signal reply_fifo_dout						: std_logic_vector(8 downto 0);
	signal reply_fifo_empty						: std_logic;
	signal reply_fifo_valid						: std_logic;
	
	signal reply_num_bytes_count				: unsigned(10 downto 0) := (others => '0');
	
	
	type   reply_send_type is (idle, waitDataReady, sendByte_7, sendByte_6, sendByte_5,
											sendByte_4, sendByte_3, sendByte_2, sendByte_1, sendByte_0,
												waitReplyReady, sendReply);
	signal reply_send_state						: reply_send_type := idle;
	signal reset_reply_num_bytes				: std_logic := '0';
	signal inc_reply_num_bytes					: std_logic := '0';
	signal inc_num_event_bytes					: std_logic := '0';
	signal reply_fifo_wr_en						: std_logic := '0';
	signal reply_fifo_din						: std_logic_vector(8 downto 0) := (others => '0');
	signal reply_available						: std_logic := '0';
	signal reply_num_bytes						: unsigned(10 downto 0) := (others => '0');
	
--	signal ip_reply_hdr_version					: std_logic_vector(3 downto 0) := (others => '0');
--	signal ip_reply_hdr_length						: std_logic_vector(3 downto 0) := (others => '0');
--	signal ip_reply_hdr_diff_services			: std_logic_vector(7 downto 0) := (others => '0');
--	signal ip_reply_hdr_total_length				: std_logic_vector(10 downto 0) := (others => '0');
--	signal ip_reply_hdr_pktid						: std_logic_vector(15 downto 0) := (others => '0');
--	signal ip_reply_hdr_flags						: std_logic_vector(2 downto 0) := (others => '0');
--	signal ip_reply_hdr_frag_offset				: std_logic_vector(12 downto 0) := (others => '0');
--	signal ip_reply_hdr_TTL							: std_logic_vector(7 downto 0) := (others => '0');
--	signal ip_reply_hdr_protocol					: std_logic_vector(7 downto 0) := (others => '0');
--	signal ip_reply_hdr_dst_addr_0				: std_logic_vector(7 downto 0) := (others => '0');
--	signal ip_reply_hdr_dst_addr_1				: std_logic_vector(7 downto 0) := (others => '0');
--	signal ip_reply_hdr_dst_addr_2				: std_logic_vector(7 downto 0) := (others => '0');
--	signal ip_reply_hdr_dst_addr_3				: std_logic_vector(7 downto 0) := (others => '0');
	
	
	component udp_packet
	port (
		clk										: in  std_logic;
		tx_clk									: in  std_logic;
		udp_rx_pkt_id							: in  std_logic_vector(15 downto 0);
		udp_rx_frag_offset					: in  std_logic_vector(12 downto 0);
		udp_reply_mac_addr_0					: in  std_logic_vector(7 downto 0);
		udp_reply_mac_addr_1					: in  std_logic_vector(7 downto 0);
		udp_reply_mac_addr_2					: in  std_logic_vector(7 downto 0);
		udp_reply_mac_addr_3					: in  std_logic_vector(7 downto 0);
		udp_reply_mac_addr_4					: in  std_logic_vector(7 downto 0);
		udp_reply_mac_addr_5					: in  std_logic_vector(7 downto 0);
		udp_rx_hdw_src_addr_0				: in  std_logic_vector(7 downto 0);
		udp_rx_hdw_src_addr_1				: in  std_logic_vector(7 downto 0);
		udp_rx_hdw_src_addr_2				: in  std_logic_vector(7 downto 0);
		udp_rx_hdw_src_addr_3				: in  std_logic_vector(7 downto 0);
		udp_rx_hdw_src_addr_4				: in  std_logic_vector(7 downto 0);
		udp_rx_hdw_src_addr_5				: in  std_logic_vector(7 downto 0);
		udp_reply_ip_addr_0					: in  std_logic_vector(7 downto 0);
		udp_reply_ip_addr_1					: in  std_logic_vector(7 downto 0);
		udp_reply_ip_addr_2					: in  std_logic_vector(7 downto 0);
		udp_reply_ip_addr_3					: in  std_logic_vector(7 downto 0);
		udp_rx_src_ip_addr_0					: in  std_logic_vector(7 downto 0);
		udp_rx_src_ip_addr_1					: in  std_logic_vector(7 downto 0);
		udp_rx_src_ip_addr_2					: in  std_logic_vector(7 downto 0);
		udp_rx_src_ip_addr_3					: in  std_logic_vector(7 downto 0);
		udp_destination_port					: in  std_logic_vector(15 downto 0);
		packet_data_available				: in  std_logic;
		udp_packet_busy						: out std_logic;
		packet_data_num_bytes				: in  unsigned(10 downto 0);
		packet_data_fifo_rd_en				: out std_logic;
		packet_data_fifo_dout				: in  std_logic_vector(8 downto 0);
		udp_packet_fifo_rd_en				: in  std_logic;
		udp_packet_fifo_dout					: out std_logic_vector(8 downto 0);
		udp_packet_fifo_empty				: out std_logic
	);
	end component;


	signal udp_reply_port					: std_logic_vector(15 downto 0);
	signal udp_reply_mac_addr_5			: std_logic_vector(7 downto 0);
	signal udp_reply_ip_addr_3				: std_logic_vector(7 downto 0);
	signal udp_events_port					: std_logic_vector(15 downto 0);
	
	signal udp_reply_packet_busy			: std_logic;
	signal reply_packet_fifo_rd_en		: std_logic;
	signal reply_packet_fifo_dout			: std_logic_vector(8 downto 0);
	signal reply_packet_fifo_empty		: std_logic;
	

	signal udp_reply_data_count			: unsigned(10 downto 0) := (others => '0');

--	type   udp_read_reply_type is (idle, readReplyFifo, readFifo_Last, waitPadding);
--	signal udp_read_reply_state			: udp_read_reply_type := idle;
--	signal udp_reply_stop_crc				: std_logic := '0';
--	signal udp_reply_first_byte			: std_logic := '0';
--	signal udp_reply_last_byte				: std_logic := '0';
--	signal udp_reply_last_data				: std_logic_vector(7 downto 0) := (others => '0');
--	signal udp_reply_data					: std_logic_vector(7 downto 0) := (others => '0');
--	signal udp_reply_reset_data_counter	: std_logic := '0';
--	signal udp_reply_inc_data_counter	: std_logic := '0';
	
	
	signal tx_crc_byte						: std_logic_vector(7 downto 0) := (others => '0');
	signal tx_crc_data						: std_logic_vector(7 downto 0) := (others => '0');
	signal tx_crc_valid						: std_logic := '0';

	type   tx_send_type is (idle, sendPreamble_0, sendPreamble_0_b, sendPreamble_1, sendPreamble_1_b,
										sendPreamble_2, sendPreamble_2_b, sendPreamble_3, sendPreamble_3_b,
											sendPreamble_4, sendPreamble_4_b, sendPreamble_5, sendPreamble_5_b,
												sendPreamble_6, sendPreamble_6_b, sendPreamble_7, sendPreamble_7_b,
													sendARPMessage, sendARPMessage_b, sendICMPMessage, sendICMPMessage_b,
														sendUDPMessage, sendUDPMessage_b, sendPadding, sendPadding_b,
															sendFCS, sendFCS_b, waitPacketGap);
	signal tx_send_state						: tx_send_type := idle;
	type   tx_msg_type is (none, arp, icmp, udp);
	signal tx_msg								: tx_msg_type := none;
	signal tx_data_out_i						: std_logic_vector(3 downto 0) := (others => '0');
	signal tx_data_enable_i					: std_logic := '0';
	signal tx_send_reset_pkt_count		: std_logic := '0';
	signal tx_send_inc_pkt_count			: std_logic := '0';
	signal tx_crc_clear						: std_logic := '0';
	signal tx_crc_calc						: std_logic := '0';
	signal tx_crc_read						: std_logic := '0';
	signal tx_pkt_counter					: unsigned(10 downto 0) := (others => '0');
	signal tx_fcs_counter					: unsigned(1 downto 0) := (others => '0');
--	signal pkt_tx_start_udp_fifo_read	: std_logic := '0';
	signal tx_nibble_hi						: std_logic_vector(3 downto 0) := (others => '0');
	signal tx_udp_pkt_end					: std_logic := '0';
	
	constant mac_addr_0						: std_logic_vector(7 downto 0) := x"00";		-- Xilinx MAC address
	constant mac_addr_1						: std_logic_vector(7 downto 0) := x"0A";
	constant mac_addr_2						: std_logic_vector(7 downto 0) := x"35";
	constant mac_addr_3						: std_logic_vector(7 downto 0) := x"00";
	constant mac_addr_4						: std_logic_vector(7 downto 0) := x"01";
	constant mac_addr_5_a					: std_logic_vector(3 downto 0) := "0000";
	signal   mac_addr_5_b					: std_logic_vector(3 downto 0) := "0000";
--	signal   mac_addr_5						: std_logic_vector(7 downto 0);
	
	constant ip_addr_0						: std_logic_vector(7 downto 0) := x"C0";			-- 192.168.1.10
	constant ip_addr_1						: std_logic_vector(7 downto 0) := x"A8";
	constant ip_addr_2						: std_logic_vector(7 downto 0) := x"01";
	constant ip_addr_3_a						: std_logic_vector(2 downto 0) := "000";
	signal   ip_addr_3_b						: std_logic_vector(4 downto 0) := "00000";
--	signal   ip_addr_3						: std_logic_vector(7 downto 0);
	
	constant udp_port_a						: std_logic_vector(12 downto 0)	:= "0010011100010";		-- 10000
	signal   udp_port_b						: std_logic_vector(2 downto 0)	:= "000";
--	signal   udp_port							: std_logic_vector(15 downto 0);
	
begin

	ip_sel_proc : process (clk)
	begin
		if rising_edge(clk) then
			case board_id is
				when "000" =>
					mac_addr_5_b <= "0010";
					ip_addr_3_b <= "01010";
					udp_port_b <= "000";
					
				when "001" =>
					mac_addr_5_b <= "0011";
					ip_addr_3_b <= "01011";
					udp_port_b <= "001";
					
				when "010" =>
					mac_addr_5_b <= "0100";
					ip_addr_3_b <= "01100";
					udp_port_b <= "010";
					
				when "011" =>
					mac_addr_5_b <= "0101";
					ip_addr_3_b <= "01101";
					udp_port_b <= "011";
					
				when "100" =>
					mac_addr_5_b <= "0110";
					ip_addr_3_b <= "01110";
					udp_port_b <= "100";
					
				when "101" =>
					mac_addr_5_b <= "0111";
					ip_addr_3_b <= "01111";
					udp_port_b <= "101";
					
				when "110" =>
					mac_addr_5_b <= "1000";
					ip_addr_3_b <= "10000";
					udp_port_b <= "110";
					
				when others =>
					mac_addr_5_b <= "1001";
					ip_addr_3_b <= "10001";
					udp_port_b <= "111";
			end case;
		end if;
	end process ip_sel_proc;


	debug <= tx_crc_valid & rx_crc_valid;
	
	num_good_pkts_counter <= std_logic_vector(num_good_pkts_counter_i);
	num_bad_pkts_counter <= std_logic_vector(num_bad_pkts_counter_i);


	rx_fifo_0 : rx_fifo
	port map (
		wr_clk			=> rx_clk,
		rd_clk			=> clk,
		din				=> rx_data_fifo_din,
		wr_en				=> rx_data_fifo_wr_en,
		rd_en				=> rx_data_fifo_rd_en,
		dout				=> rx_data_fifo_dout,
		full				=> rx_data_fifo_full,
		empty				=> rx_data_fifo_empty,
		valid				=> rx_data_fifo_valid
	);
	
	rx_data <= rx_data_fifo_dout(7 downto 0);
	rx_data_pkt_start <= rx_data_fifo_dout(9);
	rx_data_pkt_end <= rx_data_fifo_dout(8);


	rx_data_fifo_din <= rx_data_in_start & rx_data_in_end & rx_last_data_in;

	rx_last_data_in_proc: process (rx_clk)
	begin
		if rising_edge(rx_clk) then
			rx_data_fifo_wr_en <= rx_data_in_new;
			rx_last_data_in <= rx_data_in_nibble_1 & rx_data_in_nibble_0;
		end if;
	end process rx_last_data_in_proc;
	
	
	rx_data_in_start_proc: process (rx_clk)
	begin
		if rising_edge(rx_clk) then
			if (rx_data_in_new_pkt = '1') then
				rx_data_in_start <= '1';
			elsif (rx_data_fifo_wr_en = '1') then
				rx_data_in_start <= '0';
			end if;
		end if;
	end process rx_data_in_start_proc;
	
	
	rx_packet_in_proc: process (rx_clk)
	begin
		if rising_edge(rx_clk) then
			rx_data_in_new_pkt <= '0';
			rx_data_in_new <= '0';
			
			case rx_packet_in_state is
				when idle =>
					if (rx_data_valid = '1') then
						rx_data_in_new_pkt <= '1';
						rx_data_in_end <= '0';
						rx_data_in_nibble <= '1';
						rx_data_in_nibble_0 <= rx_data_in;
						rx_packet_in_state <= buildPacket;
					else
						rx_packet_in_state <= idle;
					end if;
				
				when buildPacket =>
					if (rx_data_valid = '1') then
						rx_data_in_nibble <= not rx_data_in_nibble;
						if (rx_data_in_nibble = '0') then
							rx_data_in_nibble_0 <= rx_data_in;
						else
							rx_data_in_nibble_1 <= rx_data_in;
							rx_data_in_new <= '1';
						end if;
					else
						rx_data_in_end <= '1';
						rx_packet_in_state <= idle;
					end if;
			end case;
		end if;
	end process rx_packet_in_proc;


	pkt_rx_crc: pkt_crc_32
	port map (
		CLOCK				=> clk,
		RESET				=> '0',
		DATA				=>	rx_data,
		LOAD_INIT		=> rx_crc_clear,
		CALC 				=> rx_crc_calc,
		D_VALID			=> rx_crc_read,
		CRC				=> rx_crc_byte,
		CRC_REG			=> open,
		CRC_VALID		=> rx_crc_valid
	);


	good_packet_counter_proc : process (clk)
	begin
		if rising_edge(clk) then
			if (reset_enet_counters = '1') then
				num_good_pkts_counter_i <= (others => '0');
			elsif (rx_pkt_good_checksum = '1') and (num_good_pkts_counter_i /= x"ffffffff") then
				num_good_pkts_counter_i <= num_good_pkts_counter_i + 1;
			end if;
		end if;
	end process good_packet_counter_proc;


	bad_packet_counter_proc : process (clk)
	begin
		if rising_edge(clk) then
			if (reset_enet_counters = '1') then
				num_bad_pkts_counter_i <= (others => '0');
			elsif (rx_pkt_bad_checksum = '1') and (num_bad_pkts_counter_i /= x"ffffffff") then
				num_bad_pkts_counter_i <= num_bad_pkts_counter_i + 1;
			end if;
		end if;
	end process bad_packet_counter_proc;


	pkt_rx_packet_bytes_counter_proc : process (clk)
	begin
		if rising_edge(clk) then
			if (rx_reset_pkt_rx_counter = '1') then
				rx_pkt_counter <= (others => '0');
			elsif (rx_inc_pkt_rx_counter = '1') then
				rx_pkt_counter <= rx_pkt_counter + 1;
			end if;
		end if;
	end process pkt_rx_packet_bytes_counter_proc;


	packet_receiver : process (clk)
	begin
		if rising_edge(clk) then
			rx_pkt_isIP <= '0';
			rx_pkt_isARP <= '0';
			rx_crc_clear <= '0';
			rx_crc_calc <= '0';
			rx_crc_read <= '0';
			rx_reset_pkt_rx_counter <= '0';
			rx_inc_pkt_rx_counter <= '0';
			rx_pkt_good_checksum <= '0';
			rx_pkt_bad_checksum <= '0';
		
			case pkt_rx_state is
				when idle =>
					if (rx_data_fifo_empty = '0') then
						pkt_rx_busy <= '1';
						rx_crc_clear <= '1';
						rx_data_fifo_rd_en <= '1';
						pkt_rx_state <= CheckPacketStart;
						rx_reset_pkt_rx_counter <= '1';
						rx_pkt_checksum_error_0 <= '0';
						rx_pkt_checksum_error_1 <= '0';
						rx_pkt_checksum_error_2 <= '0';
					else
						pkt_rx_busy <= '0';
						pkt_rx_state <= idle;
					end if;
				
				when CheckPacketStart =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= CheckPacketStart;
					else
						if (rx_data_pkt_start = '0') then
							pkt_rx_state <= waitPacketEnd;
						else
							pkt_rx_state <= readPreamble;
							rx_inc_pkt_rx_counter <= '1';
						end if;
					end if;
				
				when readPreamble =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= readPreamble;
					else
						rx_inc_pkt_rx_counter <= '1';
						if (rx_pkt_counter = 7) then
--							rx_crc_calc <= '1';
--							rx_crc_read <= '1';
							pkt_rx_state <= readRxHdrHdwDst_0;
						else
							pkt_rx_state <= readPreamble;
						end if;
					end if;
				
				when readRxHdrHdwDst_0 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= readRxHdrHdwDst_0;
					else
						rx_inc_pkt_rx_counter <= '1';
						rx_crc_calc <= '1';
						rx_crc_read <= '1';
--						rx_dst_mac_0 <= rx_data;
						pkt_rx_state <= readRxHdrHdwDst_1;
					end if;
				
				when readRxHdrHdwDst_1 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= readRxHdrHdwDst_1;
					else
						rx_inc_pkt_rx_counter <= '1';
						rx_crc_calc <= '1';
						rx_crc_read <= '1';
--						rx_dst_mac_1 <= rx_data;
						pkt_rx_state <= readRxHdrHdwDst_2;
					end if;
				
				when readRxHdrHdwDst_2 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= readRxHdrHdwDst_2;
					else
						rx_inc_pkt_rx_counter <= '1';
						rx_crc_calc <= '1';
						rx_crc_read <= '1';
--						rx_dst_mac_2 <= rx_data;
						pkt_rx_state <= readRxHdrHdwDst_3;
					end if;
				
				when readRxHdrHdwDst_3 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= readRxHdrHdwDst_3;
					else
						rx_inc_pkt_rx_counter <= '1';
						rx_crc_calc <= '1';
						rx_crc_read <= '1';
--						rx_dst_mac_3 <= rx_data;
						pkt_rx_state <= readRxHdrHdwDst_4;
					end if;
				
				when readRxHdrHdwDst_4 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= readRxHdrHdwDst_4;
					else
						rx_inc_pkt_rx_counter <= '1';
						rx_crc_calc <= '1';
						rx_crc_read <= '1';
--						rx_dst_mac_4 <= rx_data;
						pkt_rx_state <= readRxHdrHdwDst_5;
					end if;
				
				when readRxHdrHdwDst_5 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= readRxHdrHdwDst_5;
					else
						rx_inc_pkt_rx_counter <= '1';
						rx_crc_calc <= '1';
						rx_crc_read <= '1';
--						rx_dst_mac_5 <= rx_data;
						pkt_rx_state <= readRxHdrHdwSrc_0;
					end if;
				
				when readRxHdrHdwSrc_0 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= readRxHdrHdwSrc_0;
					else
						rx_inc_pkt_rx_counter <= '1';
						rx_crc_calc <= '1';
						rx_crc_read <= '1';
						rx_src_mac_0 <= rx_data;
						pkt_rx_state <= readRxHdrHdwSrc_1;
					end if;
				
				when readRxHdrHdwSrc_1 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= readRxHdrHdwSrc_1;
					else
						rx_inc_pkt_rx_counter <= '1';
						rx_crc_calc <= '1';
						rx_crc_read <= '1';
						rx_src_mac_1 <= rx_data;
						pkt_rx_state <= readRxHdrHdwSrc_2;
					end if;
				
				when readRxHdrHdwSrc_2 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= readRxHdrHdwSrc_2;
					else
						rx_inc_pkt_rx_counter <= '1';
						rx_crc_calc <= '1';
						rx_crc_read <= '1';
						rx_src_mac_2 <= rx_data;
						pkt_rx_state <= readRxHdrHdwSrc_3;
					end if;
				
				when readRxHdrHdwSrc_3 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= readRxHdrHdwSrc_3;
					else
						rx_inc_pkt_rx_counter <= '1';
						rx_crc_calc <= '1';
						rx_crc_read <= '1';
						rx_src_mac_3 <= rx_data;
						pkt_rx_state <= readRxHdrHdwSrc_4;
					end if;
				
				when readRxHdrHdwSrc_4 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= readRxHdrHdwSrc_4;
					else
						rx_inc_pkt_rx_counter <= '1';
						rx_crc_calc <= '1';
						rx_crc_read <= '1';
						rx_src_mac_4 <= rx_data;
						pkt_rx_state <= readRxHdrHdwSrc_5;
					end if;
				
				when readRxHdrHdwSrc_5 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= readRxHdrHdwSrc_5;
					else
						rx_inc_pkt_rx_counter <= '1';
						rx_crc_calc <= '1';
						rx_crc_read <= '1';
						rx_src_mac_5 <= rx_data;
						pkt_rx_state <= readRxHdrEthertype_0;
					end if;
				
				when readRxHdrEthertype_0 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= readRxHdrEthertype_0;
					else
						rx_inc_pkt_rx_counter <= '1';
						rx_crc_calc <= '1';
						rx_crc_read <= '1';
						rx_etype_0 <= rx_data;
						pkt_rx_state <= readRxHdrEthertype_1;
					end if;
				
				when readRxHdrEthertype_1 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= readRxHdrEthertype_1;
					else
						rx_inc_pkt_rx_counter <= '1';
						rx_crc_calc <= '1';
						rx_crc_read <= '1';
--						rx_etype_1 <= rx_data;
							
						case rx_etype_0 & rx_data is
							when x"0800" =>
								rx_pkt_isIP <= '1';
							when x"0806" =>
								rx_pkt_isARP <= '1';
							when others =>
								null;
						end case;
						pkt_rx_state <= waitPacketEnd;
					end if;
				
				when waitPacketEnd =>
					if (arp_rx_stop_crc = '1') or (icmp_rx_stop_crc = '1') or (udp_rx_stop_crc = '1') then
						pkt_rx_state <= checkFCS_0;
					elsif (rx_data_fifo_valid = '0') then
						pkt_rx_state <= waitPacketEnd;
					else
						rx_inc_pkt_rx_counter <= '1';
						rx_crc_read <= '1';
						if (rx_data_pkt_end = '0') then
							rx_crc_calc <= '1';
							pkt_rx_state <= waitPacketEnd;
						else
							rx_data_fifo_rd_en <= '0';
							pkt_rx_state <= idle;
						end if;
					end if;
				
				when checkFCS_0 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= checkFCS_0;
					else
						rx_crc_read <= '1';
						if (rx_data /= rx_crc_byte) then
							rx_pkt_checksum_error_0 <= '1';
						end if;
						pkt_rx_state <= checkFCS_1;
					end if;
				
				when checkFCS_1 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= checkFCS_1;
					else
						rx_crc_read <= '1';
						if (rx_data /= rx_crc_byte) then
							rx_pkt_checksum_error_1 <= '1';
						end if;
						pkt_rx_state <= checkFCS_2;
					end if;
				
				when checkFCS_2 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= checkFCS_2;
					else
						rx_crc_read <= '1';
						if (rx_data /= rx_crc_byte) then
							rx_pkt_checksum_error_2 <= '1';
						end if;
						pkt_rx_state <= checkFCS_3;
					end if;
				
				when checkFCS_3 =>
					if (rx_data_fifo_valid = '0') then
						pkt_rx_state <= checkFCS_3;
					else
						rx_crc_read <= '1';
						if (rx_data /= rx_crc_byte) or (rx_pkt_checksum_error_0 = '1') or
								(rx_pkt_checksum_error_1 = '1') or (rx_pkt_checksum_error_2 = '1') then
							rx_pkt_bad_checksum <= '1';
						else
							rx_pkt_good_checksum <= '1';
						end if;
						rx_data_fifo_rd_en <= '0';
						pkt_rx_state <= idle;
					end if;
			end case;
		end if;
	end process packet_receiver;
	

	arp_parser_data_counter_proc : process (clk)
	begin
		if rising_edge(clk) then
			if (arp_parser_rst_data_counter = '1') then
				arp_parser_data_counter <= (others => '0');
			elsif (arp_parser_inc_data_counter = '1') then
				arp_parser_data_counter <= arp_parser_data_counter + 1;
			end if;
		end if;
	end process arp_parser_data_counter_proc;
	

	arp_parser : process (clk)
	begin
		if rising_edge(clk) then
			arp_msg_received <= '0';
			arp_rx_stop_crc <= '0';
			arp_parser_rst_data_counter <= '0';
			arp_parser_inc_data_counter <= '0';
			
			case arp_parser_state is
				when idle =>
					if (rx_pkt_isARP = '1') then
						arp_parser_rst_data_counter <= '1';
						arp_parser_state <= readARPHeader_0;
					else
						arp_parser_state <= idle;
					end if;
			
				when readARPHeader_0 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPHeader_0;
					else
						arp_parser_inc_data_counter <= '1';
--						arp_hdw_type_0 <= rx_data;
						arp_parser_state <= readARPHeader_1;
					end if;
			
				when readARPHeader_1 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPHeader_1;
					else
						arp_parser_inc_data_counter <= '1';
--						arp_hdw_type_1 <= rx_data;
						arp_parser_state <= readARPHeader_2;
					end if;
				
				when readARPHeader_2 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPHeader_2;
					else
						arp_parser_inc_data_counter <= '1';
						arp_prtcl_type_0 <= rx_data;
						arp_parser_state <= readARPHeader_3;
					end if;
				
				when readARPHeader_3 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPHeader_3;
					else
--						arp_prtcl_type_1 <= rx_data;
						arp_parser_inc_data_counter <= '1';
						if (arp_prtcl_type_0 & rx_data = x"0800") then
							arp_parser_state <= readARPHeader_4;
						else
							arp_parser_state <= idle;
						end if;
					end if;
				
				when readARPHeader_4 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPHeader_4;
					else
						arp_parser_inc_data_counter <= '1';
--						arp_hdw_addr_len <= unsigned(rx_data(2 downto 0));
						arp_parser_state <= readARPHeader_5;
					end if;
				
				when readARPHeader_5 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPHeader_5;
					else
						arp_parser_inc_data_counter <= '1';
--						arp_prtcl_addr_len <= unsigned(rx_data(2 downto 0));
						arp_parser_state <= readARPHeader_6;
					end if;
				
				when readARPHeader_6 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPHeader_6;
					else
						arp_parser_inc_data_counter <= '1';
						arp_opcode_0 <= rx_data;
						arp_parser_state <= readARPHeader_7;
					end if;
				
				when readARPHeader_7 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPHeader_7;
					else
						arp_parser_inc_data_counter <= '1';
						arp_opcode_1 <= rx_data;
--						if (arp_hdw_addr_len = to_unsigned(6, 3)) and
--								(arp_prtcl_addr_len = to_unsigned(4, 3)) then
--						end if;
						arp_parser_state <= readARPSrcHdwAddr_0;
					end if;
				
				when readARPSrcHdwAddr_0 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPSrcHdwAddr_0;
					else
						arp_parser_inc_data_counter <= '1';
						arp_src_hdw_addr_0 <= rx_data;
						arp_parser_state <= readARPSrcHdwAddr_1;
					end if;
				
				when readARPSrcHdwAddr_1 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPSrcHdwAddr_1;
					else
						arp_parser_inc_data_counter <= '1';
						arp_src_hdw_addr_1 <= rx_data;
						arp_parser_state <= readARPSrcHdwAddr_2;
					end if;
				
				when readARPSrcHdwAddr_2 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPSrcHdwAddr_2;
					else
						arp_parser_inc_data_counter <= '1';
						arp_src_hdw_addr_2 <= rx_data;
						arp_parser_state <= readARPSrcHdwAddr_3;
					end if;
				
				when readARPSrcHdwAddr_3 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPSrcHdwAddr_3;
					else
						arp_parser_inc_data_counter <= '1';
						arp_src_hdw_addr_3 <= rx_data;
						arp_parser_state <= readARPSrcHdwAddr_4;
					end if;
				
				when readARPSrcHdwAddr_4 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPSrcHdwAddr_4;
					else
						arp_parser_inc_data_counter <= '1';
						arp_src_hdw_addr_4 <= rx_data;
						arp_parser_state <= readARPSrcHdwAddr_5;
					end if;
				
				when readARPSrcHdwAddr_5 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPSrcHdwAddr_5;
					else
						arp_parser_inc_data_counter <= '1';
						arp_src_hdw_addr_5 <= rx_data;
						arp_parser_state <= readARPSrcPrtclAddr_0;
					end if;
				
				when readARPSrcPrtclAddr_0 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPSrcPrtclAddr_0;
					else
						arp_parser_inc_data_counter <= '1';
						arp_src_prtcl_addr_0 <= rx_data;
						arp_parser_state <= readARPSrcPrtclAddr_1;
					end if;
				
				when readARPSrcPrtclAddr_1 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPSrcPrtclAddr_1;
					else
						arp_parser_inc_data_counter <= '1';
						arp_src_prtcl_addr_1 <= rx_data;
						arp_parser_state <= readARPSrcPrtclAddr_2;
					end if;
				
				when readARPSrcPrtclAddr_2 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPSrcPrtclAddr_2;
					else
						arp_parser_inc_data_counter <= '1';
						arp_src_prtcl_addr_2 <= rx_data;
						arp_parser_state <= readARPSrcPrtclAddr_3;
					end if;
				
				when readARPSrcPrtclAddr_3 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPSrcPrtclAddr_3;
					else
						arp_parser_inc_data_counter <= '1';
						arp_src_prtcl_addr_3 <= rx_data;
						arp_parser_state <= readARPDstHdwAddr_0;
					end if;
						
				when readARPDstHdwAddr_0 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPDstHdwAddr_0;
					else
						arp_parser_inc_data_counter <= '1';
--						arp_dst_hdw_addr_0 <= rx_data;
						arp_parser_state <= readARPDstHdwAddr_1;
					end if;
						
				when readARPDstHdwAddr_1 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPDstHdwAddr_1;
					else
						arp_parser_inc_data_counter <= '1';
--						arp_dst_hdw_addr_1 <= rx_data;
						arp_parser_state <= readARPDstHdwAddr_2;
					end if;
						
				when readARPDstHdwAddr_2 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPDstHdwAddr_2;
					else
						arp_parser_inc_data_counter <= '1';
--						arp_dst_hdw_addr_2 <= rx_data;
						arp_parser_state <= readARPDstHdwAddr_3;
					end if;
						
				when readARPDstHdwAddr_3 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPDstHdwAddr_3;
					else
						arp_parser_inc_data_counter <= '1';
--						arp_dst_hdw_addr_3 <= rx_data;
						arp_parser_state <= readARPDstHdwAddr_4;
					end if;
						
				when readARPDstHdwAddr_4 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPDstHdwAddr_4;
					else
						arp_parser_inc_data_counter <= '1';
--						arp_dst_hdw_addr_4 <= rx_data;
						arp_parser_state <= readARPDstHdwAddr_5;
					end if;
						
				when readARPDstHdwAddr_5 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPDstHdwAddr_5;
					else
						arp_parser_inc_data_counter <= '1';
--						arp_dst_hdw_addr_5 <= rx_data;
						arp_parser_state <= readARPDstPrtclAddr_0;
					end if;
				
				when readARPDstPrtclAddr_0 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPDstPrtclAddr_0;
					else
						arp_parser_inc_data_counter <= '1';
						arp_dst_prtcl_addr_0 <= rx_data;
						arp_parser_state <= readARPDstPrtclAddr_1;
					end if;
				
				when readARPDstPrtclAddr_1 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPDstPrtclAddr_1;
					else
						arp_parser_inc_data_counter <= '1';
						arp_dst_prtcl_addr_1 <= rx_data;
						arp_parser_state <= readARPDstPrtclAddr_2;
					end if;
				
				when readARPDstPrtclAddr_2 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPDstPrtclAddr_2;
					else
						arp_parser_inc_data_counter <= '1';
						arp_dst_prtcl_addr_2 <= rx_data;
						arp_parser_state <= readARPDstPrtclAddr_3;
					end if;
				
				when readARPDstPrtclAddr_3 =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readARPDstPrtclAddr_3;
					else
						arp_parser_inc_data_counter <= '1';
						arp_dst_prtcl_addr_3 <= rx_data;
						arp_parser_state <= readPadding;
					end if;
				
				when readPadding =>
					if (rx_data_fifo_valid = '0') then
						arp_parser_state <= readPadding;
					else
						arp_parser_inc_data_counter <= '1';
						
						if (arp_parser_data_counter = 45) then
							arp_rx_stop_crc <= '1';
							arp_msg_received <= '1';
							arp_parser_state <= idle;
						else
							arp_parser_state <= readPadding;
						end if;
					end if;
			end case;
		end if;
	end process arp_parser;


	arp_reply_fifo_0 : arp_reply_fifo
	port map (
		wr_clk			=> clk,
		rd_clk			=> tx_clk,
		din				=> arp_reply_fifo_din,
		wr_en				=> arp_reply_fifo_wr_en,
		rd_en				=> arp_reply_fifo_rd_en,
		dout				=> arp_reply_fifo_dout,
		full				=> open,
		empty				=> arp_reply_fifo_empty
	);


	arp_reply : process (clk)
	begin
		if rising_edge(clk) then
			
			case arp_reply_state is
				when idle =>
					if (arp_msg_received = '1') then
						arp_reply_state <= checkIfToReply;
					else
						arp_reply_state <= idle;
						arp_reply_fifo_wr_en <= '0';
					end if;
				
				when checkIfToReply =>
					if (((arp_opcode_0 & arp_opcode_1) = x"0001") and
							(arp_dst_prtcl_addr_0 = ip_addr_0) and
							(arp_dst_prtcl_addr_1 = ip_addr_1) and
							(arp_dst_prtcl_addr_2 = ip_addr_2) and
							(arp_dst_prtcl_addr_3 = (ip_addr_3_a & ip_addr_3_b))) then
						arp_reply_state <= waitARPReplyFifoEmpty;
					else
						arp_reply_state <= idle;
					end if;
				
				when waitARPReplyFifoEmpty =>
					if (arp_reply_fifo_empty = '1') then
						arp_reply_state <= SetDestHdwAddr_0;
					else
						arp_reply_state <= waitARPReplyFifoEmpty;
					end if;
				
				when SetDestHdwAddr_0 =>
					arp_reply_fifo_wr_en <= '1';
					arp_reply_fifo_din <= arp_src_hdw_addr_0;
					arp_reply_state <= SetDestHdwAddr_1;
				
				when SetDestHdwAddr_1 =>
					arp_reply_fifo_din <= arp_src_hdw_addr_1;
					arp_reply_state <= SetDestHdwAddr_2;
				
				when SetDestHdwAddr_2 =>
					arp_reply_fifo_din <= arp_src_hdw_addr_2;
					arp_reply_state <= SetDestHdwAddr_3;
				
				when SetDestHdwAddr_3 =>
					arp_reply_fifo_din <= arp_src_hdw_addr_3;
					arp_reply_state <= SetDestHdwAddr_4;
				
				when SetDestHdwAddr_4 =>
					arp_reply_fifo_din <= arp_src_hdw_addr_4;
					arp_reply_state <= SetDestHdwAddr_5;
				
				when SetDestHdwAddr_5 =>
					arp_reply_fifo_din <= arp_src_hdw_addr_5;
					arp_reply_state <= SetSrcHdwAddr_0;
				
				when SetSrcHdwAddr_0 =>
					arp_reply_fifo_din <= mac_addr_0;
					arp_reply_state <= SetSrcHdwAddr_1;
				
				when SetSrcHdwAddr_1 =>
					arp_reply_fifo_din <= mac_addr_1;
					arp_reply_state <= SetSrcHdwAddr_2;
				
				when SetSrcHdwAddr_2 =>
					arp_reply_fifo_din <= mac_addr_2;
					arp_reply_state <= SetSrcHdwAddr_3;
				
				when SetSrcHdwAddr_3 =>
					arp_reply_fifo_din <= mac_addr_3;
					arp_reply_state <= SetSrcHdwAddr_4;
				
				when SetSrcHdwAddr_4 =>
					arp_reply_fifo_din <= mac_addr_4;
					arp_reply_state <= SetSrcHdwAddr_5;
				
				when SetSrcHdwAddr_5 =>
					arp_reply_fifo_din <= mac_addr_5_a & mac_addr_5_b;
					arp_reply_state <= SetEthertype_0;
				
				when SetEthertype_0 =>							-- ARP
					arp_reply_fifo_din <= x"08";
					arp_reply_state <= SetEthertype_1;
				
				when SetEthertype_1 =>
					arp_reply_fifo_din <= x"06";
					arp_reply_state <= SetArpHdwType_0;
				
				when SetArpHdwType_0 =>							-- Ethernet
					arp_reply_fifo_din <= x"00";
					arp_reply_state <= SetArpHdwType_1;
				
				when SetArpHdwType_1 =>
					arp_reply_fifo_din <= x"01";
					arp_reply_state <= SetArpPrtclType_0;
				
				when SetArpPrtclType_0 =>						-- IP
					arp_reply_fifo_din <= x"08";
					arp_reply_state <= SetArpPrtclType_1;
				
				when SetArpPrtclType_1 =>
					arp_reply_fifo_din <= x"00";
					arp_reply_state <= SetArpHdwAddrLen;
				
				when SetArpHdwAddrLen =>
					arp_reply_fifo_din <= x"06";
					arp_reply_state <= SetArpPrtclAddrLen;
				
				when SetArpPrtclAddrLen =>
					arp_reply_fifo_din <= x"04";
					arp_reply_state <= SetArpOpcode_0;
				
				when SetArpOpcode_0 =>							-- Arp reply
					arp_reply_fifo_din <= x"00";
					arp_reply_state <= SetArpOpcode_1;
				
				when SetArpOpcode_1 =>
					arp_reply_fifo_din <= x"02";
					arp_reply_state <= SetArpSrcHdwAddr_0;
				
				when SetArpSrcHdwAddr_0 =>
					arp_reply_fifo_din <= mac_addr_0;
					arp_reply_state <= SetArpSrcHdwAddr_1;
				
				when SetArpSrcHdwAddr_1 =>
					arp_reply_fifo_din <= mac_addr_1;
					arp_reply_state <= SetArpSrcHdwAddr_2;
				
				when SetArpSrcHdwAddr_2 =>
					arp_reply_fifo_din <= mac_addr_2;
					arp_reply_state <= SetArpSrcHdwAddr_3;
				
				when SetArpSrcHdwAddr_3 =>
					arp_reply_fifo_din <= mac_addr_3;
					arp_reply_state <= SetArpSrcHdwAddr_4;
				
				when SetArpSrcHdwAddr_4 =>
					arp_reply_fifo_din <= mac_addr_4;
					arp_reply_state <= SetArpSrcHdwAddr_5;
				
				when SetArpSrcHdwAddr_5 =>
					arp_reply_fifo_din <= mac_addr_5_a & mac_addr_5_b;
					arp_reply_state <= SetArpSrcPrtclAddr_0;
				
				when SetArpSrcPrtclAddr_0 =>
					arp_reply_fifo_din <= ip_addr_0;
					arp_reply_state <= SetArpSrcPrtclAddr_1;
				
				when SetArpSrcPrtclAddr_1 =>
					arp_reply_fifo_din <= ip_addr_1;
					arp_reply_state <= SetArpSrcPrtclAddr_2;
				
				when SetArpSrcPrtclAddr_2 =>
					arp_reply_fifo_din <= ip_addr_2;
					arp_reply_state <= SetArpSrcPrtclAddr_3;
				
				when SetArpSrcPrtclAddr_3 =>
					arp_reply_fifo_din <= ip_addr_3_a & ip_addr_3_b;
					arp_reply_state <= SetArpDstHdwAddr_0;
				
				when SetArpDstHdwAddr_0 =>
					arp_reply_fifo_din <= arp_src_hdw_addr_0;
					arp_reply_state <= SetArpDstHdwAddr_1;
				
				when SetArpDstHdwAddr_1 =>
					arp_reply_fifo_din <= arp_src_hdw_addr_1;
					arp_reply_state <= SetArpDstHdwAddr_2;
				
				when SetArpDstHdwAddr_2 =>
					arp_reply_fifo_din <= arp_src_hdw_addr_2;
					arp_reply_state <= SetArpDstHdwAddr_3;
				
				when SetArpDstHdwAddr_3 =>
					arp_reply_fifo_din <= arp_src_hdw_addr_3;
					arp_reply_state <= SetArpDstHdwAddr_4;
				
				when SetArpDstHdwAddr_4 =>
					arp_reply_fifo_din <= arp_src_hdw_addr_4;
					arp_reply_state <= SetArpDstHdwAddr_5;
				
				when SetArpDstHdwAddr_5 =>
					arp_reply_fifo_din <= arp_src_hdw_addr_5;
					arp_reply_state <= SetArpDstPrtclAddr_0;
				
				when SetArpDstPrtclAddr_0 =>
					arp_reply_fifo_din <= arp_src_prtcl_addr_0;
					arp_reply_state <= SetArpDstPrtclAddr_1;
				
				when SetArpDstPrtclAddr_1 =>
					arp_reply_fifo_din <= arp_src_prtcl_addr_1;
					arp_reply_state <= SetArpDstPrtclAddr_2;
				
				when SetArpDstPrtclAddr_2 =>
					arp_reply_fifo_din <= arp_src_prtcl_addr_2;
					arp_reply_state <= SetArpDstPrtclAddr_3;
				
				when SetArpDstPrtclAddr_3 =>
					arp_reply_fifo_din <= arp_src_prtcl_addr_3;
					arp_reply_state <= idle;
			end case;
		end if;
	end process arp_reply;


	ip_rx_src_mac_proc : process (clk)
	begin
		if rising_edge(clk) then
			if (rx_pkt_isIP = '1') then
				ip_rx_src_mac_0 <= rx_src_mac_0;
				ip_rx_src_mac_1 <= rx_src_mac_1;
				ip_rx_src_mac_2 <= rx_src_mac_2;
				ip_rx_src_mac_3 <= rx_src_mac_3;
				ip_rx_src_mac_4 <= rx_src_mac_4;
				ip_rx_src_mac_5 <= rx_src_mac_5;
			end if;
		end if;
	end process ip_rx_src_mac_proc;
	

	ip_hdr_parser : process (clk)
	begin
		if rising_edge(clk) then
			rx_pkt_isUDP <= '0';
			rx_pkt_isICMP <= '0';
			
			case ip_hdr_parser_state is
				when idle =>
					if (rx_pkt_isIP = '1') then
						ip_hdr_parser_state <= readIPHeader_0;
					else
						ip_hdr_parser_state <= idle;
					end if;
				
				when readIPHeader_0 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_0;
					else
						ip_hdr_version <= rx_data(7 downto 4);
						ip_hdr_length <= rx_data(3 downto 0);
						ip_hdr_parser_state <= readIPHeader_1;
					end if;
				
				when readIPHeader_1 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_1;
					else
						ip_hdr_diff_services <= rx_data;
						ip_hdr_parser_state <= readIPHeader_2;
					end if;
				
				when readIPHeader_2 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_2;
					else
						ip_hdr_total_length(10 downto 8) <= rx_data(2 downto 0);
						ip_hdr_parser_state <= readIPHeader_3;
					end if;
				
				when readIPHeader_3 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_3;
					else
						ip_hdr_total_length(7 downto 0) <= rx_data;
						ip_hdr_parser_state <= readIPHeader_4;
					end if;
				
				when readIPHeader_4 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_4;
					else
						ip_hdr_pktid(15 downto 8) <= rx_data;
						ip_hdr_parser_state <= readIPHeader_5;
					end if;
				
				when readIPHeader_5 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_5;
					else
						ip_hdr_pktid(7 downto 0) <= rx_data;
						ip_hdr_parser_state <= readIPHeader_6;
					end if;
				
				when readIPHeader_6 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_6;
					else
						ip_hdr_flags <= rx_data(7 downto 5);
						ip_hdr_frag_offset(12 downto 8) <= rx_data(4 downto 0);
						ip_hdr_parser_state <= readIPHeader_7;
					end if;
				
				when readIPHeader_7 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_7;
					else
						ip_hdr_frag_offset(7 downto 0) <= rx_data;
						ip_hdr_parser_state <= readIPHeader_8;
					end if;
				
				when readIPHeader_8 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_8;
					else
						ip_hdr_TTL <= rx_data;
						ip_hdr_parser_state <= readIPHeader_9;
					end if;
				
				when readIPHeader_9 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_9;
					else
						ip_hdr_protocol <= rx_data;
						ip_hdr_parser_state <= readIPHeader_10;
					end if;
				
				when readIPHeader_10 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_10;
					else
--						ip_hdr_checksum(15 downto 8) <= rx_data;
						ip_hdr_parser_state <= readIPHeader_11;
					end if;
				
				when readIPHeader_11 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_11;
					else
--						ip_hdr_checksum(7 downto 0) <= rx_data;
						ip_hdr_parser_state <= readIPHeader_12;
					end if;
				
				when readIPHeader_12 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_12;
					else
						ip_hdr_src_addr_0 <= rx_data;
						ip_hdr_parser_state <= readIPHeader_13;
					end if;
				
				when readIPHeader_13 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_13;
					else
						ip_hdr_src_addr_1 <= rx_data;
						ip_hdr_parser_state <= readIPHeader_14;
					end if;
				
				when readIPHeader_14 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_14;
					else
						ip_hdr_src_addr_2 <= rx_data;
						ip_hdr_parser_state <= readIPHeader_15;
					end if;
				
				when readIPHeader_15 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_15;
					else
						ip_hdr_src_addr_3 <= rx_data;
						ip_hdr_parser_state <= readIPHeader_16;
					end if;
				
				when readIPHeader_16 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_16;
					else
						ip_hdr_dst_addr_0 <= rx_data;
						ip_hdr_parser_state <= readIPHeader_17;
					end if;
				
				when readIPHeader_17 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_17;
					else
						ip_hdr_dst_addr_1 <= rx_data;
						ip_hdr_parser_state <= readIPHeader_18;
					end if;
				
				when readIPHeader_18 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_18;
					else
						ip_hdr_dst_addr_2 <= rx_data;
						ip_hdr_parser_state <= readIPHeader_19;
					end if;
				
				when readIPHeader_19 =>
					if (rx_data_fifo_valid = '0') then
						ip_hdr_parser_state <= readIPHeader_19;
					else
						ip_hdr_dst_addr_3 <= rx_data;
						case ip_hdr_protocol is
							when "00000001" =>
								rx_pkt_isICMP <= '1';
							when "00010001" =>
								rx_pkt_isUDP <= '1';
							when others =>
								null;
						end case;
						ip_hdr_parser_state <= idle;
					end if;
			end case;
		end if;
	end process ip_hdr_parser;


	icmp_data_fifo_0 : icmp_fifo
	port map (
		clk			=> clk,
		din			=> icmp_data_fifo_din,
		wr_en			=> icmp_data_fifo_wr_en,
		rd_en			=> icmp_data_fifo_rd_en,
		dout			=> icmp_data_fifo_dout,
		full			=> open,
		empty			=> icmp_data_fifo_empty
	);


	icmp_copy_ip_header_data : process (clk)
	begin
		if rising_edge(clk) then
			if (icmp_rx_copy_IP_header = '1') then
				icmp_rx_ip_hdr_version <= ip_hdr_version;
				icmp_rx_ip_hdr_length <= ip_hdr_length;
				icmp_rx_diff_services <= ip_hdr_diff_services;
				icmp_rx_hdw_src_addr_0 <= ip_rx_src_mac_0;
				icmp_rx_hdw_src_addr_1 <= ip_rx_src_mac_1;
				icmp_rx_hdw_src_addr_2 <= ip_rx_src_mac_2;
				icmp_rx_hdw_src_addr_3 <= ip_rx_src_mac_3;
				icmp_rx_hdw_src_addr_4 <= ip_rx_src_mac_4;
				icmp_rx_hdw_src_addr_5 <= ip_rx_src_mac_5;
				icmp_rx_length <= ip_hdr_total_length;
				icmp_rx_pkt_id <= ip_hdr_pktid;
				icmp_rx_flags <= ip_hdr_flags;
				icmp_rx_frag_offset <= ip_hdr_frag_offset;
				icmp_rx_TTL <= ip_hdr_TTL;
				icmp_rx_protocol <= ip_hdr_protocol;
				icmp_rx_src_ip_addr_0 <= ip_hdr_src_addr_0;
				icmp_rx_src_ip_addr_1 <= ip_hdr_src_addr_1;
				icmp_rx_src_ip_addr_2 <= ip_hdr_src_addr_2;
				icmp_rx_src_ip_addr_3 <= ip_hdr_src_addr_3;
				icmp_rx_dst_ip_addr_0 <= ip_hdr_dst_addr_0;
				icmp_rx_dst_ip_addr_1 <= ip_hdr_dst_addr_1;
				icmp_rx_dst_ip_addr_2 <= ip_hdr_dst_addr_2;
				icmp_rx_dst_ip_addr_3 <= ip_hdr_dst_addr_3;
			end if;
		end if;
	end process icmp_copy_ip_header_data;

	
	icmp_rx_check_address : process (clk)
	begin
		if rising_edge(clk) then
			if ((icmp_rx_dst_ip_addr_0 = ip_addr_0) and (icmp_rx_dst_ip_addr_1 = ip_addr_1) and
						(icmp_rx_dst_ip_addr_2 = ip_addr_2) and (icmp_rx_dst_ip_addr_3 = (ip_addr_3_a & ip_addr_3_b)) and
								(icmp_rx_ip_hdr_version = "0100")) then
				icmp_rx_address_good <= '1';
			else
				icmp_rx_address_good <= '0';
			end if;
		end if;
	end process icmp_rx_check_address;


	icmp_data_counter_proc : process (clk)
	begin
		if rising_edge(clk) then
			if (icmp_rx_set_counter = '1') then
				icmp_rx_data_counter <= unsigned(ip_hdr_total_length);
			elsif (icmp_rx_dec_counter = '1') then
				icmp_rx_data_counter <= icmp_rx_data_counter - 1;
			end if;
		end if;
	end process icmp_data_counter_proc;


	icmp_receive : process (clk)
	begin
		if rising_edge(clk) then
			icmp_echo_request_received <= '0';
			icmp_data_fifo_wr_en <= '0';
			icmp_rx_copy_IP_header <= '0';
			icmp_rx_set_counter <= '0';
			icmp_rx_dec_counter <= '0';
			icmp_rx_stop_crc <= '0';
			
			case icmp_receive_state is
				when idle =>
					if (rx_pkt_isICMP = '1') then
						icmp_rx_set_counter <= '1';
						icmp_rx_copy_IP_header <= '1';
						icmp_receive_state <= readType;
					else
						icmp_receive_state <= idle;
					end if;
				
				when readType =>
					if (rx_data_fifo_valid = '0') then
						icmp_receive_state <= readType;
					else
						icmp_rx_dec_counter <= '1';
						icmp_rx_type <= rx_data;
						icmp_receive_state <= readCode;
					end if;
				
				when readCode =>
					if (rx_data_fifo_valid = '0') then
						icmp_receive_state <= readCode;
					else
						icmp_rx_dec_counter <= '1';
						icmp_rx_code <= rx_data;
						icmp_receive_state <= readChecksum_1;
					end if;
				
				when readChecksum_1 =>
					if (rx_data_fifo_valid = '0') then
						icmp_receive_state <= readChecksum_1;
					else
						icmp_rx_dec_counter <= '1';
						icmp_rx_checksum(15 downto 8) <= rx_data;
						icmp_receive_state <= readChecksum_2;
					end if;
				
				when readChecksum_2 =>
					if (rx_data_fifo_valid = '0') then
						icmp_receive_state <= readChecksum_2;
					else
						icmp_rx_dec_counter <= '1';
						icmp_rx_checksum(7 downto 0) <= rx_data;
						if (rx_data_pkt_end = '0') then
							icmp_receive_state <= readData;
						else
							icmp_receive_state <= waitChecksumResult;
						end if;
					end if;
				
				when readData =>
					if (rx_data_fifo_valid = '0') then
						icmp_receive_state <= readData;
					else
						icmp_rx_dec_counter <= '1';
						icmp_data_fifo_wr_en <= '1';
						icmp_data_fifo_din <= rx_data;
						if (icmp_rx_data_counter = 21) then		-- remember decreasing
							icmp_rx_stop_crc <= '1';
							icmp_receive_state <= waitChecksumResult;
						else
							icmp_receive_state <= readData;
						end if;
					end if;
				
				when waitChecksumResult =>
					if (rx_pkt_good_checksum = '1') then
						case icmp_rx_type is
							when x"08" =>
								icmp_echo_request_received <= icmp_rx_address_good;
								
							when others =>
								null;
						end case;
						icmp_receive_state <= idle;
					
					elsif (rx_pkt_bad_checksum = '1') then
						icmp_receive_state <= idle;
					else
						icmp_receive_state <= waitChecksumResult;
					end if;
			end case;			
		end if;
	end process icmp_receive;


	icmp_reply_checksum_arithmetic : process (clk)
	begin
		if rising_edge(clk) then
			if (icmp_reply_checksum_sub = '1') then
				icmp_reply_checksum_long <= unsigned(x"0000" & icmp_reply_checksum_a) - unsigned(x"0000" & icmp_reply_checksum_b);
			elsif (icmp_reply_checksum_add = '1') then
				icmp_reply_checksum_long <= unsigned(x"0000" & icmp_reply_checksum_a) + unsigned(x"0000" & icmp_reply_checksum_b);
			end if;
		end if;
	end process icmp_reply_checksum_arithmetic;
		

	icmp_reply_checksum_calculator : process (clk)
	begin
		if rising_edge(clk) then
			icmp_reply_checksum_sub <= '0';
			icmp_reply_checksum_add <= '0';
			
			case icmp_checksum_state is
				when idle =>
					if (icmp_reply_calc_checksum = '1') then
						icmp_checksum_state <= step_1;
						icmp_reply_checksum_valid <= '0';
					else
						icmp_checksum_state <= idle;
					end if;
				
				when step_1 =>
					icmp_reply_checksum <= not icmp_rx_checksum;
					icmp_checksum_state <= step_2;
				
				when step_2 =>
					icmp_reply_checksum_a <= icmp_reply_checksum;
					icmp_reply_checksum_b <= x"0800";
					icmp_reply_checksum_sub <= '1';
					icmp_checksum_state <= step_3;
				
				when step_3 =>
					icmp_checksum_state <= step_4;
				
				when step_4 =>
					if (icmp_reply_checksum_long(31 downto 16) = x"0000") then
						icmp_reply_checksum <= std_logic_vector(not icmp_reply_checksum_long(15 downto 0));
						icmp_reply_checksum_valid <= '1';
						icmp_checksum_state <= idle;
					else
						icmp_reply_checksum_a <= std_logic_vector(icmp_reply_checksum_long(31 downto 16));
						icmp_reply_checksum_b <= std_logic_vector(icmp_reply_checksum_long(15 downto 0));
						icmp_reply_checksum_add <= '1';
						icmp_checksum_state <= step_5;
					end if;
				
				when step_5 =>
					icmp_checksum_state <= step_3;
				
			end case;
		end if;
	end process icmp_reply_checksum_calculator;


	icmp_reply_fifo_0 : icmp_reply_fifo
	port map (
		wr_clk		=> clk,
		rd_clk		=> tx_clk,
		din			=> icmp_reply_fifo_din,
		wr_en			=> icmp_reply_fifo_wr_en,
		rd_en			=> icmp_reply_fifo_rd_en,
		dout			=> icmp_reply_fifo_dout,
		full			=> open,
		empty			=> icmp_reply_fifo_empty
	);


	icmp_reply_set_ip_header_proc : process (clk)
	begin
		if rising_edge(clk) then
			if (icmp_reply_set_ip_header = '1') then
				icmp_reply_ip_hdr_version <= icmp_rx_ip_hdr_version;
				icmp_reply_ip_hdr_length <= icmp_rx_ip_hdr_length;
				icmp_reply_ip_hdr_diff_services <= icmp_rx_diff_services;
				icmp_reply_ip_hdr_total_length <= icmp_rx_length;
				icmp_reply_ip_hdr_pktid <= icmp_rx_pkt_id;
				icmp_reply_ip_hdr_flags <= icmp_rx_flags;
				icmp_reply_ip_hdr_frag_offset <= icmp_rx_frag_offset;
				icmp_reply_ip_hdr_TTL <= icmp_rx_TTL;
				icmp_reply_ip_hdr_protocol <= icmp_rx_protocol;
				icmp_reply_ip_hdr_dst_addr_0 <= icmp_rx_src_ip_addr_0;
				icmp_reply_ip_hdr_dst_addr_1 <= icmp_rx_src_ip_addr_1;
				icmp_reply_ip_hdr_dst_addr_2 <= icmp_rx_src_ip_addr_2;
				icmp_reply_ip_hdr_dst_addr_3 <= icmp_rx_src_ip_addr_3;
			end if;
		end if;
	end process icmp_reply_set_ip_header_proc;


	icmp_reply_ip_addr_3 <= ip_addr_3_a & ip_addr_3_b;

	icmp_ip_checksum: ip_checksum
	port map (
		clk								=> clk,
		calc_ip_reply_checksum		=> icmp_calc_ip_reply_checksum,
		ip_reply_hdr_version			=> icmp_rx_ip_hdr_version,
		ip_reply_hdr_length			=> icmp_rx_ip_hdr_length,
		ip_reply_hdr_diff_services	=> icmp_rx_diff_services,
		ip_reply_hdr_total_length	=> icmp_rx_length,
		ip_reply_hdr_pktid			=> icmp_rx_pkt_id,
		ip_reply_hdr_flags			=> icmp_rx_flags,
		ip_reply_hdr_frag_offset	=> icmp_rx_frag_offset,
		ip_reply_hdr_TTL				=> icmp_rx_TTL,
		ip_reply_hdr_protocol		=> icmp_rx_protocol,
		ip_reply_addr_0 				=> ip_addr_0,
		ip_reply_addr_1 				=> ip_addr_1,
		ip_reply_addr_2 				=> ip_addr_2,
		ip_reply_addr_3 				=> icmp_reply_ip_addr_3,
		ip_reply_hdr_dst_addr_0		=> icmp_reply_ip_hdr_dst_addr_0,
		ip_reply_hdr_dst_addr_1		=> icmp_reply_ip_hdr_dst_addr_1,
		ip_reply_hdr_dst_addr_2		=> icmp_reply_ip_hdr_dst_addr_2,
		ip_reply_hdr_dst_addr_3		=> icmp_reply_ip_hdr_dst_addr_3,
		ip_reply_checksum				=> icmp_reply_ip_hdr_checksum,
		ip_reply_checksum_valid		=> icmp_reply_ip_hdr_checksum_valid
	);


	icmp_reply : process (clk)
	begin
		if rising_edge(clk) then
			icmp_reply_fifo_wr_en <= '0';
			icmp_data_fifo_rd_en <= '0';
			icmp_reply_calc_checksum <= '0';
			icmp_calc_ip_reply_checksum <= '0';
			icmp_reply_set_ip_header <= '0';
			
			case icmp_reply_state is
				when idle =>
					if (icmp_echo_request_received = '1') then
						icmp_reply_set_ip_header <= '1';
						icmp_reply_state <= waitIPHeaderSet;
					else
						icmp_reply_state <= idle;
					end if;
				
				when waitIPHeaderSet =>
					icmp_reply_calc_checksum <= '1';
					icmp_calc_ip_reply_checksum <= '1';
					icmp_reply_state <= waitChecksumsValid;
				
				when waitChecksumsValid =>
					if ((icmp_reply_checksum_valid = '0') or (icmp_reply_ip_hdr_checksum_valid = '0')) then
						icmp_reply_state <= waitChecksumsValid;
					else
						icmp_reply_state <= setDestHdwAddr_1;
					end if;
				
				when setDestHdwAddr_1 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_rx_hdw_src_addr_0;
					icmp_reply_state <= setDestHdwAddr_2;
				
				when setDestHdwAddr_2 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_rx_hdw_src_addr_1;
					icmp_reply_state <= setDestHdwAddr_3;
				
				when setDestHdwAddr_3 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_rx_hdw_src_addr_2;
					icmp_reply_state <= setDestHdwAddr_4;
				
				when setDestHdwAddr_4 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_rx_hdw_src_addr_3;
					icmp_reply_state <= setDestHdwAddr_5;
				
				when setDestHdwAddr_5 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_rx_hdw_src_addr_4;
					icmp_reply_state <= setDestHdwAddr_6;
				
				when setDestHdwAddr_6 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_rx_hdw_src_addr_5;
					icmp_reply_state <= setSrcHdwAddr_1;
				
				when setSrcHdwAddr_1 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= mac_addr_0;
					icmp_reply_state <= setSrcHdwAddr_2;
				
				when setSrcHdwAddr_2 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= mac_addr_1;
					icmp_reply_state <= setSrcHdwAddr_3;
				
				when setSrcHdwAddr_3 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= mac_addr_2;
					icmp_reply_state <= setSrcHdwAddr_4;
				
				when setSrcHdwAddr_4 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= mac_addr_3;
					icmp_reply_state <= setSrcHdwAddr_5;
				
				when setSrcHdwAddr_5 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= mac_addr_4;
					icmp_reply_state <= setSrcHdwAddr_6;
				
				when setSrcHdwAddr_6 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= mac_addr_5_a & mac_addr_5_b;
					icmp_reply_state <= setEthertype_1;
				
				when setEthertype_1 =>							-- is IP
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= x"08";
					icmp_reply_state <= setEthertype_2;
				
				when setEthertype_2 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= x"00";
					icmp_reply_state <= setIPHdr_1;
				
				when setIPHdr_1 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_reply_ip_hdr_version & icmp_reply_ip_hdr_length;		-- v4 hdrlen 20
					icmp_reply_state <= setIPHdr_2;
				
				when setIPHdr_2 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_reply_ip_hdr_diff_services;								-- diff services
					icmp_reply_state <= setIPHdrLen_1;
				
				when setIPHdrLen_1 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= "00000" & icmp_reply_ip_hdr_total_length(10 downto 8);
					icmp_reply_state <= setIPHdrLen_2;
				
				when setIPHdrLen_2 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_reply_ip_hdr_total_length(7 downto 0);
					icmp_reply_state <= setIPHdrPktID_1;
				
				when setIPHdrPktID_1 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_reply_ip_hdr_pktid(15 downto 8);
					icmp_reply_state <= setIPHdrPktID_2;
				
				when setIPHdrPktID_2 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_reply_ip_hdr_pktid(7 downto 0);
					icmp_reply_state <= setIPHdrFlagsFrag_1;
				
				when setIPHdrFlagsFrag_1 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_reply_ip_hdr_flags & icmp_reply_ip_hdr_frag_offset(12 downto 8);
					icmp_reply_state <= setIPHdrFlagsFrag_2;
				
				when setIPHdrFlagsFrag_2 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_reply_ip_hdr_frag_offset(7 downto 0);
					icmp_reply_state <= setIPHdr_TTL;
				
				when setIPHdr_TTL =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_reply_ip_hdr_TTL;
					icmp_reply_state <= setIPHdr_Protocol;
				
				when setIPHdr_Protocol =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_reply_ip_hdr_protocol;
					icmp_reply_state <= setIPHdr_Checksum_1;
				
				when setIPHdr_Checksum_1 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_reply_ip_hdr_checksum(15 downto 8);
					icmp_reply_state <= setIPHdr_Checksum_2;
				
				when setIPHdr_Checksum_2 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_reply_ip_hdr_checksum(7 downto 0);
					icmp_reply_state <= setIPHdr_SrcAddr_1;
				
				when setIPHdr_SrcAddr_1 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= ip_addr_0;
					icmp_reply_state <= setIPHdr_SrcAddr_2;
				
				when setIPHdr_SrcAddr_2 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= ip_addr_1;
					icmp_reply_state <= setIPHdr_SrcAddr_3;
				
				when setIPHdr_SrcAddr_3 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= ip_addr_2;
					icmp_reply_state <= setIPHdr_SrcAddr_4;
				
				when setIPHdr_SrcAddr_4 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= ip_addr_3_a & ip_addr_3_b;
					icmp_reply_state <= setIPHdr_DstAddr_1;
				
				when setIPHdr_DstAddr_1 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_rx_src_ip_addr_0;
					icmp_reply_state <= setIPHdr_DstAddr_2;
				
				when setIPHdr_DstAddr_2 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_rx_src_ip_addr_1;
					icmp_reply_state <= setIPHdr_DstAddr_3;
				
				when setIPHdr_DstAddr_3 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_rx_src_ip_addr_2;
					icmp_reply_state <= setIPHdr_DstAddr_4;
				
				when setIPHdr_DstAddr_4 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_rx_src_ip_addr_3;
					icmp_reply_state <= setICMP_Type;
				
				when setICMP_Type =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= x"00";		-- Echo reply
					icmp_reply_state <= setICMP_Code;
				
				when setICMP_Code =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_rx_code;
					icmp_reply_state <= setICMP_Checksum_1;
				
				when setICMP_Checksum_1 =>
					icmp_reply_fifo_wr_en <= '1';
					icmp_data_fifo_rd_en <= '1';
					icmp_reply_fifo_din <= icmp_reply_checksum(15 downto 8);
					icmp_reply_state <= setICMP_Checksum_2;
				
				when setICMP_Checksum_2 =>
					icmp_data_fifo_rd_en <= '1';
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_reply_checksum(7 downto 0);
					if (icmp_data_fifo_empty = '0') then
						icmp_reply_state <= setICMP_Data;
					else
						icmp_reply_state <= idle;
					end if;
					
				when setICMP_Data =>
					icmp_data_fifo_rd_en <= '1';
					icmp_reply_fifo_wr_en <= '1';
					icmp_reply_fifo_din <= icmp_data_fifo_dout;
					if (icmp_data_fifo_empty = '1') then
						icmp_reply_state <= idle;
					else
						icmp_reply_state <= setICMP_Data;
					end if;
					
			end case;
		end if;
	end process icmp_reply;
	

	udp_data_fifo_0 : udp_data_fifo
	port map (
		clk			=> clk,
		din			=> udp_data_fifo_din,
		wr_en			=> udp_data_fifo_wr_en,
		rd_en			=> udp_data_fifo_rd_en,
		dout			=> udp_data_fifo_dout,
		full			=> open,
		empty			=> udp_data_fifo_empty
	);


--udp_checksum_fifo_0 : udp_checksum_fifo
--	port map (
--		clk			=> tx_clk,
--		rst			=> reset,
--		din			=> udp_data_fifo_din(7 downto 0),
--		wr_en			=> udp_data_fifo_wr_en,
--		rd_en			=> udp_chksum_fifo_rd_en,
--		dout			=> udp_chksum_fifo_dout,
--		full			=> open,
--		empty			=> udp_chksum_fifo_empty
--	);


	udp_rx_copy_ip_header_data : process (clk)
	begin
		if rising_edge(clk) then
			if (udp_rx_packet_good = '1') then
--				udp_rx_ip_hdr_version <= ip_hdr_version;
				udp_rx_hdw_src_addr_0 <= ip_rx_src_mac_0;
				udp_rx_hdw_src_addr_1 <= ip_rx_src_mac_1;
				udp_rx_hdw_src_addr_2 <= ip_rx_src_mac_2;
				udp_rx_hdw_src_addr_3 <= ip_rx_src_mac_3;
				udp_rx_hdw_src_addr_4 <= ip_rx_src_mac_4;
				udp_rx_hdw_src_addr_5 <= ip_rx_src_mac_5;
--				udp_rx_length <= ip_hdr_total_length;
				udp_rx_pkt_id <= ip_hdr_pktid;
--				udp_rx_flags <= ip_hdr_flags;
				udp_rx_frag_offset <= ip_hdr_frag_offset;
--				udp_rx_TTL <= ip_hdr_TTL;
--				udp_rx_protocol <= ip_hdr_protocol;
				udp_rx_src_ip_addr_0 <= ip_hdr_src_addr_0;
				udp_rx_src_ip_addr_1 <= ip_hdr_src_addr_1;
				udp_rx_src_ip_addr_2 <= ip_hdr_src_addr_2;
				udp_rx_src_ip_addr_3 <= ip_hdr_src_addr_3;
--				udp_rx_dst_ip_addr_0 <= ip_hdr_dst_addr_0;
--				udp_rx_dst_ip_addr_1 <= ip_hdr_dst_addr_1;
--				udp_rx_dst_ip_addr_2 <= ip_hdr_dst_addr_2;
--				udp_rx_dst_ip_addr_3 <= ip_hdr_dst_addr_3;
			end if;
		end if;
	end process udp_rx_copy_ip_header_data;


	udp_rx_check_packet_good : process (clk)
	begin
		if rising_edge(clk) then
			if (reset_udp_rx_packet_good = '1') then
				udp_rx_packet_good <= '0';
			elsif ((ip_hdr_dst_addr_0 = ip_addr_0) and (ip_hdr_dst_addr_1 = ip_addr_1) and
					(ip_hdr_dst_addr_2 = ip_addr_2) and (ip_hdr_dst_addr_3 = (ip_addr_3_a & ip_addr_3_b)) and
						(udp_rx_dst_port = (udp_port_a & udp_port_b)) and (ip_hdr_version = "0100") and (check_udp_rx_packet_good= '1')) then
				udp_rx_packet_good <= '1';
--				udp_rx_copy_IP_header <= '1';
			end if;
		end if;
	end process udp_rx_check_packet_good;


	udp_rx_total_counter_proc : process (clk)
	begin
		if rising_edge(clk) then
			if (udp_rx_set_total_counter = '1') then
				udp_rx_total_counter <= unsigned(ip_hdr_total_length);
			elsif (udp_rx_dec_total_counter = '1') then
				udp_rx_total_counter <= udp_rx_total_counter - 1;
			end if;
		
		end if;
	end process udp_rx_total_counter_proc;


	udp_rx_data_counter_proc : process (clk)
	begin
		if rising_edge(clk) then
			if (udp_rx_set_data_counter = '1') then
				udp_rx_data_counter <= unsigned(udp_rx_data_length);
			elsif (udp_rx_dec_data_counter = '1') then
				udp_rx_data_counter <= udp_rx_data_counter - 1;
			end if;
		
		end if;
	end process udp_rx_data_counter_proc;
		

	udp_receive : process (clk)
	begin
		if rising_edge(clk) then
			udp_rx_pass_message <= '0';
			udp_data_fifo_wr_en <= '0';
			udp_rx_stop_crc <= '0';
			udp_rx_pass_message <= '0';
			udp_rx_flush_message <= '0';
			udp_rx_set_total_counter <= '0';
			udp_rx_dec_total_counter <= '0';
			udp_rx_set_data_counter <= '0';
			udp_rx_dec_data_counter <= '0';
			reset_udp_rx_packet_good <= '0';
			check_udp_rx_packet_good <= '0';
			
			case udp_receive_state is
				when idle =>
					if (rx_pkt_isUDP = '1') then
						udp_rx_set_total_counter <= '1';
						udp_receive_state <= readUDPHdrSrcPort_0;
					else
						reset_udp_rx_packet_good <= '1';
						udp_receive_state <= idle;
					end if;
					
				when readUDPHdrSrcPort_0 =>
					if (rx_data_fifo_valid = '0') then
						udp_receive_state <= readUDPHdrSrcPort_0;
					else
						udp_rx_dec_total_counter <= '1';
--						udp_rx_src_port(15 downto 8) <= rx_data;
						udp_receive_state <= readUDPHdrSrcPort_1;
					end if;
				
				when readUDPHdrSrcPort_1 =>
					if (rx_data_fifo_valid = '0') then
						udp_receive_state <= readUDPHdrSrcPort_1;
					else
						udp_rx_dec_total_counter <= '1';
--						udp_rx_src_port(7 downto 0) <= rx_data;
						udp_receive_state <= readUDPHdrDstPort_0;
					end if;
				
				when readUDPHdrDstPort_0 =>
					if (rx_data_fifo_valid = '0') then
						udp_receive_state <= readUDPHdrDstPort_0;
					else
						udp_rx_dec_total_counter <= '1';
						udp_rx_dst_port(15 downto 8) <= rx_data;
						udp_receive_state <= readUDPHdrDstPort_1;
					end if;
				
				when readUDPHdrDstPort_1 =>
					if (rx_data_fifo_valid = '0') then
						udp_receive_state <= readUDPHdrDstPort_1;
					else
						udp_rx_dec_total_counter <= '1';
						udp_rx_dst_port(7 downto 0) <= rx_data;
						udp_receive_state <= readUDPHdrLen_0;
					end if;
				
				when readUDPHdrLen_0 =>
					if (rx_data_fifo_valid = '0') then
						udp_receive_state <= readUDPHdrLen_0;
					else
						udp_rx_dec_total_counter <= '1';
						udp_rx_data_length(10 downto 8) <= rx_data(2 downto 0);
						udp_receive_state <= readUDPHdrLen_1;
					end if;
				
				when readUDPHdrLen_1 =>
					if (rx_data_fifo_valid = '0') then
						udp_receive_state <= readUDPHdrLen_1;
					else
						udp_rx_dec_total_counter <= '1';
						udp_rx_data_length(7 downto 0) <= rx_data;
						udp_receive_state <= readUDPHdrChkSum_0;
					end if;
				
				when readUDPHdrChkSum_0 =>
					if (rx_data_fifo_valid = '0') then
						udp_receive_state <= readUDPHdrChkSum_0;
					else
						check_udp_rx_packet_good <= '1';
						udp_rx_dec_total_counter <= '1';
--						udp_rx_checksum(15 downto 8) <= rx_data;
						udp_receive_state <= readUDPHdrChkSum_1;
					end if;
				
				when readUDPHdrChkSum_1 =>
					if (rx_data_fifo_valid = '0') then
						udp_receive_state <= readUDPHdrChkSum_1;
					else
						udp_rx_dec_total_counter <= '1';
--						udp_rx_checksum(7 downto 0) <= rx_data;
						udp_rx_set_data_counter <= '1';
--						udp_rx_header_valid <= '1';
						udp_receive_state <= readUDPData;
					end if;
				
				
				when readUDPData =>
					if (rx_data_fifo_valid = '0') then
						udp_receive_state <= readUDPData;
					else
						udp_rx_dec_data_counter <= '1';
						udp_rx_dec_total_counter <= '1';
						if (udp_rx_data_counter = 9) then
							udp_data_fifo_wr_en <= udp_rx_packet_good;
							udp_data_fifo_din <= '1' & rx_data;
							udp_receive_state <= readUDPData;
							if (rx_pkt_counter >= 67) then
								udp_rx_stop_crc <= '1';
							end if;
						elsif (udp_rx_data_counter = 8) then
							if (rx_pkt_counter = 67) then
								udp_rx_stop_crc <= '1';
								udp_receive_state <= waitChecksumResult;
							elsif (rx_pkt_counter < 67) then
								udp_receive_state <= readPadding;
							else
								udp_receive_state <= waitChecksumResult;
							end if;
						else
							udp_data_fifo_wr_en <= udp_rx_packet_good;
							udp_data_fifo_din <= '0' & rx_data;
							udp_receive_state <= readUDPData;
						end if;
					end if;
					
				when readPadding =>
					if (rx_data_fifo_valid = '0') then
						udp_receive_state <= readPadding;
					else
						if (rx_pkt_counter = 67) then
							udp_receive_state <= waitChecksumResult;
							udp_rx_stop_crc <= '1';
						else
							udp_receive_state <= readPadding;
						end if;
					end if;
				
				when waitChecksumResult =>
					if (rx_pkt_good_checksum = '1') then
						udp_receive_state <= idle;
						udp_rx_pass_message <= udp_rx_packet_good;
					elsif (rx_pkt_bad_checksum = '1') then
						udp_receive_state <= idle;
						udp_rx_flush_message <= udp_rx_packet_good;
					else
						udp_receive_state <= waitChecksumResult;
					end if;
			
			end case;
		end if;
	end process udp_receive;
	

	message_fifo_in_wr_en <= message_fifo_in_wr_en_i;
	message_fifo_in_din <= message_fifo_in_din_i;

	message_transfer_proc : process (clk)
	begin
		if rising_edge(clk) then
			message_fifo_in_wr_en_i <= '0';
			udp_data_fifo_rd_en <= '0';
			
			case message_transfer_state is
				when idle =>
					if (udp_rx_pass_message = '1') then
						message_first <= '1';
						udp_data_fifo_rd_en <= '1';
						message_transfer_state <= passMessage;
					elsif (udp_rx_flush_message = '1') then
						message_first <= '1';
						udp_data_fifo_rd_en <= '1';
						message_transfer_state <= flushMessage;
					else
						message_transfer_state <= idle;
					end if;
				
				when passMessage =>
					udp_data_fifo_rd_en <= '1';
					message_fifo_in_din_i <= udp_data_fifo_dout;
					if (message_first = '1') then
						message_first <= '0';
						message_transfer_state <= passMessage;
					else
						message_fifo_in_wr_en_i <= '1';
						if (udp_data_fifo_dout(8) = '1') then
							message_transfer_state <= idle;
						else
						message_transfer_state <= passMessage;
						end if;
					end if;
				
				when flushMessage =>
					udp_data_fifo_rd_en <= '1';
					message_fifo_in_din_i <= udp_data_fifo_dout;
					if (message_first = '1') then
						message_first <= '0';
						message_transfer_state <= flushMessage;
					else
						if (udp_data_fifo_dout(8) = '1') then
							message_transfer_state <= idle;
						else
							message_transfer_state <= flushMessage;
						end if;
					end if;
					
			end case;
		end if;
	end process message_transfer_proc;


----------------------------------------------------------------------------------------------------------


	reply_fifo_0 : reply_fifo
	port map (
		clk			=> clk,
		din			=> reply_fifo_din,
		wr_en			=> reply_fifo_wr_en,
		rd_en			=> reply_fifo_rd_en,
		dout			=> reply_fifo_dout,
		full			=> open,
		empty			=> reply_fifo_empty,
		valid			=> reply_fifo_valid
	);
	

	reply_bytes_counter_proc : process (clk)
	begin
		if rising_edge(clk) then
			if (reset_reply_num_bytes = '1') then
				reply_num_bytes_count <= (others => '0');
			elsif (inc_reply_num_bytes = '1') then
				reply_num_bytes_count <= reply_num_bytes_count + 1;
			end if;
		end if;
	end process reply_bytes_counter_proc;


	reply_send_proc : process (clk)
	begin
		if rising_edge(clk) then
			message_fifo_out_rd_en <= '0';
			reply_fifo_wr_en <= '0';
			reply_available <= '0';
			reset_reply_num_bytes <= '0';
			inc_reply_num_bytes <= '0';
			
			case reply_send_state is
				when idle =>
					if (send_reply = '1') then
						reply_busy <= '1';
						reset_reply_num_bytes <= '1';
						reply_send_state <= waitDataReady;
						message_fifo_out_rd_en <= '1';
					else
						reply_send_state <= idle;
						reply_busy <= '0';
					end if;
				
				when waitDataReady =>
					reply_send_state <= sendByte_7;
				
				when sendByte_7 =>
					inc_reply_num_bytes <= '1';
					reply_fifo_wr_en <= '1';
					reply_fifo_din <= '0' & message_fifo_out_dout(63 downto 56);
					reply_send_state <= sendByte_6;
				
				when sendByte_6 =>
					inc_reply_num_bytes <= '1';
					reply_fifo_wr_en <= '1';
					reply_fifo_din <= '0' & message_fifo_out_dout(55 downto 48);
					reply_send_state <= sendByte_5;
				
				when sendByte_5 =>
					inc_reply_num_bytes <= '1';
					reply_fifo_wr_en <= '1';
					reply_fifo_din <= '0' & message_fifo_out_dout(47 downto 40);
					reply_send_state <= sendByte_4;
				
				when sendByte_4 =>
					inc_reply_num_bytes <= '1';
					reply_fifo_wr_en <= '1';
					reply_fifo_din <= '0' & message_fifo_out_dout(39 downto 32);
					reply_send_state <= sendByte_3;
				
				when sendByte_3 =>
					inc_reply_num_bytes <= '1';
					reply_fifo_wr_en <= '1';
					reply_fifo_din <= '0' & message_fifo_out_dout(31 downto 24);
					reply_send_state <= sendByte_2;
				
				when sendByte_2 =>
					inc_reply_num_bytes <= '1';
					reply_fifo_wr_en <= '1';
					reply_fifo_din <= '0' & message_fifo_out_dout(23 downto 16);
					reply_send_state <= sendByte_1;
				
				when sendByte_1 =>
					inc_reply_num_bytes <= '1';
					reply_fifo_wr_en <= '1';
					reply_fifo_din <= '0' & message_fifo_out_dout(15 downto 8);
					reply_send_state <= sendByte_0;
				
				when sendByte_0 =>
					inc_reply_num_bytes <= '1';
					reply_fifo_wr_en <= '1';
					if (message_fifo_out_empty = '1') then
						reply_fifo_din <= '1' & message_fifo_out_dout(7 downto 0);
						reply_send_state <= waitReplyReady;
					else
						message_fifo_out_rd_en <= '1';
						reply_fifo_din <= '0' & message_fifo_out_dout(7 downto 0);
						reply_send_state <= waitDataReady;
					end if;
				
				when waitReplyReady =>
					if (udp_reply_packet_busy = '1') then
						reply_send_state <= waitReplyReady;
					else
						reply_send_state <= sendReply;
					end if;
				
				when sendReply =>
					reply_available <= '1';
					reply_num_bytes <= reply_num_bytes_count;
					if (udp_reply_packet_busy = '0') then
						reply_send_state <= idle;
					else
						reply_send_state <= sendReply;
					end if;
			end case;
		end if;
	end process reply_send_proc;


	udp_reply_port <= udp_port_a & udp_port_b;
	udp_reply_mac_addr_5 <= mac_addr_5_a & mac_addr_5_b;
	udp_reply_ip_addr_3 <= ip_addr_3_a & ip_addr_3_b;

	udp_reply_packet: udp_packet
	port map (
		clk								=> clk,
		tx_clk							=> tx_clk,
		udp_rx_pkt_id					=> udp_rx_pkt_id,
		udp_rx_frag_offset			=> udp_rx_frag_offset,
		udp_reply_mac_addr_0			=> mac_addr_0,
		udp_reply_mac_addr_1			=> mac_addr_1,
		udp_reply_mac_addr_2			=> mac_addr_2,
		udp_reply_mac_addr_3			=> mac_addr_3,
		udp_reply_mac_addr_4			=> mac_addr_4,
		udp_reply_mac_addr_5			=> udp_reply_mac_addr_5,
		udp_rx_hdw_src_addr_0		=> udp_rx_hdw_src_addr_0,
		udp_rx_hdw_src_addr_1		=> udp_rx_hdw_src_addr_1,
		udp_rx_hdw_src_addr_2		=> udp_rx_hdw_src_addr_2,
		udp_rx_hdw_src_addr_3		=> udp_rx_hdw_src_addr_3,
		udp_rx_hdw_src_addr_4		=> udp_rx_hdw_src_addr_4,
		udp_rx_hdw_src_addr_5		=> udp_rx_hdw_src_addr_5,
		udp_reply_ip_addr_0			=> ip_addr_0,
		udp_reply_ip_addr_1			=> ip_addr_1,
		udp_reply_ip_addr_2			=> ip_addr_2,
		udp_reply_ip_addr_3			=> udp_reply_ip_addr_3,
		udp_rx_src_ip_addr_0			=> udp_rx_src_ip_addr_0,
		udp_rx_src_ip_addr_1			=> udp_rx_src_ip_addr_1,
		udp_rx_src_ip_addr_2			=> udp_rx_src_ip_addr_2,
		udp_rx_src_ip_addr_3			=> udp_rx_src_ip_addr_3,
		udp_destination_port			=> udp_reply_port,
		packet_data_available		=> reply_available,
		udp_packet_busy				=> udp_reply_packet_busy,
		packet_data_num_bytes		=> reply_num_bytes,
		packet_data_fifo_rd_en		=> reply_fifo_rd_en,
		packet_data_fifo_dout		=> reply_fifo_dout,
		udp_packet_fifo_rd_en		=> reply_packet_fifo_rd_en,
		udp_packet_fifo_dout			=> reply_packet_fifo_dout,
		udp_packet_fifo_empty		=> reply_packet_fifo_empty
	);


	pkt_tx_crc: pkt_crc_32
	port map (
		CLOCK				=> tx_clk,
		RESET				=> '0',
		DATA				=>	tx_crc_data,
		LOAD_INIT		=> tx_crc_clear,
		CALC 				=> tx_crc_calc,
		D_VALID			=> tx_crc_read,
		CRC				=> tx_crc_byte,
		CRC_REG			=> open,
		CRC_VALID		=> tx_crc_valid
	);


	pkt_tx_crc_in_select : process (tx_send_state, tx_msg, arp_reply_fifo_dout, icmp_reply_fifo_dout, reply_packet_fifo_dout)
	begin
		if ((tx_send_state = sendPadding) or (tx_send_state = sendPadding_b)) then
			tx_crc_data <= (others => '0');
		else
			case tx_msg is
				when arp =>
					tx_crc_data <= arp_reply_fifo_dout;
				
				when icmp =>
					tx_crc_data <= icmp_reply_fifo_dout;
				
				when udp =>
					tx_crc_data <= reply_packet_fifo_dout(7 downto 0);
--					tx_crc_data <= udp_reply_data;
				
				when others =>
					tx_crc_data <= (others => '0');
			end case;
		end if;
	end process pkt_tx_crc_in_select;


	tx_send_pkt_counter_proc : process (tx_clk)
	begin
		if rising_edge(tx_clk) then
			if (tx_send_reset_pkt_count = '1') then
				tx_pkt_counter <= (others => '0');
			elsif (tx_send_inc_pkt_count = '1') then
				tx_pkt_counter <= tx_pkt_counter + 1;
			end if;
		end if;
	end process tx_send_pkt_counter_proc;
	

	tx_data_out <= tx_data_out_i;
	tx_data_enable <= tx_data_enable_i;
	
	tx_send_proc : process (tx_clk)
	begin
		if rising_edge(tx_clk) then
			arp_reply_fifo_rd_en <= '0';
			icmp_reply_fifo_rd_en <= '0';
			reply_packet_fifo_rd_en <= '0';
			tx_data_enable_i <= '0';
			tx_crc_clear <= '0';
			tx_crc_calc <= '0';
			tx_crc_read <= '0';
			tx_send_reset_pkt_count <= '0';
			tx_send_inc_pkt_count <= '0';
--			pkt_tx_start_udp_fifo_read <= '0';
		
			case tx_send_state is
				when idle =>
					if (arp_reply_fifo_empty = '0') then
						pkt_tx_arp_busy <= '1';
						tx_msg <= arp;
						tx_send_state <= sendPreamble_0;
						tx_send_reset_pkt_count <= '1';
						tx_crc_clear <= '1';
					elsif (reply_packet_fifo_empty = '0') then
						pkt_tx_udp_busy <= '1';
						tx_msg <= udp;
						tx_send_state <= sendPreamble_0;
						tx_send_reset_pkt_count <= '1';
						tx_crc_clear <= '1';
					elsif (icmp_reply_fifo_empty = '0') then
						tx_msg <= icmp;
						tx_send_state <= sendPreamble_0;
						tx_send_reset_pkt_count <= '1';
						tx_crc_clear <= '1';
					else
						pkt_tx_arp_busy <= '0';
						pkt_tx_udp_busy <= '0';
						tx_send_state <= idle;
					end if;
					
					
				when sendPreamble_0 =>
					tx_data_enable_i <= '1';
					tx_send_inc_pkt_count <= '1';
					tx_data_out_i <= x"5";
					tx_send_state <= sendPreamble_0_b;
					
				when sendPreamble_0_b =>
					tx_data_enable_i <= '1';
					tx_data_out_i <= x"5";
					tx_send_state <= sendPreamble_1;
					
				when sendPreamble_1 =>
					tx_data_enable_i <= '1';
					tx_send_inc_pkt_count <= '1';
					tx_data_out_i <= x"5";
					tx_send_state <= sendPreamble_1_b;
					
				when sendPreamble_1_b =>
					tx_data_enable_i <= '1';
					tx_data_out_i <= x"5";
					tx_send_state <= sendPreamble_2;
					
				when sendPreamble_2 =>
					tx_data_enable_i <= '1';
					tx_send_inc_pkt_count <= '1';
					tx_data_out_i <= x"5";
					tx_send_state <= sendPreamble_2_b;
					
				when sendPreamble_2_b =>
					tx_data_enable_i <= '1';
					tx_data_out_i <= x"5";
					tx_send_state <= sendPreamble_3;
					
				when sendPreamble_3 =>
					tx_data_enable_i <= '1';
					tx_send_inc_pkt_count <= '1';
					tx_data_out_i <= x"5";
					tx_send_state <= sendPreamble_3_b;
					
				when sendPreamble_3_b =>
					tx_data_enable_i <= '1';
					tx_data_out_i <= x"5";
					tx_send_state <= sendPreamble_4;
					
				when sendPreamble_4 =>
					tx_data_enable_i <= '1';
					tx_send_inc_pkt_count <= '1';
					tx_data_out_i <= x"5";
					tx_send_state <= sendPreamble_4_b;
					
				when sendPreamble_4_b =>
					tx_data_enable_i <= '1';
					tx_data_out_i <= x"5";
					tx_send_state <= sendPreamble_5;
					
				when sendPreamble_5 =>
					tx_data_enable_i <= '1';
					tx_send_inc_pkt_count <= '1';
					tx_data_out_i <= x"5";
					tx_send_state <= sendPreamble_5_b;
					
				when sendPreamble_5_b =>
					tx_data_enable_i <= '1';
					tx_data_out_i <= x"5";
					tx_send_state <= sendPreamble_6;
					
				when sendPreamble_6 =>
					tx_data_enable_i <= '1';
					tx_send_inc_pkt_count <= '1';
					tx_data_out_i <= x"5";
					tx_send_state <= sendPreamble_6_b;
					
				when sendPreamble_6_b =>
					tx_data_enable_i <= '1';
					tx_data_out_i <= x"5";
					tx_send_state <= sendPreamble_7;
					
				when sendPreamble_7 =>
					tx_data_enable_i <= '1';
					tx_send_inc_pkt_count <= '1';
					tx_data_out_i <= x"5";
					case tx_msg is
						when arp =>
							arp_reply_fifo_rd_en <= '1';
						when icmp =>
							icmp_reply_fifo_rd_en <= '1';
						when udp =>
							reply_packet_fifo_rd_en <= '1';
						when others =>
							null;
					end case;
					tx_send_state <= sendPreamble_7_b;
					
				when sendPreamble_7_b =>
					tx_data_enable_i <= '1';
					tx_data_out_i <= x"D";
					tx_fcs_counter <= (others => '0');
					case tx_msg is
						when arp =>
							tx_send_state <= sendARPMessage;
						when icmp =>
							tx_send_state <= sendICMPMessage;
						when udp =>
							tx_send_state <= sendUDPMessage;
						when others =>
							tx_send_state <= idle;
					end case;
					
--					if (tx_pkt_counter = 2) and (tx_msg = udp) then
--						tx_send_state <= sendPreamble;
--						pkt_tx_start_udp_fifo_read <= '1';
--					elsif (tx_pkt_counter = 5) then
--						tx_send_state <= sendPreamble;
--						case tx_msg is
--							when arp =>
--								arp_reply_fifo_rd_en <= '1';
--							when icmp =>
--								icmp_reply_fifo_rd_en <= '1';
--							when others =>
--						end case;
--					elsif (tx_pkt_counter = 6) then
--						tx_data_out_i <= x"D5";
--						tx_crc_calc <= '1';
--						tx_crc_read <= '1';
--						tx_fcs_counter <= (others => '0');
--						case tx_msg is
--							when arp =>
--								arp_reply_fifo_rd_en <= '1';
--								tx_send_state <= sendARPMessage;
--							when icmp =>
--								icmp_reply_fifo_rd_en <= '1';
--								tx_send_state <= sendICMPMessage;
--							when udp =>
--								tx_send_state <= sendUDPMessage;
--							when others =>
--								tx_send_state <= idle;
--						end case;
--					else
--						tx_data_out_i <= x"55";
--						tx_send_state <= sendPreamble;
--					end if;
					
					
				when sendARPMessage =>
					tx_data_enable_i <= '1';
					tx_crc_calc <= '1';
					tx_crc_read <= '1';
					tx_send_inc_pkt_count <= '1';
					tx_data_out_i <= arp_reply_fifo_dout(3 downto 0);
					tx_nibble_hi <= arp_reply_fifo_dout(7 downto 4);
					arp_reply_fifo_rd_en <= not arp_reply_fifo_empty;
					tx_send_state <= sendARPMessage_b;
				
				when sendARPMessage_b =>
					tx_data_enable_i <= '1';
					tx_data_out_i <= tx_nibble_hi;
					
					if (arp_reply_fifo_empty = '1') then
						if (tx_pkt_counter < 67) then
							tx_send_state <= sendPadding;
						else
							tx_send_state <= sendFCS;
						end if;
					else
						tx_send_state <= sendARPMessage;
					end if;
					
					
				when sendICMPMessage =>
					tx_data_enable_i <= '1';
					tx_crc_calc <= '1';
					tx_crc_read <= '1';
					tx_send_inc_pkt_count <= '1';
					icmp_reply_fifo_rd_en <= not icmp_reply_fifo_empty;
					tx_data_out_i <= icmp_reply_fifo_dout(3 downto 0);
					tx_nibble_hi <= icmp_reply_fifo_dout(7 downto 4);
					tx_send_state <= sendICMPMessage_b;
				
				when sendICMPMessage_b =>
					tx_data_enable_i <= '1';
					tx_data_out_i <= tx_nibble_hi;
					
					if (icmp_reply_fifo_empty = '1') then
						if (tx_pkt_counter < 67) then
							tx_send_state <= sendPadding;
						else
							tx_send_state <= sendFCS;
						end if;
					else
						tx_send_state <= sendICMPMessage;
					end if;
					
				
				when sendUDPMessage =>
					tx_data_enable_i <= '1';
					tx_crc_read <= '1';
					tx_crc_calc <= '1';
					tx_send_inc_pkt_count <= '1';
					reply_packet_fifo_rd_en <= not reply_packet_fifo_empty;
					tx_data_out_i <= reply_packet_fifo_dout(3 downto 0);
					tx_nibble_hi <= reply_packet_fifo_dout(7 downto 4);
					tx_udp_pkt_end <= reply_packet_fifo_dout(8);
					tx_send_state <= sendUDPMessage_b;
				
				when sendUDPMessage_b =>
					tx_data_enable_i <= '1';
					tx_data_out_i <= tx_nibble_hi;
					
					if (tx_udp_pkt_end = '1') then
						if (tx_pkt_counter < 67) then
							tx_send_state <= sendPadding;
						else
							tx_send_state <= sendFCS;
						end if;
					else
						tx_send_state <= sendUDPMessage;
					end if;
					
				
				when sendPadding =>
					tx_data_enable_i <= '1';
					tx_send_inc_pkt_count <= '1';
					tx_data_out_i <= x"0";
					tx_crc_calc <= '1';
					tx_crc_read <= '1';
					tx_send_state <= sendPadding_b;
				
				when sendPadding_b =>
					tx_data_enable_i <= '1';
					tx_data_out_i <= x"0";
					if (tx_pkt_counter = 67) then
						tx_send_state <= sendFCS;
					else
						tx_send_state <= sendPadding;
					end if;
				
				when sendFCS =>
					tx_data_enable_i <= '1';
					tx_crc_read <= '1';
					tx_data_out_i <= tx_crc_byte(3 downto 0);
					tx_nibble_hi <= tx_crc_byte(7 downto 4);
					tx_send_inc_pkt_count <= '1';
					tx_send_state <= sendFCS_b;
				
				when sendFCS_b =>
					tx_data_enable_i <= '1';
					tx_data_out_i <= tx_nibble_hi;
					if (tx_fcs_counter = "11") then
						tx_send_reset_pkt_count <= '1';
						tx_send_state <= waitPacketGap;			-- don't let long packets merge
					else
						tx_fcs_counter <= tx_fcs_counter + 1;
						tx_send_state <= sendFCS;
					end if;
				
				when waitPacketGap =>
					if (tx_pkt_counter = 15) then
						tx_send_state <= idle;
					else
						tx_send_inc_pkt_count <= '1';
						tx_send_state <= waitPacketGap;
					end if;
			end case;
		end if;
	end process tx_send_proc;

end Behavioral;

