diff --git a/libraries/io/eth/hdllib.cfg b/libraries/io/eth/hdllib.cfg index 445b27d5158cf1b8159b91d1e4ef6ddf36af65ff..2c2d0d2e75764aaa9141fd42a142a327023bcca8 100644 --- a/libraries/io/eth/hdllib.cfg +++ b/libraries/io/eth/hdllib.cfg @@ -8,18 +8,18 @@ build_synth_dir = synth_files = src/vhdl/eth_pkg.vhd $UNB/Firmware/modules/tse/src/vhdl/eth_checksum.vhd - $UNB/Firmware/modules/tse/src/vhdl/eth_hdr_store.vhd - $UNB/Firmware/modules/tse/src/vhdl/eth_hdr_status.vhd - $UNB/Firmware/modules/tse/src/vhdl/eth_hdr_ctrl.vhd + eth_hdr_store.vhd + eth_hdr_status.vhd + eth_hdr_ctrl.vhd $UNB/Firmware/modules/tse/src/vhdl/eth_hdr.vhd $UNB/Firmware/modules/tse/src/vhdl/eth_crc_ctrl.vhd $UNB/Firmware/modules/tse/src/vhdl/eth_crc_word.vhd $UNB/Firmware/modules/tse/src/vhdl/eth_mm_registers.vhd - $UNB/Firmware/modules/tse/src/vhdl/eth_mm_reg_frame.vhd + eth_mm_reg_frame.vhd $UNB/Firmware/modules/tse/src/vhdl/eth_udp_channel.vhd $UNB/Firmware/modules/tse/src/vhdl/eth_buffer.vhd - $UNB/Firmware/modules/tse/src/vhdl/eth_control.vhd - $UNB/Firmware/modules/tse/src/vhdl/eth_ihl_to_20.vhd + eth_control.vhd + eth_ihl_to_20.vhd src/vhdl/eth.vhd src/vhdl/avs_eth.vhd @@ -32,4 +32,4 @@ test_bench_files = tb/vhdl/tb_eth.vhd tb/vhdl/tb_tb_eth.vhd tb/vhdl/tb_eth_udp_offload.vhd - $UNB/Firmware/modules/tse/tb/vhdl/tb_eth_ihl_to_20.vhd + tb/vhdl/tb_eth_ihl_to_20.vhd diff --git a/libraries/io/eth/src/vhdl/eth_control.vhd b/libraries/io/eth/src/vhdl/eth_control.vhd new file mode 100644 index 0000000000000000000000000000000000000000..912840e7f78ed2d4c1232ef36f56d641e01b86f9 --- /dev/null +++ b/libraries/io/eth/src/vhdl/eth_control.vhd @@ -0,0 +1,366 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib, dp_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; +USE dp_lib.dp_stream_pkg.ALL; +USE common_lib.eth_layers_pkg.ALL; +USE work.eth_pkg.ALL; + +ENTITY eth_control IS + PORT ( + -- Clocks and reset + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + + -- Control registers + reg_config : IN t_eth_mm_reg_config; + reg_control : IN t_eth_mm_reg_control; + reg_continue_wr : IN STD_LOGIC; -- used to know that control reg has been written and ETH module can continue from TX_PENDING state + reg_status : OUT t_eth_mm_reg_status; + reg_status_wr : IN STD_LOGIC; -- used to know that status reg has been read and can be cleared to remove the sla_interrupt + + -- Streaming sink Rx frame + -- . The rcv_rd, rcv_ack and rcv_done act instead of rcv_out.ready to have rcv_out ready per frame instead of per data word + rcv_rd : OUT STD_LOGIC; + rcv_ack : IN STD_LOGIC; + rcv_done : IN STD_LOGIC; + rcv_in : IN t_dp_sosi; -- packet memory is always ready for packet data + rcv_hdr_words : IN t_eth_total_header_arr; + rcv_hdr_status : IN t_eth_hdr_status; + + -- Streaming source Tx frame + xmt_in : IN t_dp_siso; + xmt_out : OUT t_dp_sosi; + + -- MM frame memory + mem_in : OUT t_mem_mosi; + mem_out : IN t_mem_miso + ); +END eth_control; + + +ARCHITECTURE rtl OF eth_control IS + + -- Internal source ready latency of this component + CONSTANT c_this_src_latency : NATURAL := 1; -- xmt_in, xmt_out + + TYPE t_state_enum IS ( + s_idle, + s_rx_request, + s_rx_frame, + s_hdr_response, + s_tx_pending, + s_tx_frame, + s_tx_done + ); + + SIGNAL nxt_rcv_rd : STD_LOGIC; + SIGNAL rcv_in_eop_dly : STD_LOGIC; + + SIGNAL hdr_response : t_eth_total_header_arr; + SIGNAL nxt_hdr_response : t_eth_total_header_arr; + SIGNAL hdr_word_cnt : NATURAL RANGE 0 TO c_eth_total_header_nof_words := c_eth_total_header_nof_words; + SIGNAL nxt_hdr_word_cnt : NATURAL; + SIGNAL hdr_en : STD_LOGIC; + SIGNAL nxt_hdr_en : STD_LOGIC; + SIGNAL hdr_done : STD_LOGIC; + SIGNAL nxt_hdr_done : STD_LOGIC; + + SIGNAL tx_insert : STD_LOGIC; + SIGNAL nxt_tx_insert : STD_LOGIC; + + SIGNAL i_reg_status : t_eth_mm_reg_status; + SIGNAL nxt_reg_status : t_eth_mm_reg_status; + + SIGNAL xmt_start : STD_LOGIC; + SIGNAL nxt_xmt_start : STD_LOGIC; + SIGNAL xmt_en : STD_LOGIC; + SIGNAL nxt_xmt_en : STD_LOGIC; + SIGNAL xmt_done : STD_LOGIC; + SIGNAL nxt_xmt_done : STD_LOGIC; + SIGNAL xmt_word_cnt : NATURAL RANGE 0 TO c_eth_frame_nof_words; + SIGNAL nxt_xmt_word_cnt : NATURAL; + SIGNAL tx_nof_words : STD_LOGIC_VECTOR(c_eth_frame_nof_words_w-1 DOWNTO 0); + SIGNAL nxt_tx_nof_words : STD_LOGIC_VECTOR(c_eth_frame_nof_words_w-1 DOWNTO 0); + SIGNAL tx_empty : STD_LOGIC_VECTOR(c_eth_empty_w-1 DOWNTO 0); + SIGNAL nxt_tx_empty : STD_LOGIC_VECTOR(c_eth_empty_w-1 DOWNTO 0); + + SIGNAL i_mem_in : t_mem_mosi; + SIGNAL nxt_mem_in : t_mem_mosi; + SIGNAL rd_val : STD_LOGIC_VECTOR(0 TO c_mem_ram_rd_latency); -- use [0] to combinatorially store rd (= rd_en) + SIGNAL nxt_rd_val : STD_LOGIC_VECTOR(1 TO c_mem_ram_rd_latency); + SIGNAL rd_sop : STD_LOGIC_VECTOR( rd_val'RANGE); + SIGNAL nxt_rd_sop : STD_LOGIC_VECTOR(nxt_rd_val'RANGE); + SIGNAL rd_eop : STD_LOGIC_VECTOR( rd_val'RANGE); + SIGNAL nxt_rd_eop : STD_LOGIC_VECTOR(nxt_rd_val'RANGE); + + SIGNAL xmt_siso : t_dp_siso; + SIGNAL xmt_sosi : t_dp_sosi; + + SIGNAL i_xmt_out : t_dp_sosi; + + -- ST->MM state machine + SIGNAL state : t_state_enum; + SIGNAL nxt_state : t_state_enum; + +BEGIN + + reg_status <= i_reg_status; + mem_in <= i_mem_in; + xmt_out <= i_xmt_out; + + p_clk : PROCESS(rst, clk) + BEGIN + IF rst='1' THEN + rcv_rd <= '0'; + rcv_in_eop_dly <= '0'; + hdr_word_cnt <= c_eth_total_header_nof_words; + hdr_en <= '0'; + hdr_done <= '0'; + tx_insert <= '0'; + xmt_start <= '0'; + i_reg_status <= c_eth_mm_reg_status_rst; + tx_nof_words <= (OTHERS=>'0'); + tx_empty <= (OTHERS=>'0'); + xmt_word_cnt <= 0; + xmt_en <= '0'; + xmt_done <= '0'; + i_mem_in <= c_mem_mosi_rst; + rd_val(nxt_rd_val'RANGE) <= (OTHERS=>'0'); + rd_sop(nxt_rd_val'RANGE) <= (OTHERS=>'0'); + rd_eop(nxt_rd_val'RANGE) <= (OTHERS=>'0'); + state <= s_idle; + ELSIF rising_edge(clk) THEN + rcv_rd <= nxt_rcv_rd; + rcv_in_eop_dly <= rcv_in.eop; + hdr_response <= nxt_hdr_response; + hdr_word_cnt <= nxt_hdr_word_cnt; + hdr_en <= nxt_hdr_en; + hdr_done <= nxt_hdr_done; + tx_insert <= nxt_tx_insert; + xmt_start <= nxt_xmt_start; + i_reg_status <= nxt_reg_status; + tx_nof_words <= nxt_tx_nof_words; + tx_empty <= nxt_tx_empty; + xmt_word_cnt <= nxt_xmt_word_cnt; + xmt_en <= nxt_xmt_en; + xmt_done <= nxt_xmt_done; + i_mem_in <= nxt_mem_in; + rd_val(nxt_rd_val'RANGE) <= nxt_rd_val; + rd_sop(nxt_rd_val'RANGE) <= nxt_rd_sop; + rd_eop(nxt_rd_val'RANGE) <= nxt_rd_eop; + state <= nxt_state; + END IF; + END PROCESS; + + ------------------------------------------------------------------------------ + -- Response header + ------------------------------------------------------------------------------ + + p_hdr_response : PROCESS(rcv_hdr_words, rcv_hdr_status, reg_config) + BEGIN + nxt_hdr_response <= func_eth_response_header( rcv_hdr_words, reg_config.mac_address); + IF rcv_hdr_status.is_arp='1' THEN + nxt_hdr_response <= func_eth_arp_response_header( rcv_hdr_words, reg_config.mac_address, reg_config.ip_address); + ELSIF rcv_hdr_status.is_icmp='1' THEN + nxt_hdr_response <= func_eth_icmp_response_header(rcv_hdr_words, reg_config.mac_address); + ELSIF rcv_hdr_status.is_udp='1' THEN + nxt_hdr_response <= func_eth_udp_response_header( rcv_hdr_words, reg_config.mac_address); + ELSIF rcv_hdr_status.is_ip='1' THEN + -- If it is another protocol over IP than ICMP or UDP, then prepare only IP response + nxt_hdr_response <= func_eth_ip_response_header( rcv_hdr_words, reg_config.mac_address); + END IF; + END PROCESS; + + ------------------------------------------------------------------------------ + -- Frame memory access + ------------------------------------------------------------------------------ + + p_mem_access : PROCESS(i_mem_in, rcv_in, hdr_en, hdr_word_cnt, hdr_response, xmt_en, xmt_siso, xmt_word_cnt) + BEGIN + nxt_mem_in.address <= i_mem_in.address; + nxt_mem_in.wrdata <= i_mem_in.wrdata; + nxt_mem_in.wr <= '0'; + nxt_mem_in.rd <= '0'; + nxt_hdr_word_cnt <= 0; + nxt_xmt_word_cnt <= 0; + -- Rx frame + IF rcv_in.valid='1' THEN + nxt_mem_in.address <= INCR_UVEC(i_mem_in.address, 1); + nxt_mem_in.wrdata <= RESIZE_MEM_DATA(rcv_in.data); + nxt_mem_in.wr <= '1'; + END IF; + IF rcv_in.sop='1' THEN + nxt_mem_in.address <= TO_MEM_ADDRESS(c_eth_ram_rx_offset); -- rx buffer starts at address 0 + END IF; + -- Prepare the Tx header + IF hdr_en='1' THEN -- tx buffer starts at address 380 + nxt_mem_in.address <= TO_MEM_ADDRESS(c_eth_ram_tx_offset + hdr_word_cnt); + nxt_mem_in.wrdata <= RESIZE_MEM_DATA(hdr_response(hdr_word_cnt)); + nxt_mem_in.wr <= '1'; + nxt_hdr_word_cnt <= hdr_word_cnt+1; + END IF; + -- Tx frame + IF xmt_en='1' THEN + nxt_mem_in.address <= TO_MEM_ADDRESS(c_eth_ram_tx_offset + xmt_word_cnt); + nxt_xmt_word_cnt <= xmt_word_cnt; + IF xmt_siso.ready='1' THEN + nxt_mem_in.rd <= '1'; + nxt_xmt_word_cnt <= xmt_word_cnt+1; -- rd_en side counter + END IF; + END IF; + END PROCESS; + + + ------------------------------------------------------------------------------ + -- Tx frame output control + ------------------------------------------------------------------------------ + + rd_val(0) <= i_mem_in.rd; + rd_sop(0) <= i_mem_in.rd WHEN xmt_word_cnt=1 ELSE '0'; + rd_eop(0) <= i_mem_in.rd WHEN xmt_word_cnt=UNSIGNED(tx_nof_words) ELSE '0'; + + -- rd_val side after read latency + nxt_rd_val <= rd_val(0 TO rd_val'HIGH-1); + nxt_rd_sop <= rd_sop(0 TO rd_val'HIGH-1); + nxt_rd_eop <= rd_eop(0 TO rd_val'HIGH-1); + + xmt_sosi.data <= RESIZE_DP_DATA(mem_out.rddata); + xmt_sosi.valid <= rd_val(rd_val'HIGH); + xmt_sosi.sop <= rd_sop(rd_val'HIGH); + xmt_sosi.eop <= rd_eop(rd_val'HIGH); + xmt_sosi.empty <= RESIZE_DP_EMPTY(tx_empty); + + -- adapt mem read latency to xmt ready latency + u_xmt_out_adapter : ENTITY dp_lib.dp_latency_adapter + GENERIC MAP ( + g_in_latency => c_mem_ram_rd_latency+1, -- = 2 + 1 latency for xmt_siso.ready -> nxt_mem_in.rd + g_out_latency => c_this_src_latency -- = 1 + ) + PORT MAP ( + rst => rst, + clk => clk, + -- ST sink + snk_out => xmt_siso, + snk_in => xmt_sosi, + -- ST source + src_in => xmt_in, + src_out => i_xmt_out + ); + + nxt_xmt_done <= i_xmt_out.eop; + + ------------------------------------------------------------------------------ + -- Master-slave handshake control via status register + ------------------------------------------------------------------------------ + + -- Gather Rx and Tx status into the status register + p_reg_status : PROCESS(i_reg_status, reg_status_wr, rcv_in, rcv_in_eop_dly, i_mem_in, tx_insert, hdr_done, xmt_done) + BEGIN + nxt_reg_status <= i_reg_status; + -- MM write access to the status address clears the status register, which removes the interrupt + -- using write instead of read access to remove the interrupt allows for polling reg_status + IF reg_status_wr='1' THEN nxt_reg_status <= c_eth_mm_reg_status_rst; END IF; + -- Events that set the status register + IF rcv_in.eop='1' THEN nxt_reg_status.rx_empty <= rcv_in.empty(c_eth_empty_w-1 DOWNTO 0); END IF; + IF rcv_in_eop_dly='1' THEN + nxt_reg_status.rx_nof_words <= (OTHERS=>'0'); + nxt_reg_status.rx_nof_words(c_eth_frame_nof_words_w-1 DOWNTO 0) <= INCR_UVEC(i_mem_in.address(c_eth_frame_nof_words_w-1 DOWNTO 0), 1); + END IF; + IF tx_insert='1' THEN nxt_reg_status.tx_avail <= '1'; END IF; + IF hdr_done='1' THEN nxt_reg_status.rx_avail <= '1'; END IF; + IF xmt_done='1' THEN nxt_reg_status.tx_done <= '1'; END IF; + END PROCESS; + + -- Capture the control register tx fields for a new transmit + nxt_tx_nof_words <= reg_control.tx_nof_words(c_eth_frame_nof_words_w-1 DOWNTO 0) WHEN xmt_start='1' ELSE tx_nof_words; + nxt_tx_empty <= reg_control.tx_empty WHEN xmt_start='1' ELSE tx_empty; + + + ------------------------------------------------------------------------------ + -- State machine + ------------------------------------------------------------------------------ + + p_state : PROCESS(state, reg_control, reg_continue_wr, rcv_in, rcv_ack, rcv_done, hdr_word_cnt, xmt_siso, xmt_word_cnt, tx_nof_words, xmt_done) + BEGIN + nxt_rcv_rd <= '0'; -- read frame start pulse + nxt_hdr_en <= '0'; -- prepare response header enable + nxt_hdr_done <= '0'; -- prepare response header done + nxt_tx_insert <= '0'; -- accept tx insert request + nxt_xmt_start <= '0'; -- write frame start pulse + nxt_xmt_en <= '0'; -- write frame enable + + nxt_state <= state; + CASE state IS + WHEN s_idle => + IF reg_control.tx_request='1' THEN -- MM master requests to insert an extra tx frame + nxt_tx_insert <= '1'; + nxt_state <= s_tx_pending; + END IF; + IF reg_control.rx_en='1' THEN -- MM master enables default Rx-Tx operation so request a new rx frame + nxt_rcv_rd <= '1'; + nxt_state <= s_rx_request; + END IF; + WHEN s_rx_request => + IF rcv_in.sop='1' THEN -- busy receiving a new frame (best check for sop, so no need to check rcv_busy, rcv_err in eth_rx_frame) + nxt_state <= s_rx_frame; + ELSIF rcv_ack='1' THEN -- rcv ack with no rcv sop, so there was no rx frame pending, try request again + nxt_state <= s_idle; + END IF; + WHEN s_rx_frame => + IF rcv_done='1' THEN -- frame received + nxt_state <= s_hdr_response; + END IF; + WHEN s_hdr_response => -- prepare tx header for rx response + IF hdr_word_cnt < c_eth_total_header_nof_words-1 THEN + nxt_hdr_en <= '1'; + ELSE + nxt_hdr_done <= '1'; -- new frame received and response prepared + nxt_state <= s_tx_pending; + END IF; + WHEN s_tx_pending => -- wait for MM master + IF reg_continue_wr='1' THEN -- MM master done + IF reg_control.tx_en='1' THEN -- continue with response tx frame + nxt_xmt_start <= '1'; + nxt_state <= s_tx_frame; + ELSE -- do not response tx frame + nxt_state <= s_idle; + END IF; + END IF; + WHEN s_tx_frame => + IF xmt_siso.ready='0' OR xmt_word_cnt < UNSIGNED(tx_nof_words)-1 THEN + nxt_xmt_en <= '1'; + ELSE + nxt_state <= s_tx_done; -- frame transmitted + END IF; + WHEN OTHERS => -- s_tx_done + IF xmt_done='1' THEN + nxt_state <= s_idle; -- frame transmitted, to be safe wait for xmt_done + END IF; + END CASE; + END PROCESS; + +END rtl; diff --git a/libraries/io/eth/src/vhdl/eth_hdr_ctrl.vhd b/libraries/io/eth/src/vhdl/eth_hdr_ctrl.vhd new file mode 100644 index 0000000000000000000000000000000000000000..55260a2ca594acd1b827a66dc718349c7b42fe56 --- /dev/null +++ b/libraries/io/eth/src/vhdl/eth_hdr_ctrl.vhd @@ -0,0 +1,212 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib, dp_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_lib.common_pkg.ALL; +USE dp_lib.dp_stream_pkg.ALL; +USE common_lib.eth_layers_pkg.ALL; +USE work.eth_pkg.ALL; + +-- Description: +-- . for IP frames replace IP header checksum with the calculated value +-- . discard frames shorter than 11 words +-- . frame discard input to optionally discard a frame based on some header criteria + +ENTITY eth_hdr_ctrl IS + PORT ( + -- Clocks and reset + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + + -- Stored header + hdr_words : IN t_eth_total_header_arr; + hdr_status : IN t_eth_hdr_status; + + frm_discard : IN STD_LOGIC := '0'; + + -- ST interface + snk_in_word_cnt : IN NATURAL RANGE 0 TO c_eth_total_header_nof_words; -- snk_in hdr word count + snk_in : IN t_dp_sosi; + snk_out : OUT t_dp_siso; + src_in : IN t_dp_siso; + src_out : OUT t_dp_sosi + ); +END eth_hdr_ctrl; + + +ARCHITECTURE rtl OF eth_hdr_ctrl IS + + -- Internal sink ready latency and source ready latency of this component + CONSTANT c_this_snk_latency : NATURAL := 1; + CONSTANT c_this_src_latency : NATURAL := 1; + + TYPE t_state_enum IS ( + s_idle, + s_store_header, + s_src_header_sop, + s_src_header, + s_src_data + ); + + SIGNAL state : t_state_enum; + SIGNAL nxt_state : t_state_enum; + + -- Sink + SIGNAL snk_in_eop_hold : STD_LOGIC; + SIGNAL nxt_snk_in_eop_hold : STD_LOGIC; + + -- Source + SIGNAL next_src_out : t_dp_sosi; + SIGNAL i_src_out : t_dp_sosi; + SIGNAL nxt_src_out : t_dp_sosi; + SIGNAL src_word_cnt : NATURAL RANGE 0 TO c_eth_total_header_nof_words; + SIGNAL nxt_src_word_cnt : NATURAL; + +BEGIN + + src_out <= i_src_out; + + p_reg : PROCESS(rst, clk) + BEGIN + IF rst='1' THEN + state <= s_idle; + snk_in_eop_hold <= '0'; + src_word_cnt <= 0; + i_src_out <= c_dp_sosi_rst; + ELSIF rising_edge(clk) THEN + state <= nxt_state; + snk_in_eop_hold <= nxt_snk_in_eop_hold; + src_word_cnt <= nxt_src_word_cnt; + i_src_out <= nxt_src_out; + END IF; + END PROCESS; + + + ------------------------------------------------------------------------------ + -- Hold the sink input for source output + ------------------------------------------------------------------------------ + + u_snk_in : ENTITY dp_lib.dp_hold_input + PORT MAP ( + rst => rst, + clk => clk, + -- ST sink + snk_out => OPEN, + snk_in => snk_in, + -- ST source + src_in => src_in, -- must not use snk_out.ready here to avoid wrong first payload data + next_src_out => next_src_out, + pend_src_out => OPEN, + src_out_reg => i_src_out + ); + + ------------------------------------------------------------------------------ + -- Control state machine for the source output + ------------------------------------------------------------------------------ + + -- Hold snk_in.eop, because snk_in packet can only have a header of exactly 11 words (e.g. ARP). + -- Discard snk_in packets that are shorter than 11 words (e.g. ???), for both Rx and Tx shorter frames are not useful. + nxt_snk_in_eop_hold <= '0' WHEN snk_in.sop='1' ELSE + '1' WHEN snk_in.eop='1' ELSE snk_in_eop_hold; + + p_state : PROCESS(state, src_word_cnt, i_src_out, snk_in, snk_in_word_cnt, frm_discard, src_in, hdr_words, hdr_status, snk_in_eop_hold, next_src_out) + BEGIN + snk_out.ready <= '1'; -- default accept sink input + nxt_state <= state; + nxt_src_word_cnt <= src_word_cnt; + nxt_src_out <= i_src_out; + nxt_src_out.valid <= '0'; + nxt_src_out.sop <= '0'; + nxt_src_out.eop <= '0'; + CASE state IS + WHEN s_idle => + -- wait for new frame, then store the frame header and no output yet + IF snk_in.sop='1' THEN + nxt_src_word_cnt <= 0; + nxt_state <= s_store_header; + END IF; + WHEN s_store_header => + -- stop the sink when the whole header has arrived or when a eop has arrived + IF snk_in.valid='1' AND snk_in_word_cnt=c_eth_total_header_nof_words-1 THEN + snk_out.ready <= '0'; + nxt_state <= s_src_header_sop; + ELSIF snk_in.eop='1' THEN + -- discard not supported packets shorter than 11 words + snk_out.ready <= '0'; + nxt_state <= s_idle; + END IF; + WHEN s_src_header_sop => + -- stop the input and output the (modified) header, first the sop + snk_out.ready <= '0'; + IF frm_discard='1' THEN + -- discard this frame because of optional external reasons + nxt_state <= s_idle; + ELSIF src_in.ready='1' THEN + nxt_src_out.data <= RESIZE_DP_DATA(hdr_words(src_word_cnt)); + nxt_src_out.valid <= '1'; + nxt_src_out.sop <= '1'; + nxt_src_out.eop <= '0'; + nxt_src_word_cnt <= src_word_cnt+1; + nxt_state <= s_src_header; + END IF; + WHEN s_src_header => + -- stop the input and output the rest of the (modified) header + snk_out.ready <= '0'; + IF src_in.ready='1' THEN + nxt_src_out.data <= RESIZE_DP_DATA(hdr_words(src_word_cnt)); + nxt_src_out.valid <= '1'; + nxt_src_out.sop <= '0'; + nxt_src_out.eop <= '0'; + nxt_src_word_cnt <= src_word_cnt+1; + -- Replace IP header checksum with the calculated checksum from hdr_status.ip_checksum: + -- . for Rx: the verified checksum, so will be 0 when OK or != when the received frame has an IP header error. + -- . for Tx: the calculated checksum for the Tx IP header. + IF hdr_status.is_ip='1' AND src_word_cnt=c_ip_header_checksum_wi THEN + nxt_src_out.data(c_ip_header_checksum_w-1 DOWNTO 0) <= hdr_status.ip_checksum; + END IF; + IF src_word_cnt=c_eth_total_header_nof_words-1 THEN + IF snk_in_eop_hold='1' THEN + -- header only packet + nxt_src_out.eop <= '1'; + nxt_state <= s_idle; + ELSE + -- packet with payload + nxt_state <= s_src_data; + END IF; + END IF; + END IF; + WHEN OTHERS => -- s_src_data + -- continue the input payload and output it + snk_out.ready <= src_in.ready; + nxt_src_out <= next_src_out; + IF next_src_out.eop='1' THEN + nxt_state <= s_idle; + END IF; + END CASE; + + -- Pass on frame level flow control + snk_out.xon <= src_in.xon; + END PROCESS; + +END rtl; diff --git a/libraries/io/eth/src/vhdl/eth_hdr_status.vhd b/libraries/io/eth/src/vhdl/eth_hdr_status.vhd new file mode 100644 index 0000000000000000000000000000000000000000..d729f14b85f887f344d14649e54a3c4a45846f51 --- /dev/null +++ b/libraries/io/eth/src/vhdl/eth_hdr_status.vhd @@ -0,0 +1,144 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib, dp_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_lib.common_pkg.ALL; +USE dp_lib.dp_stream_pkg.ALL; +USE common_lib.eth_layers_pkg.ALL; +USE work.eth_pkg.ALL; + +-- Purpose: +-- Determine the Ethernet header status. +-- Description: +-- Determines all relevant information from the Ethernet header after every +-- asserted sop. +-- Remark: +-- . g_ip_header_checksum_calculate: +-- = TRUE, then the IP header checksum is calculated for hdr_status +-- = FALSE, then the IP header checksum is read for hdr_status + +ENTITY eth_hdr_status IS + GENERIC ( + g_ip_header_checksum_calculate : BOOLEAN := TRUE + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + + -- Total header + hdr_words : IN t_eth_total_header_arr; + hdr_words_val : IN STD_LOGIC_VECTOR(0 TO c_eth_total_header_nof_words-1); + hdr_fields : IN t_eth_total_header; + hdr_fields_val : IN STD_LOGIC_VECTOR(0 TO c_eth_total_header_nof_words-1); + hdr_data : IN STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + hdr_data_val : IN STD_LOGIC; + + -- Header status + hdr_status : OUT t_eth_hdr_status + ); +END eth_hdr_status; + + +ARCHITECTURE rtl OF eth_hdr_status IS + + SIGNAL i_hdr_status : t_eth_hdr_status; + SIGNAL nxt_hdr_status : t_eth_hdr_status; + + SIGNAL ip_header : t_dp_sosi; + SIGNAL calc_checksum : STD_LOGIC_VECTOR(c_halfword_w-1 DOWNTO 0); + SIGNAL calc_checksum_val : STD_LOGIC; + SIGNAL ip_checksum : STD_LOGIC_VECTOR(c_halfword_w-1 DOWNTO 0); + SIGNAL ip_checksum_val : STD_LOGIC; + +BEGIN + + hdr_status <= i_hdr_status; + + p_clk : PROCESS(rst, clk) + BEGIN + IF rst='1' THEN + i_hdr_status <= c_eth_hdr_status_rst; + ELSIF rising_edge(clk) THEN + i_hdr_status <= nxt_hdr_status; + END IF; + END PROCESS; + + ------------------------------------------------------------------------------ + -- Packet type + nxt_hdr_status.is_arp <= '1' WHEN UNSIGNED(hdr_fields.eth.eth_type)=c_eth_type_arp AND hdr_fields_val(c_eth_type_wi)='1' ELSE '0'; + nxt_hdr_status.is_ip <= '1' WHEN UNSIGNED(hdr_fields.eth.eth_type)=c_eth_type_ip AND hdr_fields_val(c_eth_type_wi)='1' ELSE '0'; + nxt_hdr_status.is_icmp <= '1' WHEN UNSIGNED(hdr_fields.ip.protocol)=c_ip_protocol_icmp AND hdr_fields_val(c_ip_protocol_wi)='1' AND i_hdr_status.is_ip='1' ELSE '0'; + nxt_hdr_status.is_udp <= '1' WHEN UNSIGNED(hdr_fields.ip.protocol)=c_ip_protocol_udp AND hdr_fields_val(c_ip_protocol_wi)='1' AND i_hdr_status.is_ip='1' ELSE '0'; + nxt_hdr_status.is_dhcp <= '1' WHEN UNSIGNED(hdr_fields.udp.dst_port)=c_udp_port_dhcp_in AND hdr_fields_val(c_udp_dst_port_wi)='1' AND i_hdr_status.is_udp='1' ELSE '0'; + + ------------------------------------------------------------------------------ + -- IP Header checksum + ip_header.data <= RESIZE_dp_data(hdr_data); + ip_header.valid <= '1' WHEN UNSIGNED(hdr_words_val(c_ip_lo_wi TO c_ip_hi_wi))/=0 ELSE '0'; + ip_header.sop <= hdr_words_val(c_ip_lo_wi); + ip_header.eop <= hdr_words_val(c_ip_hi_wi); + ip_header.empty <= (OTHERS=>'0'); + + -- Calculate the IP header checksum for the packet header + u_calc_checksum : ENTITY work.eth_checksum + PORT MAP ( + rst => rst, + clk => clk, + snk_in => ip_header, + checksum => calc_checksum, + checksum_val => calc_checksum_val + ); + + -- Use calculated IP header checksum + gen_calc : IF g_ip_header_checksum_calculate=TRUE GENERATE + ip_checksum <= calc_checksum; + ip_checksum_val <= calc_checksum_val; + END GENERATE; + + -- Use IP header checksum field read from the frame header + gen_read : IF g_ip_header_checksum_calculate=FALSE GENERATE + ip_checksum <= hdr_fields.ip.header_checksum; + ip_checksum_val <= calc_checksum_val; -- use u_calc_checksum only for valid control + END GENERATE; + + -- If it is an IP packet then update the hdr_status IP checksum fields, else leave them 0 + p_hdr_status_ip_checksum : PROCESS(i_hdr_status, ip_checksum, ip_checksum_val) + BEGIN + nxt_hdr_status.ip_checksum_val <= '0'; + nxt_hdr_status.ip_checksum <= (OTHERS=>'0'); + nxt_hdr_status.ip_checksum_is_ok <= '0'; + IF i_hdr_status.is_ip='1' AND ip_checksum_val='1' THEN + nxt_hdr_status.ip_checksum_val <= '1'; + nxt_hdr_status.ip_checksum <= ip_checksum; + IF UNSIGNED(ip_checksum)=0 THEN + nxt_hdr_status.ip_checksum_is_ok <= '1'; + END IF; + END IF; + END PROCESS; + + ------------------------------------------------------------------------------ + -- UDP port number + nxt_hdr_status.udp_port <= hdr_fields.udp.dst_port; + +END rtl; diff --git a/libraries/io/eth/src/vhdl/eth_hdr_store.vhd b/libraries/io/eth/src/vhdl/eth_hdr_store.vhd new file mode 100644 index 0000000000000000000000000000000000000000..8bd70fdd4445d42306bb2180f0f990354786c749 --- /dev/null +++ b/libraries/io/eth/src/vhdl/eth_hdr_store.vhd @@ -0,0 +1,156 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib, dp_lib; +USE IEEE.std_logic_1164.ALL; +USE common_lib.common_pkg.ALL; +USE dp_lib.dp_stream_pkg.ALL; +USE common_lib.eth_layers_pkg.ALL; +USE work.eth_pkg.ALL; + +-- Purpose: +-- Provide the total Ethernet header information for the user to decide upon. +-- Description: +-- Extract the first c_eth_total_header_nof_words = 11 words from an Ethernet +-- packet and make them available via as a word array and as records fields. +-- The component has three output formats: +-- 1) hdr_words, the 11 word header store +-- 2) hdr_data, the currently active header word +-- 3) hdr_fields, combinatorial mapping of the 11 word header store to +-- various Ethernet packet header fields. +-- Which format to use depends on the application. The logic for words or +-- header fields that are not used, will get optimized away during synthesis. +-- The snk_in word count is also output for the header words. The first valid +-- word marked by the snk_in.sop has count 0, the last header word has count +-- 10 and the payload words have count 11. +-- Remarks: +-- . This component acts as a stream monitor, therefore it does not output a +-- snk_out.ready. +-- . The word indices from eth_pkg together with hdr_words_val[] can be used +-- to determine which word of the Ethernet header words is valid. +-- . The word indices from eth_pkg together with hdr_fields_val[] can be used +-- to determine which fields of the Ethernet header records are valid. The +-- difference with hdr_words_val is that hdr_words_val is a single cycle +-- pulse, whereas hdr_fields_val keeps its state until the next sop. +-- + +ENTITY eth_hdr_store IS + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + + -- Streaming Sink + snk_in : IN t_dp_sosi; + snk_in_word_cnt : OUT NATURAL RANGE 0 TO c_eth_total_header_nof_words; + + -- Total header + hdr_words : OUT t_eth_total_header_arr; + hdr_words_val : OUT STD_LOGIC_VECTOR(0 TO c_eth_total_header_nof_words-1); + -- Combinatorial map of the 11 header words on to the Ethernet header records + hdr_fields : OUT t_eth_total_header; + hdr_fields_val : OUT STD_LOGIC_VECTOR(0 TO c_eth_total_header_nof_words-1); + -- Support also outputting only the currently valid header data + hdr_data : OUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + hdr_data_val : OUT STD_LOGIC + ); +END eth_hdr_store; + + +ARCHITECTURE rtl OF eth_hdr_store IS + + SIGNAL word_cnt : NATURAL RANGE 0 TO c_eth_total_header_nof_words; + SIGNAL nxt_word_cnt : NATURAL; + + SIGNAL i_hdr_words : t_eth_total_header_arr := (OTHERS=>(OTHERS=>'0')); -- init to avoid numeric_std warning, no need to rst + SIGNAL nxt_hdr_words : t_eth_total_header_arr; + SIGNAL nxt_hdr_words_val : STD_LOGIC_VECTOR(hdr_fields_val'RANGE); + SIGNAL i_hdr_fields_val : STD_LOGIC_VECTOR(hdr_fields_val'RANGE); + SIGNAL nxt_hdr_fields_val : STD_LOGIC_VECTOR(hdr_fields_val'RANGE); + SIGNAL i_hdr_data : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0) := (OTHERS=>'0'); -- init to avoid numeric_std warning, no need to rst + SIGNAL nxt_hdr_data : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + SIGNAL nxt_hdr_data_val : STD_LOGIC; + +BEGIN + + snk_in_word_cnt <= word_cnt; + + hdr_words <= i_hdr_words; + hdr_fields_val <= i_hdr_fields_val; + hdr_data <= i_hdr_data; + + p_clk : PROCESS(rst, clk) + BEGIN + IF rst='1' THEN + -- input + -- internal + word_cnt <= 0; + -- output + hdr_words_val <= (OTHERS=>'0'); + i_hdr_fields_val <= (OTHERS=>'0'); + hdr_data_val <= '0'; + ELSIF rising_edge(clk) THEN + -- input + -- internal + word_cnt <= nxt_word_cnt; + -- output + i_hdr_words <= nxt_hdr_words; -- stored array of 11 words + hdr_words_val <= nxt_hdr_words_val; + i_hdr_fields_val <= nxt_hdr_fields_val; + i_hdr_data <= nxt_hdr_data; -- current word + hdr_data_val <= nxt_hdr_data_val; + END IF; + END PROCESS; + + -- Extract the 11 words from the Ethernet header + p_words : PROCESS(word_cnt, i_hdr_words, i_hdr_fields_val, i_hdr_data, snk_in) + BEGIN + nxt_word_cnt <= word_cnt; + nxt_hdr_words <= i_hdr_words; + nxt_hdr_words_val <= (OTHERS=>'0'); + nxt_hdr_fields_val <= i_hdr_fields_val; + nxt_hdr_data <= i_hdr_data; + nxt_hdr_data_val <= '0'; + IF snk_in.sop='1' THEN + nxt_hdr_fields_val <= (OTHERS=>'0'); + END IF; + IF snk_in.valid='1' AND word_cnt < c_eth_total_header_nof_words THEN + -- new Ethernet header is arriving + nxt_word_cnt <= word_cnt + 1; + nxt_hdr_words( word_cnt) <= snk_in.data(c_eth_data_w-1 DOWNTO 0); + nxt_hdr_words_val( word_cnt) <= '1'; + nxt_hdr_fields_val(word_cnt) <= '1'; + nxt_hdr_data <= snk_in.data(c_eth_data_w-1 DOWNTO 0); + nxt_hdr_data_val <= '1'; + END IF; + IF snk_in.eop='1' THEN + nxt_word_cnt <= 0; + END IF; + END PROCESS; + + -- Combinatorial map of the total header on to the Ethernet header records + hdr_fields.eth <= func_eth_map_eth_header( i_hdr_words); + hdr_fields.arp <= func_eth_map_arp_packet( i_hdr_words); + hdr_fields.ip <= func_eth_map_ip_header( i_hdr_words); + hdr_fields.icmp <= func_eth_map_icmp_header(i_hdr_words); + hdr_fields.udp <= func_eth_map_udp_header( i_hdr_words); + +END rtl; diff --git a/libraries/io/eth/src/vhdl/eth_ihl_to_20.vhd b/libraries/io/eth/src/vhdl/eth_ihl_to_20.vhd new file mode 100644 index 0000000000000000000000000000000000000000..edaae16bb9a6f33ce938e3910725b704b3fcd60f --- /dev/null +++ b/libraries/io/eth/src/vhdl/eth_ihl_to_20.vhd @@ -0,0 +1,216 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- This program IS free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program IS distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib, dp_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_lib.common_pkg.ALL; +USE dp_lib.dp_stream_pkg.ALL; +USE common_lib.eth_layers_pkg.ALL; +USE work.eth_pkg.ALL; + +-- Purpose: +-- Description: +-- Strip IPv4 headers to 20 bytes +-- Set ihl to 5 (5 * 4 = 20 bytes) +-- Ajust IPv4 Packet Total Length field +-- IP Header Checksum is set to 0x0000 and should be ignored +-- The checksum could be updated using https://tools.ietf.org/html/rfc1624 +-- Remarks: +-- . c_this_snk_latency = 1 +-- . c_this_src_latency = 1 + + ------------------------------------------------------------------------------ + -- IPv4 Packet + -- + -- 0 3 4 7 8 15 16 18 19 31 wi + -- |----------------------------------------------------------------------| + -- | Version | ihl | Services | IPv4 Packet Total Length | 4 + -- |----------------------------------------------------------------------| + -- | Identification | Flags | Fragment Offset | 5 + -- |----------------------------------------------------------------------| + -- | TTL | Protocol | Header Checksum | 6 + -- |----------------------------------------------------------------------| + -- | Source IP Address | 7 + -- |----------------------------------------------------------------------| + -- | Destination IP Address | 8 + -- |----------------------------------------------------------------------| + -- | Options ... | + -- |----------------------------------------------------------------------| + + +ENTITY eth_ihl_to_20 IS + GENERIC ( + incoming_ihl : NATURAL := 20 + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + + -- Streaming Sink + snk_in : IN t_dp_sosi; + snk_out : OUT t_dp_siso; + + -- Streaming Source + src_in : IN t_dp_siso; + src_out : OUT t_dp_sosi + ); +END eth_ihl_to_20; + + +ARCHITECTURE rtl OF eth_ihl_to_20 IS + + SIGNAL i_src_out : t_dp_sosi; + + TYPE state_type IS (Idle, Eth_DestMAC0, Eth_DestMAC1, Eth_SrcMAC0, Eth_SrcMAC1, IPv4_lengths, IPv4_ID, IPv4_TTL, IPv4_SrcIP, IPv4_DestIP, IPv4_Options, IPv4_Payload); + SIGNAL state : state_type; + + SIGNAL ihl : unsigned(c_ip_header_length_w-1 downto 0); + +BEGIN + + src_out <= i_src_out; + --i_src_out <= snk_in; + + -- Pass on frame level flow control + snk_out.xon <= src_in.xon; + + -- No change in ready latency, c_this_snk_latency = c_this_src_latency + snk_out.ready <= src_in.ready; + + + PROCESS(clk, rst) + BEGIN + IF rst = '1' THEN + state <= Idle; + ihl <= TO_UNSIGNED(0,ihl'LENGTH); + ELSIF (rising_edge(clk)) THEN + i_src_out <= snk_in; + CASE state IS + WHEN Idle=> + IF snk_in.sop = '1' and snk_in.valid = '1' THEN + state <= Eth_DestMAC0; + END IF; + + WHEN Eth_DestMAC0=> + IF snk_in.valid = '1' THEN + state <= Eth_DestMAC1; + END IF; + + + WHEN Eth_DestMAC1 => + IF snk_in.valid = '1' THEN + state <= Eth_SrcMAC0; + END IF; + + + WHEN Eth_SrcMAC0 => + IF snk_in.valid = '1' THEN + IF snk_in.data(c_eth_type_slv'RANGE) = TO_UVEC(c_eth_type_ip,c_eth_type_w) THEN -- if IPv4 frame + state <= Eth_SrcMAC1; + ELSE + state <= Idle; + END IF; + END IF; + + + WHEN Eth_SrcMAC1 => + IF snk_in.valid = '1' THEN + IF snk_in.data(27 downto 24) = TO_UVEC(c_ip_header_length,c_ip_header_length_w) THEN -- if ihl = 20, nothing to do + state <= Idle; + ELSE + state <= IPv4_lengths; + i_src_out.data(27 downto 24) <= TO_UVEC(c_ip_header_length ,c_ip_header_length_w); -- make IHL = 20 + i_src_out.data(c_ip_total_length_w-1 downto 0) <= std_logic_vector(unsigned(snk_in.data(c_ip_total_length_w-1 downto 0)) + - ((unsigned(snk_in.data(27 downto 24)) + - to_unsigned(c_ip_header_length ,c_ip_header_length_w) + ) & "00" + ) + ); -- correct Total Length + ihl <= unsigned(snk_in.data(27 downto 24)); -- save IHL + END IF; + END IF; + + + WHEN IPv4_lengths => + IF snk_in.valid = '1' THEN + state <= IPv4_ID; + ihl <= ihl - 1; + END IF; + + + WHEN IPv4_ID => + IF snk_in.valid = '1' THEN + state <= IPv4_TTL; + i_src_out.data(c_ip_header_checksum_w-1 downto 0) <= TO_UVEC(0,c_ip_header_checksum_w); + ihl <= ihl - 1; + END IF; + + + WHEN IPv4_TTL => + IF snk_in.valid = '1' THEN + state <= IPv4_SrcIP; + ihl <= ihl - 1; + END IF; + + + WHEN IPv4_SrcIP => + IF snk_in.valid = '1' THEN + state <= IPv4_DestIP; + ihl <= ihl - 1; + END IF; + + + WHEN IPv4_DestIP => + IF snk_in.valid = '1' THEN + i_src_out.valid <= '0'; -- do not allow option words to get through this module + state <= IPv4_Options; + ihl <= ihl - 1; + IF ihl = 2 THEN + state <= IPv4_Payload; + END IF; + END IF; + + WHEN IPv4_Options => + IF snk_in.valid = '1' THEN + i_src_out.valid <= '0'; -- do not allow option words to get through this module + ihl <= ihl - 1; + IF ihl = 2 THEN + state <= IPv4_Payload; + END IF; + END IF; + + + WHEN IPv4_Payload => + IF snk_in.eop = '1' and snk_in.valid = '1' THEN + state <= Idle; + END IF; + + WHEN others => + state <= Idle; + END CASE; + END IF; + end PROCESS ; + + + +END rtl; diff --git a/libraries/io/eth/src/vhdl/eth_mm_reg_frame.vhd b/libraries/io/eth/src/vhdl/eth_mm_reg_frame.vhd new file mode 100644 index 0000000000000000000000000000000000000000..2c2d537dc3f692c6b52a6b9a6e1dec65638e46d4 --- /dev/null +++ b/libraries/io/eth/src/vhdl/eth_mm_reg_frame.vhd @@ -0,0 +1,74 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.eth_layers_pkg.ALL; +USE work.eth_pkg.ALL; + +ENTITY eth_mm_reg_frame IS + PORT ( + -- Clocks and reset + rst : IN STD_LOGIC; -- reset synchronous with clk + clk : IN STD_LOGIC; -- packet stream clock + + -- Inputs need for the frame register + hdr_fields : IN t_eth_total_header; -- Header info + hdr_status : IN t_eth_hdr_status; -- Header info + erc_word : IN STD_LOGIC_VECTOR(c_eth_data_w-1 DOWNTO 0); -- Extracted CRC word (enumerate: 0=OK, >0 AND odd = Error) + reg_config : IN t_eth_mm_reg_config; -- Config setting + + -- Frame register + reg_frame : OUT t_eth_mm_reg_frame + ); +END eth_mm_reg_frame; + + +ARCHITECTURE str OF eth_mm_reg_frame IS + + SIGNAL nxt_reg_frame : t_eth_mm_reg_frame; + +BEGIN + + p_reg : PROCESS(rst, clk) + BEGIN + IF rst='1' THEN + reg_frame <= c_eth_mm_reg_frame_rst; + ELSIF rising_edge(clk) THEN + reg_frame <= nxt_reg_frame; + END IF; + END PROCESS; + + nxt_reg_frame.is_dhcp <= hdr_status.is_dhcp; + nxt_reg_frame.is_udp_ctrl_port <= '1' WHEN hdr_status.is_udp='1' AND UNSIGNED(hdr_status.udp_port)=UNSIGNED(reg_config.udp_port) ELSE '0'; + nxt_reg_frame.is_udp <= hdr_status.is_udp; + nxt_reg_frame.is_icmp <= hdr_status.is_icmp; + nxt_reg_frame.ip_address_match <= '1' WHEN UNSIGNED(hdr_fields.ip.dst_ip_addr)=UNSIGNED(reg_config.ip_address) ELSE '0'; + nxt_reg_frame.ip_checksum_is_ok <= hdr_status.ip_checksum_is_ok; + nxt_reg_frame.is_ip <= hdr_status.is_ip; + nxt_reg_frame.is_arp <= hdr_status.is_arp; + nxt_reg_frame.mac_address_match <= '1' WHEN UNSIGNED(hdr_fields.eth.dst_mac)=UNSIGNED(reg_config.mac_address) ELSE '0'; + nxt_reg_frame.eth_mac_error <= erc_word(c_eth_error_w-1 DOWNTO 0); + +END str; diff --git a/libraries/io/eth/tb/vhdl/tb_eth_ihl_to_20.vhd b/libraries/io/eth/tb/vhdl/tb_eth_ihl_to_20.vhd new file mode 100644 index 0000000000000000000000000000000000000000..683990729ab5873bde46737ae101119070b8864c --- /dev/null +++ b/libraries/io/eth/tb/vhdl/tb_eth_ihl_to_20.vhd @@ -0,0 +1,225 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib, dp_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_lib.common_pkg.ALL; +USE dp_lib.dp_stream_pkg.ALL; +USE common_lib.eth_layers_pkg.ALL; +USE work.eth_pkg.ALL; + + +ENTITY tb_eth_IHL_to_20 IS +END tb_eth_IHL_to_20; + + +ARCHITECTURE tb OF tb_eth_IHL_to_20 IS + + CONSTANT clk_period : TIME := 5 ns; -- 100 MHz + + + SIGNAL clk : STD_LOGIC := '0'; + SIGNAL rst : STD_LOGIC; + + + SIGNAL snk_in : t_dp_sosi := c_dp_sosi_rst; + SIGNAL snk_out : t_dp_siso; + + SIGNAL src_in : t_dp_siso := c_dp_siso_rdy; + SIGNAL src_out : t_dp_sosi; + + TYPE int_arr is array (natural range <>) of integer; + CONSTANT c_IHL_to_test : int_arr(1 to 11) := (5,6,7,8,9,10,11,12,13,14,15); + CONSTANT c_len_to_test : int_arr(1 to 5) := (0,1,16,20,3000); + + + PROCEDURE gen_eth_frame (constant IHL : natural; + constant UDP_payload_len : natural; + signal clk : in std_logic; + signal snk_in : inout t_dp_sosi) is + BEGIN + snk_in.sop <= '1'; + snk_in.valid <= '1'; + snk_in.data <= RESIZE_DP_DATA(X"0000FFFF"); -- Eth header + WAIT UNTIL rising_edge(clk); + snk_in.sop <= '0'; + snk_in.data <= RESIZE_DP_DATA(X"FFFFFFFF"); + WAIT UNTIL rising_edge(clk); + snk_in.data <= RESIZE_DP_DATA(X"10FA0004"); + WAIT UNTIL rising_edge(clk); + snk_in.data <= RESIZE_DP_DATA(X"00000800"); + WAIT UNTIL rising_edge(clk); + snk_in.data <= RESIZE_DP_DATA(X"4" & -- IPv4 header + TO_UVEC(IHL,c_ip_header_length_w) & + X"00" & + TO_UVEC((IHL+UDP_payload_len+2)*4,c_ip_total_length_w)); + WAIT UNTIL rising_edge(clk); + snk_in.data <= RESIZE_DP_DATA(X"00004000"); + WAIT UNTIL rising_edge(clk); + snk_in.data <= RESIZE_DP_DATA(X"80110000"); + WAIT UNTIL rising_edge(clk); + snk_in.data <= RESIZE_DP_DATA(X"0A0B0001"); + WAIT UNTIL rising_edge(clk); + snk_in.data <= RESIZE_DP_DATA(X"0A0B00FF"); + WAIT UNTIL rising_edge(clk); + + FOR I IN 6 TO IHL LOOP + snk_in.data <= RESIZE_DP_DATA(X"BAD000" & TO_UVEC(I,c_ip_header_length_w)); -- optionnal option word + WAIT UNTIL rising_edge(clk); + END LOOP; + + snk_in.data <= RESIZE_DP_DATA(X"10FA10FA"); -- UDP header + WAIT UNTIL rising_edge(clk); + snk_in.data <= RESIZE_DP_DATA(TO_UVEC((UDP_payload_len+2)*4,c_udp_total_length_w) & X"0000"); + WAIT UNTIL rising_edge(clk); + + FOR I IN 0 TO UDP_payload_len-1 LOOP -- UDP payload + snk_in.data <= RESIZE_DP_DATA(X"BEEF" & TO_UVEC(I,16)); + WAIT UNTIL rising_edge(clk); + END LOOP; + + snk_in.data <= RESIZE_DP_DATA(X"CCCCCCCC"); -- Eth CRC + snk_in.eop <= '1'; + WAIT UNTIL rising_edge(clk); + + snk_in.data <= (OTHERS=>'0'); + snk_in.valid <= '0'; + snk_in.eop <= '0'; + WAIT UNTIL rising_edge(clk); + END PROCEDURE gen_eth_frame; + + PROCEDURE check_eth_frame ( constant UDP_payload_len : natural; + signal clk : in std_logic; + signal src_out : in t_dp_sosi) is + CONSTANT c_IHL : natural := 5; + BEGIN + + -- Eth header + WAIT UNTIL falling_edge(clk) and src_out.valid = '1' and src_out.sop = '1'; + ASSERT src_out.data(31 downto 0) = X"0000FFFF" REPORT "Wrong word align and Destination MAC" SEVERITY FAILURE; + WAIT UNTIL falling_edge(clk) and src_out.valid = '1'; + ASSERT src_out.data(31 downto 0) = X"FFFFFFFF" REPORT "Wrong Destination MAC" SEVERITY FAILURE; + WAIT UNTIL falling_edge(clk) and src_out.valid = '1'; + ASSERT src_out.data(31 downto 0) = X"10FA0004" REPORT "Wrong Source MAC" SEVERITY FAILURE; + WAIT UNTIL falling_edge(clk) and src_out.valid = '1'; + ASSERT src_out.data(31 downto 0) = X"00000800" REPORT "Wrong Source MAC and EtherType" SEVERITY FAILURE; + + -- IPv4 header + WAIT UNTIL falling_edge(clk) and src_out.valid = '1'; + ASSERT src_out.data(31 downto 0) = X"4" & + TO_UVEC(c_IHL,c_ip_header_length_w) & + X"00" & + TO_UVEC((c_IHL+UDP_payload_len+2)*4,c_ip_total_length_w) + REPORT "Wrong Version / IHL / Total Length" SEVERITY FAILURE; + + WAIT UNTIL falling_edge(clk) and src_out.valid = '1'; + ASSERT src_out.data(31 downto 0) = X"00004000" REPORT "Wrong identification / Flags / Frag Offset" SEVERITY FAILURE; + WAIT UNTIL falling_edge(clk) and src_out.valid = '1'; + ASSERT src_out.data(31 downto 0) = X"80110000" REPORT "Wrong TTL / Protocol / Checksum" SEVERITY FAILURE; + WAIT UNTIL falling_edge(clk) and src_out.valid = '1'; + ASSERT src_out.data(31 downto 0) = X"0A0B0001" REPORT "Wrong Source IP" SEVERITY FAILURE; + WAIT UNTIL falling_edge(clk) and src_out.valid = '1'; + ASSERT src_out.data(31 downto 0) = X"0A0B00FF" REPORT "Wrong Dest IP" SEVERITY FAILURE; + -- No options Here + + -- UDP header + WAIT UNTIL falling_edge(clk) and src_out.valid = '1'; + ASSERT src_out.data(31 downto 0) = X"10FA10FA" REPORT "Wrong UDP ports" SEVERITY FAILURE; + WAIT UNTIL falling_edge(clk) and src_out.valid = '1'; + ASSERT src_out.data(31 downto 0) = TO_UVEC((UDP_payload_len+2)*4,c_udp_total_length_w) & + X"0000" + REPORT "Wrong UDP length / CRC" SEVERITY FAILURE; + -- UDP payload + FOR I IN 0 TO UDP_payload_len-1 LOOP + WAIT UNTIL falling_edge(clk) and src_out.valid = '1'; + ASSERT src_out.data(31 downto 0) = X"BEEF" & TO_UVEC(I,16) REPORT "Wrong UDP Payload" SEVERITY FAILURE; +-- ASSERT src_out.data(31 downto 0) = X"BEEF" & TO_UVEC(I,16) REPORT "Wrong UDP Payload: 0xBEEF" & TO_UVEC(I,16)'IMAGE SEVERITY FAILURE; + END LOOP; + + -- Eth CRC + WAIT UNTIL falling_edge(clk) and src_out.valid = '1' and src_out.eop <= '1'; + ASSERT src_out.data(31 downto 0) = X"CCCCCCCC" REPORT "Wrong Eth CRC" SEVERITY FAILURE; + + END PROCEDURE check_eth_frame; + + +BEGIN + + clk <= NOT clk AFTER clk_period/2; + rst <= '1', '0' AFTER clk_period*7; + + + gen_frame: PROCESS + BEGIN + WAIT UNTIL rst='0'; + + FOR len_n in c_len_to_test'RANGE LOOP + FOR IHL_n in c_IHL_to_test'RANGE LOOP + WAIT FOR 50 ns; WAIT UNTIL rising_edge(clk); + gen_eth_frame (c_IHL_to_test(IHL_n), + c_len_to_test(len_n), + clk,snk_in); + END LOOP; + END LOOP; + + wait for 1 ms; + ASSERT FALSE REPORT "ERROR: Processing was too long. DUT is stuck" SEVERITY FAILURE; + WAIT; + END PROCESS; + + + dut : ENTITY work.eth_IHL_to_20 + GENERIC MAP ( + incoming_IHL => 24 + ) + PORT MAP ( + rst => rst, + clk => clk, + + -- Streaming Sink + snk_in => snk_in, + snk_out => snk_out, + + -- Streaming Source + src_in => src_in, + src_out => src_out + ); + + + check_frame: PROCESS + BEGIN + + FOR len_n in c_len_to_test'RANGE LOOP + FOR IHL_n in c_IHL_to_test'RANGE LOOP + check_eth_frame ( + c_len_to_test(len_n), + clk,src_out); + END LOOP; + END LOOP; + + WAIT for 1 us; + ASSERT FALSE REPORT "Simulation finished: NO ERRORS" SEVERITY FAILURE; + WAIT; + END PROCESS; + +END tb;