177 lines
6.9 KiB
VHDL
177 lines
6.9 KiB
VHDL
-- -------------------------------------------------------------------------- --
|
|
-- 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 <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_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;
|