diff --git a/design/top_hwitl.vhd b/design/top_hwitl.vhd index ced3654..5286061 100644 --- a/design/top_hwitl.vhd +++ b/design/top_hwitl.vhd @@ -226,6 +226,14 @@ 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); 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 @@ -344,13 +352,7 @@ begin if (ethernet_ii_out(PROT_ARP).rx_header_rcv) then report "RX ARP"; end if; - 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; diff --git a/trashernet/fifo.vhd b/trashernet/fifo.vhd new file mode 100644 index 0000000..a5af238 --- /dev/null +++ b/trashernet/fifo.vhd @@ -0,0 +1,98 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET - A Trashy Ethernet Stack for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- fifo.vhd : Basic single-clock FIFO +-- Implements a basic single-clock FIFO +-- -------------------------------------------------------------------------- -- +-- 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; + +entity fifo is + generic( + DEPTH : natural -- Number of elements the FIFO can hold + ); + port( + -- Global + clk : in std_logic; -- Global clock + rst : in std_logic; -- Asynchronous reset + + -- FIFO + clear : in std_logic; -- Synchronous reset (clear FIFO) + data_in : in std_logic_vector; -- Data into the FIFO (automatically constrains width) + push : in std_logic; -- Push `data_in` into the FIFO + full : out std_logic; -- No further elements can be pushed into the FIFO + data_out : out std_logic_vector; -- Data out of the FIFO + pop : in std_logic; -- Get an element from the FIFO + empty : out std_logic -- FIFO is empty + ); +end entity fifo; + +architecture rtl of fifo is + type memory_t is array (natural range <>) of std_logic_vector(data_in'range); + signal memory : memory_t(0 to DEPTH - 1); + + subtype memory_pointer_t is integer range 0 to DEPTH - 1; + subtype usage_counter_t is integer range 0 to DEPTH; + + signal read_pointer : memory_pointer_t; + signal write_pointer : memory_pointer_t; + signal usage_counter : usage_counter_t; +begin + fifo_proc : process(clk, rst) is + procedure increment_pointer(signal pointer : inout memory_pointer_t) is + begin + if pointer = pointer'subtype'high then + pointer <= 0; + else + pointer <= pointer + 1; + end if; + end procedure increment_pointer; + + variable pushed : boolean; + variable popped : boolean; + begin + if rst then + read_pointer <= 0; + write_pointer <= 0; + usage_counter <= 0; + + elsif rising_edge(clk) then + pushed := false; + popped := false; + + if push and not full then + memory(write_pointer) <= data_in; + increment_pointer(write_pointer); + usage_counter <= usage_counter + 1; + pushed := true; + end if; + + if pop and not empty then + increment_pointer(read_pointer); + usage_counter <= usage_counter - 1; + popped := true; + end if; + + if pushed and popped then + usage_counter <= usage_counter; + end if; + + if clear then + read_pointer <= 0; + write_pointer <= 0; + usage_counter <= 0; + end if; + end if; + end process fifo_proc; + + data_out <= memory(read_pointer); + empty <= '1' when usage_counter = 0 else '0'; + full <= '1' when usage_counter = DEPTH else '0'; + +end architecture rtl; diff --git a/trashernet/trashernet_icmp.vhd b/trashernet/trashernet_icmp.vhd new file mode 100644 index 0000000..4be3bdd --- /dev/null +++ b/trashernet/trashernet_icmp.vhd @@ -0,0 +1,172 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET - A Trashy Ethernet Stack for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- trashernet_icmp.vhd : Ethernet OSI Layer 3, Network (ICMP) +-- Implements ICMP echo replies +-- -------------------------------------------------------------------------- -- +-- 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_icmp is + port( + -- Global + clk : in std_logic; -- Global clock + rst : in std_logic; -- Asynchronous reset + + -- IPv4 application interface + ipv4_protocol_out : in ipv4_protocol_out_t; -- IPv4 Protocol IF (out from IP Mux) + ipv4_protocol_in : out ipv4_protocol_in_t -- IPv4 Protocol IF (into IP Mux) + ); +end entity trashernet_icmp; + +architecture rtl of trashernet_icmp is + signal fifo_clear : std_logic; + signal fifo_data_in : std_logic_vector(byte'range); + signal fifo_push : std_logic; + signal fifo_full : std_logic; -- TODO: FIFO full error handling + signal fifo_data_out : std_logic_vector(fifo_data_in'range); + signal fifo_pop : std_logic; + signal fifo_empty : std_logic; + + signal tx_response : std_logic; + signal rx_checksum : std_logic_vector(15 downto 0); +begin + fifo_inst : entity work.fifo + generic map( + DEPTH => 64 + ) + port map( + clk => clk, + rst => rst, + clear => fifo_clear, + data_in => fifo_data_in, + push => fifo_push, + full => fifo_full, + data_out => fifo_data_out, + pop => fifo_pop, + empty => fifo_empty + ); + + rx : block + signal sr : byte_vector(0 to 3); + alias sr_type is sr(0); + alias sr_code is sr(1); + alias sr_checksum is sr(2 to 3); + + type state_t is (HEADER, DATA, IGNORE); + signal state : state_t; + + signal byte_count : integer range 0 to sr'length; + + begin + rx_fsm : process(clk, rst) is + begin + if rst then + state <= HEADER; + byte_count <= byte_count'subtype'high; + + elsif rising_edge(clk) then + + if (ipv4_protocol_out.rx_data_valid = '1') and (byte_count /= 0) then + byte_count <= byte_count - 1; + if state = HEADER then + sr <= sr(sr'low + 1 to sr'high) & ipv4_protocol_out.rx_data; + end if; + end if; + + if ipv4_protocol_out.rx_header_rcv then + byte_count <= byte_count'subtype'high; + state <= HEADER; + end if; + + case state is + when HEADER => + if byte_count = 0 then + if sr_type = x"08" then + state <= DATA; + else + state <= IGNORE; + end if; + end if; + + when DATA => + null; -- We just wait here and collect data + + when IGNORE => -- Just wait until it's over + null; + end case; + end if; + end process rx_fsm; + fifo_clear <= ipv4_protocol_out.rx_header_rcv; -- TODO: This will break things when we get another Ping too soon + fifo_push <= ipv4_protocol_out.rx_data_valid when state = DATA else '0'; + fifo_data_in <= ipv4_protocol_out.rx_data; + + tx_response <= ipv4_protocol_out.rx_ok_stb when state = DATA else '0'; + rx_checksum <= sr_checksum(2) & sr_checksum(3); + end block rx; + + tx : block + signal checksum : unsigned(16 downto 0); + signal checksum_ones : unsigned(15 downto 0); + + type state_t is (IDLE, HEADER, PAYLOAD); + signal state : state_t; + + signal sr : byte_vector(0 to 3); + signal byte_count : integer range 0 to sr'subtype'high; + begin + + checksum <= ('0' & not (unsigned(rx_checksum))) - unsigned'('0' & x"0800"); -- The only change is Type 8 changes to Type 0 + checksum_ones <= not (checksum(15 downto 0) - checksum(16)); + + tx_fsm : process(clk, rst) is + begin + if rst then + + elsif rising_edge(clk) then + if (ipv4_protocol_out.tx_data_ack = '1') and (byte_count /= 0) then + sr <= sr(sr'low + 1 to sr'high) & x"00"; + byte_count <= byte_count - 1; + end if; + + case state is + when IDLE => + if tx_response then + ipv4_protocol_in.tx_ip_address <= ipv4_protocol_out.rx_ip_address; -- TODO: This is technically too late, but ¯\_(ツ)_/¯ + ipv4_protocol_in.tx_length <= ipv4_protocol_out.rx_length; + sr <= byte_vector'( + x"00", + x"00", + std_logic_vector(checksum_ones(15 downto 8)), + std_logic_vector(checksum_ones(7 downto 0)) + ); + byte_count <= sr'subtype'high; + state <= HEADER; + end if; + + when HEADER => + if (byte_count = 0) and (ipv4_protocol_out.tx_data_ack = '1') then + state <= PAYLOAD; + end if; + + when PAYLOAD => + if fifo_empty then + state <= IDLE; + end if; + end case; + end if; + end process tx_fsm; + + fifo_pop <= ipv4_protocol_out.tx_data_ack when state = PAYLOAD else '0'; + ipv4_protocol_in.tx_en <= '1' when (state = HEADER) or (state = PAYLOAD) else '0'; + ipv4_protocol_in.tx_data <= sr(sr'low) when state = HEADER else fifo_data_out; + end block tx; +end architecture rtl;