-- -------------------------------------------------------------------------- -- -- TRASHERNET - A Trashy Ethernet Stack for FPGAs -- -- -------------------------------------------------------------------------- -- -- top_hwitl.vhd: Top level design for hardware-in-the-loop tests -- -- Target: Simulation -- -------------------------------------------------------------------------- -- -- 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 ieee.math_real.all; library trashernet; use trashernet.trashernet_pkg.all; entity top_hwitl is port( clk : in std_logic; rst_n : in std_logic; rx_p : in std_logic; tx_p : out std_logic; tx_n : out std_logic; led_n : out std_logic_vector(7 downto 0); -- @suppress: Used in different architectures button_n : in std_logic_vector(3 downto 0); -- @suppress: Used in different architectures debug_data : out std_logic_vector(7 downto 0) -- @suppress: Used in different architectures ); end entity top_hwitl; architecture mac of top_hwitl is component pll0 port( CLK : in std_logic; CLKOP : out std_logic; LOCK : out std_logic ); end component pll0; constant F_CLK : integer := 50000000; constant F_CLK_PHY : integer := 140000000; constant ETH_CONFIG : configuration_t := ( mac_address => (x"00", x"ff", x"ff", x"11", x"22", x"33") ); signal rst : std_logic; signal clk_phy : std_logic; signal phy_out : phy_out_t; signal phy_in : phy_in_t; signal mac_out : mac_out_t; signal mac_in : mac_in_t; type status_t is (IDLE, TX); signal state : status_t; constant BYTE_CNT_MAX : integer := 100; signal byte_cnt : integer range 0 to BYTE_CNT_MAX; constant TMO_MAX : integer := integer(round((real(F_CLK) * 0.25))) - 1; signal tmo : integer range 0 to TMO_MAX; begin trashernet_mac_inst : entity trashernet.trashernet_mac port map( clk => clk, rst => rst, phy_out => phy_out, phy_in => phy_in, mac_out => mac_out, mac_in => mac_in ); pll0_inst : pll0 port map( CLK => clk, CLKOP => clk_phy, LOCK => open ); trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc generic map( F_CLK => F_CLK, F_CLK_PHY => F_CLK_PHY ) port map( clk => clk, phy_clk => clk_phy, rst => rst, phy_out => phy_out, phy_in => phy_in, rx_p => rx_p, tx_p => tx_p, tx_n => tx_n ); reset_sync : process(clk, rst_n) is begin if (rst_n = '0') then rst <= '1'; elsif (rising_edge(clk)) then rst <= '0'; end if; end process reset_sync; receiver : process(clk, rst) is begin if rst then state <= IDLE; mac_in.tx_mac_data_en <= '0'; tmo <= 0; elsif rising_edge(clk) then if (tmo /= 0) then tmo <= tmo - 1; end if; case state is when IDLE => if mac_out.rx_mac_crc_ok then if (mac_out.rx_header.mac_destination = ETH_CONFIG.mac_address) then -- Note: We won't respond to broadcasts! state <= TX; byte_cnt <= BYTE_CNT_MAX; mac_in.tx_header.mac_destination <= mac_out.rx_header.mac_source; end if; end if; if tmo = 0 then state <= TX; byte_cnt <= BYTE_CNT_MAX; mac_in.tx_header.mac_destination <= (others => x"FF"); end if; when TX => tmo <= TMO_MAX; mac_in.tx_header.mac_ethertype <= (x"00", std_logic_vector(to_unsigned(BYTE_CNT_MAX, 8))); mac_in.tx_mac_data_en <= '1'; mac_in.tx_mac_data <= std_logic_vector(to_unsigned(byte_cnt, 8)); if mac_out.tx_mac_data_ack then if byte_cnt = 1 then mac_in.tx_mac_data_en <= '0'; state <= IDLE; else byte_cnt <= byte_cnt - 1; end if; end if; end case; end if; end process receiver; mac_in.tx_header.mac_source <= ETH_CONFIG.mac_address; debug_data(0) <= tx_p; debug_data(1) <= tx_n; end architecture mac; -- -------------------------------------------------------------------------- -- -- top_hwitl (eth) -- -------------------------------------------------------------------------- -- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use ieee.math_real.all; library trashernet; use trashernet.trashernet_pkg.all; architecture eth of top_hwitl is component pll0 port( CLK : in std_logic; CLKOP : out std_logic; LOCK : out std_logic ); end component pll0; constant F_CLK : integer := 50000000; constant F_CLK_PHY : integer := 140000000; constant ETH_CONFIG : configuration_t := ( mac_address => (x"00", x"FF", x"FF", x"11", x"22", x"33") ); constant IP_CONFIG : ipv4_configuration_t := ( ip_address => (x"C0", x"A8", x"02", x"02"), subnet_mask => (x"FF", x"FF", x"FF", x"00"), gateway => (x"C0", x"A8", x"02", x"01") ); signal rst : std_logic; signal clk_phy : std_logic; signal phy_out : phy_out_t; signal phy_in : phy_in_t; signal mac_out : mac_out_t; signal mac_in : mac_in_t; constant PROT_ARP : integer := 0; constant PROT_IPV4 : integer := 1; constant ETHERNET_II_PROTOCOLS : ethernet_ii_protocol_vector := ( PROT_ARP => ETHERNET_II_PROTOCOL_ARP, PROT_IPV4 => ETHERNET_II_PROTOCOL_IPV4 ); signal ethernet_i_out : ethernet_i_out_t; signal ethernet_i_in : ethernet_i_in_t; signal ethernet_ii_out : ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range); signal ethernet_ii_in : ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range); signal arp_out : arp_out_t; signal arp_in : arp_in_t; signal ipv4_out : ipv4_out_t; signal ipv4_in : ipv4_in_t; constant IPROT_ICMP : integer := 0; constant IPROT_UDP : integer := 1; constant IPV4_PROTOCOLS : ipv4_protocol_vector := ( IPROT_ICMP => IPV4_PROTOCOL_ICMP, IPROT_UDP => IPV4_PROTOCOL_UDP ); signal ipv4_protocol_out : ipv4_protocol_out_vector(IPV4_PROTOCOLS'range); signal ipv4_protocol_in : ipv4_protocol_in_vector(IPV4_PROTOCOLS'range); begin trashernet_icmp_inst : entity trashernet.trashernet_icmp port map( clk => clk, rst => rst, ipv4_protocol_out => ipv4_protocol_out(IPROT_ICMP), ipv4_protocol_in => ipv4_protocol_in(IPROT_ICMP) ); trashernet_ipv4prot_inst : entity trashernet.trashernet_ipv4prot generic map( IPV4_PROTOCOLS => IPV4_PROTOCOLS ) port map( clk => clk, rst => rst, ipv4_out => ipv4_out, ipv4_in => ipv4_in, ipv4_protocol_out => ipv4_protocol_out, ipv4_protocol_in => ipv4_protocol_in ); trashernet_ipv4_inst : entity trashernet.trashernet_ipv4 port map( clk => clk, rst => rst, ipv4_config => IP_CONFIG, arp_out => arp_out, arp_in => arp_in, ethernet_ii_out => ethernet_ii_out(PROT_IPV4), ethernet_ii_in => ethernet_ii_in(PROT_IPV4), ipv4_out => ipv4_out, ipv4_in => ipv4_in ); trashernet_arp_inst : entity trashernet.trashernet_arp generic map( SYSTICK_FREQ => F_CLK ) port map( clk => clk, rst => rst, systick => '1', mac_config => ETH_CONFIG, ip_config => IP_CONFIG, arp_out => arp_out, arp_in => arp_in, ethernet_ii_out => ethernet_ii_out(PROT_ARP), ethernet_ii_in => ethernet_ii_in(PROT_ARP) ); ethernet_i_in <= ethernet_i_in_t'( tx_mac_address => (others => (others => '-')), tx_data => (others => '-'), tx_en => '0', tx_length => (others => '-') ); trashernet_eth_inst : entity trashernet.trashernet_eth generic map( ETHERNET_II_PROTOCOLS => ETHERNET_II_PROTOCOLS ) port map( clk => clk, rst => rst, mac_out => mac_out, mac_in => mac_in, config => ETH_CONFIG, ethernet_i_out => ethernet_i_out, ethernet_i_in => ethernet_i_in, ethernet_ii_out => ethernet_ii_out, ethernet_ii_in => ethernet_ii_in ); trashernet_mac_inst : entity trashernet.trashernet_mac port map( clk => clk, rst => rst, phy_out => phy_out, phy_in => phy_in, mac_out => mac_out, mac_in => mac_in ); pll0_inst : pll0 port map( CLK => clk, CLKOP => clk_phy, LOCK => open ); trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc generic map( F_CLK => F_CLK, F_CLK_PHY => F_CLK_PHY ) port map( clk => clk, phy_clk => clk_phy, rst => rst, phy_out => phy_out, phy_in => phy_in, rx_p => rx_p, tx_p => tx_p, tx_n => tx_n ); reset_sync : process(clk, rst_n) is begin if (rst_n = '0') then rst <= '1'; elsif (rising_edge(clk)) then rst <= '0'; end if; end process reset_sync; receiver : process(clk, rst) is begin if rst then elsif rising_edge(clk) then if ethernet_i_out.rx_header_rcv then report "RX ETH-I: " & to_string(to_integer(ethernet_i_out.rx_length)); end if; if (ethernet_ii_out(PROT_ARP).rx_header_rcv) then report "RX ARP"; end if; if ipv4_out.rx_ok_stb then report "RX IP OK"; end if; if ipv4_out.rx_error_stb then report "RX IP error"; end if; if ipv4_out.rx_header_rcv then report "RX IP start. PROT=" & to_hstring(ipv4_out.rx_protocol) & ", FROM=" & ip_to_string(ipv4_out.rx_ip_address); end if; end if; end process receiver; udp : process(clk) is begin if rising_edge(clk) then if ipv4_protocol_out(IPROT_UDP).rx_header_rcv then report "Receive UDP"; end if; end if; end process udp; tx_udp_p : block type state_t is (IDLE, TX, DONE); signal state : state_t; constant PACKET : byte_vector := ( x"10", x"00", -- Source port x"04", x"00", -- Destination port x"00", x"15", -- Length x"00", x"00", -- Checksum (not used) x"48", x"65", x"6C", x"6C", x"6F", x"20", x"57", x"6F", x"72", x"6C", x"64", x"21", x"0a" -- Payload ); signal sr : byte_vector(PACKET'range); signal cnt : integer range 0 to sr'length - 1; begin tx_udp : process(clk, rst) is begin if rst then state <= IDLE; elsif rising_edge(clk) then case state is when IDLE => if button_n(0) = '0' then state <= TX; sr <= PACKET; cnt <= sr'length - 1; report "UDP: Start TX"; end if; when TX => if ipv4_protocol_out(IPROT_UDP).tx_data_ack then sr <= sr(sr'low + 1 to sr'high) & x"00"; if cnt = 0 then state <= DONE; else cnt <= cnt - 1; end if; end if; if ipv4_protocol_out(IPROT_UDP).tx_err_stb then report "UDP: TX ERROR"; state <= DONE; end if; if ipv4_protocol_out(IPROT_UDP).tx_ok_stb then report "UDP: TX OK stb in TX state -- that shouldn't happen, right?"; end if; when DONE => null; end case; end if; end process tx_udp; ipv4_protocol_in(IPROT_UDP).tx_en <= '1' when state = TX else '0'; ipv4_protocol_in(IPROT_UDP).tx_ip_address <= (x"C0", x"A8", x"02", x"01"); ipv4_protocol_in(IPROT_UDP).tx_length <= to_unsigned(sr'length, 16); ipv4_protocol_in(IPROT_UDP).tx_data <= sr(0); end block tx_udp_p; icmp : process(clk) is begin if rising_edge(clk) then if ipv4_protocol_out(IPROT_ICMP).rx_header_rcv then report "Receive ICMP"; end if; end if; end process icmp; end architecture eth;