From 10cda546bf0e9be3b74d412eb15a5b6980066402 Mon Sep 17 00:00:00 2001 From: Greek Date: Wed, 13 May 2020 13:37:23 +0200 Subject: [PATCH] * Add documentation - IPv4 RFC - FPGA Network Stack Master Thesis * Updated .gitignore * Added Single Port RAM - Xillinx Specific * Added IPv4 Parser - Dynamic Re-assembly Buffer selection - Main entity documentation missing - Synthesized, but not tested or simulated * Added Vivado (Zedboard) project for synthesis testing --- .gitignore | 14 + doc/TFM - MEMORY .pdf | 3 + doc/rfc791.pdf | 3 + doc/rfc815.pdf | 3 + src/ip_package.vhd | 18 + src/ipv4_in.vhd | 729 ++++++++++++++++++++++++++++++++++++++++ src/math_pkg.vhd | 58 ++++ src/single_port_ram.vhd | 66 ++++ src/top.xdc | 6 + syn/project_1.xpr | 208 ++++++++++++ 10 files changed, 1108 insertions(+) create mode 100644 doc/TFM - MEMORY .pdf create mode 100644 doc/rfc791.pdf create mode 100644 doc/rfc815.pdf create mode 100644 src/ip_package.vhd create mode 100644 src/ipv4_in.vhd create mode 100644 src/math_pkg.vhd create mode 100644 src/single_port_ram.vhd create mode 100644 src/top.xdc create mode 100644 syn/project_1.xpr diff --git a/.gitignore b/.gitignore index e69de29..b5c410c 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,14 @@ +#Ignore List +/syn/** +/modelsim/** +/download/** + + +#Unignore Directories (Needed to unignore files in Subdirectories) +!*/ + +#WHITELIST +#Vivado Project File +!*.xpr +#Modelsim Do files +!*.do \ No newline at end of file diff --git a/doc/TFM - MEMORY .pdf b/doc/TFM - MEMORY .pdf new file mode 100644 index 0000000..a46f22f --- /dev/null +++ b/doc/TFM - MEMORY .pdf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e4f632c819912abb5e4ebe4f6e975b0d19b58f761da9f820c2710f89a03bf127 +size 1344794 diff --git a/doc/rfc791.pdf b/doc/rfc791.pdf new file mode 100644 index 0000000..7332c10 --- /dev/null +++ b/doc/rfc791.pdf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:28266bd52148aa3b6f3ab8bde2606eb4f5ce1efb939189896dce31cf7240348d +size 85519 diff --git a/doc/rfc815.pdf b/doc/rfc815.pdf new file mode 100644 index 0000000..82ac9a9 --- /dev/null +++ b/doc/rfc815.pdf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d346beaaf4dd177a844bd8a8199abacf98e386fb0cff8ec4fe6bf92b51fb0f18 +size 13924 diff --git a/src/ip_package.vhd b/src/ip_package.vhd new file mode 100644 index 0000000..b5274b2 --- /dev/null +++ b/src/ip_package.vhd @@ -0,0 +1,18 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +package ip_package is + + -- TODO : Fix assignemnt of 1 wide array + constant LAYER3_PROTOCOL_NUM : integer := 1; + type LAYER3_PROTOCOL_TYPE is array (LAYER3_PROTOCOL_NUM-1 downto 0) of std_logic_vector(7 downto 0); + constant LAYER3_PROTOCOLS : LAYER3_PROTOCOL_TYPE := (0 => x"11"); + + --OUTPUT TYPES + type IP_OUTPUT_TYPE is array (LAYER3_PROTOCOL_NUM-1 downto 0) of std_logic_vector(31 downto 0); + +end package; + +package body ip_package is +end package body; \ No newline at end of file diff --git a/src/ipv4_in.vhd b/src/ipv4_in.vhd new file mode 100644 index 0000000..ea23657 --- /dev/null +++ b/src/ipv4_in.vhd @@ -0,0 +1,729 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.ip_package.all; +use work.math_pkg.all; + +entity ipv4_in is + generic( + CLK_FREQ : integer := 20000000; + MAX_FRAG_SIZE : integer := 1600; + MAX_PARALLEL_FRAG : integer := 1; + DEFAULT_FRAG_TIMEOUT: integer := 5 + ); + port ( + clk : in std_logic; + reset : in std_logic; + empty : in std_logic; + data_in : in std_logic_vector(31 downto 0); + rd : out std_logic; + wr : out std_logic_vector(LAYER3_PROTOCOL_NUM-1 downto 0); + full : in std_logic_vector(LAYER3_PROTOCOL_NUM-1 downto 0); + data_out: out IP_OUTPUT_TYPE + ); +end entity; + +architecture arch of ipv4_in 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 delow 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(MAX_PARALLEL_FRAG-1,0) 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(MAX_PARALLEL_FRAG-1,0) 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(MAX_PARALLEL_FRAG-1,0) downto 0) of unsigned(RAM_ADDR_WIDTH-1 downto 0); + -- Array of packet sizes in buffers + type FRAG_SIZE_ARRAY is array (max(MAX_PARALLEL_FRAG-1,0) 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-assebled + type BUFFER_WORD_COUNTER_ARRAY is array (max(MAX_PARALLEL_FRAG-1,0) downto 0) of integer range 0 to (MAX_FRAG_SIZE/4); + + + --*****SIGNAL DECLARATION***** + -- FSM state + signal stage, stage_next : PARSER_STAGE_TYPE := IPv4_INIT; + -- 32-bit aligned total packet length + signal packet_length, packet_length_next : unsigned(13 downto 0) := (others => '0'); + -- 32-bit aligned header length + signal header_length, header_length_next : unsigned(3 downto 0) := (others => '0'); + -- 32-bit word counter (Counts words read from input fifo) + signal read_cnt : unsigned(13 downto 0) := (others => '0'); + -- Intermediate input read signal. (Read from output port not allowed) + signal rd_sig : std_logic := '0'; + -- States if the current processed packet is a fragment + signal is_fragment, is_fragment_next : std_logic := '0'; + -- 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 := '0'; + -- Clock with 1 Hz Frequency + signal sec_clk : std_logic := '0'; + -- Counter used to generate 'sec_clk' + signal sec_cnt : integer range 0 to (CLK_FREQ/2)-1 := 0; + -- ID of current output fifo (Used to MUX output FIFOs) + signal output_id, output_id_next : integer range 0 to LAYER3_PROTOCOL_NUM-1 := 0; + -- Buffer Identifiers for each buffer. See 'BUFFER_ID_ARRAY' + signal buffer_id, buffer_id_next : BUFFER_ID_ARRAY := (others => (others => '0')); + -- Used as Constant. See 'BUFFER_ADDR_OFFSET_TYPE' + signal buffer_addr_offset : BUFFER_ADDR_OFFSET_TYPE := (others => (others => '0')); + -- Bitmap array. See 'BUFFER_BITMAP_ARRAY' + signal buffer_bitmap, buffer_bitmap_next : BUFFER_BITMAP_ARRAY := (others => (others => '0')); + -- 32-bit word aligned fragment offset + signal frag_offset, frag_offset_next : unsigned(13 downto 0) := (others => '0'); + -- Packet size array. See 'FRAG_SIZE_ARRAY' + signal frag_size, frag_size_next : FRAG_SIZE_ARRAY := (others => (others => '0')); + -- 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 := (others => (others => '0')); + -- Received fragment word counters. See BUFFER_WORD_COUNTER_ARRAY + signal buffer_word_cnt, buffer_word_cnt_next : BUFFER_WORD_COUNTER_ARRAY := (others => 0); + -- Timeout values of buffer timers + signal max_buffer_timer, max_buffer_timer_next : BUFFER_TIMER_ARRAY := (others => (others => '0')); + -- Signal used to reset buffer timers + signal reset_buffer_timer : std_logic := '0'; + -- ID of buffer timer to be reset + signal reset_buffer_timer_id : integer range 0 to max(MAX_PARALLEL_FRAG-1,0) := 0; + -- ID of current buffer + signal cur_buffer_id, cur_buffer_id_next : integer range 0 to max(MAX_PARALLEL_FRAG-1,0) := 0; + -- 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) := (others => '0'); + signal buffer_wen, buffer_ren : std_logic := '0'; + signal buffer_wr_data, buffer_rd_data : std_logic_vector(31 downto 0) := (others => '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) := (others => '0'); + + + --*****ALIAS DEFINATION***** + --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 positive" 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 + 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***** + buffer_gen : if (MAX_PARALLEL_FRAG /= 0) generate + 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 + ); + end generate; + + rd <= rd_sig; + + -- 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 DESCRIPSION + -- 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 packetre-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) := 0; + variable tmp_frag_offset : unsigned(buffer_addr'length-1 downto 0) := (others => '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(MAX_PARALLEL_FRAG-1,0) 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 missmatch, 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 normaly + 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 + if (MAX_PARALLEL_FRAG = 0) then + stage_next <= SKIP_PACKET; + -- Store Fragment relevant data + 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; + 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 cntouer + 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(MAX_PARALLEL_FRAG-1,0) 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; \ No newline at end of file diff --git a/src/math_pkg.vhd b/src/math_pkg.vhd new file mode 100644 index 0000000..8b37393 --- /dev/null +++ b/src/math_pkg.vhd @@ -0,0 +1,58 @@ +package math_pkg is + -- calculates the logarithm dualis of the operand and rounds up + -- the result to the next integer value. + function log2c(constant value : in integer) return integer; + -- returns the maximum of the two operands + function max(constant value1, value2 : in integer) return integer; + -- returns the maximum of the three operands + function max(constant value1, value2, value3 : in integer) return integer; + -- returns the minimum of the two operands + function min(constant value1, value2 : in integer) return integer; +end package; + + +package body math_pkg is + + --*****FUNCTION DEFINITION***** + + function log2c(constant value : in integer) return integer is + variable ret_value : integer; + variable cur_value : integer; + begin + ret_value := 0; + cur_value := 1; + + while cur_value < value loop + ret_value := ret_value + 1; + cur_value := cur_value * 2; + end loop; + return ret_value; + end function; + + function max(constant value1, value2 : in integer) return integer is + variable ret_value : integer; + begin + if value1 > value2 then + ret_value := value1; + else + ret_value := value2; + end if; + return ret_value; + end function; + + function max(constant value1, value2, value3 : in integer) return integer is + begin + return max(max(value1, value2), value3); + end function; + + function min(constant value1, value2 : in integer) return integer is + variable ret_value : integer; + begin + if value1 < value2 then + ret_value := value1; + else + ret_value := value2; + end if; + return ret_value; + end function; + end package body; \ No newline at end of file diff --git a/src/single_port_ram.vhd b/src/single_port_ram.vhd new file mode 100644 index 0000000..8dd05f9 --- /dev/null +++ b/src/single_port_ram.vhd @@ -0,0 +1,66 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +Library xpm; +use xpm.vcomponents.all; + +entity 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 entity; + +architecture arch of single_port_ram is + +begin + + xpm_memory_spram_inst : xpm_memory_spram + generic map ( + ADDR_WIDTH_A => ADDR_WIDTH, + AUTO_SLEEP_TIME => 0, + BYTE_WRITE_WIDTH_A => DATA_WIDTH, + ECC_MODE => "no_ecc", + MEMORY_INIT_FILE => "none", + MEMORY_INIT_PARAM => "0", + MEMORY_OPTIMIZATION => "true", + MEMORY_PRIMITIVE => "auto", + MEMORY_SIZE => DATA_WIDTH*(2**ADDR_WIDTH), + MESSAGE_CONTROL => 0, + READ_DATA_WIDTH_A => DATA_WIDTH, + READ_LATENCY_A => 1, + READ_RESET_VALUE_A => "0", + RST_MODE_A => "SYNC", + USE_MEM_INIT => 1, + WAKEUP_TIME => "disable_sleep", + WRITE_DATA_WIDTH_A => DATA_WIDTH, + WRITE_MODE_A => "read_first" + ) + port map ( + dbiterra => open, + douta => rd_data, + sbiterra => open, + addra => addr, + clka => clk, + dina => wr_data, + ena => (ren or wen), + injectdbiterra => '0', + injectsbiterra => '0', + regcea => '1', + rsta => '0', + sleep => '0', + wea => (others => wen) --1-bit Vector + ); + +end architecture; \ No newline at end of file diff --git a/src/top.xdc b/src/top.xdc new file mode 100644 index 0000000..c09e14d --- /dev/null +++ b/src/top.xdc @@ -0,0 +1,6 @@ +#100 Mhz +create_clock -period 10.000 -name sys_clk -waveform {0.000 5.000} [get_ports clk] +# 166 Mhz +#create_clock -period 6.000 -name sys_clk -waveform {0.000 3.000} [get_ports clk] +# 200 Mhz +#create_clock -period 5.000 -name sys_clk -waveform {0.000 2.500} [get_ports clk] \ No newline at end of file diff --git a/syn/project_1.xpr b/syn/project_1.xpr new file mode 100644 index 0000000..18cbe54 --- /dev/null +++ b/syn/project_1.xpr @@ -0,0 +1,208 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Vivado Synthesis Defaults + + + + + + + + + + + Default settings for Implementation. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + default_dashboard + + + + + +