diff --git a/bench/bench_trashernet_mac.vhd b/bench/bench_trashernet_mac.vhd new file mode 100644 index 0000000..6372905 --- /dev/null +++ b/bench/bench_trashernet_mac.vhd @@ -0,0 +1,147 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.bench_pkg.all; + +library trashernet; +use trashernet.trashernet_types.all; + +entity bench_trashernet_mac is +end entity bench_trashernet_mac; + +architecture bench of bench_trashernet_mac is + signal clk : std_logic; + signal phy_clk : std_logic; + signal rst : std_logic; + signal rx_data : std_logic_vector(7 downto 0); + signal rx_data_valid : std_logic; + signal rx_active : std_logic; + signal tx_data : std_logic_vector(7 downto 0); + signal tx_data_en : std_logic; + signal tx_data_ack : std_logic; + signal carrier_detect : std_logic; + signal rx_error : std_logic; + signal rx_p : std_logic; + signal tx_p : std_logic; + + signal rx_mac_destination : mac_addr_t; + signal rx_mac_source : mac_addr_t; + signal rx_mac_ethertype : ethertype_t; + signal rx_mac_data : byte; + signal rx_mac_valid : std_logic; + signal rx_mac_crc_ok : std_logic; + signal rx_mac_crc_error : std_logic; + +begin + trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc + generic map( + F_CLK => 49000000, + F_CLK_PHY => 100000000 + ) + port map( + clk => clk, + phy_clk => phy_clk, + rst => rst, + rx_data => rx_data, + rx_data_valid => rx_data_valid, + rx_active => rx_active, + tx_data => tx_data, + tx_data_en => tx_data_en, + tx_data_ack => tx_data_ack, + carrier_detect => carrier_detect, + rx_error => rx_error, + rx_p => rx_p, + tx_p => tx_p + ); + + trashernet_mac_inst : entity trashernet.trashernet_mac + port map( + clk => clk, + rst => rst, + rx_data => rx_data, + rx_data_valid => rx_data_valid, + rx_active => rx_active, + tx_data => tx_data, + tx_data_en => tx_data_en, + tx_data_ack => tx_data_ack, + carrier_detect => carrier_detect, + rx_error => rx_error, + rx_mac_destination => rx_mac_destination, + rx_mac_source => rx_mac_source, + rx_mac_ethertype => rx_mac_ethertype, + rx_mac_data => rx_mac_data, + rx_mac_valid => rx_mac_valid, + rx_mac_crc_ok => rx_mac_crc_ok, + rx_mac_crc_error => rx_mac_crc_error + ); + + clock_driver : process + constant period : time := 20 ns; + begin + clk <= '0'; + wait for period / 2; + clk <= '1'; + wait for period / 2; + end process clock_driver; + + phy_clock_driver : process + constant period : time := 10 ns; + begin + phy_clk <= '0'; + wait for period / 2; + phy_clk <= '1'; + wait for period / 2; + end process phy_clock_driver; + + test : process is + begin + rx_p <= '0'; + + rst <= '1'; + wait for 20 ns; + rst <= '0'; + wait for 20 ns; + + send_data(rx_p, byte_vector'( + x"55", x"55", x"55", x"D5", + x"11", x"22", x"33", x"44", x"55", x"66", x"00", x"e0", x"4c", x"36", x"00", x"2f", x"08", x"00", x"45", x"00", x"00", x"54", x"fe", x"22", x"40", x"00", x"40", x"01", x"f5", x"28", x"c0", x"a8", x"63", x"0a", x"c0", x"a8", x"63", x"02", x"08", x"00", x"90", x"e6", x"00", x"06", x"00", x"01", x"da", x"bd", x"2c", x"61", x"00", x"00", x"00", x"00", x"9f", x"20", x"02", x"00", x"00", x"00", x"00", x"00", x"10", x"11", x"12", x"13", x"14", x"15", x"16", x"17", x"18", x"19", x"1a", x"1b", x"1c", x"1d", x"1e", x"1f", x"20", x"21", x"22", x"23", x"24", x"25", x"26", x"27", x"28", x"29", x"2a", x"2b", x"2c", x"2d", x"2e", x"2f", x"30", x"31", x"32", x"33", x"34", x"35", x"36", x"37", + x"e9", x"f2", x"6b", x"30" + )); + + send_data(rx_p, byte_vector'( + x"55", x"55", x"55", x"D5", + x"11", x"22", x"33", x"44", x"55", x"66", x"00", x"e0", x"4c", x"36", x"00", x"2f", x"08", x"00", x"45", x"00", x"00", x"54", x"ca", x"78", x"40", x"00", x"40", x"01", x"28", x"d4", x"c0", x"a8", x"63", x"0a", x"c0", x"a8", x"63", x"01", x"08", x"00", x"93", x"f5", x"00", x"03", x"00", x"01", x"3c", x"bc", x"2c", x"61", x"00", x"00", x"00", x"00", x"38", x"16", x"04", x"00", x"00", x"00", x"00", x"00", x"10", x"11", x"12", x"13", x"14", x"15", x"16", x"17", x"18", x"19", x"1a", x"1b", x"1c", x"1d", x"1e", x"1f", x"20", x"21", x"22", x"23", x"24", x"25", x"26", x"27", x"28", x"29", x"2a", x"2b", x"2c", x"2d", x"2e", x"2f", x"30", x"31", x"32", x"33", x"34", x"35", x"36", x"37", + x"b9", x"7f", x"c7", x"91" + )); + + send_data(rx_p, byte_vector'( + x"55", x"55", x"55", x"D5", + x"12", x"34", x"56", x"78", x"90", x"12", + x"AB", x"CD", x"EF", x"AA", x"BB", x"CC", + x"08", x"00", + x"11", x"22", x"33", x"44", x"55", x"66", + x"CC", x"2C", x"CC", x"2C" + )); + + wait; + end process test; + + receiver : process is + begin + wait until rising_edge(clk); + if rx_data_valid then + report "[PHY] RX byte: " & to_hstring(rx_data); + end if; + if rx_mac_valid then + report "[MAC] RX byte: " & to_hstring(rx_mac_data); + end if; + if rx_mac_crc_ok then + report "[MAC] RX CRC OK"; + end if; + if rx_mac_crc_error then + report "[MAC] RX CRC error"; + end if; + end process receiver; + +end architecture bench; diff --git a/trashernet/trashernet_mac.vhd b/trashernet/trashernet_mac.vhd new file mode 100644 index 0000000..af37594 --- /dev/null +++ b/trashernet/trashernet_mac.vhd @@ -0,0 +1,139 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.trashernet_types.all; + +entity trashernet_mac is + port( + -- Global + clk : in std_logic; -- Global clock + rst : in std_logic; -- Asynchronous reset + + -- PHY signals + rx_data : in std_logic_vector(7 downto 0); -- RX Data + rx_data_valid : in std_logic; -- RX Data valid + rx_active : in std_logic; -- RX of packet in progress + + tx_data : out std_logic_vector(7 downto 0); -- TX Data + tx_data_en : out std_logic; -- Transmitter enable + tx_data_ack : in std_logic; -- Latched data_tx + + carrier_detect : in std_logic; -- Carrier detected + rx_error : in std_logic; -- Receive error + + -- MAC signals + rx_mac_destination : out mac_addr_t; -- Destination MAC address + rx_mac_source : out mac_addr_t; -- Source MAC address + rx_mac_ethertype : out ethertype_t; -- Ethertype or length + rx_mac_data : out byte; -- Ethernet data (after Ethertype) + rx_mac_valid : out std_logic; -- `rx_mac` values (headers + data) are valid + rx_mac_header_rcv : out std_logic; -- `rx_mac` header have been received and are valid + rx_mac_crc_ok : out std_logic; -- End of packet, CRC OK (strobe independent from other rx_mac fields) + rx_mac_crc_error : out std_logic -- End of packet, CRC invalid + ); +end entity trashernet_mac; + +architecture rtl of trashernet_mac is + signal crc : std_logic_vector(31 downto 0); + signal crc_ok : std_logic; + signal crc_clear : std_logic; + +begin + rx : block + type state_t is (HEAD, PAYLOAD); + + constant HEAD_LENGTH : integer := rx_mac_destination'length + rx_mac_source'length + rx_mac_ethertype'length; + constant CRC_LENGTH : integer := 4; + signal state : state_t; + signal sr_head : byte_vector(0 to HEAD_LENGTH - 1); + constant BYTE_COUNT_MAX : integer := maximum(HEAD_LENGTH, CRC_LENGTH) - 1; + signal byte_count : integer range 0 to BYTE_COUNT_MAX; + + signal sr_payload : byte_vector(0 to 4); + + constant ETH_POLYNOMIAL : std_logic_vector(crc'range) := x"04C11DB7"; + begin + crc_inst : entity work.crc + generic map( + POLYNOMIAL => ETH_POLYNOMIAL, + START => x"FFFFFFFF", + FINAL_XOR => x"FFFFFFFF", + REVERSE_OUT => true + ) + port map( + clk => clk, + rst => rst, + data => rx_data, + data_valid => rx_data_valid, + crc_clear => crc_clear, + crc_out => crc + ); + + crc_ok <= '1' when crc = x"2144DF1C" else '0'; + + main : process(clk, rst) is + begin + if rst then + byte_count <= 0; + rx_mac_valid <= '0'; + rx_mac_crc_error <= '0'; + rx_mac_crc_ok <= '0'; + rx_mac_header_rcv <= '0'; + crc_clear <= '0'; + + elsif rising_edge(clk) then + rx_mac_valid <= '0'; + rx_mac_crc_error <= '0'; + rx_mac_crc_ok <= '0'; + rx_mac_header_rcv <= '0'; + crc_clear <= '0'; + + case state is + when HEAD => + if rx_data_valid then + sr_head <= sr_head(sr_head'low + 1 to sr_head'high) & rx_data; + if byte_count = (HEAD_LENGTH - 1) then + state <= PAYLOAD; + byte_count <= 0; + rx_mac_header_rcv <= '1'; + end if; + + if (byte_count /= BYTE_COUNT_MAX) then + byte_count <= byte_count + 1; + end if; + end if; + when PAYLOAD => + if rx_data_valid then + sr_payload <= sr_payload(sr_payload'low + 1 to sr_payload'high) & rx_data; + if byte_count = CRC_LENGTH then + rx_mac_valid <= '1'; + else + byte_count <= byte_count + 1; + end if; + end if; + if not rx_active then + rx_mac_crc_ok <= crc_ok; + rx_mac_crc_error <= not crc_ok; + end if; + if rx_error then + rx_mac_crc_error <= '1'; + end if; + end case; + + if (not rx_active) or rx_error then + byte_count <= 0; + state <= HEAD; + crc_clear <= '1'; + end if; + end if; + end process main; + + rx_mac_destination <= sr_head(0 to 5); + rx_mac_source <= sr_head(6 to 11); + rx_mac_ethertype <= sr_head(12 to 13); + rx_mac_data <= sr_payload(0); + + end block rx; + +end architecture rtl;