-- 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;