From 277d01737ddd46bbb3ee5573e90f90d6c826c4c0 Mon Sep 17 00:00:00 2001 From: Markus Koch Date: Sun, 30 Oct 2022 22:32:47 +0100 Subject: [PATCH] ipv4: Implement RX --- design/top_hwitl.vhd | 47 +++++++++-- trashernet/trashernet_arp.vhd | 2 +- trashernet/trashernet_ipv4.vhd | 142 +++++++++++++++++++++++++++++++++ trashernet/trashernet_pkg.vhd | 71 ++++++++++++++++- 4 files changed, 249 insertions(+), 13 deletions(-) create mode 100644 trashernet/trashernet_ipv4.vhd diff --git a/design/top_hwitl.vhd b/design/top_hwitl.vhd index bd0c3df..f1a5f86 100644 --- a/design/top_hwitl.vhd +++ b/design/top_hwitl.vhd @@ -182,11 +182,13 @@ architecture eth of top_hwitl is 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 ETH_CONFIG : configuration_t := ( + mac_address => (x"00", x"FF", x"FF", x"11", x"22", x"33") ); - constant IP_CONFIG : ip_configuration_t := ( - ip_address => (x"C0", x"A8", x"02", x"02") + 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; @@ -199,10 +201,10 @@ architecture eth of top_hwitl is signal mac_in : mac_in_t; constant PROT_ARP : integer := 0; - constant PROT_IP : integer := 1; + constant PROT_IPV4 : integer := 1; constant ETHERNET_II_PROTOCOLS : ethernet_ii_protocol_vector := ( - PROT_ARP => ETHERNET_II_PROTOCOL_ARP, - PROT_IP => ETHERNET_II_PROTOCOL_IP + 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; @@ -211,7 +213,23 @@ architecture eth of top_hwitl is signal arp_out : arp_out_t; signal arp_in : arp_in_t; + + signal ipv4_out : ipv4_out_t; + signal ipv4_in : ipv4_in_t; begin + 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 @@ -304,9 +322,22 @@ begin if (ethernet_ii_out(PROT_ARP).rx_header_rcv) then report "RX ARP"; end if; - if (ethernet_ii_out(PROT_IP).rx_header_rcv) then + if (ethernet_ii_out(PROT_IPV4).rx_header_rcv) then report "RX IP"; end if; + + if ipv4_out.rx_data_valid then + report "RX IP data: " & to_hstring(ipv4_out.rx_data); + 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; diff --git a/trashernet/trashernet_arp.vhd b/trashernet/trashernet_arp.vhd index a560c69..c63d7c4 100644 --- a/trashernet/trashernet_arp.vhd +++ b/trashernet/trashernet_arp.vhd @@ -27,7 +27,7 @@ entity trashernet_arp is -- Configuration mac_config : in configuration_t; -- Trashernet MAC configuration - ip_config : in ip_configuration_t; -- Trashernet IP configuration + ip_config : in ipv4_configuration_t; -- Trashernet IP configuration -- ARP application interface arp_out : out arp_out_t; -- ARP IF (out from ARP) diff --git a/trashernet/trashernet_ipv4.vhd b/trashernet/trashernet_ipv4.vhd new file mode 100644 index 0000000..05f5e8e --- /dev/null +++ b/trashernet/trashernet_ipv4.vhd @@ -0,0 +1,142 @@ +-- -------------------------------------------------------------------------- -- +-- 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_ipv4 is + port( + -- Global + clk : in std_logic; -- Global clock + rst : in std_logic; -- Asynchronous reset + + -- Configuration + ipv4_config : in ipv4_configuration_t; -- Trashernet IP configuration + + -- ARP application interface + arp_out : in arp_out_t; -- ARP IF (out from ARP) + arp_in : out arp_in_t; -- ARP IF (into ARP) + + -- Ethernet II application interface + ethernet_ii_out : in ethernet_ii_out_t; -- Ethernet II IF (out from MAC) + ethernet_ii_in : out ethernet_ii_in_t; -- Ethernet II IF (into MAC) + + -- IPv4 application interface + ipv4_out : out ipv4_out_t; -- IPv4 IF (out from IP) + ipv4_in : in ipv4_in_t -- IPv4 IF (into IP) + ); +end entity trashernet_ipv4; + +architecture rtl of trashernet_ipv4 is +begin + rx : block + constant BYTECOUNT_HEADER : integer := 20; -- Mandatory header fields + + signal sr : byte_vector(0 to BYTECOUNT_HEADER - 1); + alias sr_version is sr(0)(7 downto 4); + alias sr_ihl is sr(0)(3 downto 0); + alias sr_dscp is sr(1)(7 downto 2); + alias sr_ecn is sr(1)(1 downto 0); + alias sr_total_length is sr(2 to 3); + alias sr_identification is sr(4 to 5); + alias sr_flags is sr(6)(7 downto 5); + signal sr_fragment_offset : std_logic_vector(12 downto 0); + alias sr_ttl is sr(8); + alias sr_protocol is sr(9); + alias sr_header_cks is sr(10 to 11); + alias sr_source_ip is sr(12 to 15); + alias sr_destination_ip is sr(16 to 19); + + signal header_ok : std_logic; + + signal bytecount : integer range 0 to 65535; + signal shifted : std_logic; + signal block_done : std_logic; + + type state_t is (HEADER, OPT, PAYLOAD, DONE); + signal state : state_t; + begin + sr_fragment_offset <= sr(6)(4 downto 0) & sr(7); + + block_done <= '1' when bytecount = 0 else '0'; + header_ok <= '1' when -- + (std_logic_vector'(sr_flags(7) & sr_flags(5)) = "00") and -- Reserved = 0, MF unset + (to_integer(unsigned(sr_fragment_offset)) = 0) and -- No fragment offset -> not last fragment + (sr_destination_ip = ipv4_config.ip_address) else -- It's addressed to us. TODO: We do not support broadcasts + '0'; + rx_fsm : process(clk, rst) is + begin + if rst then + state <= HEADER; + shifted <= '0'; + ipv4_out.rx_ok_stb <= '0'; + ipv4_out.rx_error_stb <= '0'; + ipv4_out.rx_header_rcv <= '0'; + bytecount <= BYTECOUNT_HEADER; + + elsif rising_edge(clk) then + ipv4_out.rx_ok_stb <= '0'; + ipv4_out.rx_error_stb <= '0'; + ipv4_out.rx_header_rcv <= '0'; + + shifted <= ethernet_ii_out.rx_data_valid; + + if (ethernet_ii_out.rx_crc_ok or ethernet_ii_out.rx_crc_error) then + state <= HEADER; + bytecount <= BYTECOUNT_HEADER; + + ipv4_out.rx_error_stb <= ethernet_ii_out.rx_crc_error or to_std_logic(state = PAYLOAD); + ipv4_out.rx_ok_stb <= ethernet_ii_out.rx_crc_ok and to_std_logic(state = DONE); + end if; + if (ethernet_ii_out.rx_data_valid = '1') and (bytecount > 0) then + if state = HEADER then + sr <= sr(sr'low + 1 to sr'high) & ethernet_ii_out.rx_data; + end if; + bytecount <= bytecount - 1; + end if; + + case state is + when HEADER => + if block_done then + bytecount <= to_integer(unsigned(sr_ihl) - 5) * 4; -- five 32-bit words is the header itself + if header_ok then + state <= OPT; + ipv4_out.rx_header_rcv <= '1'; + end if; + end if; + + when OPT => + if block_done then + bytecount <= to_integer(unsigned(std_logic_vector'(sr_total_length(2) & sr_total_length(3)))) - to_integer(unsigned(sr_ihl)) * 4; + state <= PAYLOAD; + end if; + + when PAYLOAD => + if block_done then + state <= DONE; + end if; + + when DONE => -- @suppress "Dead state 'DONE'": Outgoing state transition is outside of case statement + null; -- We just wait here until the MAC gives us a CRC OK/error + end case; + end if; + end process rx_fsm; + + ipv4_out.rx_data <= ethernet_ii_out.rx_data; + ipv4_out.rx_data_valid <= ethernet_ii_out.rx_data_valid when state = PAYLOAD else '0'; + ipv4_out.rx_ip_address <= sr_source_ip; + ipv4_out.rx_protocol <= sr_protocol; + + end block rx; +end architecture rtl; diff --git a/trashernet/trashernet_pkg.vhd b/trashernet/trashernet_pkg.vhd index 43f2435..75228a0 100644 --- a/trashernet/trashernet_pkg.vhd +++ b/trashernet/trashernet_pkg.vhd @@ -37,9 +37,11 @@ package trashernet_pkg is type configuration_t is record mac_address : mac_addr_t; -- MAC address of this node end record configuration_t; - type ip_configuration_t is record - ip_address : ip_addr_t; -- IP address of this node - end record ip_configuration_t; + type ipv4_configuration_t is record + ip_address : ip_addr_t; -- IP address of this node + gateway : ip_addr_t; -- Peer to use when target is outside of subnet + subnet_mask : ip_addr_t; -- Subnet mask of this node + end record ipv4_configuration_t; -- PHY interface type phy_in_t is record @@ -83,7 +85,7 @@ package trashernet_pkg is end record; type ethernet_ii_protocol_vector is array (natural range <>) of ethernet_ii_protocol_t; constant ETHERNET_II_PROTOCOLS_NONE : ethernet_ii_protocol_vector(0 to -1) := (others => (ethertype => (x"00", x"00"))); -- @suppress "Null range": We want a NULL vector here to remove the logic - constant ETHERNET_II_PROTOCOL_IP : ethernet_ii_protocol_t := (ethertype => (x"08", x"00")); + constant ETHERNET_II_PROTOCOL_IPV4 : ethernet_ii_protocol_t := (ethertype => (x"08", x"00")); constant ETHERNET_II_PROTOCOL_ARP : ethernet_ii_protocol_t := (ethertype => (x"08", x"06")); type ethernet_i_out_t is record @@ -123,6 +125,7 @@ package trashernet_pkg is type ethernet_ii_in_vector is array (natural range <>) of ethernet_ii_in_t; constant ETHERNET_II_IN_UNUSED : ethernet_ii_in_t := (tx_mac_address => (others => x"00"), tx_data => x"00", others => '0'); + -- ARP interface type arp_out_t is record arp_mac : mac_addr_t; -- Resulting MAC address for query arp_ok_stb : std_logic; -- ARP request successful, `arp_mac` valid @@ -132,6 +135,59 @@ package trashernet_pkg is arp_ip : ip_addr_t; -- IP address to query arp_query_stb : std_logic; -- Request MAC for IP end record arp_in_t; + + -- IPv4 interface + subtype ipv4_protocol is byte; + type ipv4_protocol_vector is array (natural range <>) of ipv4_protocol; + constant IPV4_PROTOCOL_ICMP : ipv4_protocol := x"01"; + constant IPV4_PROTOCOL_TCP : ipv4_protocol := x"06"; + constant IPV4_PROTOCOL_UDP : ipv4_protocol := x"11"; + + type ipv4_out_t is record + rx_ip_address : ip_addr_t; -- Source IP address + rx_protocol : ipv4_protocol; -- Transport Protocol + + rx_data : byte; -- RX data + rx_data_valid : std_logic; -- RX data valid strobe + rx_ok_stb : std_logic; -- End of packet, checksum OK + rx_error_stb : std_logic; -- End of packet, checksum invalid + rx_header_rcv : std_logic; -- Start of reception (`rx_ip_address` and `rx_protocol` are valid) + + tx_data_ack : std_logic; -- Give next data byte of disable `tx_en` + tx_ok_stb : std_logic; -- Transmission successful + tx_err_stb : std_logic; -- Transmission failed + end record ipv4_out_t; + type ipv4_in_t is record + tx_ip_address : ip_addr_t; -- Destination IP address + tx_protocol : ipv4_protocol; -- Transport Protocol + tx_data : byte; -- TX data + tx_en : std_logic; -- Start and continue transmitting + end record ipv4_in_t; + + type ipv4_protocol_out_t is record + rx_ip_address : ip_addr_t; -- Source IP address + + rx_data : byte; -- RX data + rx_data_valid : std_logic; -- RX data valid strobe + rx_ok : std_logic; -- End of packet, checksum OK + rx_error_stb : std_logic; -- End of packet, checksum invalid + rx_start_stb : std_logic; -- Start of reception + + tx_data_ack : std_logic; -- Give next data byte of disable `tx_en` + tx_ok_stb : std_logic; -- Transmission successful + tx_err_stb : std_logic; -- Transmission failed + end record ipv4_protocol_out_t; + type ipv4_protocol_in_t is record + tx_ip_address : ip_addr_t; -- Destination IP address + + tx_data : byte; -- TX data + tx_en : std_logic; -- Start and continue transmitting + end record ipv4_protocol_in_t; + 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; + + -- General helper functions + function to_std_logic(constant bool : boolean) return std_logic; end package trashernet_pkg; package body trashernet_pkg is @@ -156,4 +212,11 @@ package body trashernet_pkg is to_string(to_integer(unsigned(ip_address(3)))) -- ; end function ip_to_string; + + function to_std_logic(constant bool : boolean) return std_logic is + variable ret : std_logic; + begin + ret := '1' when bool else '0'; + return ret; + end function to_std_logic; end package body trashernet_pkg;