diff --git a/design/top_hwitl.vhd b/design/top_hwitl.vhd index 4c6fe15..57aa49e 100644 --- a/design/top_hwitl.vhd +++ b/design/top_hwitl.vhd @@ -228,7 +228,24 @@ architecture eth of top_hwitl is signal udp_out : udp_out_t; signal udp_in : udp_in_t; + + signal udpprot_rx_out : udpprot_rx_out_vector(0 to 0); + signal udpprot_rx_in : udpprot_rx_in_vector(0 to 0); + signal udpprot_tx_out : udpprot_tx_out_vector(0 to 0); + signal udpprot_tx_in : udpprot_tx_in_vector(0 to 0); begin + trashernet_udpprot_inst : entity trashernet.trashernet_udpprot + port map( + clk => clk, + rst => rst, + udp_out => udp_out, + udp_in => udp_in, + udpprot_rx_out => udpprot_rx_out, + udpprot_rx_in => udpprot_rx_in, + udpprot_tx_out => udpprot_tx_out, + udpprot_tx_in => udpprot_tx_in + ); + trashernet_udp_inst : entity trashernet.trashernet_udp port map( clk => clk, @@ -398,12 +415,12 @@ begin 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); + udpprot_tx_in(0).tx_ip_address <= (x"C0", x"A8", x"02", x"01"); + udpprot_tx_in(0).tx_destination_port <= x"00FF"; + udpprot_tx_in(0).tx_source_port <= x"ABCD"; + udpprot_tx_in(0).tx_length <= to_unsigned(sr'length, 16); + udpprot_tx_in(0).tx_data <= sr(0); + udpprot_tx_in(0).tx_en <= '1' when state = TX else '0'; tx_udp : process(clk, rst) is begin @@ -417,12 +434,12 @@ begin state <= TX; sr <= PACKET; cnt <= sr'length - 1; - report "UDP: Start TX"; + report "UDP(0): Start TX"; end if; when TX => - if udp_out.tx_data_ack then - report "UDP: byte ack"; + if udpprot_tx_out(0).tx_data_ack then + report "UDP(0): byte ack"; sr <= sr(sr'low + 1 to sr'high) & x"00"; if cnt = 0 then state <= DONE; @@ -430,12 +447,12 @@ begin cnt <= cnt - 1; end if; end if; - if udp_out.tx_err_stb then - report "UDP: TX ERROR"; + if udpprot_tx_out(0).tx_err_stb then + report "UDP(0): TX ERROR"; state <= DONE; end if; - if udp_out.tx_ok_stb then - report "UDP: TX OK stb in TX state -- that shouldn't happen, right?"; + if udpprot_tx_out(0).tx_ok_stb then + report "UDP(0): TX OK stb in TX state -- that shouldn't happen, right?"; end if; when DONE => @@ -446,6 +463,8 @@ begin end process tx_udp; end block tx_udp_p; + udpprot_rx_in(0).port_bind <= x"0400"; + rx_udp_p : block begin rx_udp : process(clk, rst) is @@ -453,13 +472,13 @@ 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_header.destination_port)) & " from port " & -- - to_string(to_integer(udp_out.rx_header.source_port)); + if udpprot_rx_out(0).rx_header_rcv then + report "UDP(0): RX on port " & -- + to_string(to_integer(udpprot_rx_out(0).rx_header.destination_port)) & " from port " & -- + to_string(to_integer(udpprot_rx_out(0).rx_header.source_port)); end if; - if udp_out.rx_data_valid then - report "UDP: RX: " & to_hstring(udp_out.rx_data); + if udpprot_rx_out(0).rx_data_valid then + report "UDP(0): RX: " & to_hstring(udpprot_rx_out(0).rx_data); end if; end if; end process rx_udp; diff --git a/trashernet/trashernet_pkg.vhd b/trashernet/trashernet_pkg.vhd index 5b037a1..56e3805 100644 --- a/trashernet/trashernet_pkg.vhd +++ b/trashernet/trashernet_pkg.vhd @@ -261,20 +261,37 @@ package trashernet_pkg is tx_data : byte; end record udp_in_t; - type udpprot_rx_out_t is record - temp : std_logic; + type udpprot_rx_out_t is record + rx_data : byte; -- RX Data + rx_data_valid : std_logic; -- RX data valid strobe + + rx_header : udp_header_t; -- UDP header + rx_header_rcv : std_logic; -- Start of reception, `rx_header` valid + + rx_ok_stb : std_logic; -- End of packet, checksum OK + rx_error_stb : std_logic; -- End of packet, checksum invalid end record udpprot_rx_out_t; - type udpprot_rx_in_t is record - temp : std_logic; + type udpprot_rx_in_t is record + port_bind : udp_port_t; end record udpprot_rx_in_t; - type udpprot_tx_out_t is record - temp : std_logic; + type udpprot_tx_out_t is record + 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 udpprot_tx_out_t; - type udpprot_tx_in_t is record - temp : std_logic; + type udpprot_tx_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 udpprot_tx_in_t; - subtype portnum is integer range 0 to 65535; - type udp_port_vector is array (natural range <>) of portnum; + type udpprot_tx_out_vector is array (natural range <>) of udpprot_tx_out_t; + type udpprot_tx_in_vector is array (natural range <>) of udpprot_tx_in_t; + type udpprot_rx_out_vector is array (natural range <>) of udpprot_rx_out_t; + type udpprot_rx_in_vector is array (natural range <>) of udpprot_rx_in_t; -- ------------------------ -- General helper functions diff --git a/trashernet/trashernet_udp.vhd b/trashernet/trashernet_udp.vhd index 61ff8a1..622e0e6 100644 --- a/trashernet/trashernet_udp.vhd +++ b/trashernet/trashernet_udp.vhd @@ -1,8 +1,8 @@ -- -------------------------------------------------------------------------- -- -- TRASHERNET - A Trashy Ethernet Stack for FPGAs -- -- -------------------------------------------------------------------------- -- --- trashernet_ipv4.vhd : Ethernet OSI Layer 3, Network (IPv4) --- Implements packet handling and IP-Layer en-/decoding. +-- trashernet_udp.vhd : Ethernet OSI Layer 4, Transport (UDP) +-- Implements UDP frame encoding and decoding. -- -------------------------------------------------------------------------- -- -- Author : Markus Koch -- Contributors : None @@ -16,10 +16,6 @@ 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 @@ -30,12 +26,8 @@ entity trashernet_udp is 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) + udp_out : out udp_out_t; -- UDP application IF (out from UDP) + udp_in : in udp_in_t -- UDP application IF (into UDP) ); end entity trashernet_udp; diff --git a/trashernet/trashernet_udpprot.vhd b/trashernet/trashernet_udpprot.vhd new file mode 100644 index 0000000..42e0eb6 --- /dev/null +++ b/trashernet/trashernet_udpprot.vhd @@ -0,0 +1,130 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET - A Trashy Ethernet Stack for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- trashernet_udpprot.vhd : Ethernet OSI Layer 4, Transport (UDP) +-- Provides a convenient port-based muxed interface for UDP connections. +-- -------------------------------------------------------------------------- -- +-- 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_udpprot is + port( + -- Global + clk : in std_logic; -- Global clock + rst : in std_logic; -- Asynchronous reset + + -- UDP application interface + udp_out : in udp_out_t; -- UDP application IF (out from UDP) + udp_in : out udp_in_t; -- UDP application IF (into UDP) + + -- UDP protocols interface + udpprot_rx_out : out udpprot_rx_out_vector; -- UDP Application IF for receiving data (out from UDP) + udpprot_rx_in : in udpprot_rx_in_vector; -- UDP Application IF for receiving data (into UDP) + udpprot_tx_out : out udpprot_tx_out_vector; -- UDP Application IF for transmitting data (out from UDP) + udpprot_tx_in : in udpprot_tx_in_vector -- UDP Application IF for transmitting data (in from UDP) + ); +end entity trashernet_udpprot; + +architecture rtl of trashernet_udpprot is + constant RX_SEL_PROTOCOL_NONE : integer := udpprot_rx_out'low - 1; + + signal rx_sel : integer range RX_SEL_PROTOCOL_NONE to udpprot_rx_out'high; + signal tx_sel : integer range udpprot_tx_out'low to udpprot_tx_out'high; +begin + assert udpprot_tx_out'length = udpprot_tx_in'length report "UDP in and out ports must have the same length" severity FAILURE; + assert udpprot_rx_out'length = udpprot_rx_in'length report "UDP in and out ports must have the same length" severity FAILURE; + + rx : block + signal rx_header_rcv_delayed : std_logic; + + begin + arb : process(clk, rst) is + begin + if rst then + rx_sel <= RX_SEL_PROTOCOL_NONE; + rx_header_rcv_delayed <= '0'; + + elsif rising_edge(clk) then + rx_header_rcv_delayed <= udp_out.rx_header_rcv; + + if udp_out.rx_header_rcv then + rx_sel <= RX_SEL_PROTOCOL_NONE; + for i in udpprot_rx_out'range loop + if (udp_out.rx_header.destination_port = udpprot_rx_in(i).port_bind) then + rx_sel <= i; + end if; + end loop; + end if; + end if; + end process arb; + + mux : for i in udpprot_rx_out'range generate + udpprot_rx_out(i).rx_data <= udp_out.rx_data; + udpprot_rx_out(i).rx_header <= udp_out.rx_header; + + udpprot_rx_out(i).rx_data_valid <= udp_out.rx_data_valid when rx_sel = i else '0'; + udpprot_rx_out(i).rx_error_stb <= udp_out.rx_error_stb when rx_sel = i else '0'; + udpprot_rx_out(i).rx_ok_stb <= udp_out.rx_ok_stb when rx_sel = i else '0'; + udpprot_rx_out(i).rx_header_rcv <= rx_header_rcv_delayed when rx_sel = i else '0'; + end generate mux; + + tx_mux : for i in udpprot_tx_out'range generate + udpprot_tx_out(i).tx_data_ack <= udp_out.tx_data_ack when tx_sel = i else '0'; + udpprot_tx_out(i).tx_err_stb <= udp_out.tx_err_stb when tx_sel = i else '0'; + udpprot_tx_out(i).tx_ok_stb <= udp_out.tx_ok_stb when tx_sel = i else '0'; + end generate; + end block rx; + + tx : block + type state_t is (IDLE, TXD, WAITDONE); + signal state : state_t; + + begin + arb : process(clk, rst) is + begin + if rst then + state <= IDLE; + tx_sel <= udpprot_tx_in'left; + + elsif rising_edge(clk) then + case state is + when IDLE => + for i in udpprot_tx_in'range loop + if udpprot_tx_in(i).tx_en then + tx_sel <= i; + state <= TXD; + exit; -- Prioritize according to vector + end if; + end loop; + + when TXD => + state <= WAITDONE when (not udp_in.tx_en); + + when WAITDONE => + if udp_out.tx_err_stb or udp_out.tx_ok_stb or udp_out.tx_data_ack then + state <= IDLE; + tx_sel <= udpprot_tx_in'left; -- To avoid arbitration errors, always select the highest priority one by default + end if; + end case; + end if; + end process arb; + + demux : block + begin + udp_in.tx_data <= udpprot_tx_in(tx_sel).tx_data; + udp_in.tx_en <= udpprot_tx_in(tx_sel).tx_en; + udp_in.tx_ip_address <= udpprot_tx_in(tx_sel).tx_ip_address; + udp_in.tx_length <= udpprot_tx_in(tx_sel).tx_length; + udp_in.tx_source_port <= udpprot_tx_in(tx_sel).tx_source_port; + udp_in.tx_destination_port <= udpprot_tx_in(tx_sel).tx_destination_port; + end block demux; + end block tx; +end architecture rtl;