rtps-fpga/src/ipv4_in_handler.vhd

1040 lines
50 KiB
VHDL

-- altera vhdl_input_version vhdl_2008
-- XXX: QSYS Fix (https://www.intel.com/content/www/us/en/support/programmable/articles/000079458.html)
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.ip_package.all;
use work.math_pkg.all;
-- IPv4 Handler
-- This entity parses IPv4 packets read from the input FIFO, and extracts the underlying Protocol (Payload)
-- to the respective output FIFO together with the Src and Dest Addresses.
-- The input FIFO has to be a 32-bit wide FWFT (First Word Fall Through) FIFO. The IPv4 packet has to be
-- 32-bit word aligned (padded with zeroes in the end to the closest 32-bit boundary), and the first word
-- before the actual packet has to be the length of the following IPv4 packet (in 32-bit words + padding).
-- This is necessary to recover from spurious packets.
-- ex.
-- 31 0
-- +------------------------------+
-- | LENGTH=4 |
-- +------------------------------+
-- | IPv4 Packet |
-- + +
-- | |
-- + +
-- | |
-- + +--------------+
-- | | PADDING |
-- +---------------+--------------+
-- The output FIFO distribution is controlled by the 'LAYER3_PROTOCOLS' constant in the 'ip_package' package.
-- The array position of the protocol version number is also the id of the output FIFO where this protocol
-- is sent to. First word on the output FIFO is the Source address, second the Destination Address, third
-- the payload length (in 32-bit words + padding), followed by the actual packet.
-- ex.
-- 31 0
-- +------------------------------+
-- | Source Address |
-- +------------------------------+
-- | Destination Address |
-- +------------------------------+
-- | LENGTH=4 |
-- +------------------------------+
-- | UDP Packet |
-- + +
-- | |
-- | +
-- | |
-- + +--------------+
-- | | PADDING |
-- +---------------+--------------+
-- This Entity features 2 architecture implementations.
-- The 'no_frag' implementation does not support fragmentation and just drops fragmented packets. This needs the least amount of resources
-- The 'with_frag' implementation supports fragmentation as stated in RFC 791 (fragments of any size received in any order with auto adjusted
-- timeout according to transmitted TTL). The re-assembly needs considerable more resources for packet buffering and state holding.
-- The generics allow tuning of the supported packet sizes and used resources.
entity ipv4_in_handler is
generic(
CLK_FREQ : integer := 20000000; -- Frequency of input clock in Hz
MAX_FRAG_SIZE : integer := 1600; -- Maximum re-assembled IP packet size. If a re-assembled packet exceeds this threshold it is dropped. (Does not limit the size of received non-fragmented packets)
MAX_PARALLEL_FRAG : integer := 1; -- Maximum number of parallel running re-assemble procedures. If a fragment of a new Ip packet is received, while 'MAX_PARALLEL_FRAG' packets are already in the re-assembly procedure, it is dropped.
DEFAULT_FRAG_TIMEOUT: integer := 5 -- Default timeout in seconds until which the re-assembly procedure of an IP packet has to be completed before being dropped to fre up resources.
);
port (
clk : in std_logic; -- Input Clock
reset : in std_logic; -- Synchronous Reset
empty : in std_logic; -- Input FIFO empty flag
rd : out std_logic; -- Input FIFO read signal
data_in : in std_logic_vector(31 downto 0); -- Input FIFO data signal
full : in std_logic_vector(LAYER3_PROTOCOL_NUM-1 downto 0); -- Output FIFO full flag
wr : out std_logic_vector(LAYER3_PROTOCOL_NUM-1 downto 0); -- Output FIFO write signal
data_out: out IP_OUTPUT_TYPE -- Output FIFO data signal
);
end entity;
architecture with_frag of ipv4_in_handler is
--*****COMPONENT DECLARATION*****
-- The RAM is used for reassembly of ip fragments.
-- The RAM should have a READ latency of 1 clock cycle,
-- and a WRITE latency smaller than 5 clock cycles
component single_port_ram is
generic (
ADDR_WIDTH : integer := 8;
DATA_WIDTH : integer := 12;
MEMORY_SIZE : integer := DATA_WIDTH*(2**ADDR_WIDTH)
);
port (
clk : in std_logic;
addr : in std_logic_vector(ADDR_WIDTH-1 downto 0);
wen : in std_logic;
ren : in std_logic;
wr_data : in std_logic_vector(DATA_WIDTH-1 downto 0);
rd_data : out std_logic_vector(DATA_WIDTH-1 downto 0)
);
end component;
--*****CONSTANT DECLARATION*****
-- The width of the RAM address has a lower bound equal to the fragment offset field
constant RAM_ADDR_WIDTH : integer := max(log2c(MAX_PARALLEL_FRAG*MAX_FRAG_SIZE), 14);
--*****TYPE DECLARATION*****
-- FSM states. Explained below in detail
type PARSER_STAGE_TYPE is (IPv4_INIT, IPv4_HEADER_1, IPv4_HEADER_2, IPv4_HEADER_3, IPv4_HEADER_4,
IPv4_HEADER_5, IPv4_PAYLOAD_LENGTH, IPv4_PAYLOAD, IPv4_FRAGMENT_PRE, IPv4_FRAGMENT,
IPv4_FRAGMENT_POST, IPv4_BUFFER_SRC, IPv4_BUFFER_DEST, IPv4_BUFFER_LENGTH, IPv4_BUFFER_PAYLOAD,
SKIP_HEADER, SKIP_PACKET);
-- Array of buffer identifiers. A buffer identifier is the concatenation of source, destination, protocol, and identification fields
-- NOTE: The MAX_PARALLEL_FRAG array position is not mapped to a buffer, but is used for comparison purposes
type BUFFER_ID_ARRAY is array (MAX_PARALLEL_FRAG downto 0) of std_logic_vector(87 downto 0);
-- Array of bitmaps used to identify received fragment words.
type BUFFER_BITMAP_ARRAY is array (MAX_PARALLEL_FRAG-1 downto 0) of std_logic_vector((MAX_FRAG_SIZE/4)-1 downto 0);
-- Array of buffer timers used for timeout checking
type BUFFER_TIMER_ARRAY is array (MAX_PARALLEL_FRAG-1 downto 0) of unsigned(7 downto 0);
-- Array of address offsets for the RAM. Used as constant
type BUFFER_ADDR_OFFSET_TYPE is array (MAX_PARALLEL_FRAG-1 downto 0) of unsigned(RAM_ADDR_WIDTH-1 downto 0);
-- Array of packet sizes in buffers
type FRAG_SIZE_ARRAY is array (MAX_PARALLEL_FRAG-1 downto 0) of std_logic_vector(13 downto 0);
-- Array of 32-bit buffer word counters. Used for counting new received fragments and determining if the packet is completely re-assembled
type BUFFER_WORD_COUNTER_ARRAY is array (MAX_PARALLEL_FRAG-1 downto 0) of integer range 0 to (MAX_FRAG_SIZE/4);
--*****SIGNAL DECLARATION*****
-- FSM state
signal stage, stage_next : PARSER_STAGE_TYPE;
-- 32-bit aligned total packet length
signal packet_length, packet_length_next : unsigned(13 downto 0);
-- 32-bit aligned header length
signal header_length, header_length_next : unsigned(3 downto 0);
-- 32-bit word counter (Counts words read from input fifo)
signal read_cnt : unsigned(13 downto 0);
-- Intermediate input read signal. (Read from output port not allowed)
signal rd_sig : std_logic;
-- States if the current processed packet is a fragment
signal is_fragment, is_fragment_next : std_logic;
-- States if the current processed packet is the last fragment (Needed to determine total payload size)
signal is_last_fragment, is_last_fragment_next : std_logic;
-- Clock with 1 Hz Frequency
signal sec_clk : std_logic;
-- Counter used to generate 'sec_clk'
signal sec_cnt : integer range 0 to (CLK_FREQ/2)-1;
-- ID of current output fifo (Used to MUX output FIFOs)
signal output_id, output_id_next : integer range 0 to LAYER3_PROTOCOL_NUM-1;
-- Buffer Identifiers for each buffer. See 'BUFFER_ID_ARRAY'
signal buffer_id, buffer_id_next : BUFFER_ID_ARRAY;
-- Used as Constant. See 'BUFFER_ADDR_OFFSET_TYPE'
signal buffer_addr_offset : BUFFER_ADDR_OFFSET_TYPE;
-- Bitmap array. See 'BUFFER_BITMAP_ARRAY'
signal buffer_bitmap, buffer_bitmap_next : BUFFER_BITMAP_ARRAY;
-- 32-bit word aligned fragment offset
signal frag_offset, frag_offset_next : unsigned(13 downto 0);
-- Packet size array. See 'FRAG_SIZE_ARRAY'
signal frag_size, frag_size_next : FRAG_SIZE_ARRAY;
-- TimeToLive temporary storage
signal ttl, ttl_next : std_logic_vector(7 downto 0);
-- Buffer Timers. See 'BUFFER_TIMER_ARRAY'
signal buffer_timer : BUFFER_TIMER_ARRAY;
-- Received fragment word counters. See BUFFER_WORD_COUNTER_ARRAY
signal buffer_word_cnt, buffer_word_cnt_next : BUFFER_WORD_COUNTER_ARRAY;
-- Timeout values of buffer timers
signal max_buffer_timer, max_buffer_timer_next : BUFFER_TIMER_ARRAY;
-- Signal used to reset buffer timers
signal reset_buffer_timer : std_logic;
-- ID of buffer timer to be reset
signal reset_buffer_timer_id : integer range 0 to MAX_PARALLEL_FRAG-1;
-- ID of current buffer
signal cur_buffer_id, cur_buffer_id_next : integer range 0 to MAX_PARALLEL_FRAG-1;
-- Signal used to reset the word counter
signal reset_read_cnt : std_logic;
-- Intermediate output signal
signal data_out_sig : std_logic_vector(31 downto 0);
-- Intermediate output signal, and its 1 clk cycle delayed variant
signal wr_sig, wr_sig_del : std_logic;
-- RAM Signals
signal buffer_addr, buffer_addr_next : unsigned(RAM_ADDR_WIDTH-1 downto 0);
signal buffer_wen, buffer_ren : std_logic;
signal buffer_wr_data, buffer_rd_data : std_logic_vector(31 downto 0);
-- Old RAM Address (Used to reset address on read error)
signal buffer_addr_old, buffer_addr_old_next : unsigned(RAM_ADDR_WIDTH-1 downto 0);
--*****ALIAS DEFINITION*****
--IPv4 HEADER
alias ip_version : std_logic_vector(3 downto 0) is data_in(31 downto 28);
alias ip_ihl : std_logic_vector(3 downto 0) is data_in(27 downto 24);
alias ip_dscp : std_logic_vector(5 downto 0) is data_in(23 downto 18);
alias ip_ecn : std_logic_vector(1 downto 0) is data_in(17 downto 16);
alias ip_length : std_logic_vector(15 downto 0) is data_in(15 downto 0);
alias ip_id : std_logic_vector(15 downto 0) is data_in(31 downto 16);
alias ip_DF_flag : std_logic is data_in(14);
alias ip_MF_flag : std_logic is data_in(13);
alias ip_frag_offset: std_logic_vector(12 downto 0) is data_in(12 downto 0);
alias ip_ttl : std_logic_vector(7 downto 0) is data_in(31 downto 24);
alias ip_protocol : std_logic_vector(7 downto 0) is data_in(23 downto 16);
alias ip_checksum : std_logic_vector(15 downto 0) is data_in(15 downto 0);
--*****FUNCTION DECLARATION*****
-- Compares all buffer identifiers with the last identifier. If there is a match, it returns the
-- array index of the buffer identifier that matches, else it returns the highest index (which is the
-- base of the comparisons)
function match_buffer_id (ids : BUFFER_ID_ARRAY) return integer is
variable id, max_id : integer := 0;
begin
max_id := ids'length - 1;
id := max_id;
for i in 0 to ids'length - 2 loop
if(ids(i) = ids(max_id)) then
id := i;
end if;
end loop;
return id;
end function;
-- Returns the last zero buffer id index
function empty_buffer_id (ids : BUFFER_ID_ARRAY) return integer is
variable id : integer := 0;
begin
id := ids'length - 1;
for i in 0 to ids'length - 2 loop
if(ids(i) = (ids(i)'reverse_range => '0')) then
id := i;
end if;
end loop;
return id;
end function;
-- Truncates the lower 2 bits of the input, and if they are not equal zero, adds one to the result.
-- This is used to round the byte length to 32-bit word length.
function normalize_length (len : std_logic_vector(15 downto 0)) return std_logic_vector is
variable tmp : std_logic_vector(13 downto 0) := (others => '0');
begin
tmp := len(15 downto 2);
if(len(1 downto 0) /= "00") then
tmp := std_logic_vector(unsigned(tmp) + to_unsigned(1, tmp'length));
end if;
return tmp;
end function;
begin
-- Generic Assertions
assert (MAX_PARALLEL_FRAG > 0) report "MAX_PARALLEL_FRAG has to be greater than 0" severity failure;
assert (MAX_FRAG_SIZE mod 4 = 0) report "MAX_FRAG_SIZE has to be multiple of 4" severity failure;
assert (MAX_FRAG_SIZE <= 65516) report "MAX_FRAG_SIZE has to be smaller or equal to 65516" severity error;
assert (DEFAULT_FRAG_TIMEOUT <= 255) report "DEFAULT_FRAG_TIMEOUT has to be smaller or equal to 255" severity error;
-- Generic Dependant Constant
-- Does not change its value after generation
-- TODO: Change to constant with function initialization
const_gen : for i in 0 to buffer_addr_offset'length-1 generate
buffer_addr_offset(i) <= to_unsigned(MAX_FRAG_SIZE*i,buffer_addr_offset(i)'length);
end generate;
--*****COMPONENT INSTANTIATION*****
ram_inst : single_port_ram
generic map (
ADDR_WIDTH => RAM_ADDR_WIDTH,
DATA_WIDTH => 32,
MEMORY_SIZE => MAX_PARALLEL_FRAG*MAX_FRAG_SIZE
)
port map (
clk => clk,
addr => std_logic_vector(buffer_addr),
wen => buffer_wen,
ren => buffer_ren,
wr_data => buffer_wr_data,
rd_data => buffer_rd_data
);
rd <= rd_sig;
-- TODO: Make only wr signal conditional and connect ALL output FIFOs to the Signal
-- This process is responsible for MUXing the correct signal to the correct output FIFO
output_prc : process(all)
begin
-- Reading from input FIFO
if(stage /= IPv4_BUFFER_PAYLOAD) then
data_out(output_id) <= data_out_sig;
wr(output_id) <= wr_sig;
-- Reading from RAM Buffer
else
data_out(output_id) <= buffer_rd_data;
-- The state machine toggles the 'wr_sig_del' on the first cycle when full is high
-- The and gate here prevents this from propagating to the actual fifo.
wr(output_id) <= wr_sig_del and (not full(output_id));
end if;
end process;
-- Main State Machine
-- STATE DESCRIPTION
-- IPv4_INIT Initial and idle state. Responsible for checking the buffer timers, and reading the packet length from the input FIFO
-- IPv4_HEADER_1 Parsing first word of IPv4 HEADER
-- IPv4_HEADER_2 Parsing second word of IPv4 HEADER
-- IPv4_HEADER_3 Parsing third word of IPv4 HEADER
-- IPv4_HEADER_4 Parsing fourth word of IPv4 HEADER and writing SRC Addr to output FIFO (if not a fragment)
-- IPv4_HEADER_5 Parsing fifth word of IPv4 HEADER and writing DEST Addr to output FIFO (if not a fragment)
-- IPv4_PAYLOAD_LENGTH Writing packet(payload) length to output FIFO
-- IPv4_PAYLOAD Writing of packet(payload) from input FIFO to output FIFO
-- IPv4_FRAGMENT_PRE Fragment pre-processing. Checking if fragment is part of already initiated packet re-assembly, initiating new packet re-assembly, or dropping packet if no resources available
-- IPv4_FRAGMENT Writing fragment from input FIFO to RAM buffer
-- IPv4_FRAGMENT_POST Fragment post-processing. Checking if received fragment completes the re-assembled packet.
-- IPv4_BUFFER_SRC Writing re-assembled packet(payload) SRC Address to output FIFO
-- IPv4_BUFFER_DEST Writing re-assembled packet(payload) DEST Address to output FIFO
-- IPv4_BUFFER_LENGTH Writing re-assembled packet(payload) length to output FIFO
-- IPv4_BUFFER_PAYLOAD Writing re-assembled packet(payload) from RAM to output FIFO
-- SKIP_HEADER Skip to beginning of Payload in input FIFO
-- SKIP_PACKET Skip to end of packet in input FIFO
parser_prc : process(all)
variable tmp : integer range 0 to max(MAX_PARALLEL_FRAG,LAYER3_PROTOCOL_NUM);
variable tmp_frag_offset : unsigned(buffer_addr'length-1 downto 0);
begin
--DEFAULT Registered
stage_next <= stage;
packet_length_next <= packet_length;
header_length_next <= header_length;
is_fragment_next <= is_fragment;
is_last_fragment_next <= is_last_fragment;
frag_offset_next <= frag_offset;
buffer_id_next <= buffer_id;
ttl_next <= ttl;
output_id_next <= output_id;
frag_size_next <= frag_size;
cur_buffer_id_next <= cur_buffer_id;
buffer_bitmap_next <= buffer_bitmap;
buffer_addr_next <= buffer_addr;
buffer_addr_old_next <= buffer_addr_old;
buffer_word_cnt_next <= buffer_word_cnt;
-- DEFAULT Unregistered
rd_sig <= '0';
wr_sig <= '0';
data_out_sig <= (others => '0');
reset_buffer_timer <= '0';
reset_buffer_timer_id <= 0;
buffer_wen <= '0';
buffer_ren <= '0';
buffer_wr_data <= (others => '0');
reset_read_cnt <= '0';
case(stage) is
-- Initial/Idle State
when IPv4_INIT =>
-- Reset packet Byte Counter
reset_read_cnt <= '1';
-- Check Buffer timers for timeouts
for i in 0 to MAX_PARALLEL_FRAG-1 loop
-- If timeout reached
if(buffer_timer(i) >= max_buffer_timer(i)) then
-- Free Buffer ID
buffer_id_next(i) <= (others => '0');
-- The rest of the Buffer related values are reset upon buffer selection
end if;
end loop;
-- Read Packet Length from input
if (empty = '0') then
packet_length_next <= unsigned(data_in(13 downto 0));
rd_sig <= '1';
-- Reset Parsing Flags
is_fragment_next <= '0';
is_last_fragment_next <= '0';
-- Begin Processing
stage_next <= IPv4_HEADER_1;
end if;
-- First IPv4 Header word (Fields: Version, IHL, DSCP, ECN, Total Length)
when IPv4_HEADER_1 =>
if (empty = '0') then
rd_sig <= '1';
-- Wrong IP Version or Packet Length mismatch, skip packet
if (ip_version /= "0100" or normalize_length(ip_length) /= std_logic_vector(packet_length)) then
stage_next <= SKIP_PACKET;
-- Store data and continue Parsing
else
header_length_next <= unsigned(ip_ihl);
stage_next <= IPv4_HEADER_2;
end if;
end if;
-- Second IPv4 Header word (Fields: Identification, Flags, Fragment Offset)
when IPv4_HEADER_2 =>
if (empty = '0') then
rd_sig <= '1';
-- No fragmentation, continue parsing normally
if (ip_MF_flag = '0' and ip_frag_offset = (ip_frag_offset'reverse_range => '0')) then
stage_next <= IPv4_HEADER_3;
-- IP Fragmentation
else
is_fragment_next <= '1';
frag_offset_next <= unsigned(std_logic_vector'(ip_frag_offset & "0")); --'std_logic_vector needed to avoid "Ambiguous type in infix expression"
-- Temporal storage for comparison
buffer_id_next(MAX_PARALLEL_FRAG)(15 downto 0) <= ip_id;
-- Needed to determine the total size of the reassembled packet
if (ip_MF_flag = '0') then
is_last_fragment_next <= '1';
end if;
-- Continue Parsing
stage_next <= IPv4_HEADER_3;
end if;
end if;
-- Third IPv4 Header word (Fields: TTL, Protocol, Checksum)
when IPv4_HEADER_3 =>
if (empty = '0') then
rd_sig <= '1';
-- Select Output according to Layer 3 Protocol
tmp := LAYER3_PROTOCOL_NUM;
for i in 0 to LAYER3_PROTOCOL_NUM-1 loop
if(ip_protocol = LAYER3_PROTOCOLS(i)) then
tmp := i;
end if;
end loop;
-- No Protocol match, skip packet
if (tmp = LAYER3_PROTOCOL_NUM) then
stage_next <= SKIP_PACKET;
-- Protocol Match, continue parsing
else
output_id_next <= tmp;
stage_next <= IPv4_HEADER_4;
end if;
-- Store Fragment relevant data
if (is_fragment = '1') then
ttl_next <= ip_ttl;
-- Temporal storage for comparison
buffer_id_next(MAX_PARALLEL_FRAG)(23 downto 16) <= ip_protocol;
end if;
end if;
-- Fourth IPv4 Header word (Fields: Src Address)
when IPv4_HEADER_4 =>
if (empty = '0') then
-- Store relevant fragment data
if (is_fragment = '1') then
rd_sig <= '1';
-- Temporal storage for comparison
buffer_id_next(MAX_PARALLEL_FRAG)(87 downto 56) <= data_in;
-- Write Src Address to Output FIFO
elsif (full(output_id) = '0') then
rd_sig <= '1';
wr_sig <= '1';
data_out_sig <= data_in;
end if;
-- In both cases, Continue Parsing
if (is_fragment = '1' or full(output_id) = '0') then
stage_next <= IPv4_HEADER_5;
end if;
end if;
-- Fourth IPv4 Header word (Fields: Dest Address)
when IPv4_HEADER_5 =>
if (empty = '0') then
-- Store relevant fragment data
if (is_fragment = '1') then
rd_sig <= '1';
-- Temporal storage for comparison
buffer_id_next(MAX_PARALLEL_FRAG)(55 downto 24) <= data_in;
-- Write Dst Address to Output FIFO
elsif (full(output_id) = '0') then
rd_sig <= '1';
wr_sig <= '1';
data_out_sig <= data_in;
end if;
-- In both cases
if (is_fragment = '1' or full(output_id) = '0') then
-- Check Header Size
if (read_cnt /= ("0000000000" & header_length)) then
-- If Header has "Options", skip to Payload
stage_next <= SKIP_HEADER;
else
if (is_fragment = '1') then
-- Fragment Processing
stage_next <= IPv4_FRAGMENT_PRE;
else
-- Payload Processing
stage_next <= IPv4_PAYLOAD_LENGTH;
end if;
end if;
end if;
end if;
-- Push Payload Length
when IPv4_PAYLOAD_LENGTH =>
-- Write Payload Length to Output FIFO
if (full(output_id) = '0') then
wr_sig <= '1';
data_out_sig(13 downto 0) <= std_logic_vector(packet_length - ("0000000000" & header_length));
stage_next <= IPv4_PAYLOAD;
end if;
-- Push Payload
when IPv4_PAYLOAD =>
-- Write to Output FIFO
if (empty = '0' and full(output_id) = '0') then
rd_sig <= '1';
wr_sig <= '1';
data_out_sig <= data_in;
-- End Of Packet
if (read_cnt = packet_length) then
-- Done, process next packet
stage_next <= IPv4_INIT;
end if;
end if;
-- Fragment Pre-Processing
when IPv4_FRAGMENT_PRE =>
tmp := match_buffer_id(buffer_id);
-- Existing Buffer ID Match (Fragment is part of packet in a buffer)
if(tmp /= MAX_PARALLEL_FRAG) then
-- Calculate buffer address
tmp_frag_offset := (others => '0');
tmp_frag_offset(13 downto 0) := frag_offset;
buffer_addr_next <= buffer_addr_offset(tmp) + tmp_frag_offset;
-- Reset timeout if needed
if (max_buffer_timer(tmp) < unsigned(ttl)) then
max_buffer_timer_next(tmp) <= unsigned(ttl);
end if;
-- Save Buffer ID
cur_buffer_id_next <= tmp;
-- Store Fragment
stage_next <= IPv4_FRAGMENT;
-- Fragment is not part of a packet in a buffer
else
-- Search for free buffer
tmp := empty_buffer_id(buffer_id);
-- Buffer Found
if (tmp /= MAX_PARALLEL_FRAG) then
-- Store Buffer ID
buffer_id_next(tmp) <= buffer_id(MAX_PARALLEL_FRAG);
-- Reset Bitmap
buffer_bitmap_next(tmp) <= (others => '0');
-- Reset Buffer Timer
reset_buffer_timer <= '1';
reset_buffer_timer_id <= tmp;
-- Reset Maximum Fragment Size
frag_size_next(tmp) <= (others => '0');
-- Reset fragment word counter
buffer_word_cnt_next(tmp) <= 0;
-- Calculate buffer address
tmp_frag_offset := (others => '0');
tmp_frag_offset(13 downto 0) := frag_offset;
buffer_addr_next <= buffer_addr_offset(tmp) + tmp_frag_offset;
-- Reset timeout if needed
if (max_buffer_timer(tmp) < unsigned(ttl)) then
max_buffer_timer_next(tmp) <= unsigned(ttl);
end if;
-- Save Buffer ID
cur_buffer_id_next <= tmp;
stage_next <= IPv4_FRAGMENT;
-- No buffer available, skip packet.
else
stage_next <= SKIP_PACKET;
end if;
end if;
-- Store Fragment
when IPv4_FRAGMENT =>
if (empty = '0') then
-- Re-assembled packet exceeds buffer resources or fragments included in packet already received
if (to_integer(frag_offset) > MAX_FRAG_SIZE or buffer_bitmap(cur_buffer_id)(to_integer(frag_offset)) = '0') then
-- Free Buffer ID
buffer_id_next(cur_buffer_id) <= (others => '0');
-- The rest of the Buffer related values are reset upon buffer selection
-- Skip Rest of Packet
stage_next <= SKIP_PACKET;
else
rd_sig <= '1';
-- Write Data to Buffer
buffer_wen <= '1';
buffer_wr_data <= data_in;
-- Mark BITMAP
buffer_bitmap_next(cur_buffer_id)(to_integer(frag_offset)) <= '1';
-- Increment Word Counter
buffer_word_cnt_next(cur_buffer_id) <= buffer_word_cnt(cur_buffer_id) + 1;
-- End of Packet
if (read_cnt = packet_length) then
-- If this is the last fragment, save total length
if (is_last_fragment = '1') then
frag_size_next(cur_buffer_id) <= std_logic_vector(frag_offset);
end if;
-- Continue with Post-Processing
stage_next <= IPv4_FRAGMENT_POST;
else
-- Increment Pointers
buffer_addr_next <= buffer_addr + 1;
frag_offset_next <= frag_offset + 1;
end if;
end if;
end if;
-- Fragment Postprocessing
when IPv4_FRAGMENT_POST =>
-- Check if BITMAP complete (All fragments of packet received)
-- Done by comparing the number of received fragment words to the expected size
-- 'frag_size' is either zero or the expected value, and 'buffer_word_cnt' is at least 1 when this state is entered
if (to_integer(unsigned(frag_size(cur_buffer_id))) = buffer_word_cnt(cur_buffer_id)) then
-- Reset Buffer Address
buffer_addr_next <= buffer_addr_offset(cur_buffer_id);
buffer_addr_old_next<= buffer_addr_offset(cur_buffer_id);
-- Reset read count
reset_read_cnt <= '1';
-- Write buffer packet to Output FIFO
stage_next <= IPv4_BUFFER_SRC;
else
-- Done, process next packet
stage_next <= IPv4_INIT;
end if;
-- Push Src Address
when IPv4_BUFFER_SRC =>
if (full(output_id) = '0') then
-- Write Packet Length
wr_sig <= '1';
data_out_sig <= buffer_id(MAX_PARALLEL_FRAG)(87 downto 56);
-- Next State
stage_next <= IPv4_BUFFER_DEST;
end if;
-- Push Dest Address
when IPv4_BUFFER_DEST =>
if (full(output_id) = '0') then
-- Write Packet Length
wr_sig <= '1';
data_out_sig <= buffer_id(MAX_PARALLEL_FRAG)(55 downto 24);
-- Next State
stage_next <= IPv4_BUFFER_LENGTH;
end if;
-- Push Payload Length
when IPv4_BUFFER_LENGTH =>
if (full(output_id) = '0') then
-- Write Packet Length
wr_sig <= '1';
data_out_sig(13 downto 0) <= frag_size(cur_buffer_id);
-- Next State
stage_next <= IPv4_BUFFER_PAYLOAD;
end if;
-- Push Payload from Buffer
when IPv4_BUFFER_PAYLOAD =>
-- Buffer read complete (End Of Packet)
if (buffer_addr_old = packet_length) then
-- Free Buffer ID
buffer_id_next(cur_buffer_id) <= (others => '0');
-- The rest of the Buffer related values are reset upon buffer selection
-- Done, process next packet
stage_next <= IPv4_INIT;
-- Buffer Read in progress
else
-- Write from Buffer to Output FIFO
if (full(output_id) = '0') then
-- Read, and write output next cycle
-- Note that 'wr_sig_del' is actually connected to the output fifo
wr_sig <= '1';
buffer_ren <= '1';
buffer_addr_next <= buffer_addr + 1;
buffer_addr_old_next<= buffer_addr;
-- Output Fifo Full, reset read position
else
-- Reset buffer address
buffer_addr_next <= buffer_addr_old;
end if;
end if;
-- Skip until beginning of payload
when SKIP_HEADER =>
if (empty = '0') then
rd_sig <= '1';
-- End of Header
if(read_cnt = header_length) then
if (is_fragment = '1') then
-- Fragment Processing
stage_next <= IPv4_FRAGMENT_PRE;
else
-- Payload Processing
stage_next <= IPv4_PAYLOAD;
end if;
end if;
end if;
-- Skip entire packet
when SKIP_PACKET =>
if (empty = '0') then
rd_sig <= '1';
-- End of Packet
if(read_cnt = packet_length) then
-- Continue parsing next packet
stage_next <= IPv4_INIT;
end if;
end if;
when others =>
null;
end case;
end process;
-- Process responsible for the buffer timers
-- The timers increment each second until they reach the maximum value, and stay there until a reset
buffer_timer_prc : process(sec_clk)
begin
if rising_edge(sec_clk) then
-- Reset timer specified by id
if(reset_buffer_timer = '0') then
buffer_timer(reset_buffer_timer_id) <= (others => '0');
else
-- Increment Timers
for i in 0 to MAX_PARALLEL_FRAG-1 loop
-- Timers stay at highest value until reset
if(buffer_timer(i) /= (buffer_timer(i)'reverse_range => '1')) then
buffer_timer(i) <= buffer_timer(i) + 1;
end if;
end loop;
end if;
end if;
end process;
-- Process responsible for counting read words
-- This process uses the actual RAM and FIFO read signals to determine reads
word_counter_prc : process(clk, reset)
begin
if rising_edge(clk) then
-- Reset Read counter
if (reset = '1' or reset_read_cnt = '1') then
read_cnt <= to_unsigned(1, read_cnt'length);
-- Increment read counter each time rd is high
elsif (rd_sig = '1' or buffer_ren = '1') then
read_cnt <= read_cnt + to_unsigned(1, read_cnt'length);
end if;
end if;
end process;
-- 1 Hz Clock Generator
sec_clk_prc : process(clk, reset)
begin
if rising_edge(clk) then
if (reset = '1') then
sec_cnt <= 0;
sec_clk <= '0';
else
sec_cnt <= sec_cnt + 1;
-- Toggle sec_clk every second
if (sec_cnt = (CLK_FREQ/2)-1) then
sec_clk <= not sec_clk;
end if;
end if;
end if;
end process;
-- State Machine Sync Process (Registers)
sync : process(clk)
begin
if rising_edge(clk) then
if (reset = '1') then
stage <= IPv4_INIT;
packet_length <= (others => '0');
header_length <= (others => '0');
is_fragment <= '0';
is_last_fragment <= '0';
frag_offset <= (others => '0');
buffer_id <= (others => (others => '0'));
ttl <= (others => '0');
output_id <= 0;
frag_size <= (others => (others => '0'));
cur_buffer_id <= 0;
buffer_bitmap <= (others => (others => '0'));
buffer_addr <= (others => '0');
buffer_addr_old <= (others => '0');
wr_sig_del <= '0';
buffer_word_cnt <= (others => 0);
else
stage <= stage_next;
packet_length <= packet_length_next;
header_length <= header_length_next;
is_fragment <= is_fragment_next;
is_last_fragment <= is_last_fragment_next;
frag_offset <= frag_offset_next;
buffer_id <= buffer_id;
ttl <= ttl_next;
output_id <= output_id_next;
frag_size <= frag_size_next;
cur_buffer_id <= cur_buffer_id_next;
buffer_bitmap <= buffer_bitmap_next;
buffer_addr <= buffer_addr_next;
buffer_addr_old <= buffer_addr_old_next;
wr_sig_del <= wr_sig;
buffer_word_cnt <= buffer_word_cnt_next;
end if;
end if;
end process;
end architecture;
architecture no_frag of ipv4_in_handler is
--*****TYPE DECLARATION*****
-- FSM states. Explained below in detail
type PARSER_STAGE_TYPE is (IPv4_INIT, IPv4_HEADER_1, IPv4_HEADER_2, IPv4_HEADER_3, IPv4_HEADER_4,
IPv4_HEADER_5, IPv4_PAYLOAD_LENGTH, IPv4_PAYLOAD, SKIP_HEADER, SKIP_PACKET);
--*****SIGNAL DECLARATION*****
-- FSM state
signal stage, stage_next : PARSER_STAGE_TYPE;
-- 32-bit aligned total packet length
signal packet_length, packet_length_next : unsigned(13 downto 0);
-- 32-bit aligned header length
signal header_length, header_length_next : unsigned(3 downto 0);
-- 32-bit word counter (Counts words read from input fifo)
signal read_cnt : unsigned(13 downto 0);
-- Intermediate input read signal. (Read from output port not allowed)
signal rd_sig : std_logic;
-- ID of current output fifo (Used to MUX output FIFOs)
signal output_id, output_id_next : integer range 0 to LAYER3_PROTOCOL_NUM-1;
-- Signal used to reset the word counter
signal reset_read_cnt : std_logic;
-- Intermediate output signal
signal data_out_sig : std_logic_vector(31 downto 0);
-- Intermediate output signal, and its 1 clk cycle delayed variant
signal wr_sig : std_logic;
--*****ALIAS DEFINITION*****
--IPv4 HEADER
alias ip_version : std_logic_vector(3 downto 0) is data_in(31 downto 28);
alias ip_ihl : std_logic_vector(3 downto 0) is data_in(27 downto 24);
alias ip_dscp : std_logic_vector(5 downto 0) is data_in(23 downto 18);
alias ip_ecn : std_logic_vector(1 downto 0) is data_in(17 downto 16);
alias ip_length : std_logic_vector(15 downto 0) is data_in(15 downto 0);
alias ip_id : std_logic_vector(15 downto 0) is data_in(31 downto 16);
alias ip_DF_flag : std_logic is data_in(14);
alias ip_MF_flag : std_logic is data_in(13);
alias ip_frag_offset: std_logic_vector(12 downto 0) is data_in(12 downto 0);
alias ip_ttl : std_logic_vector(7 downto 0) is data_in(31 downto 24);
alias ip_protocol : std_logic_vector(7 downto 0) is data_in(23 downto 16);
alias ip_checksum : std_logic_vector(15 downto 0) is data_in(15 downto 0);
--*****FUNCTION DECLARATION*****
-- Truncates the lower 2 bits of the input, and if they are not equal zero, adds one to the result.
-- This is used to round the byte length to 32-bit word length.
function normalize_length (len : std_logic_vector(15 downto 0)) return std_logic_vector is
variable tmp : std_logic_vector(13 downto 0) := (others => '0');
begin
tmp := len(15 downto 2);
if(len(1 downto 0) /= "00") then
tmp := std_logic_vector(unsigned(tmp) + to_unsigned(1, tmp'length));
end if;
return tmp;
end function;
begin
rd <= rd_sig;
-- This process is responsible for MUXing the correct signal to the correct output FIFO
output_prc : process(all)
begin
data_out(output_id) <= data_out_sig;
wr(output_id) <= wr_sig;
end process;
-- Main State Machine
-- STATE DESCRIPTION
-- IPv4_INIT Initial and idle state. Responsible for checking the buffer timers, and reading the packet length from the input FIFO
-- IPv4_HEADER_1 Parsing first word of IPv4 HEADER
-- IPv4_HEADER_2 Parsing second word of IPv4 HEADER
-- IPv4_HEADER_3 Parsing third word of IPv4 HEADER
-- IPv4_HEADER_4 Parsing fourth word of IPv4 HEADER and writing SRC Addr to output FIFO (if not a fragment)
-- IPv4_HEADER_5 Parsing fifth word of IPv4 HEADER and writing DEST Addr to output FIFO (if not a fragment)
-- IPv4_PAYLOAD_LENGTH Writing packet(payload) length to output FIFO
-- IPv4_PAYLOAD Writing of packet(payload) from input FIFO to output FIFO
-- SKIP_HEADER Skip to beginning of Payload in input FIFO
-- SKIP_PACKET Skip to end of packet in input FIFO
parser_prc : process(all)
variable tmp : integer range 0 to max(MAX_PARALLEL_FRAG,LAYER3_PROTOCOL_NUM);
begin
--DEFAULT Registered
stage_next <= stage;
packet_length_next <= packet_length;
header_length_next <= header_length;
output_id_next <= output_id;
-- DEFAULT Unregistered
rd_sig <= '0';
wr_sig <= '0';
data_out_sig <= (others => '0');
reset_read_cnt <= '0';
case(stage) is
-- Initial/Idle State
when IPv4_INIT =>
-- Reset packet Byte Counter
reset_read_cnt <= '1';
-- Read Packet Length from input
if (empty = '0') then
packet_length_next <= unsigned(data_in(13 downto 0));
rd_sig <= '1';
-- Begin Processing
stage_next <= IPv4_HEADER_1;
end if;
-- First IPv4 Header word (Fields: Version, IHL, DSCP, ECN, Total Length)
when IPv4_HEADER_1 =>
if (empty = '0') then
rd_sig <= '1';
-- Wrong IP Version or Packet Length mismatch, skip packet
if (ip_version /= "0100" or normalize_length(ip_length) /= std_logic_vector(packet_length)) then
stage_next <= SKIP_PACKET;
-- Store data and continue Parsing
else
header_length_next <= unsigned(ip_ihl);
stage_next <= IPv4_HEADER_2;
end if;
end if;
-- Second IPv4 Header word (Fields: Identification, Flags, Fragment Offset)
when IPv4_HEADER_2 =>
if (empty = '0') then
rd_sig <= '1';
-- No fragmentation, continue parsing normally
if (ip_MF_flag = '0' and ip_frag_offset = (ip_frag_offset'reverse_range => '0')) then
stage_next <= IPv4_HEADER_3;
-- IP Fragmentation
else
-- Fragmentation Reassembly disabled, skip packet
stage_next <= SKIP_PACKET;
end if;
end if;
-- Third IPv4 Header word (Fields: TTL, Protocol, Checksum)
when IPv4_HEADER_3 =>
if (empty = '0') then
rd_sig <= '1';
-- Select Output according to Layer 3 Protocol
tmp := LAYER3_PROTOCOL_NUM;
for i in 0 to LAYER3_PROTOCOL_NUM-1 loop
if(ip_protocol = LAYER3_PROTOCOLS(i)) then
tmp := i;
end if;
end loop;
-- No Protocol match, skip packet
if (tmp = LAYER3_PROTOCOL_NUM) then
stage_next <= SKIP_PACKET;
-- Protocol Match, continue parsing
else
output_id_next <= tmp;
stage_next <= IPv4_HEADER_4;
end if;
end if;
-- Fourth IPv4 Header word (Fields: Src Address)
when IPv4_HEADER_4 =>
if (empty = '0') then
-- Write Src Address to Output FIFO
if (full(output_id) = '0') then
rd_sig <= '1';
wr_sig <= '1';
data_out_sig <= data_in;
-- Continue parsing
stage_next <= IPv4_HEADER_5;
end if;
end if;
-- Fourth IPv4 Header word (Fields: Dest Address)
when IPv4_HEADER_5 =>
if (empty = '0') then
-- Write Dst Address to Output FIFO
if (full(output_id) = '0') then
rd_sig <= '1';
wr_sig <= '1';
data_out_sig <= data_in;
-- Check Header Size
if (read_cnt /= ("0000000000" & header_length)) then
-- If Header has "Options", skip to Payload
stage_next <= SKIP_HEADER;
else
-- Payload Processing
stage_next <= IPv4_PAYLOAD_LENGTH;
end if;
end if;
end if;
-- Push Payload Length
when IPv4_PAYLOAD_LENGTH =>
-- Write Payload Length to Output FIFO
if (full(output_id) = '0') then
wr_sig <= '1';
data_out_sig(13 downto 0) <= std_logic_vector(packet_length - ("0000000000" & header_length));
stage_next <= IPv4_PAYLOAD;
end if;
-- Push Payload
when IPv4_PAYLOAD =>
-- Write to Output FIFO
if (empty = '0' and full(output_id) = '0') then
rd_sig <= '1';
wr_sig <= '1';
data_out_sig <= data_in;
-- End Of Packet
if (read_cnt = packet_length) then
-- Done, process next packet
stage_next <= IPv4_INIT;
end if;
end if;
-- Skip until beginning of payload
when SKIP_HEADER =>
if (empty = '0') then
rd_sig <= '1';
-- End of Header
if(read_cnt = header_length) then
-- Payload Processing
stage_next <= IPv4_PAYLOAD;
end if;
end if;
-- Skip entire packet
when SKIP_PACKET =>
if (empty = '0') then
rd_sig <= '1';
-- End of Packet
if(read_cnt = packet_length) then
-- Continue parsing next packet
stage_next <= IPv4_INIT;
end if;
end if;
when others =>
null;
end case;
end process;
-- Process responsible for counting read words
-- This process uses the actual RAM and FIFO read signals to determine reads
word_counter_prc : process(clk, reset)
begin
if rising_edge(clk) then
-- Reset Read counter
if (reset = '1' or reset_read_cnt = '1') then
read_cnt <= to_unsigned(1, read_cnt'length);
-- Increment read counter each time rd is high
elsif (rd_sig = '1') then
read_cnt <= read_cnt + to_unsigned(1, read_cnt'length);
end if;
end if;
end process;
-- State Machine Sync Process (Registers)
sync : process(clk)
begin
if rising_edge(clk) then
if (reset = '1') then
stage <= IPv4_INIT;
packet_length <= (others => '0');
header_length <= (others => '0');
output_id <= 0;
else
stage <= stage_next;
packet_length <= packet_length_next;
header_length <= header_length_next;
output_id <= output_id_next;
end if;
end if;
end process;
end architecture;