diff --git a/trashernet/trashernet_eth.vhd b/trashernet/trashernet_eth.vhd new file mode 100644 index 0000000..09a3c2f --- /dev/null +++ b/trashernet/trashernet_eth.vhd @@ -0,0 +1,170 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET - A Trashy Ethernet Stack for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- trashernet_eth.vhd : Ethernet OSI Layer 2, Data Link, ETH I/II Ethertype +-- Implements arbitration of different ethertypes. +-- -------------------------------------------------------------------------- -- +-- 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_eth is + generic( + ETHERNET_II_PROTOCOLS : ethernet_ii_protocol_vector := ETHERNET_II_PROTOCOLS_NONE + ); + port( + -- Global + clk : in std_logic; -- Global clock + rst : in std_logic; -- Asynchronous reset + + -- MAC application interface + mac_out : in mac_out_t; -- MAC application IF (out of MAC) + mac_in : out mac_in_t; -- MAC application IF (into MAC) + + -- Configuration + config : in configuration_t; -- Global Trashernet configuration + + -- Ethernet 802.3 application interface + ethernet_i_out : out ethernet_i_out_t; -- Ethernet 802.3 IF (out from MAC) + ethernet_i_in : in ethernet_i_in_t := ETHERNET_I_IN_UNUSED; -- Ethernet 802.3 IF (out from MAC) + + -- Ethernet II application interface + ethernet_ii_out : out ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range); -- Ethernet II IF (out from MAC) + ethernet_ii_in : in ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range) := (others => ETHERNET_II_IN_UNUSED) -- Ethernet II IF (into MAC) + ); +end entity trashernet_eth; + +architecture rtl of trashernet_eth is + signal tx_data_ack_ii : std_logic_vector(ethernet_ii_out'range); + signal tx_data_ack_i : std_logic; +begin + rx : block + constant SEL_ETH_I : integer := ethernet_ii_out'low - 1; + constant SEL_ETH_NONE : integer := ethernet_ii_out'low - 2; + signal sel : integer range SEL_ETH_NONE to ethernet_ii_out'high; + signal mac_destination_matches : std_logic; + signal rx_mac_header_rcv_delayed : std_logic; + + begin + mac_destination_matches <= '1' when -- + ((mac_out.rx_header.mac_destination = (x"FF", x"FF", x"FF", x"FF", x"FF", x"FF")) or -- + (mac_out.rx_header.mac_destination = config.mac_address)) else + '0'; + + mux : process(clk, rst) is + begin + if rst then + sel <= SEL_ETH_NONE; + rx_mac_header_rcv_delayed <= '0'; + + elsif rising_edge(clk) then + rx_mac_header_rcv_delayed <= mac_out.rx_mac_header_rcv; + + if mac_out.rx_mac_header_rcv then + sel <= SEL_ETH_NONE; -- By default, let's assume it's not for us + if mac_destination_matches then + if (unsigned(std_logic_vector'(mac_out.rx_header.mac_ethertype(0) & mac_out.rx_header.mac_ethertype(1))) < 1500) then -- Ethernet 802.3 Frame + sel <= SEL_ETH_I; + + else -- Ethernet II Frame + for i in ETHERNET_II_PROTOCOLS'range loop + if (mac_out.rx_header.mac_ethertype = ETHERNET_II_PROTOCOLS(i).ethertype) then + sel <= i; + end if; + end loop; + end if; + end if; + end if; + end if; + end process mux; + + -- Shared fields in the Ethernet II application interface + shared_fields : for i in ethernet_ii_out'range generate + ethernet_ii_out(i).rx_mac_address <= mac_out.rx_header.mac_source; + ethernet_ii_out(i).rx_data <= mac_out.rx_mac_data; + ethernet_ii_out(i).rx_data_valid <= mac_out.rx_mac_valid when sel = i else '0'; + ethernet_ii_out(i).rx_crc_error <= mac_out.rx_mac_crc_error when sel = i else '0'; + ethernet_ii_out(i).rx_crc_ok <= mac_out.rx_mac_crc_ok when sel = i else '0'; + ethernet_ii_out(i).rx_header_rcv <= rx_mac_header_rcv_delayed when sel = i else '0'; + end generate shared_fields; + + -- Shared fields in the Ethernet application interface + ethernet_i_out.rx_data <= mac_out.rx_mac_data; + ethernet_i_out.rx_data_valid <= mac_out.rx_mac_valid when sel = SEL_ETH_I; + ethernet_i_out.rx_crc_error <= mac_out.rx_mac_crc_error when sel = SEL_ETH_I else '0'; + ethernet_i_out.rx_crc_ok <= mac_out.rx_mac_crc_ok when sel = SEL_ETH_I else '0'; + ethernet_i_out.rx_header_rcv <= rx_mac_header_rcv_delayed when sel = SEL_ETH_I else '0'; + ethernet_i_out.rx_mac_address <= mac_out.rx_header.mac_source; + ethernet_i_out.rx_length <= unsigned(mac_out.rx_header.mac_ethertype(0)) & unsigned(mac_out.rx_header.mac_ethertype(1)); + + -- TX fields (need to be driven here to avoid multiple drivers (blocks drive ALL signals of a record)) + tx_fields : for i in ethernet_ii_out'range generate + ethernet_ii_out(i).tx_data_ack <= tx_data_ack_ii(i); + end generate; + ethernet_i_out.tx_data_ack <= tx_data_ack_i; + end block rx; + + tx : block + type state_t is (IDLE, TXD); + signal state : state_t; + constant SEL_ETH_I : integer := ethernet_ii_out'low - 1; + signal sel : integer range SEL_ETH_I to ethernet_ii_in'high; + begin + arb : process(clk, rst) is + begin + if rst then + + elsif rising_edge(clk) then + case state is + when IDLE => + if mac_out.tx_active then + if (ethernet_i_in.tx_en) then -- ETH I has priority + sel <= SEL_ETH_I; + state <= TXD; + else + for i in ethernet_ii_in'range loop + if ethernet_ii_in(i).tx_en then + sel <= i; + state <= TXD; + exit; -- Prioritize according to vector + end if; + end loop; + end if; + end if; + + when TXD => + state <= IDLE when (mac_in.tx_mac_data_en or mac_out.tx_active); + end case; + end if; + end process arb; + + mux : process(all) is + begin + if (sel = SEL_ETH_I) then + mac_in.tx_mac_data_en <= ethernet_i_in.tx_en; + mac_in.tx_mac_data <= ethernet_i_in.tx_data; + tx_data_ack_i <= mac_out.tx_mac_data_ack; + mac_in.tx_header.mac_destination <= ethernet_i_in.tx_mac_address; + mac_in.tx_header.mac_ethertype <= byte(ethernet_i_in.tx_length(15 downto 8)) & byte(ethernet_i_in.tx_length(7 downto 0)); + else + mac_in.tx_mac_data_en <= ethernet_ii_in(sel).tx_en; + mac_in.tx_mac_data <= ethernet_ii_in(sel).tx_data; + txack : for i in ethernet_ii_out'range loop + tx_data_ack_ii(i) <= mac_out.tx_mac_data_ack when sel = i else '0'; + end loop txack; + + mac_in.tx_header.mac_destination <= ethernet_ii_in(sel).tx_mac_address; + mac_in.tx_header.mac_ethertype <= ETHERNET_II_PROTOCOLS(sel).ethertype; + end if; + + mac_in.tx_header.mac_source <= config.mac_address; + end process mux; + end block tx; +end architecture rtl; diff --git a/trashernet/trashernet_pkg.vhd b/trashernet/trashernet_pkg.vhd index 104e8fd..fd3a04b 100644 --- a/trashernet/trashernet_pkg.vhd +++ b/trashernet/trashernet_pkg.vhd @@ -10,6 +10,7 @@ library ieee; use ieee.std_logic_1164.all; +use ieee.numeric_std.all; package trashernet_pkg is -- General types @@ -70,6 +71,51 @@ package trashernet_pkg is rx_mac_crc_error : std_logic; -- End of packet, CRC invalid end record mac_out_t; + -- MAC ETH interface + type ethernet_ii_protocol_t is record + ethertype : ethertype_t; + end record; + type ethernet_ii_protocol_vector is array (natural range <>) of ethernet_ii_protocol_t; + constant ETHERNET_II_PROTOCOLS_NONE : ethernet_ii_protocol_vector(0 to -1) := (others => (ethertype => (x"00", x"00"))); -- NULL range + constant ETHERNET_II_PROTOCOL_IP : ethernet_ii_protocol_t := (ethertype => (x"08", x"00")); + constant ETHERNET_II_PROTOCOL_ARP : ethernet_ii_protocol_t := (ethertype => (x"08", x"06")); + + type ethernet_i_out_t is record + rx_mac_address : mac_addr_t; -- Source MAC address + rx_crc_ok : std_logic; -- End of packet, CRC OK + rx_crc_error : std_logic; -- End of packet, CRC invalid + + rx_data : byte; -- RX data + rx_data_valid : std_logic; -- RX data valid strobe + rx_length : unsigned(15 downto 0); -- RX payload length in bytes + rx_header_rcv : std_logic; -- Start of reception, `rx_length` is valid + tx_data_ack : std_logic; -- Give next data byte or disable `tx_en` + end record; + type ethernet_i_in_t is record + tx_mac_address : mac_addr_t; -- Destination MAC address + tx_data : byte; -- TX data + tx_en : std_logic; -- Start and continue transmitting + tx_length : unsigned(15 downto 0); -- TX payload length in bytes + end record; + constant ETHERNET_I_IN_UNUSED : ethernet_i_in_t := (tx_mac_address => (others => x"00"), tx_data => x"00", tx_length => x"0000", others => '0'); -- TODO + + type ethernet_ii_out_t is record + rx_mac_address : mac_addr_t; -- Source MAC address + rx_data : byte; -- RX data + rx_data_valid : std_logic; -- RX data valid strobe + rx_crc_ok : std_logic; -- End of packet, CRC OK + rx_crc_error : std_logic; -- End of packet, CRC invalid + rx_header_rcv : std_logic; -- Start of reception + tx_data_ack : std_logic; -- Give next data byte or disable `tx_en` + end record; + type ethernet_ii_in_t is record + tx_mac_address : mac_addr_t; -- Destination MAC address + tx_data : byte; -- TX data + tx_en : std_logic; -- Start and continue transmitting + end record; + type ethernet_ii_out_vector is array (natural range <>) of ethernet_ii_out_t; + type ethernet_ii_in_vector is array (natural range <>) of ethernet_ii_in_t; + constant ETHERNET_II_IN_UNUSED : ethernet_ii_in_t := (tx_mac_address => (others => x"00"), tx_data => x"00", others => '0'); end package trashernet_pkg; package body trashernet_pkg is