-- -------------------------------------------------------------------------- -- -- 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 = MAC_ADDR_BROADCAST) 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 sel <= SEL_ETH_I; elsif rising_edge(clk) then case state is when IDLE => if not 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 (not mac_in.tx_mac_data_en or not mac_out.tx_active); end case; end if; end process arb; mux : process(all) is begin -- Defaults to avoid latch tx_data_ack_i <= '0'; tx_data_ack_ii <= (others => '0'); -- Actual MUX 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;