173 lines
5.2 KiB
VHDL
173 lines
5.2 KiB
VHDL
-- -------------------------------------------------------------------------- --
|
|
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
|
-- -------------------------------------------------------------------------- --
|
|
-- trashernet_icmp.vhd : Ethernet OSI Layer 3, Network (ICMP)
|
|
-- Implements ICMP echo replies
|
|
-- -------------------------------------------------------------------------- --
|
|
-- Author : Markus Koch <markus@notsyncing.net>
|
|
-- 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;
|