From fdcb1cb719e9435c98cd875953afcb2b5fffc29f Mon Sep 17 00:00:00 2001 From: Markus Koch Date: Mon, 31 Oct 2022 23:46:46 +0100 Subject: [PATCH] ipv4: Implement IPv4 TX --- design/top_hwitl.vhd | 77 +++++++++++++++----- trashernet/trashernet_ipv4.vhd | 109 +++++++++++++++++++++++++++++ trashernet/trashernet_ipv4prot.vhd | 1 + trashernet/trashernet_pkg.vhd | 3 + 4 files changed, 172 insertions(+), 18 deletions(-) diff --git a/design/top_hwitl.vhd b/design/top_hwitl.vhd index 1241f35..ced3654 100644 --- a/design/top_hwitl.vhd +++ b/design/top_hwitl.vhd @@ -372,6 +372,65 @@ begin 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 @@ -381,22 +440,4 @@ begin end if; end process icmp; - arp_request_test : process(clk, rst) is - begin - if rst then - arp_in.arp_ip <= (x"C0", x"A8", x"02", x"01"); - arp_in.arp_query_stb <= '0'; - - elsif rising_edge(clk) then - arp_in.arp_query_stb <= not button_n(0); - - if arp_out.arp_ok_stb then - report "Found MAC: " & mac_to_string(arp_out.arp_mac); - end if; - if arp_out.arp_fail_stb then - report "ARP failed"; - end if; - end if; - end process arp_request_test; - end architecture eth; diff --git a/trashernet/trashernet_ipv4.vhd b/trashernet/trashernet_ipv4.vhd index d27ceec..390f8f0 100644 --- a/trashernet/trashernet_ipv4.vhd +++ b/trashernet/trashernet_ipv4.vhd @@ -135,4 +135,113 @@ begin ipv4_out.rx_protocol <= sr_protocol; end block rx; + + tx : block + type state_t is (IDLE, ARP, CALC_CKS, HEADER, PAYLOAD); + signal state : state_t; + + signal full_length : ipv4_length; + + constant TTL : byte := x"40"; + impure function get_header(checksum : std_logic_vector(15 downto 0)) return byte_vector is + begin + return -- + byte_vector'( + x"45", x"00", byte(full_length(15 downto 8)), byte(full_length(7 downto 0)), -- Ver/IHL, DSCP/ECN, Len + x"00", x"00", x"00", x"00" -- Identification, Flags/FragOffset + ) & TTL & ipv4_in.tx_protocol & checksum(15 downto 8) & checksum(7 downto 0) & -- TTL, Protocol, Header Checksum + ipv4_config.ip_address & -- Source IP + ipv4_in.tx_ip_address -- Destination IP + ; + end function get_header; + + constant PSEUDO_HEADER : byte_vector := get_header(x"0000"); + signal sr : byte_vector(PSEUDO_HEADER'range); + signal byte_cnt : integer range 0 to sr'length - 1; + signal alt_byte : std_logic; + signal checksum : unsigned(20 downto 0); -- 20 Header fields -> 19 chances for carry -> 5 additional bits + signal checksum_ones : unsigned(15 downto 0); + begin + full_length <= PSEUDO_HEADER'length + ipv4_in.tx_length; + checksum_ones <= checksum(15 downto 0) + checksum(checksum'high downto 16); + + tx_fsm : process(clk, rst) is + begin + if rst then + state <= IDLE; + ipv4_out.tx_data_ack <= '0'; + arp_in.arp_query_stb <= '0'; + ipv4_out.tx_err_stb <= '0'; + + elsif rising_edge(clk) then + arp_in.arp_query_stb <= '0'; + ipv4_out.tx_data_ack <= '0'; + ipv4_out.tx_err_stb <= '0'; + + if (ethernet_ii_out.tx_data_ack = '1') or (state = CALC_CKS) 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 ipv4_in.tx_en then + arp_in.arp_query_stb <= '1'; + state <= ARP; + end if; + + when ARP => + if arp_out.arp_ok_stb then + ethernet_ii_in.tx_mac_address <= arp_out.arp_mac; + + byte_cnt <= sr'length - 1; + sr <= get_header(x"0000"); + alt_byte <= '1'; + checksum <= (others => '0'); + state <= CALC_CKS; + end if; + if arp_out.arp_fail_stb then + ipv4_out.tx_err_stb <= '1'; + -- Then we lock up in this state until tx_en is disabled + end if; + + when CALC_CKS => + alt_byte <= not alt_byte; + + if alt_byte then + checksum <= checksum + unsigned(std_logic_vector'(sr(sr'low) & sr(sr'low + 1))); + else + if byte_cnt = 0 then + state <= HEADER; + byte_cnt <= sr'length - 1; + sr <= get_header(not std_logic_vector(checksum_ones)); + end if; + end if; + + when HEADER => + if byte_cnt = 0 then + state <= PAYLOAD; + end if; + + when PAYLOAD => + if ethernet_ii_out.tx_data_ack then + sr(sr'low) <= ipv4_in.tx_data; + ipv4_out.tx_data_ack <= '1'; + if not ipv4_in.tx_en then + state <= IDLE; + end if; + end if; + end case; + end if; + end process tx_fsm; + + ethernet_ii_in.tx_data <= sr(sr'low); + ethernet_ii_in.tx_en <= '1' when (state = HEADER) or (state = PAYLOAD) else '0'; + + arp_in.arp_ip <= ipv4_in.tx_ip_address; + + end block tx; + end architecture rtl; diff --git a/trashernet/trashernet_ipv4prot.vhd b/trashernet/trashernet_ipv4prot.vhd index edd6708..962d441 100644 --- a/trashernet/trashernet_ipv4prot.vhd +++ b/trashernet/trashernet_ipv4prot.vhd @@ -114,6 +114,7 @@ begin ipv4_in.tx_data <= ipv4_protocol_in(tx_sel).tx_data; ipv4_in.tx_en <= ipv4_protocol_in(tx_sel).tx_en; ipv4_in.tx_ip_address <= ipv4_protocol_in(tx_sel).tx_ip_address; + ipv4_in.tx_length <= ipv4_protocol_in(tx_sel).tx_length; ipv4_in.tx_protocol <= IPV4_PROTOCOLS(tx_sel); end block demux; end block tx; diff --git a/trashernet/trashernet_pkg.vhd b/trashernet/trashernet_pkg.vhd index a370797..c8de077 100644 --- a/trashernet/trashernet_pkg.vhd +++ b/trashernet/trashernet_pkg.vhd @@ -142,6 +142,7 @@ package trashernet_pkg is constant IPV4_PROTOCOL_ICMP : ipv4_protocol := x"01"; constant IPV4_PROTOCOL_TCP : ipv4_protocol := x"06"; constant IPV4_PROTOCOL_UDP : ipv4_protocol := x"11"; + subtype ipv4_length is unsigned(15 downto 0); type ipv4_out_t is record rx_ip_address : ip_addr_t; -- Source IP address @@ -160,6 +161,7 @@ package trashernet_pkg is type ipv4_in_t is record tx_ip_address : ip_addr_t; -- Destination IP address tx_protocol : ipv4_protocol; -- Transport Protocol + tx_length : ipv4_length; -- Length of payload tx_data : byte; -- TX data tx_en : std_logic; -- Start and continue transmitting end record ipv4_in_t; @@ -179,6 +181,7 @@ package trashernet_pkg is end record ipv4_protocol_out_t; type ipv4_protocol_in_t is record tx_ip_address : ip_addr_t; -- Destination IP address + tx_length : ipv4_length; -- Length of payload tx_data : byte; -- TX data tx_en : std_logic; -- Start and continue transmitting