2022-05-15 14:44:37 +02:00
-- -------------------------------------------------------------------------- --
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
-- -------------------------------------------------------------------------- --
-- trashernet_arp.vhd : Address Resolution Protocol
-- Implements simple single-entry cache ARP.
-- -------------------------------------------------------------------------- --
-- Author : Markus Koch <markus@notsyncing.net>
-- 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
2022-10-29 19:18:51 +02:00
generic (
2022-10-30 17:27:43 +01:00
SYSTICK_FREQ : integer
2022-10-29 19:18:51 +02:00
) ;
2022-05-15 14:44:37 +02:00
port (
-- Global
clk : in std_logic ; -- Global clock
rst : in std_logic ; -- Asynchronous reset
2022-10-30 17:27:43 +01:00
systick : in std_logic ; -- Global time reference for slow events (here: timeout)
2022-05-15 14:44:37 +02:00
-- Configuration
mac_config : in configuration_t ; -- Trashernet MAC configuration
2022-10-30 22:32:47 +01:00
ip_config : in ipv4_configuration_t ; -- Trashernet IP configuration
2022-05-15 14:44:37 +02:00
-- 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" ) ;
2022-10-29 19:18:51 +02:00
constant ARP_TIMEOUT : time : = 1 sec ;
2022-10-28 18:52:17 +02:00
signal arp_tx_reply_rq : std_logic ; -- Request to transmit an ARP reply
signal arp_tx_reply_tha : mac_addr_t ; -- Target MAC to use in reply (SHA in received packet)
signal arp_tx_reply_tpa : ip_addr_t ; -- Target IP to use in reply (SPA in received packet)
signal arp_tx_reply_ack : std_logic ; -- ARP reply has been latched and is being sent
signal arp_tx_request_rq : std_logic ; -- Request to transmit an ARP request
signal arp_tx_request_tpa : ip_addr_t ; -- IP address that we want to know the MAC address of
signal arp_tx_request_ack : std_logic ; -- ARP request has been latched and is being sent
signal arp_rx_reply_stb : std_logic ; -- An ARP reply was received (strobe)
signal arp_rx_sha : mac_addr_t ; -- The MAC address of the reply sender, valid with arp_rx_reply
signal arp_rx_spa : ip_addr_t ; -- The IP address of the reply sender, valid with arp_rx_reply
2022-10-29 19:19:16 +02:00
2022-05-15 14:44:37 +02:00
begin
2022-10-28 18:52:17 +02:00
resolver : block
type resolver_state_t is ( IDLE , QUERY_MAC ) ;
signal state : resolver_state_t ;
signal query_mac_found : std_logic ;
signal query_timeout : std_logic : = '0' ; -- TODO: Implement
signal replied_ip : ip_addr_t ;
signal replied_mac : mac_addr_t ;
begin
arp_resolver_main : process ( rst , clk ) is
begin
if rst then
state < = IDLE ;
replied_ip < = ( others = > x"00" ) ; -- 0.0.0.0
arp_out . arp_ok_stb < = '0' ;
arp_out . arp_fail_stb < = '0' ;
2022-10-29 19:19:16 +02:00
arp_tx_request_rq < = '0' ;
2022-10-28 18:52:17 +02:00
elsif rising_edge ( clk ) then
arp_out . arp_ok_stb < = '0' ;
arp_out . arp_fail_stb < = '0' ;
if arp_rx_reply_stb then
if arp_tx_request_tpa = arp_rx_spa then -- If this reply is actually for our request
replied_mac < = arp_rx_sha ;
replied_ip < = arp_rx_spa ;
end if ;
end if ;
case state is
when IDLE = >
if arp_in . arp_query_stb then
if query_mac_found then
arp_out . arp_ok_stb < = '1' ;
else
arp_tx_request_rq < = '1' ;
state < = QUERY_MAC ;
end if ;
end if ;
when QUERY_MAC = >
if arp_tx_request_ack then
arp_tx_request_rq < = '0' ;
end if ;
if query_mac_found or query_timeout then
2022-10-29 19:19:16 +02:00
arp_out . arp_ok_stb < = query_mac_found ;
2022-10-28 18:52:17 +02:00
arp_out . arp_fail_stb < = not query_mac_found ;
2022-10-29 19:19:16 +02:00
state < = IDLE ;
2022-10-28 18:52:17 +02:00
end if ;
end case ;
end if ;
end process arp_resolver_main ;
2022-10-29 19:19:16 +02:00
query_mac_found < = '1' when ( arp_tx_request_tpa = replied_ip ) else '0' ;
2022-10-28 18:52:17 +02:00
arp_out . arp_mac < = replied_mac ;
arp_tx_request_tpa < = arp_in . arp_ip ;
2022-10-29 19:18:51 +02:00
timeout_timer_inst : entity work . timer
generic map (
2022-10-30 17:27:43 +01:00
F_TICK = > SYSTICK_FREQ ,
2022-10-29 19:18:51 +02:00
DURATION = > ARP_TIMEOUT ,
AUTOSTART = > false
)
port map (
clk = > clk ,
rst = > rst ,
2022-10-30 17:27:43 +01:00
tick = > systick ,
2022-10-29 19:18:51 +02:00
start = > arp_tx_request_rq ,
expired = > query_timeout ,
expired_stb = > open
) ;
2022-10-28 18:52:17 +02:00
end block resolver ;
2022-05-15 14:44:37 +02:00
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 ) ;
2022-10-30 17:29:14 +01:00
alias sr_addresses_tha is sr ( 18 + SR_ADDRESSES_OFFSET to 23 + SR_ADDRESSES_OFFSET ) ; -- @suppress "Unused declaration": We don't need to check this as this is already done by the MAC
2022-05-15 14:44:37 +02:00
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
2022-10-28 18:52:17 +02:00
state < = HEAD ;
shifted < = '0' ;
bytecount < = BYTECOUNT_HEAD ;
arp_rx_reply_stb < = '0' ;
2022-05-15 14:44:37 +02:00
elsif rising_edge ( clk ) then
2022-10-28 18:52:17 +02:00
arp_rx_reply_stb < = '0' ;
2022-05-15 14:44:37 +02:00
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
2022-10-28 18:52:17 +02:00
arp_rx_reply_stb < = '1' ;
2022-05-15 14:44:37 +02:00
end if ;
end if ;
when SENDREPLY = >
2022-10-28 18:52:17 +02:00
if arp_tx_reply_ack then
2022-05-15 14:44:37 +02:00
bytecount < = BYTECOUNT_HEAD ;
state < = HEAD ;
end if ;
2022-10-30 17:29:14 +01:00
when IGNORE = > -- @suppress "Dead state 'IGNORE'": Outgoing transition provided outside of case statement (on RX ok/err)
2022-05-15 14:44:37 +02:00
null ;
end case ;
end if ;
end process rx_fsm ;
2022-10-28 18:52:17 +02:00
arp_tx_reply_rq < = '1' when state = SENDREPLY else '0' ;
arp_tx_reply_tha < = sr_addresses_sha ;
arp_tx_reply_tpa < = sr_addresses_spa ;
arp_rx_sha < = sr_addresses_sha ;
arp_rx_spa < = sr_addresses_spa ;
2022-05-15 14:44:37 +02:00
end block rx ;
tx : block
2022-10-28 18:52:17 +02:00
signal sr : byte_vector ( 0 to 27 ) ;
signal arp_reply_preload : byte_vector ( sr 'range ) ;
signal arp_request_preload : byte_vector ( sr 'range ) ;
constant BYTECOUNT_MAX : integer : = sr 'length + 1 ;
signal bytecount : integer range 0 to BYTECOUNT_MAX ;
2022-05-15 14:44:37 +02:00
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
2022-10-28 18:52:17 +02:00
arp_tx_reply_tha & arp_tx_reply_tpa ; -- Target
arp_request_preload < = HTYPE & PTYPE & HLEN & PLEN & OPER_REQUEST & -- Header
mac_config . mac_address & ip_config . ip_address & -- Sender
mac_addr_t ' ( x"00" , x"00" , x"00" , x"00" , x"00" , x"00" ) & arp_tx_request_tpa ; -- Target
2022-05-15 14:44:37 +02:00
tx_fsm : process ( clk , rst ) is
begin
if rst then
2022-10-29 19:19:16 +02:00
arp_tx_reply_ack < = '0' ;
2022-10-28 18:52:17 +02:00
arp_tx_request_ack < = '0' ;
2022-05-15 14:44:37 +02:00
elsif rising_edge ( clk ) then
2022-10-28 18:52:17 +02:00
arp_tx_reply_ack < = '0' ;
arp_tx_request_ack < = '0' ;
2022-05-15 14:44:37 +02:00
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 = >
2022-10-28 18:52:17 +02:00
if arp_tx_reply_rq then
2022-10-29 19:19:16 +02:00
state < = TRANSMIT ;
arp_tx_reply_ack < = '1' ;
sr < = arp_reply_preload ;
2022-10-28 18:52:17 +02:00
ethernet_ii_in . tx_mac_address < = arp_tx_reply_tha ;
elsif arp_tx_request_rq then
2022-10-29 19:19:16 +02:00
state < = TRANSMIT ;
arp_tx_request_ack < = '1' ;
sr < = arp_request_preload ;
2022-10-28 18:52:17 +02:00
ethernet_ii_in . tx_mac_address < = MAC_ADDR_BROADCAST ;
2022-05-15 14:44:37 +02:00
end if ;
bytecount < = BYTECOUNT_MAX ;
when TRANSMIT = >
if bytecount = 0 then
state < = IDLE ;
end if ;
end case ;
end if ;
end process tx_fsm ;
2022-10-29 19:19:16 +02:00
ethernet_ii_in . tx_en < = '1' when state = TRANSMIT else '0' ;
ethernet_ii_in . tx_data < = sr ( 0 ) ;
2022-05-15 14:44:37 +02:00
end block tx ;
end architecture rtl ;