diff --git a/design/top_hwitl.vhd b/design/top_hwitl.vhd index 5286061..835fa89 100644 --- a/design/top_hwitl.vhd +++ b/design/top_hwitl.vhd @@ -225,7 +225,20 @@ architecture eth of top_hwitl is ); signal ipv4_protocol_out : ipv4_protocol_out_vector(IPV4_PROTOCOLS'range); signal ipv4_protocol_in : ipv4_protocol_in_vector(IPV4_PROTOCOLS'range); + + signal udp_out : udp_out_t; + signal udp_in : udp_in_t; begin + trashernet_udp_inst : entity trashernet.trashernet_udp + port map( + clk => clk, + rst => rst, + ipv4_protocol_in => ipv4_protocol_in(IPROT_UDP), + ipv4_protocol_out => ipv4_protocol_out(IPROT_UDP), + udp_out => udp_out, + udp_in => udp_in + ); + trashernet_icmp_inst : entity trashernet.trashernet_icmp port map( clk => clk, @@ -375,20 +388,23 @@ begin end process udp; tx_udp_p : block - type state_t is (IDLE, TX, DONE); - signal state : state_t; + 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 + udp_in.tx_en <= '1' when state = TX else '0'; + udp_in.tx_ip_address <= (x"C0", x"A8", x"02", x"01"); + udp_in.tx_source_port <= x"ABCD"; + udp_in.tx_destination_port <= x"00FF"; + udp_in.tx_length <= to_unsigned(sr'length, 16); + udp_in.tx_data <= sr(0); + tx_udp : process(clk, rst) is begin if rst then @@ -405,7 +421,8 @@ begin end if; when TX => - if ipv4_protocol_out(IPROT_UDP).tx_data_ack then + if udp_out.tx_data_ack then + report "UDP: byte ack"; sr <= sr(sr'low + 1 to sr'high) & x"00"; if cnt = 0 then state <= DONE; @@ -413,11 +430,11 @@ begin cnt <= cnt - 1; end if; end if; - if ipv4_protocol_out(IPROT_UDP).tx_err_stb then + if udp_out.tx_err_stb then report "UDP: TX ERROR"; state <= DONE; end if; - if ipv4_protocol_out(IPROT_UDP).tx_ok_stb then + if udp_out.tx_ok_stb then report "UDP: TX OK stb in TX state -- that shouldn't happen, right?"; end if; @@ -427,12 +444,27 @@ begin 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; + rx_udp_p : block + begin + rx_udp : process(clk, rst) is + begin + if rst then + + elsif rising_edge(clk) then + if udp_out.rx_header_rcv then + report "UDP: RX on port " & -- + to_string(to_integer(udp_out.rx_destination_port)) & " from port " & -- + to_string(to_integer(udp_out.rx_source_port)); + end if; + if udp_out.rx_data_valid then + report "UDP: RX: " & to_hstring(udp_out.rx_data); + end if; + end if; + end process rx_udp; + end block rx_udp_p; + icmp : process(clk) is begin if rising_edge(clk) then diff --git a/trashernet/trashernet_pkg.vhd b/trashernet/trashernet_pkg.vhd index 9ed6ded..3fba0b8 100644 --- a/trashernet/trashernet_pkg.vhd +++ b/trashernet/trashernet_pkg.vhd @@ -191,6 +191,50 @@ package trashernet_pkg is type ipv4_protocol_out_vector is array (natural range <>) of ipv4_protocol_out_t; type ipv4_protocol_in_vector is array (natural range <>) of ipv4_protocol_in_t; + -- UDP interface + subtype udp_port_t is unsigned(15 downto 0); + subtype udp_length_t is unsigned(15 downto 0); + type udp_out_t is record + rx_data : byte; -- RX Data + rx_data_valid : std_logic; -- RX data valid strobe + + rx_header_rcv : std_logic; -- Start of reception + rx_source_port : udp_port_t; -- UDP source port + rx_destination_port : udp_port_t; -- UDP destination port + rx_length : udp_length_t; -- UDP length + + rx_ok_stb : std_logic; -- End of packet, checksum OK + rx_error_stb : std_logic; -- End of packet, checksum invalid + + tx_data_ack : std_logic; -- Give next data byte or disable `tx_en` + tx_ok_stb : std_logic; -- Transmission successful + tx_err_stb : std_logic; -- Transmission failed + end record udp_out_t; + type udp_in_t is record + tx_ip_address : ip_addr_t; -- Destination IP address + tx_source_port : udp_port_t; -- UDP source port + tx_destination_port : udp_port_t; -- UDP destination port + tx_length : udp_length_t; -- UDP length + + tx_en : std_logic; -- Start and continue transmitting + tx_data : byte; + end record udp_in_t; + + type udpprot_rx_out_t is record + temp : std_logic; + end record udpprot_rx_out_t; + type udpprot_rx_in_t is record + temp : std_logic; + end record udpprot_rx_in_t; + type udpprot_tx_out_t is record + temp : std_logic; + end record udpprot_tx_out_t; + type udpprot_tx_in_t is record + temp : std_logic; + end record udpprot_tx_in_t; + subtype portnum is integer range 0 to 65535; + type udp_port_vector is array (natural range <>) of portnum; + -- General helper functions function to_std_logic(constant bool : boolean) return std_logic; end package trashernet_pkg; diff --git a/trashernet/trashernet_udp.vhd b/trashernet/trashernet_udp.vhd new file mode 100644 index 0000000..f5e7cc1 --- /dev/null +++ b/trashernet/trashernet_udp.vhd @@ -0,0 +1,160 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET - A Trashy Ethernet Stack for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- trashernet_ipv4.vhd : Ethernet OSI Layer 3, Network (IPv4) +-- Implements packet handling and IP-Layer en-/decoding. +-- -------------------------------------------------------------------------- -- +-- 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_udp is + -- generic( + -- UDP_PORTS_RX : udp_port_vector; -- Ports to receive on + -- UDP_PORTS_TX : udp_port_vector -- Ports to transmit on + -- ); + port( + -- Global + clk : in std_logic; -- Global clock + rst : in std_logic; -- Asynchronous reset + + -- IPv4 application interface + ipv4_protocol_in : out ipv4_protocol_in_t; -- IPv4 IF (out from IP Protocol) + ipv4_protocol_out : in ipv4_protocol_out_t; -- IPv4 IF (into IP Protocol) + + -- UDP application interface + udp_out : out udp_out_t; + udp_in : in udp_in_t + -- udpprot_rx_out : out udpprot_rx_out_t; -- UDP Application IF for receiving data (out from UDP) + -- udpprot_rx_in : in udpprot_rx_in_t; -- UDP Application IF for receiving data (into UDP) + -- udpprot_tx_out : out udpprot_tx_out_t; -- UDP Application IF for transmitting data (out from UDP) + -- udpprot_tx_in : in udpprot_tx_in_t -- UDP Application IF for transmitting data (in from UDP) + ); +end entity trashernet_udp; + +architecture rtl of trashernet_udp is +begin + receiver : block + constant BYTECOUNT_HEADER : integer := 8; -- Mandatory header fields + signal sr : byte_vector(0 to BYTECOUNT_HEADER - 1) := (others => x"00"); -- Init to zero to squelch metavalue warnings + alias sr_source_port is sr(0 to 1); + alias sr_destination_port is sr(2 to 3); + alias sr_length is sr(4 to 5); + alias sr_source_checksum is sr(6 to 7); + signal bytecount : integer range 0 to BYTECOUNT_HEADER - 1; + + type state_t is (HEADER, PAYLOAD); + signal state : state_t; + begin + rxp : process(rst, clk) is + begin + if rst then + state <= HEADER; + udp_out.rx_header_rcv <= '0'; + udp_out.rx_error_stb <= '0'; + udp_out.rx_ok_stb <= '0'; + + elsif rising_edge(clk) then + udp_out.rx_header_rcv <= '0'; + udp_out.rx_error_stb <= ipv4_protocol_out.rx_error_stb; + udp_out.rx_ok_stb <= ipv4_protocol_out.rx_ok_stb; + + case state is + when HEADER => + if ipv4_protocol_out.rx_data_valid then + sr <= sr(sr'low + 1 to sr'high) & ipv4_protocol_out.rx_data; + if bytecount = BYTECOUNT_HEADER - 1 then + state <= PAYLOAD; + udp_out.rx_header_rcv <= '1'; + else + bytecount <= bytecount + 1; + end if; + end if; + + when PAYLOAD => + udp_out.rx_data <= ipv4_protocol_out.rx_data; + udp_out.rx_data_valid <= ipv4_protocol_out.rx_data_valid; + end case; + + if ipv4_protocol_out.rx_header_rcv or ipv4_protocol_out.rx_error_stb or ipv4_protocol_out.rx_ok_stb then + state <= HEADER; + bytecount <= 0; + end if; + end if; + end process rxp; + + udp_out.rx_source_port <= unsigned(std_logic_vector'(sr_source_port(0) & sr_source_port(1))); + udp_out.rx_destination_port <= unsigned(std_logic_vector'(sr_destination_port(2) & sr_destination_port(3))); + udp_out.rx_length <= unsigned(std_logic_vector'(sr_length(4) & sr_length(5))); + end block receiver; + + transmitter : block + constant BYTECOUNT_HEADER : integer := 8; -- Mandatory header fields + signal sr : byte_vector(0 to BYTECOUNT_HEADER - 1) := (others => x"00"); -- Init to zero to squelch metavalue warnings + signal byte_cnt : integer range 0 to sr'length - 1; + + type state_t is (IDLE, HEADER, PAYLOAD); + signal state : state_t; + begin + txp : process(rst, clk) is + variable full_length : udp_length_t; + begin + if rst then + udp_out.tx_data_ack <= '0'; + udp_out.tx_err_stb <= '0'; + udp_out.tx_ok_stb <= '0'; + + elsif rising_edge(clk) then + udp_out.tx_data_ack <= '0'; + udp_out.tx_err_stb <= ipv4_protocol_out.tx_err_stb; + udp_out.tx_ok_stb <= ipv4_protocol_out.tx_ok_stb; + + if ipv4_protocol_out.tx_data_ack = '1' then + if byte_cnt > 0 then + byte_cnt <= byte_cnt - 1; + end if; + sr <= sr(sr'low + 1 to sr'high) & x"00"; + end if; + case state is + when IDLE => + if udp_in.tx_en then + full_length := udp_in.tx_length + BYTECOUNT_HEADER; + sr <= byte_vector'(byte(udp_in.tx_source_port(15 downto 8)), byte(udp_in.tx_source_port(7 downto 0)), -- + byte(udp_in.tx_destination_port(15 downto 8)), byte(udp_in.tx_destination_port(7 downto 0)), -- + byte(full_length(15 downto 8)), byte(full_length(7 downto 0)), -- + x"00", x"00"); + byte_cnt <= sr'length - 1; + ipv4_protocol_in.tx_length <= full_length; + ipv4_protocol_in.tx_ip_address <= udp_in.tx_ip_address; + state <= HEADER; + end if; + + when HEADER => + if byte_cnt = 0 then + state <= PAYLOAD; + end if; + + when PAYLOAD => + if ipv4_protocol_out.tx_data_ack then + sr(sr'low) <= udp_in.tx_data; + udp_out.tx_data_ack <= '1'; + if not udp_in.tx_en then + state <= IDLE; + end if; + end if; + end case; + end if; + end process txp; + + ipv4_protocol_in.tx_data <= sr(sr'low); + ipv4_protocol_in.tx_en <= '1' when (state /= IDLE) else '0'; + end block transmitter; + +end architecture rtl;