trashernet/trashernet/trashernet_arp.vhd

211 lines
6.7 KiB
VHDL

-- -------------------------------------------------------------------------- --
-- 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
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;