-- -------------------------------------------------------------------------- -- -- TRASHERNET - A Trashy Ethernet Stack for FPGAs -- -- -------------------------------------------------------------------------- -- -- trashernet_icmp.vhd : Ethernet OSI Layer 3, Network (ICMP) -- Implements ICMP echo replies -- -------------------------------------------------------------------------- -- -- Author : Markus Koch -- Contributors : None -- License : Mozilla Public License (MPL) Version 2 -- -------------------------------------------------------------------------- -- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use work.trashernet_pkg.all; entity trashernet_icmp is port( -- Global clk : in std_logic; -- Global clock rst : in std_logic; -- Asynchronous reset -- IPv4 application interface ipv4_protocol_out : in ipv4_protocol_out_t; -- IPv4 Protocol IF (out from IP Mux) ipv4_protocol_in : out ipv4_protocol_in_t -- IPv4 Protocol IF (into IP Mux) ); end entity trashernet_icmp; architecture rtl of trashernet_icmp is signal fifo_clear : std_logic; signal fifo_data_in : std_logic_vector(byte'range); signal fifo_push : std_logic; signal fifo_full : std_logic; -- TODO: FIFO full error handling signal fifo_data_out : std_logic_vector(fifo_data_in'range); signal fifo_pop : std_logic; signal fifo_empty : std_logic; signal tx_response : std_logic; signal rx_checksum : std_logic_vector(15 downto 0); begin fifo_inst : entity work.fifo generic map( DEPTH => 64 ) port map( clk => clk, rst => rst, clear => fifo_clear, data_in => fifo_data_in, push => fifo_push, full => fifo_full, data_out => fifo_data_out, pop => fifo_pop, empty => fifo_empty ); rx : block signal sr : byte_vector(0 to 3); alias sr_type is sr(0); alias sr_code is sr(1); alias sr_checksum is sr(2 to 3); type state_t is (HEADER, DATA, IGNORE); signal state : state_t; signal byte_count : integer range 0 to sr'length; begin rx_fsm : process(clk, rst) is begin if rst then state <= HEADER; byte_count <= byte_count'subtype'high; elsif rising_edge(clk) then if (ipv4_protocol_out.rx_data_valid = '1') and (byte_count /= 0) then byte_count <= byte_count - 1; if state = HEADER then sr <= sr(sr'low + 1 to sr'high) & ipv4_protocol_out.rx_data; end if; end if; if ipv4_protocol_out.rx_header_rcv then byte_count <= byte_count'subtype'high; state <= HEADER; end if; case state is when HEADER => if byte_count = 0 then if sr_type = x"08" then state <= DATA; else state <= IGNORE; end if; end if; when DATA => null; -- We just wait here and collect data when IGNORE => -- Just wait until it's over null; end case; end if; end process rx_fsm; fifo_clear <= ipv4_protocol_out.rx_header_rcv; -- TODO: This will break things when we get another Ping too soon fifo_push <= ipv4_protocol_out.rx_data_valid when state = DATA else '0'; fifo_data_in <= ipv4_protocol_out.rx_data; tx_response <= ipv4_protocol_out.rx_ok_stb when state = DATA else '0'; rx_checksum <= sr_checksum(2) & sr_checksum(3); end block rx; tx : block signal checksum : unsigned(16 downto 0); signal checksum_ones : unsigned(15 downto 0); type state_t is (IDLE, HEADER, PAYLOAD); signal state : state_t; signal sr : byte_vector(0 to 3); signal byte_count : integer range 0 to sr'subtype'high; begin checksum <= ('0' & not (unsigned(rx_checksum))) - unsigned'('0' & x"0800"); -- The only change is Type 8 changes to Type 0 checksum_ones <= not (checksum(15 downto 0) - checksum(16)); tx_fsm : process(clk, rst) is begin if rst then elsif rising_edge(clk) then if (ipv4_protocol_out.tx_data_ack = '1') and (byte_count /= 0) then sr <= sr(sr'low + 1 to sr'high) & x"00"; byte_count <= byte_count - 1; end if; case state is when IDLE => if tx_response then ipv4_protocol_in.tx_ip_address <= ipv4_protocol_out.rx_ip_address; -- TODO: This is technically too late, but ¯\_(ツ)_/¯ ipv4_protocol_in.tx_length <= ipv4_protocol_out.rx_length; sr <= byte_vector'( x"00", x"00", std_logic_vector(checksum_ones(15 downto 8)), std_logic_vector(checksum_ones(7 downto 0)) ); byte_count <= sr'subtype'high; state <= HEADER; end if; when HEADER => if (byte_count = 0) and (ipv4_protocol_out.tx_data_ack = '1') then state <= PAYLOAD; end if; when PAYLOAD => if fifo_empty then state <= IDLE; end if; end case; end if; end process tx_fsm; fifo_pop <= ipv4_protocol_out.tx_data_ack when state = PAYLOAD else '0'; ipv4_protocol_in.tx_en <= '1' when (state = HEADER) or (state = PAYLOAD) else '0'; ipv4_protocol_in.tx_data <= sr(sr'low) when state = HEADER else fifo_data_out; end block tx; end architecture rtl;