-- -------------------------------------------------------------------------- -- -- TRASHERNET - A Trashy Ethernet Stack for FPGAs -- -- -------------------------------------------------------------------------- -- -- trashernet_arp.vhd : Address Resolution Protocol -- Implements simple single-entry cache ARP. -- -------------------------------------------------------------------------- -- -- 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_arp is port( -- Global clk : in std_logic; -- Global clock rst : in std_logic; -- Asynchronous reset -- Configuration mac_config : in configuration_t; -- Trashernet MAC configuration ip_config : in ip_configuration_t; -- Trashernet IP configuration -- ARP application interface arp_out : out arp_out_t; -- ARP IF (out from ARP) arp_in : in arp_in_t; -- ARP IF (into ARP) -- Ethernet II application interface ethernet_ii_out : in ethernet_ii_out_t; -- Ethernet II IF (out from MAC) ethernet_ii_in : out ethernet_ii_in_t -- Ethernet II IF (into MAC) ); end entity trashernet_arp; architecture rtl of trashernet_arp is constant HTYPE : byte_vector := (x"00", x"01"); constant PTYPE : byte_vector := (x"08", x"00"); constant HLEN : byte := x"06"; constant PLEN : byte := x"04"; constant OPER_REQUEST : byte_vector := (x"00", x"01"); constant OPER_REPLY : byte_vector := (x"00", x"02"); signal arp_reply_rq : std_logic; -- Request to transmit an ARP reply signal arp_reply_tha : mac_addr_t; -- Target MAC to use in reply (SHA in received packet) signal arp_reply_tpa : ip_addr_t; -- Target IP to use in reply (SPA in received packet) signal arp_reply_ack : std_logic; -- ARP reply has been latched and is being sent begin rx : block constant BYTECOUNT_HEAD : integer := 8; -- HTYPE -> OPER constant BYTECOUNT_ADDRESSES : integer := 20; -- SHA -> TPA signal sr : byte_vector(0 to BYTECOUNT_ADDRESSES - 1); constant SR_HEAD_OFFSET : integer := BYTECOUNT_ADDRESSES - BYTECOUNT_HEAD; alias sr_head_htype is sr(SR_HEAD_OFFSET + 0 to SR_HEAD_OFFSET + 1); alias sr_head_ptype is sr(SR_HEAD_OFFSET + 2 to SR_HEAD_OFFSET + 3); alias sr_head_hlen is sr(SR_HEAD_OFFSET + 4); alias sr_head_plen is sr(SR_HEAD_OFFSET + 5); alias sr_head_oper is sr(SR_HEAD_OFFSET + 6 to SR_HEAD_OFFSET + 7); constant SR_ADDRESSES_OFFSET : integer := -8; alias sr_addresses_sha is sr(8 + SR_ADDRESSES_OFFSET to 13 + SR_ADDRESSES_OFFSET); alias sr_addresses_spa is sr(14 + SR_ADDRESSES_OFFSET to 17 + SR_ADDRESSES_OFFSET); alias sr_addresses_tha is sr(18 + SR_ADDRESSES_OFFSET to 23 + SR_ADDRESSES_OFFSET); alias sr_addresses_tpa is sr(24 + SR_ADDRESSES_OFFSET to 27 + SR_ADDRESSES_OFFSET); type state_t is (HEAD, ADDRESSES, WAITCRC, SENDREPLY, IGNORE); signal state : state_t; signal bytecount : integer range 0 to BYTECOUNT_ADDRESSES; signal shifted : std_logic; signal block_done : std_logic; signal header_ok : std_logic; signal we_are_asked : std_logic; signal is_request : std_logic; begin block_done <= '1' when shifted = '1' and bytecount = 0 else '0'; header_ok <= '1' when -- sr_head_htype = HTYPE and -- sr_head_ptype = PTYPE and -- sr_head_hlen = HLEN and sr_head_plen = PLEN -- else '0'; we_are_asked <= '1' when -- sr_addresses_tpa = ip_config.ip_address else '0'; rx_fsm : process(clk, rst) is begin if rst then state <= HEAD; shifted <= '0'; bytecount <= BYTECOUNT_HEAD; elsif rising_edge(clk) then if (ethernet_ii_out.rx_crc_ok or ethernet_ii_out.rx_crc_error) then state <= HEAD; -- Safe return from any state, may be overridden below bytecount <= BYTECOUNT_HEAD; end if; shifted <= ethernet_ii_out.rx_data_valid; if (ethernet_ii_out.rx_data_valid = '1') and (bytecount > 0) then sr <= sr(sr'low + 1 to sr'high) & ethernet_ii_out.rx_data; bytecount <= bytecount - 1; end if; case state is when HEAD => if block_done then if header_ok then bytecount <= BYTECOUNT_ADDRESSES; state <= ADDRESSES; is_request <= '1' when (sr_head_oper = OPER_REQUEST) else '0'; else state <= IGNORE; end if; end if; when ADDRESSES => if block_done then if (we_are_asked) then state <= WAITCRC; else state <= IGNORE; end if; end if; when WAITCRC => if ethernet_ii_out.rx_crc_ok then if is_request then state <= SENDREPLY; else report "reply"; null; -- TODO: process response end if; end if; when SENDREPLY => if arp_reply_ack then bytecount <= BYTECOUNT_HEAD; state <= HEAD; end if; when IGNORE => null; end case; end if; end process rx_fsm; arp_reply_rq <= '1' when state = SENDREPLY else '0'; arp_reply_tha <= sr_addresses_sha; arp_reply_tpa <= sr_addresses_spa; end block rx; tx : block signal sr : byte_vector(0 to 27); signal arp_reply_preload : byte_vector(sr'range); constant BYTECOUNT_MAX : integer := sr'length + 1; signal bytecount : integer range 0 to BYTECOUNT_MAX; type state_t is (IDLE, TRANSMIT); signal state : state_t; begin arp_reply_preload <= HTYPE & PTYPE & HLEN & PLEN & OPER_REPLY & -- Header mac_config.mac_address & ip_config.ip_address & -- Sender arp_reply_tha & arp_reply_tpa; -- Target tx_fsm : process(clk, rst) is begin if rst then arp_reply_ack <= '0'; elsif rising_edge(clk) then arp_reply_ack <= '0'; if (ethernet_ii_out.tx_data_ack = '1') and (bytecount > 0) then sr <= sr(sr'low + 1 to sr'high) & x"00"; bytecount <= bytecount - 1; end if; case state is when IDLE => if arp_reply_rq then state <= TRANSMIT; arp_reply_ack <= '1'; sr <= arp_reply_preload; end if; bytecount <= BYTECOUNT_MAX; when TRANSMIT => if bytecount = 0 then state <= IDLE; end if; end case; end if; end process tx_fsm; ethernet_ii_in.tx_mac_address <= arp_reply_tha; ethernet_ii_in.tx_en <= '1' when state = TRANSMIT else '0'; ethernet_ii_in.tx_data <= sr(0); end block tx; end architecture rtl;