----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    15:17:26 02/25/2015 
-- Design Name: 
-- Module Name:    pp_ip_checksum - 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 ip_checksum is
	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 ip_checksum;



architecture Behavioral of ip_checksum is

	signal ip_reply_checksum_i					: std_logic_vector(15 downto 0) := (others => '0');
	signal ip_reply_checksum_a					: unsigned(15 downto 0) := (others => '0');
	signal ip_reply_checksum_b					: unsigned(15 downto 0) := (others => '0');
	signal ip_reply_checksum_set				: std_logic := '0';
	signal ip_reply_checksum_add				: std_logic := '0';
	signal ip_reply_checksum_add_2			: std_logic := '0';
		
	signal ip_reply_checksum_long				: unsigned(31 downto 0) := (others => '0');
		
	signal ip_reply_checksum_long_2			: unsigned(16 downto 0) := (others => '0');
	
	type   ip_reply_calc_checksum_type is (idle, sumHdr_1, sumHdr_2, sumHdr_3, sumHdr_4, sumHdr_5,
																sumHdr_6, sumHdr_7, sumHdr_8, wait_8, sumWords, waitSum, checkOverflow);
	signal ip_reply_calc_checksum_state		: ip_reply_calc_checksum_type := idle;
	signal ip_reply_checksum_valid_i			: std_logic := '0';

begin

ip_reply_checksum <= ip_reply_checksum_i;

ip_reply_checksum_arith_1 : process (clk)
	begin
		if rising_edge(clk) then
			if (ip_reply_checksum_set = '1') then
				ip_reply_checksum_long <= unsigned(x"0000" & ip_reply_checksum_a);
			elsif (ip_reply_checksum_add = '1') then
				ip_reply_checksum_long <= ip_reply_checksum_long + unsigned(x"0000" & ip_reply_checksum_a);
			end if;
		end if;
	end process ip_reply_checksum_arith_1;


ip_reply_checksum_arith_2 : process (clk)
	begin
		if rising_edge(clk) then
			if (ip_reply_checksum_add_2 = '1') then
				ip_reply_checksum_long_2 <= unsigned("0" & ip_reply_checksum_a) + unsigned("0" & ip_reply_checksum_b);
			end if;
		end if;
	end process ip_reply_checksum_arith_2;


ip_reply_checksum_valid <= ip_reply_checksum_valid_i;

ip_reply_checksum_calculator : process (clk)
	begin
		if rising_edge(clk) then
			ip_reply_checksum_set <= '0';
			ip_reply_checksum_add <= '0';
			ip_reply_checksum_add_2 <= '0';
		
			case ip_reply_calc_checksum_state is
				when idle =>
					if (calc_ip_reply_checksum = '1') then
						ip_reply_checksum_valid_i <= '0';
						ip_reply_checksum_a <= unsigned(ip_reply_hdr_version & ip_reply_hdr_length & ip_reply_hdr_diff_services);
						ip_reply_checksum_set <= '1';
						ip_reply_checksum_i <= (others => '0');
						ip_reply_calc_checksum_state <= sumHdr_1;
					else
						ip_reply_calc_checksum_state <= idle;
					end if;
					
				when sumHdr_1 =>
					ip_reply_checksum_a <= unsigned("00000" & ip_reply_hdr_total_length);
					ip_reply_checksum_add <= '1';
					ip_reply_calc_checksum_state <= sumHdr_2;
					
				when sumHdr_2 =>
					ip_reply_checksum_a <= unsigned(ip_reply_hdr_pktid);
					ip_reply_checksum_add <= '1';
					ip_reply_calc_checksum_state <= sumHdr_3;
					
				when sumHdr_3 =>
					ip_reply_checksum_a <= unsigned(ip_reply_hdr_flags & ip_reply_hdr_frag_offset);
					ip_reply_checksum_add <= '1';
					ip_reply_calc_checksum_state <= sumHdr_4;
					
				when sumHdr_4 =>
					ip_reply_checksum_a <= unsigned(ip_reply_hdr_TTL & ip_reply_hdr_protocol);
					ip_reply_checksum_add <= '1';
					ip_reply_calc_checksum_state <= sumHdr_5;
					
				when sumHdr_5 =>
					ip_reply_checksum_a <= unsigned(ip_reply_addr_0 & ip_reply_addr_1);
					ip_reply_checksum_add <= '1';
					ip_reply_calc_checksum_state <= sumHdr_6;
					
				when sumHdr_6 =>
					ip_reply_checksum_a <= unsigned(ip_reply_addr_2 & ip_reply_addr_3);
					ip_reply_checksum_add <= '1';
					ip_reply_calc_checksum_state <= sumHdr_7;
					
				when sumHdr_7 =>
					ip_reply_checksum_a <= unsigned(ip_reply_hdr_dst_addr_0 & ip_reply_hdr_dst_addr_1);
					ip_reply_checksum_add <= '1';
					ip_reply_calc_checksum_state <= sumHdr_8;
					
				when sumHdr_8 =>
					ip_reply_checksum_a <= unsigned(ip_reply_hdr_dst_addr_2 & ip_reply_hdr_dst_addr_3);
					ip_reply_checksum_add <= '1';
					ip_reply_calc_checksum_state <= wait_8;
				
				when wait_8 =>
					ip_reply_calc_checksum_state <= sumWords;
				
				when sumWords =>
					ip_reply_checksum_a <= unsigned(ip_reply_checksum_long(31 downto 16));
					ip_reply_checksum_b <= ip_reply_checksum_long(15 downto 0);
					ip_reply_checksum_add_2 <= '1';
					ip_reply_calc_checksum_state <= waitSum;
				
				when waitSum =>
					ip_reply_calc_checksum_state <= checkOverflow;
				
				when checkOverflow =>
					if (ip_reply_checksum_long_2(16) = '0') then
						ip_reply_checksum_i <= not std_logic_vector(ip_reply_checksum_long_2(15 downto 0));
						ip_reply_checksum_valid_i <= '1';
						ip_reply_calc_checksum_state <= idle;
					else
						ip_reply_checksum_a <= "00000000" & "0000000" & ip_reply_checksum_long_2(16);
						ip_reply_checksum_b <= ip_reply_checksum_long_2(15 downto 0);
						ip_reply_checksum_add_2 <= '1';
						ip_reply_calc_checksum_state <= waitSum;
					end if;
			end case;
		end if;
	end process ip_reply_checksum_calculator;

end Behavioral;

