diff --git a/applications/rdma_demo/hdllib.cfg b/applications/rdma_demo/hdllib.cfg index eecdcb43a3072f940e2b96c89e6045fa99ba1d2f..16571f029fdcc518a908057760335b93a074d154 100644 --- a/applications/rdma_demo/hdllib.cfg +++ b/applications/rdma_demo/hdllib.cfg @@ -7,6 +7,7 @@ hdl_lib_technology = synth_files = src/vhdl/rdma_demo_pkg.vhd src/vhdl/rdma_demo_eth_tester_wrapper.vhd + src/vhdl/rdma_demo_roce_tester_wrapper.vhd test_bench_files = diff --git a/applications/rdma_demo/src/vhdl/rdma_demo_eth_tester_wrapper.vhd b/applications/rdma_demo/src/vhdl/rdma_demo_eth_tester_wrapper.vhd index e617ef70a63f654fc358ab14e7204643bbc77512..a8639774e04480b29b516a8726d238269f4738f4 100644 --- a/applications/rdma_demo/src/vhdl/rdma_demo_eth_tester_wrapper.vhd +++ b/applications/rdma_demo/src/vhdl/rdma_demo_eth_tester_wrapper.vhd @@ -189,10 +189,11 @@ begin generic map ( g_nof_octet_generate => c_rdma_demo_nof_octet_generate_100gbe, g_nof_octet_output => c_rdma_demo_nof_octet_output_100gbe, - g_use_network_header => false, + g_use_eth_header => false, + g_use_ip_udp_header => false, g_use_dp_header => true, - g_hdr_field_arr => c_rdma_demo_hdr_field_arr, - g_hdr_field_sel => c_rdma_demo_hdr_field_sel, + g_hdr_field_arr => c_rdma_demo_dp_hdr_field_arr, + g_hdr_field_sel => c_rdma_demo_dp_hdr_field_sel, g_remove_crc => false ) port map ( diff --git a/applications/rdma_demo/src/vhdl/rdma_demo_pkg.vhd b/applications/rdma_demo/src/vhdl/rdma_demo_pkg.vhd index 87d7dec3538d19f95271654d4d7ac1a730f0830f..438bd8acd6f28a0d75cea9da7cfd6ebe2106b507 100644 --- a/applications/rdma_demo/src/vhdl/rdma_demo_pkg.vhd +++ b/applications/rdma_demo/src/vhdl/rdma_demo_pkg.vhd @@ -53,19 +53,70 @@ package rdma_demo_pkg is -- to 0 by declaring hdr_fields_in_arr with all 0. Hence e.g. udp_checksum -- = 0 can be achieve via data path and default hdr_fields_in_arr = 0 or -- via MM controlled and field_default(0). - constant c_rdma_demo_nof_hdr_fields : natural := 4; - constant c_rdma_demo_hdr_field_sel : std_logic_vector(c_rdma_demo_nof_hdr_fields - 1 downto 0) := "0100"; - - constant c_rdma_demo_hdr_field_arr : t_common_field_arr(c_rdma_demo_nof_hdr_fields - 1 downto 0) := ( - ( field_name_pad("dp_length" ), "RW", 16, field_default(0) ), - ( field_name_pad("dp_reserved" ), "RW", 15, field_default(0) ), - ( field_name_pad("dp_sync" ), "RW", 1, field_default(0) ), - ( field_name_pad("dp_bsn" ), "RW", 64, field_default(0) ) + constant c_rdma_demo_dp_nof_hdr_fields : natural := 4; + constant c_rdma_demo_dp_hdr_field_sel : std_logic_vector(c_rdma_demo_dp_nof_hdr_fields - 1 downto 0) := "0100"; + + constant c_rdma_demo_dp_hdr_field_arr : t_common_field_arr(c_rdma_demo_dp_nof_hdr_fields - 1 downto 0) := ( + ( field_name_pad("dp_length" ), "RW", 16, field_default(0) ), + ( field_name_pad("dp_reserved"), "RW", 15, field_default(0) ), + ( field_name_pad("dp_sync" ), "RW", 1, field_default(0) ), + ( field_name_pad("dp_bsn" ), "RW", 64, field_default(0) ) + ); + constant c_rdma_demo_dp_reg_hdr_dat_addr_w : natural := ceil_log2(field_nof_words(c_rdma_demo_dp_hdr_field_arr, c_word_w)); + constant c_rdma_demo_dp_reg_hdr_dat_addr_span : natural := 2**c_rdma_demo_dp_reg_hdr_dat_addr_w; + + constant c_rdma_demo_dp_app_hdr_len : natural := 12; -- octets + + + -- RoCEv2 header for RDMA write operation (excluding ETH, IP, UDP) + -- Base Transport Header (BTH) + RDMA Extended Transport Header (RETH) + Immediate Data + constant c_rdma_demo_roce_nof_hdr_fields : natural := 12 + 4 + 13 + 3 + 1; + constant c_rdma_demo_roce_hdr_field_sel : std_logic_vector(c_rdma_demo_roce_nof_hdr_fields - 1 downto 0) := "111011111001" & "0100" & "1111111111111" & "111" & "1"; + + constant c_rdma_demo_roce_hdr_field_arr : t_common_field_arr(c_rdma_demo_roce_nof_hdr_fields - 1 downto 0) := ( + ( field_name_pad("ip_version" ), "RW", 4, field_default(4) ), + ( field_name_pad("ip_header_length" ), "RW", 4, field_default(5) ), + ( field_name_pad("ip_services" ), "RW", 8, field_default(0) ), + ( field_name_pad("ip_total_length" ), "RW", 16, field_default(0) ), -- depends on BG block size, so set by data path + ( field_name_pad("ip_identification" ), "RW", 16, field_default(0) ), + ( field_name_pad("ip_flags" ), "RW", 3, field_default(2) ), + ( field_name_pad("ip_fragment_offset" ), "RW", 13, field_default(0) ), + ( field_name_pad("ip_time_to_live" ), "RW", 8, field_default(127) ), + ( field_name_pad("ip_protocol" ), "RW", 8, field_default(17) ), + ( field_name_pad("ip_header_checksum" ), "RW", 16, field_default(0) ), + ( field_name_pad("ip_src_addr" ), "RW", 32, field_default(0) ), + ( field_name_pad("ip_dst_addr" ), "RW", 32, field_default(0) ), -- c_eth_tester_ip_dst_addr + + ( field_name_pad("udp_src_port" ), "RW", 16, field_default(0) ), + ( field_name_pad("udp_dst_port" ), "RW", 16, field_default(0) ), -- c_eth_tester_udp_dst_port + ( field_name_pad("udp_total_length" ), "RW", 16, field_default(0) ), -- depends on BG block size, so set by data path + ( field_name_pad("udp_checksum" ), "RW", 16, field_default(0) ), + + ( field_name_pad("bth_opcode" ), "RW", 8, field_default(0) ), + ( field_name_pad("bth_se" ), "RW", 1, field_default(0) ), + ( field_name_pad("bth_m" ), "RW", 1, field_default(0) ), + ( field_name_pad("bth_pad" ), "RW", 2, field_default(0) ), + ( field_name_pad("bth_tver" ), "RW", 4, field_default(0) ), + ( field_name_pad("bth_partition_key" ), "RW", 16, field_default(0) ), + ( field_name_pad("bth_fres" ), "RW", 1, field_default(0) ), + ( field_name_pad("bth_bres" ), "RW", 1, field_default(0) ), + ( field_name_pad("bth_reserved_a" ), "RW", 6, field_default(0) ), + ( field_name_pad("bth_dest_qp" ), "RW", 16, field_default(0) ), + ( field_name_pad("bth_ack_req" ), "RW", 1, field_default(0) ), + ( field_name_pad("bth_reserved_b" ), "RW", 7, field_default(0) ), + ( field_name_pad("bth_psn" ), "RW", 32, field_default(0) ), + + ( field_name_pad("reth_virtual_address"), "RW", 64, field_default(0) ), + ( field_name_pad("reth_r_key" ), "RW", 32, field_default(0) ), + ( field_name_pad("reth_dma_length" ), "RW", 32, field_default(0) ), + + ( field_name_pad("immediate_data" ), "RW", 32, field_default(0) ) ); - constant c_rdma_demo_reg_hdr_dat_addr_w : natural := ceil_log2(field_nof_words(c_rdma_demo_hdr_field_arr, c_word_w)); -- = 5 - constant c_rdma_demo_reg_hdr_dat_addr_span : natural := 2**c_rdma_demo_reg_hdr_dat_addr_w; -- = 32 + constant c_rdma_demo_roce_reg_hdr_dat_addr_w : natural := ceil_log2(field_nof_words(c_rdma_demo_roce_hdr_field_arr, c_word_w)); + constant c_rdma_demo_roce_reg_hdr_dat_addr_span : natural := 2**c_rdma_demo_roce_reg_hdr_dat_addr_w; - constant c_rdma_demo_app_hdr_len : natural := 12; -- octets + constant c_rdma_demo_roce_hdr_len : natural := 32; -- octets + constant c_rdma_demo_roce_icrc_len : natural := 4; -- octets end rdma_demo_pkg; diff --git a/applications/rdma_demo/src/vhdl/rdma_demo_roce_tester_wrapper.vhd b/applications/rdma_demo/src/vhdl/rdma_demo_roce_tester_wrapper.vhd new file mode 100644 index 0000000000000000000000000000000000000000..7eab44990b6ef4e6cbc5a271f6d7d7647c196969 --- /dev/null +++ b/applications/rdma_demo/src/vhdl/rdma_demo_roce_tester_wrapper.vhd @@ -0,0 +1,387 @@ +------------------------------------------------------------------------------- +-- +-- Copyright 2023 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +------------------------------------------------------------------------------- +-- Author: R. van der Walle +-- Purpose: Provide AXI-4-stream interfaces + standard avalon MM interfaces for +-- eth_tester.vhd such that it can be used to create a Vivado IP block. +-- Description: +-- . The rdma_demo_roce_tester_wrapper uses axi4_stream_dp_bridge to convert the dp +-- sosi/siso interfaces of the eth_tester into AXI4-Stream interfaces. +-- . In order for this component to be suitable as a Vivado IP, the ports are +-- exclusively STD_LOGIC(_VECTOR) where the widths are hard-coded as demanded +-- by the Vivado IP creator (only supports VHDL-93). +-- . * roce = RDMA Over Converged Ethernet +-- Remark +-- . Avalon is used for all MM interfaces, which can be bridged to AXI4-Lite in +-- vivado using the AXI AMM Bridge IP. + +library IEEE, common_lib, dp_lib, axi4_lib, eth_lib; +use IEEE.std_logic_1164.all; +use common_lib.common_pkg.all; +use common_lib.common_mem_pkg.all; +use common_lib.common_network_layers_pkg.all; +use dp_lib.dp_stream_pkg.all; +use dp_lib.dp_components_pkg.all; +use axi4_lib.axi4_stream_pkg.all; +use eth_lib.eth_pkg.all; +use eth_lib.eth_tester_pkg.all; +use work.rdma_demo_pkg.all; + +entity rdma_demo_roce_tester_wrapper is + port ( + -- Clocks and reset + mm_clk : in std_logic; + st_clk : in std_logic; + st_pps : in std_logic; + aresetn : in std_logic; + -- UDP transmit interface + eth_src_mac : in std_logic_vector(6 * 8 - 1 downto 0); + ip_src_addr : in std_logic_vector(4 * 8 - 1 downto 0); + udp_src_port : in std_logic_vector(2 * 8 - 1 downto 0); + + tx_fifo_rd_emp_arr : out std_logic_vector(0 downto 0); + + -- tx_udp + -- Source In and Sink Out + tx_udp_tready : in std_logic; + + -- Source Out and Sink In + tx_udp_tvalid : out std_logic; + tx_udp_tdata : out std_logic_vector(512 - 1 downto 0); + tx_udp_tstrb : out std_logic_vector(512 / 8 - 1 downto 0); + tx_udp_tkeep : out std_logic_vector(512 / 8 - 1 downto 0); + tx_udp_tlast : out std_logic; + tx_udp_tid : out std_logic_vector(4 - 1 downto 0); + tx_udp_tdest : out std_logic_vector(32 - 1 downto 0); + tx_udp_tuser : out std_logic_vector(70 - 1 downto 0); + + -- rx_udp + -- Source In and Sink Out + rx_udp_tready : out std_logic; + + -- Source Out and Sink In + rx_udp_tvalid : in std_logic; + rx_udp_tdata : in std_logic_vector(512 - 1 downto 0); + rx_udp_tstrb : in std_logic_vector(512 / 8 - 1 downto 0); + rx_udp_tkeep : in std_logic_vector(512 / 8 - 1 downto 0); + rx_udp_tlast : in std_logic; + rx_udp_tid : in std_logic_vector(4 - 1 downto 0); + rx_udp_tdest : in std_logic_vector(32 - 1 downto 0); + rx_udp_tuser : in std_logic_vector(70 - 1 downto 0); + + -- reg_bg_ctrl + reg_bg_ctrl_avs_address : in std_logic_vector(32 - 1 downto 0); + reg_bg_ctrl_avs_read : in std_logic; + reg_bg_ctrl_avs_readdata : out std_logic_vector(32 - 1 downto 0); + reg_bg_ctrl_avs_readdatavalid : out std_logic; + reg_bg_ctrl_avs_waitrequest : out std_logic; + reg_bg_ctrl_avs_write : in std_logic; + reg_bg_ctrl_avs_writedata : in std_logic_vector(32 - 1 downto 0); + + -- reg_hdr_dat + reg_hdr_dat_avs_address : in std_logic_vector(32 - 1 downto 0); + reg_hdr_dat_avs_read : in std_logic; + reg_hdr_dat_avs_readdata : out std_logic_vector(32 - 1 downto 0); + reg_hdr_dat_avs_readdatavalid : out std_logic; + reg_hdr_dat_avs_waitrequest : out std_logic; + reg_hdr_dat_avs_write : in std_logic; + reg_hdr_dat_avs_writedata : in std_logic_vector(32 - 1 downto 0); + + -- reg_bsn_monitor_v2_tx + reg_bsn_monitor_v2_tx_avs_address : in std_logic_vector(32 - 1 downto 0); + reg_bsn_monitor_v2_tx_avs_read : in std_logic; + reg_bsn_monitor_v2_tx_avs_readdata : out std_logic_vector(32 - 1 downto 0); + reg_bsn_monitor_v2_tx_avs_readdatavalid : out std_logic; + reg_bsn_monitor_v2_tx_avs_waitrequest : out std_logic; + reg_bsn_monitor_v2_tx_avs_write : in std_logic; + reg_bsn_monitor_v2_tx_avs_writedata : in std_logic_vector(32 - 1 downto 0); + + -- reg_strobe_total_count_tx + reg_strobe_total_count_tx_avs_address : in std_logic_vector(32 - 1 downto 0); + reg_strobe_total_count_tx_avs_read : in std_logic; + reg_strobe_total_count_tx_avs_readdata : out std_logic_vector(32 - 1 downto 0); + reg_strobe_total_count_tx_avs_readdatavalid : out std_logic; + reg_strobe_total_count_tx_avs_waitrequest : out std_logic; + reg_strobe_total_count_tx_avs_write : in std_logic; + reg_strobe_total_count_tx_avs_writedata : in std_logic_vector(32 - 1 downto 0); + + -- reg_dp_split + reg_dp_split_avs_address : in std_logic_vector(32 - 1 downto 0); + reg_dp_split_avs_read : in std_logic; + reg_dp_split_avs_readdata : out std_logic_vector(32 - 1 downto 0); + reg_dp_split_avs_readdatavalid : out std_logic; + reg_dp_split_avs_waitrequest : out std_logic; + reg_dp_split_avs_write : in std_logic; + reg_dp_split_avs_writedata : in std_logic_vector(32 - 1 downto 0); + + -- reg_bsn_monitor_v2_rx + reg_bsn_monitor_v2_rx_avs_address : in std_logic_vector(32 - 1 downto 0); + reg_bsn_monitor_v2_rx_avs_read : in std_logic; + reg_bsn_monitor_v2_rx_avs_readdata : out std_logic_vector(32 - 1 downto 0); + reg_bsn_monitor_v2_rx_avs_readdatavalid : out std_logic; + reg_bsn_monitor_v2_rx_avs_waitrequest : out std_logic; + reg_bsn_monitor_v2_rx_avs_write : in std_logic; + reg_bsn_monitor_v2_rx_avs_writedata : in std_logic_vector(32 - 1 downto 0); + + -- reg_strobe_total_count_rx + reg_strobe_total_count_rx_avs_address : in std_logic_vector(32 - 1 downto 0); + reg_strobe_total_count_rx_avs_read : in std_logic; + reg_strobe_total_count_rx_avs_readdata : out std_logic_vector(32 - 1 downto 0); + reg_strobe_total_count_rx_avs_readdatavalid : out std_logic; + reg_strobe_total_count_rx_avs_waitrequest : out std_logic; + reg_strobe_total_count_rx_avs_write : in std_logic; + reg_strobe_total_count_rx_avs_writedata : in std_logic_vector(32 - 1 downto 0) + + ); +end rdma_demo_roce_tester_wrapper; + + +architecture str of rdma_demo_roce_tester_wrapper is + constant c_nof_byte : natural := c_rdma_demo_nof_octet_output_100gbe; + + signal rx_udp_sosi_arr : t_dp_sosi_arr(0 downto 0) := (others => c_dp_sosi_rst); + signal rx_udp_siso_arr : t_dp_siso_arr(0 downto 0) := (others => c_dp_siso_rdy); + signal tx_udp_sosi_arr : t_dp_sosi_arr(0 downto 0) := (others => c_dp_sosi_rst); + signal tx_udp_siso_arr : t_dp_siso_arr(0 downto 0) := (others => c_dp_siso_rdy); + + signal rx_udp_axi4_sosi : t_axi4_sosi := c_axi4_sosi_rst; + signal rx_udp_axi4_siso : t_axi4_siso := c_axi4_siso_rst; + signal tx_udp_axi4_sosi : t_axi4_sosi := c_axi4_sosi_rst; + signal tx_udp_axi4_siso : t_axi4_siso := c_axi4_siso_rst; + + signal reg_bg_ctrl_copi : t_mem_copi := c_mem_copi_rst; + signal reg_bg_ctrl_cipo : t_mem_cipo; + signal reg_hdr_dat_copi : t_mem_copi := c_mem_copi_rst; + signal reg_hdr_dat_cipo : t_mem_cipo; + signal reg_bsn_monitor_v2_tx_copi : t_mem_copi := c_mem_copi_rst; + signal reg_bsn_monitor_v2_tx_cipo : t_mem_cipo; + signal reg_strobe_total_count_tx_copi : t_mem_copi := c_mem_copi_rst; + signal reg_strobe_total_count_tx_cipo : t_mem_cipo; + signal reg_dp_split_copi : t_mem_copi := c_mem_copi_rst; + signal reg_dp_split_cipo : t_mem_cipo; + + signal reg_bsn_monitor_v2_rx_copi : t_mem_copi := c_mem_copi_rst; + signal reg_bsn_monitor_v2_rx_cipo : t_mem_cipo; + signal reg_strobe_total_count_rx_copi : t_mem_copi := c_mem_copi_rst; + signal reg_strobe_total_count_rx_cipo : t_mem_cipo; + + signal mm_rst : std_logic := '0'; + signal st_rst : std_logic := '0'; + +begin + + u_eth_tester : entity eth_lib.eth_tester + generic map ( + g_nof_octet_generate => c_rdma_demo_nof_octet_generate_100gbe, + g_nof_octet_output => c_rdma_demo_nof_octet_output_100gbe, + g_use_eth_header => false, + g_use_ip_udp_header => true, + g_use_dp_header => false, + g_hdr_calc_ip_crc => true, + g_hdr_field_arr => c_rdma_demo_roce_hdr_field_arr, + g_hdr_field_sel => c_rdma_demo_roce_hdr_field_sel, + -- Add icrc length here as g_hdr_app_len is used to calculate the total packet length. + g_hdr_app_len => c_rdma_demo_roce_hdr_len + c_rdma_demo_roce_icrc_len, + g_remove_crc => false + ) + port map ( + -- Clocks and reset + mm_rst => mm_rst, + mm_clk => mm_clk, + st_rst => st_rst, + st_clk => st_clk, + st_pps => st_pps, + + -- UDP transmit interface + eth_src_mac => eth_src_mac, + ip_src_addr => ip_src_addr, + udp_src_port => udp_src_port, + + tx_fifo_rd_emp_arr => tx_fifo_rd_emp_arr, + + tx_udp_sosi_arr => tx_udp_sosi_arr, + tx_udp_siso_arr => tx_udp_siso_arr, + + -- UDP receive interface + rx_udp_sosi_arr => rx_udp_sosi_arr, + + -- Memory Mapped Slaves (one per stream) + reg_bg_ctrl_copi => reg_bg_ctrl_copi, + reg_bg_ctrl_cipo => reg_bg_ctrl_cipo, + reg_hdr_dat_copi => reg_hdr_dat_copi, + reg_hdr_dat_cipo => reg_hdr_dat_cipo, + reg_bsn_monitor_v2_tx_copi => reg_bsn_monitor_v2_tx_copi, + reg_bsn_monitor_v2_tx_cipo => reg_bsn_monitor_v2_tx_cipo, + reg_strobe_total_count_tx_copi => reg_strobe_total_count_tx_copi, + reg_strobe_total_count_tx_cipo => reg_strobe_total_count_tx_cipo, + reg_dp_split_copi => reg_dp_split_copi, + reg_dp_split_cipo => reg_dp_split_cipo, + + reg_bsn_monitor_v2_rx_copi => reg_bsn_monitor_v2_rx_copi, + reg_bsn_monitor_v2_rx_cipo => reg_bsn_monitor_v2_rx_cipo, + reg_strobe_total_count_rx_copi => reg_strobe_total_count_rx_copi, + reg_strobe_total_count_rx_cipo => reg_strobe_total_count_rx_cipo + ); + + -- DP to AXI4 + u_axi4_tx_udp : entity axi4_lib.axi4_stream_dp_bridge + generic map ( + g_axi4_rl => 0, + g_dp_rl => 1, + g_active_low_rst => true + ) + port map ( + in_clk => st_clk, + in_rst => aresetn, + + dp_rst => st_rst, + + dp_in_sosi => tx_udp_sosi_arr(0), + dp_in_siso => tx_udp_siso_arr(0), + + axi4_out_sosi => tx_udp_axi4_sosi, + axi4_out_siso => tx_udp_axi4_siso + ); + + u_axi4_rx_udp : entity axi4_lib.axi4_stream_dp_bridge + generic map ( + g_axi4_rl => 0, + g_dp_rl => 1, + g_active_low_rst => true + ) + port map ( + in_clk => st_clk, + in_rst => aresetn, + + axi4_in_sosi => rx_udp_axi4_sosi, + axi4_in_siso => rx_udp_axi4_siso, + + dp_out_sosi => rx_udp_sosi_arr(0), + dp_out_siso => rx_udp_siso_arr(0) + ); + + -- Wire Records to IN/OUT ports. + -- tx_udp + tx_udp_axi4_siso.tready <= tx_udp_tready; + + tx_udp_tvalid <= tx_udp_axi4_sosi.tvalid; + tx_udp_tstrb <= tx_udp_axi4_sosi.tstrb; + tx_udp_tlast <= tx_udp_axi4_sosi.tlast; + tx_udp_tid <= tx_udp_axi4_sosi.tid; + tx_udp_tdest <= tx_udp_axi4_sosi.tdest; + tx_udp_tuser <= tx_udp_axi4_sosi.tuser; + -- reverse order of bytes + gen_tx_data : for I in 0 to c_nof_byte - 1 generate + tx_udp_tdata( (I + 1) * c_octet_w - 1 downto I * c_octet_w) <= tx_udp_axi4_sosi.tdata((c_nof_byte - I) * c_octet_w - 1 downto (c_nof_byte - 1 - I) * c_octet_w); + tx_udp_tkeep(I) <= tx_udp_axi4_sosi.tkeep(c_nof_byte - 1 - I); + end generate; + + -- rx_udp + rx_udp_tready <= rx_udp_axi4_siso.tready; + + rx_udp_axi4_sosi.tvalid <= rx_udp_tvalid; + rx_udp_axi4_sosi.tstrb <= rx_udp_tstrb; + rx_udp_axi4_sosi.tlast <= rx_udp_tlast; + rx_udp_axi4_sosi.tid <= rx_udp_tid; + rx_udp_axi4_sosi.tdest <= rx_udp_tdest; + rx_udp_axi4_sosi.tuser <= rx_udp_tuser; + -- reverse order of bytes + gen_rx_data : for I in 0 to c_nof_byte - 1 generate + rx_udp_axi4_sosi.tdata( (I + 1) * c_octet_w - 1 downto I * c_octet_w) <= rx_udp_tdata((c_nof_byte - I) * c_octet_w - 1 downto (c_nof_byte - 1 - I) * c_octet_w); + rx_udp_axi4_sosi.tkeep(I) <= rx_udp_tkeep(c_nof_byte - 1 - I); + end generate; + + -- reg_bg_ctrl + -- copi + reg_bg_ctrl_copi.address <= reg_bg_ctrl_avs_address; + reg_bg_ctrl_copi.wrdata <= RESIZE_UVEC(reg_bg_ctrl_avs_writedata, c_mem_data_w); + reg_bg_ctrl_copi.wr <= reg_bg_ctrl_avs_write; + reg_bg_ctrl_copi.rd <= reg_bg_ctrl_avs_read; + -- cipo + reg_bg_ctrl_avs_readdata <= RESIZE_UVEC_32(reg_bg_ctrl_cipo.rddata); + reg_bg_ctrl_avs_readdatavalid <= reg_bg_ctrl_cipo.rdval; + reg_bg_ctrl_avs_waitrequest <= reg_bg_ctrl_cipo.waitrequest; + + -- reg_hdr_dat + -- copi + reg_hdr_dat_copi.address <= reg_hdr_dat_avs_address; + reg_hdr_dat_copi.wrdata <= RESIZE_UVEC(reg_hdr_dat_avs_writedata, c_mem_data_w); + reg_hdr_dat_copi.wr <= reg_hdr_dat_avs_write; + reg_hdr_dat_copi.rd <= reg_hdr_dat_avs_read; + -- cipo + reg_hdr_dat_avs_readdata <= RESIZE_UVEC_32(reg_hdr_dat_cipo.rddata); + reg_hdr_dat_avs_readdatavalid <= reg_hdr_dat_cipo.rdval; + reg_hdr_dat_avs_waitrequest <= reg_hdr_dat_cipo.waitrequest; + + -- reg_bsn_monitor_v2_tx + -- copi + reg_bsn_monitor_v2_tx_copi.address <= reg_bsn_monitor_v2_tx_avs_address; + reg_bsn_monitor_v2_tx_copi.wrdata <= RESIZE_UVEC(reg_bsn_monitor_v2_tx_avs_writedata, c_mem_data_w); + reg_bsn_monitor_v2_tx_copi.wr <= reg_bsn_monitor_v2_tx_avs_write; + reg_bsn_monitor_v2_tx_copi.rd <= reg_bsn_monitor_v2_tx_avs_read; + -- cipo + reg_bsn_monitor_v2_tx_avs_readdata <= RESIZE_UVEC_32(reg_bsn_monitor_v2_tx_cipo.rddata); + reg_bsn_monitor_v2_tx_avs_readdatavalid <= reg_bsn_monitor_v2_tx_cipo.rdval; + reg_bsn_monitor_v2_tx_avs_waitrequest <= reg_bsn_monitor_v2_tx_cipo.waitrequest; + + -- reg_strobe_total_count_tx + -- copi + reg_strobe_total_count_tx_copi.address <= reg_strobe_total_count_tx_avs_address; + reg_strobe_total_count_tx_copi.wrdata <= RESIZE_UVEC(reg_strobe_total_count_tx_avs_writedata, c_mem_data_w); + reg_strobe_total_count_tx_copi.wr <= reg_strobe_total_count_tx_avs_write; + reg_strobe_total_count_tx_copi.rd <= reg_strobe_total_count_tx_avs_read; + -- cipo + reg_strobe_total_count_tx_avs_readdata <= RESIZE_UVEC_32(reg_strobe_total_count_tx_cipo.rddata); + reg_strobe_total_count_tx_avs_readdatavalid <= reg_strobe_total_count_tx_cipo.rdval; + reg_strobe_total_count_tx_avs_waitrequest <= reg_strobe_total_count_tx_cipo.waitrequest; + + -- reg_dp_split + -- copi + reg_dp_split_copi.address <= reg_dp_split_avs_address; + reg_dp_split_copi.wrdata <= RESIZE_UVEC(reg_dp_split_avs_writedata, c_mem_data_w); + reg_dp_split_copi.wr <= reg_dp_split_avs_write; + reg_dp_split_copi.rd <= reg_dp_split_avs_read; + -- cipo + reg_dp_split_avs_readdata <= RESIZE_UVEC_32(reg_dp_split_cipo.rddata); + reg_dp_split_avs_readdatavalid <= reg_dp_split_cipo.rdval; + reg_dp_split_avs_waitrequest <= reg_dp_split_cipo.waitrequest; + + -- reg_bsn_monitor_v2_rx + -- copi + reg_bsn_monitor_v2_rx_copi.address <= reg_bsn_monitor_v2_rx_avs_address; + reg_bsn_monitor_v2_rx_copi.wrdata <= RESIZE_UVEC(reg_bsn_monitor_v2_rx_avs_writedata, c_mem_data_w); + reg_bsn_monitor_v2_rx_copi.wr <= reg_bsn_monitor_v2_rx_avs_write; + reg_bsn_monitor_v2_rx_copi.rd <= reg_bsn_monitor_v2_rx_avs_read; + -- cipo + reg_bsn_monitor_v2_rx_avs_readdata <= RESIZE_UVEC_32(reg_bsn_monitor_v2_rx_cipo.rddata); + reg_bsn_monitor_v2_rx_avs_readdatavalid <= reg_bsn_monitor_v2_rx_cipo.rdval; + reg_bsn_monitor_v2_rx_avs_waitrequest <= reg_bsn_monitor_v2_rx_cipo.waitrequest; + + -- reg_strobe_total_count_rx + -- copi + reg_strobe_total_count_rx_copi.address <= reg_strobe_total_count_rx_avs_address; + reg_strobe_total_count_rx_copi.wrdata <= RESIZE_UVEC(reg_strobe_total_count_rx_avs_writedata, c_mem_data_w); + reg_strobe_total_count_rx_copi.wr <= reg_strobe_total_count_rx_avs_write; + reg_strobe_total_count_rx_copi.rd <= reg_strobe_total_count_rx_avs_read; + -- cipo + reg_strobe_total_count_rx_avs_readdata <= RESIZE_UVEC_32(reg_strobe_total_count_rx_cipo.rddata); + reg_strobe_total_count_rx_avs_readdatavalid <= reg_strobe_total_count_rx_cipo.rdval; + reg_strobe_total_count_rx_avs_waitrequest <= reg_strobe_total_count_rx_cipo.waitrequest; + +end str; diff --git a/libraries/base/common/src/vhdl/common_network_layers_pkg.vhd b/libraries/base/common/src/vhdl/common_network_layers_pkg.vhd index 50a30cdf2c736c1c715c72000d912d9495fe8967..520d585be9ad53a73ee97e3de2209ec7c69c7c63 100644 --- a/libraries/base/common/src/vhdl/common_network_layers_pkg.vhd +++ b/libraries/base/common/src/vhdl/common_network_layers_pkg.vhd @@ -26,6 +26,7 @@ library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; use work.common_pkg.all; +use work.common_field_pkg.all; package common_network_layers_pkg is @@ -355,8 +356,36 @@ package common_network_layers_pkg is constant c_network_udp_header_ones : t_network_udp_header := ("0000000000000001", "0000000000000001", "0000000000000001", "0000000000000001"); + function func_network_ip_header_checksum(field_arr : t_common_field_arr; hdr_fields_slv : std_logic_vector) return std_logic_vector; + + end common_network_layers_pkg; package body common_network_layers_pkg is + + function func_network_ip_header_checksum(field_arr : t_common_field_arr; hdr_fields_slv : std_logic_vector) return std_logic_vector is + -- function to calculate the ip header checksum based on a header field array. + constant c_cin_w : natural := 4; -- bit width of carry + constant c_nof_halfword : natural := (c_network_ip_header_len / c_halfword_sz) - 1; -- -1 as we exclude the checksum field itself for calculation. + + variable sum : unsigned(c_network_ip_header_checksum_w + c_cin_w - 1 downto 0) := (others => '0'); + variable crc : std_logic_vector(c_network_ip_header_checksum_w - 1 downto 0); + variable vec : std_logic_vector(c_halfword_w * c_nof_halfword - 1 downto 0); + begin + -- vec = whole ip header excluding ip_header_checksum. + vec := + hdr_fields_slv(field_hi(field_arr, "ip_version" ) downto field_lo(field_arr, "ip_protocol" )) + & hdr_fields_slv(field_hi(field_arr, "ip_src_addr" ) downto field_lo(field_arr, "ip_dst_addr" )); + + -- sum up vec in halfwords + for i in 0 to c_nof_halfword - 1 loop + sum := sum + unsigned(vec(( i + 1 ) * c_halfword_w - 1 downto i * c_halfword_w)); + end loop; + + -- checksum = inverted (sum + carry) + crc := not(std_logic_vector(sum(c_halfword_w - 1 downto 0) + sum(sum'high downto c_halfword_w))); + return crc; + end func_network_ip_header_checksum; + end common_network_layers_pkg; diff --git a/libraries/io/eth/hdllib.cfg b/libraries/io/eth/hdllib.cfg index 909d905bed2e804092d562c2646aefd752c37d4b..86eb82159e6c2e1299e59bb71258a3df01426a58 100644 --- a/libraries/io/eth/hdllib.cfg +++ b/libraries/io/eth/hdllib.cfg @@ -7,7 +7,7 @@ hdl_lib_technology = synth_files = src/vhdl/eth_pkg.vhd src/vhdl/eth_checksum.vhd - src/vhdl/eth_checksum_10g.vhd + src/vhdl/eth_ip_header_checksum.vhd src/vhdl/eth_hdr_store.vhd src/vhdl/eth_hdr_status.vhd src/vhdl/eth_hdr_ctrl.vhd @@ -33,6 +33,7 @@ test_bench_files = src/vhdl/eth_statistics.vhd tb/vhdl/tb_eth_checksum.vhd tb/vhdl/tb_eth_crc_ctrl.vhd + tb/vhdl/tb_eth_ip_header_checksum.vhd tb/vhdl/tb_eth_hdr.vhd tb/vhdl/tb_eth.vhd tb/vhdl/tb_eth_tester_pkg.vhd @@ -46,6 +47,7 @@ test_bench_files = tb/vhdl/tb_eth_udp_offload.vhd tb/vhdl/tb_eth_ihl_to_20.vhd tb/vhdl/tb_tb_tb_eth_regression.vhd + tb/vhdl/tb_tb_eth_ip_header_checksum.vhd regression_test_vhdl = tb/vhdl/tb_eth_checksum.vhd diff --git a/libraries/io/eth/src/vhdl/eth_checksum_10g.vhd b/libraries/io/eth/src/vhdl/eth_checksum_10g.vhd deleted file mode 100644 index 1519fdcd353e552effcc5c89a6f12557369d4da6..0000000000000000000000000000000000000000 --- a/libraries/io/eth/src/vhdl/eth_checksum_10g.vhd +++ /dev/null @@ -1,157 +0,0 @@ -------------------------------------------------------------------------------- --- --- 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; ----------------------------------------------------------------------------------- --- Purpose: -- --- Can be used to calculate and insert the checksum for the IP header -- --- in a 10GbE package, if the correct longwords are provided. -- --- Description: -- --- Determine the 16 bit 1-complement checksum according IPv4 to for the valid -- --- words between snk_in.sop and the last ip header field. -- --- After calculation, the checksum is inserted in the outgoing stream -- ----------------------------------------------------------------------------------- - -entity eth_checksum_10g is - port ( - rst : in std_logic; - clk : in std_logic; - - src_out : out t_dp_sosi; - snk_in : in t_dp_sosi; - - src_in : in t_dp_siso; - snk_out : out t_dp_siso - ); -end eth_checksum_10g; - -architecture rtl of eth_checksum_10g is - - constant c_cin_w : natural := 4; -- bit width of carry - constant c_pipeline_delay : natural := 2; - - signal sum : unsigned(c_halfword_w + c_cin_w - 1 downto 0) := (others => '0'); - signal checksum : std_logic_vector(c_halfword_w - 1 downto 0); - signal cnt_clr, cnt_p_clr : std_logic; - signal cnt_en, cnt_p_en : std_logic; - signal count, count_p : std_logic_vector(31 downto 0); - signal dp_pipeline_src_out : t_dp_sosi; - -begin - ------------------------------------------------- - -- process to calculate the ip_header_checksum -- - ------------------------------------------------- - p_calc_chksum : process(clk) - begin - if rst = '1' then - sum <= (others => '0'); - - elsif rising_edge(clk) then - if cnt_clr = '1' then - sum <= (others => '0'); - - elsif cnt_en = '1' then - case TO_UINT(count) is - when 0 => -- 0 is the cycle after the sop due to the common_counter latency - sum <= sum + unsigned(snk_in.data(c_halfword_w - 1 downto 0)); -- ip_version, ip_header_length, ip_services - when 1 => - sum <= sum + unsigned(snk_in.data(c_halfword_w * 4 - 1 downto c_halfword_w * 3)) -- ip_total_length - + unsigned(snk_in.data(c_halfword_w * 3 - 1 downto c_halfword_w * 2)) -- ip_identification - + unsigned(snk_in.data(c_halfword_w * 2 - 1 downto c_halfword_w)) -- ip_flags, ip_fragment_offset - + unsigned(snk_in.data(c_halfword_w - 1 downto 0)); -- ip_time_to_live, ip_protocol - when 2 => -- skip ip_header_checksum - sum <= sum + unsigned(snk_in.data(c_halfword_w * 3 - 1 downto c_halfword_w * 2)) -- ip_src_addr(1/2) - + unsigned(snk_in.data(c_halfword_w * 2 - 1 downto c_halfword_w)) -- ip_src_addr(2/2) - + unsigned(snk_in.data(c_halfword_w - 1 downto 0)); -- ip_dst_addr(1/2) - when 3 => - sum <= sum + unsigned(snk_in.data(c_halfword_w * 4 - 1 downto c_halfword_w * 3)); -- ip_dst_addr(2/2) - - when others => - end case; - end if; - end if; - end process; - - --------------------------------------------------- - -- process to insert checksum in outgoing stream -- - --------------------------------------------------- - checksum <= not(std_logic_vector(sum(c_halfword_w - 1 downto 0) + sum(sum'high downto c_halfword_w))); -- checksum = inverted (sum + carry) - p_insert_chksum : process(dp_pipeline_src_out, checksum, count) - begin - src_out <= dp_pipeline_src_out; - if TO_UINT(count_p) = 2 then - src_out.data(c_halfword_w * 4 - 1 downto c_halfword_w * 3) <= checksum; - end if; - end process; - - ------------------------------------------------------------------------------------------- - -- useing common_counter to keep track of the word alignment during checksum calculation -- - ------------------------------------------------------------------------------------------- - cnt_en <= snk_in.valid; -- only count when valid - cnt_clr <= snk_in.sop; -- restart counter on sop - - u_calc_counter : entity common_lib.common_counter - port map ( - rst => rst, - clk => clk, - cnt_clr => cnt_clr, - cnt_en => cnt_en, - count => count - ); - ----------------------------------------------------------------------------------------- - -- useing common_counter to keep track of the word alignment during checksum insertion -- - ----------------------------------------------------------------------------------------- - cnt_p_en <= dp_pipeline_src_out.valid; -- only count when valid - cnt_p_clr <= dp_pipeline_src_out.sop; -- restart counter on sop - - u_pipe_counter : entity common_lib.common_counter - port map ( - rst => rst, - clk => clk, - cnt_clr => cnt_p_clr, - cnt_en => cnt_p_en, - count => count_p - ); - - -------------------------------------------------------------------------------- - -- useing dp_pipeline to make room for the checksum calculation and insertion -- - -------------------------------------------------------------------------------- - u_dp_pipeline : entity dp_lib.dp_pipeline - generic map ( - g_pipeline => c_pipeline_delay - ) - port map ( - rst => rst, - clk => clk, - -- ST sink - snk_out => snk_out, - snk_in => snk_in, - -- ST source - src_in => src_in, - src_out => dp_pipeline_src_out - ); - -end rtl; diff --git a/libraries/io/eth/src/vhdl/eth_ip_header_checksum.vhd b/libraries/io/eth/src/vhdl/eth_ip_header_checksum.vhd new file mode 100644 index 0000000000000000000000000000000000000000..5f5067f9520f1b9b3042e41e0ae4fbbb8809ed7d --- /dev/null +++ b/libraries/io/eth/src/vhdl/eth_ip_header_checksum.vhd @@ -0,0 +1,176 @@ +-- -------------------------------------------------------------------------- +-- Copyright 2023 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- -------------------------------------------------------------------------- +-- +-- Author: R. van der Walle +-- Purpose: +-- Can be used to calculate and insert the checksum for the IP header +-- in a network package, if the correct hdr_fields_slv_in is provided. +-- Description: +-- Determine the 16 bit 1-complement checksum according IPv4 to for the +-- hdr_fields_slv_in. +-- After calculation, the checksum is inserted in the outgoing stream at +-- corresponding position based on g_hdr_field_arr and g_data_w. +-- Remarks: +-- The hdr_fields_slv_in should be valid on the snk_in.sop + + +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_field_pkg.all; +use common_lib.common_network_layers_pkg.all; +use dp_lib.dp_stream_pkg.all; + +entity eth_ip_header_checksum is + generic ( + g_data_w : natural := 64; + g_hdr_field_arr : t_common_field_arr + ); + port ( + rst : in std_logic; + clk : in std_logic; + + src_out : out t_dp_sosi; + snk_in : in t_dp_sosi; + + src_in : in t_dp_siso; + snk_out : out t_dp_siso; + + hdr_fields_slv_in : std_logic_vector(1023 downto 0) := (others => '0') + ); +end eth_ip_header_checksum; + +architecture rtl of eth_ip_header_checksum is + + constant c_hdr_len : natural := field_slv_len(g_hdr_field_arr); + constant c_hdr_crc_bit_hi : natural := field_hi(g_hdr_field_arr, "ip_header_checksum"); + constant c_hdr_crc_bit_lo : natural := field_lo(g_hdr_field_arr, "ip_header_checksum"); + + -- calculate which word(s) of the incoming snk_in stream should contain the checksum. + constant c_hdr_crc_word_hi : natural := sel_a_b((c_hdr_crc_bit_hi / g_data_w) > 0, c_hdr_crc_bit_hi / g_data_w, + sel_a_b( c_hdr_crc_bit_hi > (c_hdr_len mod g_data_w), 1, 0)); --special case as the last hdr word can be < g_data_w. + constant c_hdr_crc_word_lo : natural := sel_a_b((c_hdr_crc_bit_lo / g_data_w) > 0, c_hdr_crc_bit_lo / g_data_w, + sel_a_b( c_hdr_crc_bit_lo > (c_hdr_len mod g_data_w), 1, 0)); --special case as the last hdr word can be < g_data_w. + + -- calculate in which bit range of the selected word(s) the checksum should go. + constant c_crc_hi_bit_in_word : natural := g_data_w - ((c_hdr_len - c_hdr_crc_bit_hi) mod g_data_w); + constant c_crc_lo_bit_in_word : natural := (g_data_w - ((c_hdr_len - c_hdr_crc_bit_lo) mod g_data_w)) mod g_data_w; + + constant c_hdr_nof_words : natural := ceil_div(c_hdr_len, g_data_w); + constant c_crc_word_span : natural := 1 + c_hdr_crc_word_hi - c_hdr_crc_word_lo; + constant c_crc_in_one_word : boolean := c_crc_word_span = 1; + + signal checksum : std_logic_vector(c_network_ip_header_checksum_w - 1 downto 0) := (others => '0'); + signal count : std_logic_vector(31 downto 0); + signal dp_pipeline_src_out : t_dp_sosi; + signal reg_done : std_logic := '0'; + signal nxt_reg_done : std_logic := '0'; + +begin + + -- calculate checksum + checksum <= func_network_ip_header_checksum(g_hdr_field_arr, hdr_fields_slv_in) when rising_edge(clk); + + -- register to know when crc has been inserted. + reg_done <= nxt_reg_done when rising_edge(clk); + + --------------------------------------------------- + -- process to insert checksum in outgoing stream -- + --------------------------------------------------- + gen_insert_crc_one : if c_crc_in_one_word generate -- checksum is in 1 word. + p_insert_crc_one : process(dp_pipeline_src_out, checksum, count, reg_done) + variable v_count : natural := 0; + begin + v_count := TO_UINT(count); + src_out <= dp_pipeline_src_out; + nxt_reg_done <= reg_done; + if reg_done = '0' and dp_pipeline_src_out.valid = '1' and v_count = c_hdr_crc_word_hi then + src_out.data(c_crc_hi_bit_in_word downto c_crc_lo_bit_in_word) <= checksum; + nxt_reg_done <= '1'; + end if; + + if reg_done = '1' and dp_pipeline_src_out.eop = '1' then + nxt_reg_done <= '0'; + end if; + end process; + end generate; + + gen_insert_crc_multi : if not c_crc_in_one_word generate + p_insert_crc_multi : process(dp_pipeline_src_out, checksum, count, reg_done) + variable v_count : natural := 0; + variable v_hi : natural := 0; + variable v_lo : natural := 0; + begin + v_count := TO_UINT(count); + src_out <= dp_pipeline_src_out; + nxt_reg_done <= reg_done; + if reg_done = '0' and dp_pipeline_src_out.valid = '1' then + if v_count = c_hdr_crc_word_hi then + src_out.data(c_crc_hi_bit_in_word downto 0) <= checksum(c_network_ip_header_checksum_w - 1 downto c_network_ip_header_checksum_w - c_crc_hi_bit_in_word - 1); + elsif v_count = c_hdr_crc_word_lo then + src_out.data(g_data_w - 1 downto c_crc_lo_bit_in_word) <= checksum(g_data_w - c_crc_lo_bit_in_word - 1 downto 0); + nxt_reg_done <= '1'; + elsif v_count < c_hdr_crc_word_hi and v_count > c_hdr_crc_word_lo then + v_hi := c_network_ip_header_checksum_w - 1 - c_crc_hi_bit_in_word - 1 - g_data_w * (c_hdr_crc_word_hi - v_count - 1); + v_lo := v_hi + 1 - g_data_w; + src_out.data(g_data_w - 1 downto 0) <= checksum(v_hi downto v_lo); + end if; + end if; + + if reg_done = '1' and dp_pipeline_src_out.eop = '1' then + nxt_reg_done <= '0'; + end if; + end process; + end generate; + + ------------------------------------------------------------------------------------------ + -- using common_counter to keep track of the word alignment during checksum calculation -- + ------------------------------------------------------------------------------------------ + u_calc_counter : entity common_lib.common_counter + generic map ( + g_init => c_hdr_nof_words - 1, + g_step_size => -1 + ) + port map ( + rst => rst, + clk => clk, + cnt_ld => snk_in.sop, + cnt_en => snk_in.valid, + count => count + ); + + ------------------------------------------------------------------------------- + -- using dp_pipeline to make room for the checksum calculation and insertion -- + ------------------------------------------------------------------------------- + u_dp_pipeline : entity dp_lib.dp_pipeline + generic map ( + g_pipeline => 1 -- fixed to 1 as common_counter has fixed latency of 1 (cannot be higher) + ) + port map ( + rst => rst, + clk => clk, + -- ST sink + snk_out => snk_out, + snk_in => snk_in, + -- ST source + src_in => src_in, + src_out => dp_pipeline_src_out + ); + +end rtl; diff --git a/libraries/io/eth/src/vhdl/eth_tester.vhd b/libraries/io/eth/src/vhdl/eth_tester.vhd index f6e1e97f5a354312eb27b1372e4ee76b9ae65f2e..51aeb7a357bd1e5ad8e752464314628293f44e55 100644 --- a/libraries/io/eth/src/vhdl/eth_tester.vhd +++ b/libraries/io/eth/src/vhdl/eth_tester.vhd @@ -42,10 +42,13 @@ entity eth_tester is g_bg_sync_timeout : natural := c_eth_tester_sync_timeout; g_nof_octet_generate : natural := 1; g_nof_octet_output : natural := 4; -- must be multiple of g_nof_octet_generate. - g_use_network_header : boolean := true; + g_use_eth_header : boolean := true; + g_use_ip_udp_header : boolean := true; g_use_dp_header : boolean := true; + g_hdr_calc_ip_crc : boolean := false; g_hdr_field_arr : t_common_field_arr := c_eth_tester_hdr_field_arr; g_hdr_field_sel : std_logic_vector := c_eth_tester_hdr_field_sel; + g_hdr_app_len : natural := c_eth_tester_app_hdr_len; g_remove_crc : boolean := true -- use TRUE when using sim_tse and tech_tse link interface, -- use FALSE when streaming link interface ); @@ -125,10 +128,13 @@ begin g_bg_sync_timeout => g_bg_sync_timeout, g_nof_octet_generate => g_nof_octet_generate, g_nof_octet_output => g_nof_octet_output, - g_use_network_header => g_use_network_header, + g_use_eth_header => g_use_eth_header, + g_use_ip_udp_header => g_use_ip_udp_header, g_use_dp_header => g_use_dp_header, + g_hdr_calc_ip_crc => g_hdr_calc_ip_crc, g_hdr_field_arr => g_hdr_field_arr, - g_hdr_field_sel => g_hdr_field_sel + g_hdr_field_sel => g_hdr_field_sel, + g_hdr_app_len => g_hdr_app_len ) port map ( -- Clocks and reset diff --git a/libraries/io/eth/src/vhdl/eth_tester_tx.vhd b/libraries/io/eth/src/vhdl/eth_tester_tx.vhd index bc98e8848db0064c31edb423a52649701a86397a..43d049dcd8a677cb56777f7599019289a8586d71 100644 --- a/libraries/io/eth/src/vhdl/eth_tester_tx.vhd +++ b/libraries/io/eth/src/vhdl/eth_tester_tx.vhd @@ -48,11 +48,13 @@ entity eth_tester_tx is g_bg_sync_timeout : natural := 220 * 10**6; -- 10% margin for nominal 1 s with st_clk at 200MHz g_nof_octet_generate : natural := 1; g_nof_octet_output : natural := 4; -- must be multiple of g_nof_octet_generate - g_use_network_header : boolean := true; + g_use_eth_header : boolean := true; + g_use_ip_udp_header : boolean := true; g_use_dp_header : boolean := true; + g_hdr_calc_ip_crc : boolean := false; g_hdr_field_arr : t_common_field_arr := c_eth_tester_hdr_field_arr; - g_hdr_field_sel : std_logic_vector := c_eth_tester_hdr_field_sel - + g_hdr_field_sel : std_logic_vector := c_eth_tester_hdr_field_sel; + g_hdr_app_len : natural := c_eth_tester_app_hdr_len ); port ( -- Clocks and reset @@ -109,6 +111,7 @@ architecture str of eth_tester_tx is constant c_out_data_w : natural := g_nof_octet_output * c_octet_w; constant c_nof_symbols_max : natural := c_network_eth_payload_jumbo_max; constant c_use_split : boolean := sel_a_b(g_nof_octet_generate > 1, true, false); + constant c_hdr_calc_ip_crc : boolean := g_use_ip_udp_header and g_hdr_calc_ip_crc; signal ip_total_length : natural; signal udp_total_length : natural; @@ -134,6 +137,8 @@ architecture str of eth_tester_tx is signal i_tx_fifo_rd_emp : std_logic; signal tx_offload_siso : t_dp_siso; signal tx_offload_sosi : t_dp_sosi; + signal tx_offload_frame_siso : t_dp_siso; + signal tx_offload_frame_sosi : t_dp_sosi; signal i_ref_sync : std_logic := '0'; signal in_strobe_arr : std_logic_vector(c_nof_total_counts - 1 downto 0); @@ -334,12 +339,15 @@ begin -- until it restarts, so no need to pass bg_block_len on via e.g. the channel -- field in u_fifo. bg_block_len <= split_nof_symbols when c_use_split else TO_UINT(bg_ctrl_hold.samples_per_packet(15 downto 0)); -- packet lenghts fit in 16b - app_total_length <= c_eth_tester_app_hdr_len + bg_block_len when rising_edge(st_clk); + app_total_length <= g_hdr_app_len + bg_block_len when rising_edge(st_clk); udp_total_length <= c_network_udp_header_len + app_total_length when rising_edge(st_clk); ip_total_length <= c_network_ip_header_len + udp_total_length when rising_edge(st_clk); - gen_network_header : if g_use_network_header generate + gen_eth_header : if g_use_eth_header generate hdr_fields_slv_in(field_hi(g_hdr_field_arr, "eth_src_mac" ) downto field_lo(g_hdr_field_arr, "eth_src_mac" )) <= eth_src_mac; + end generate; + + gen_ip_udp_header : if g_use_ip_udp_header generate hdr_fields_slv_in(field_hi(g_hdr_field_arr, "ip_total_length" ) downto field_lo(g_hdr_field_arr, "ip_total_length" )) <= TO_UVEC(ip_total_length, 16); hdr_fields_slv_in(field_hi(g_hdr_field_arr, "ip_src_addr" ) downto field_lo(g_hdr_field_arr, "ip_src_addr" )) <= ip_src_addr; hdr_fields_slv_in(field_hi(g_hdr_field_arr, "udp_src_port" ) downto field_lo(g_hdr_field_arr, "udp_src_port" )) <= udp_src_port; @@ -387,6 +395,33 @@ begin hdr_fields_rec_in <= func_eth_tester_map_header(hdr_fields_slv_in); hdr_fields_rec_tx <= func_eth_tester_map_header(hdr_fields_slv_tx); + ------------------------------------------------------------------------------- + -- IP header checksum + ------------------------------------------------------------------------------- + gen_ip_crc : if c_hdr_calc_ip_crc generate + u_eth_ip_header_checksum : entity work.eth_ip_header_checksum + generic map ( + g_data_w => c_out_data_w, + g_hdr_field_arr => g_hdr_field_arr + ) + port map ( + rst => st_rst, + clk => st_clk, + + snk_in => tx_offload_sosi, + snk_out => tx_offload_siso, + + src_out => tx_offload_frame_sosi, + src_in => tx_offload_frame_siso, + + hdr_fields_slv_in => hdr_fields_slv_tx + ); + end generate; + + gen_no_ip_crc : if not c_hdr_calc_ip_crc generate + tx_offload_frame_sosi <= tx_offload_sosi; + tx_offload_siso <= tx_offload_frame_siso; + end generate; ------------------------------------------------------------------------------- -- dp_pipeline_ready to ease timing closure @@ -396,8 +431,8 @@ begin rst => st_rst, clk => st_clk, - snk_out => tx_offload_siso, - snk_in => tx_offload_sosi, + snk_out => tx_offload_frame_siso, + snk_in => tx_offload_frame_sosi, src_in => tx_udp_siso, src_out => i_tx_udp_sosi ); diff --git a/libraries/io/eth/tb/vhdl/tb_eth_ip_header_checksum.vhd b/libraries/io/eth/tb/vhdl/tb_eth_ip_header_checksum.vhd new file mode 100644 index 0000000000000000000000000000000000000000..58c8d41dfeebcb8000991107099a6a4cde6a2c35 --- /dev/null +++ b/libraries/io/eth/tb/vhdl/tb_eth_ip_header_checksum.vhd @@ -0,0 +1,674 @@ +-- -------------------------------------------------------------------------- +-- Copyright 2023 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- -------------------------------------------------------------------------- +-- Author: R. van der Walle +-- Purpose: +-- . Test bench for eth_ip_header_checksum. +-- Description: +-- u_tx DUT u_rx +-- ___________________ ___________________ ___________________ +-- |dp_offload_tx_v3 | |eth_ip_header_ | |dp_offload_rx | +-- stimuli_src -->| |----->|checksum |----->| |--> verify_snk +-- | in out | | in out | | | in out | +-- |___________________| |___________________| | |___________________| +-- | +-- i link_offload_sosi +-- +-- The verification of the header - data block boundary is controlled via +-- g_symbol_w: +-- . g_symbol_w = g_data_w : boundary at g_data_w +-- . g_symbol_w < g_data_w : boundary at g_symbol_w, by reducing the number +-- of the header dp_bsn field by 1 symbol. If the c_bsn_w <= 32 then the +-- header MM interface only needs one MM word to read the header, so +-- therefore there are two sizes of c_expected_tx_hdr_word_arr_* and +-- c_expected_rx_hdr_word_arr_*. +-- +-- Remarks: +-- . The g_flow_control_verify has to be e_active, otherwise the tb fails, +-- probably due to limitation in dp_offload_rx.vhd. +-- . It appears that the tx_hdr_word read values are the MM write values, so +-- read of value from logic fields (with MM override '0', e.g. dp_bsn, +-- eth_src_mac) is not supported. +-- . testbench is based on tb_offload_tx_v3. +-- +-- Usage: +-- > as 10 +-- > run -all +-- + +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_lfsr_sequences_pkg.all; +use common_lib.common_mem_pkg.all; +use common_lib.common_field_pkg.all; +use common_lib.common_str_pkg.all; +use common_lib.tb_common_pkg.all; +use common_lib.tb_common_mem_pkg.all; +use dp_lib.dp_stream_pkg.all; +use dp_lib.tb_dp_pkg.all; + +entity tb_eth_ip_header_checksum is + generic ( + -- general + g_flow_control_stimuli : t_dp_flow_control_enum := e_pulse; -- always e_active, e_random or e_pulse flow control + g_flow_control_verify : t_dp_flow_control_enum := e_active; -- always e_active, e_random or e_pulse flow control + g_print_en : boolean := true; + -- specific + g_data_w : natural := 64; + g_symbol_w : natural := 8; + g_empty : natural := 6; -- number of empty symbols in header when g_symbol_w < g_data_w, must be < c_nof_symbols_per_data + g_pkt_len : natural := 240; + g_pkt_gap : natural := 16 + ); +end tb_eth_ip_header_checksum; + + +architecture tb of tb_eth_ip_header_checksum is + + constant c_mm_clk_period : time := 1 ns; + constant c_dp_clk_period : time := 5 ns; + + -- Simulate header / payload boundary at g_data_w boundary or at g_symbol_w + -- boundary with one empty symbol, by adapting the size of the header dp_bsn + -- field: + -- . If g_symbol_w = g_data_w then boundary is at g_data_w, so empty is 0. + -- . If g_symbol_w < g_data_w then boundary is at last symbol, so empty is 1. + constant c_nof_symbols_per_data : natural := g_data_w / g_symbol_w; + constant c_nof_symbols_per_bsn : natural := c_dp_stream_bsn_w / g_symbol_w; -- = 64 / g_symbol_w + constant c_bsn_w : natural := sel_a_b(c_nof_symbols_per_data = 1, + g_symbol_w * c_nof_symbols_per_bsn, + g_symbol_w * (c_nof_symbols_per_bsn - g_empty)); + constant c_use_shortened_header : boolean := c_bsn_w <= c_word_w; + + -- dp_stream_stimuli + constant c_stimuli_pulse_active : natural := 3; + constant c_stimuli_pulse_period : natural := 4; + + -- dp_stream_verify + constant c_verify_pulse_active : natural := 1; + constant c_verify_pulse_period : natural := 5; + + constant c_data_max : unsigned(g_data_w - 1 downto 0) := (others => '1'); + constant c_dsp_max : unsigned(g_data_w - 1 downto 0) := (others => '1'); + + constant c_verify_snk_in_cnt_max : t_dp_sosi_unsigned := TO_DP_SOSI_UNSIGNED('0', '0', '0', '0', c_data_max, c_dsp_max, c_dsp_max, c_unsigned_0, c_unsigned_0, c_unsigned_0, c_unsigned_0); + constant c_verify_snk_in_cnt_gap : t_dp_sosi_unsigned := c_dp_sosi_unsigned_ones; -- default only accept increment +1 + + constant c_expected_pkt_len : natural := g_pkt_len; + constant c_sync_period : natural := 5; + constant c_sync_offset : natural := 2; + constant c_data_init : natural := 17; + constant c_bsn_init : std_logic_vector(c_dp_stream_bsn_w - 1 downto 0) := TO_DP_BSN(0); + constant c_nof_sync : natural := 3; + constant c_nof_packets : natural := c_sync_period * c_nof_sync; + + constant c_hdr_len : natural := ceil_div(448, g_data_w); + constant c_wait_last_evt : natural := 100 + c_nof_packets * c_hdr_len; + + ----------------------------------------------------------------------------- + -- Tx offload + ----------------------------------------------------------------------------- + -- From apertif_udp_offload_pkg.vhd: + constant c_udp_offload_nof_hdr_fields : natural := 3 + 12 + 4 + 3; -- 22, 448b; 7 64b words + constant c_udp_offload_nof_hdr_words_default : natural := 26; -- 23 single word + 3 double word = 26 32b words + constant c_udp_offload_nof_hdr_words_shortened : natural := c_udp_offload_nof_hdr_words_default - 1; + constant c_udp_offload_nof_hdr_words : natural := sel_a_b(c_use_shortened_header, c_udp_offload_nof_hdr_words_shortened, c_udp_offload_nof_hdr_words_default); + + -- Notes: + -- . pre-calculated ip_header_checksum is valid only for UNB0, FN0 targeting IP 10.10.10.10 + -- . udp_total_length = 176 beamlets * 64b / 8b = 1408B + 14 DP bytes + 8 UDP bytes = 1430B + constant c_udp_offload_hdr_field_arr : t_common_field_arr(c_udp_offload_nof_hdr_fields - 1 downto 0) := ( -- index + ( field_name_pad("eth_dst_mac" ), "RW", 48, field_default(x"001B214368AC") ), -- 21 + ( field_name_pad("eth_src_mac" ), "RW", 48, field_default(x"0123456789AB") ), -- 20 + ( field_name_pad("eth_type" ), "RW", 16, field_default(x"0800") ), -- 19 + ( field_name_pad("ip_version" ), "RW", 4, field_default(4) ), -- 18 + ( field_name_pad("ip_header_length" ), "RW", 4, field_default(5) ), -- 17 + ( field_name_pad("ip_services" ), "RW", 8, field_default(0) ), -- 16 + ( field_name_pad("ip_total_length" ), "RW", 16, field_default(1450) ), -- 15 + ( field_name_pad("ip_identification" ), "RW", 16, field_default(0) ), -- 14 + ( field_name_pad("ip_flags" ), "RW", 3, field_default(2) ), -- 13 + ( field_name_pad("ip_fragment_offset" ), "RW", 13, field_default(0) ), -- 12 + ( field_name_pad("ip_time_to_live" ), "RW", 8, field_default(127) ), -- 11 + ( field_name_pad("ip_protocol" ), "RW", 8, field_default(17) ), -- 10 + ( field_name_pad("ip_header_checksum" ), "RW", 16, field_default(0) ), -- 9, will be calculated by DUT + ( field_name_pad("ip_src_addr" ), "RW", 32, field_default(x"C0A80009") ), -- 8 + ( field_name_pad("ip_dst_addr" ), "RW", 32, field_default(x"C0A80001") ), -- 7 + ( field_name_pad("udp_src_port" ), "RW", 16, field_default(0) ), -- 6 + ( field_name_pad("udp_dst_port" ), "RW", 16, field_default(0) ), -- 5 + ( field_name_pad("udp_total_length" ), "RW", 16, field_default(1430) ), -- 4 + ( field_name_pad("udp_checksum" ), "RW", 16, field_default(0) ), -- 3 + ( field_name_pad("dp_reserved" ), "RW", 47, field_default(x"010203040506") ), -- 2 + ( field_name_pad("dp_sync" ), "RW", 1, field_default(0) ), -- 1 + ( field_name_pad("dp_bsn" ), "RW", c_bsn_w, field_default(0) ) ); -- 0 + + -- TX: Corresponding storage of c_udp_offload_hdr_field_arr in MM register words + -- . Note: It appears that the tx_hdr_word read values are the MM write values, so read of value from logic fields (with MM override '0', e.g. dp_bsn, eth_src_mac) is not supported. + constant c_expected_tx_hdr_word_arr_default : t_slv_32_arr(0 to c_udp_offload_nof_hdr_words_default - 1) := ( -- word address + X"00000000", -- 0 = dp_bsn[31:0] -- readback is MM value, not the logic value + X"00000000", -- 1 = dp_bsn[c_bsn_w-1:32] + X"00000000", -- 2 = dp_sync + X"03040506", -- 3 = dp_reserved[31:0] + X"00000102", -- 4 = dp_reserved[47:32] + X"00000000", -- 5 = udp_checksum + X"00000596", -- 6 = udp_total_length + X"00000000", -- 7 = udp_dst_port + X"00000000", -- 8 = udp_src_port -- readback is MM value, not the logic value + X"C0A80001", -- 9 = ip_dst_addr + X"C0A80009", -- 10 = ip_src_addr + X"00000000", -- 11 = ip_header_checksum + X"00000011", -- 12 = ip_protocol + X"0000007F", -- 13 = ip_time_to_live + X"00000000", -- 14 = ip_fragment_offset + X"00000002", -- 15 = ip_flags + X"00000000", -- 16 = ip_identification + X"000005AA", -- 17 = ip_total_length + X"00000000", -- 18 = ip_services + X"00000005", -- 19 = ip_header_length + X"00000004", -- 20 = ip_version + X"00000800", -- 21 = eth_type[15:0] + X"456789AB", -- 22 = eth_src_mac[31:0] -- readback is MM value, not the logic value + X"00000123", -- 23 = eth_src_mac[47:32] + X"214368AC", -- 24 = eth_dst_mac[31:0] + X"0000001B"); -- 25 = eth_dst_mac[47:32] + + constant c_expected_tx_hdr_word_arr_shortened : t_slv_32_arr(0 to c_udp_offload_nof_hdr_words_shortened - 1) := ( -- word address + X"00000000", -- 0 = dp_bsn[c_bsn_w-1:0] -- readback is MM value, not the logic value + X"00000000", -- 1 = dp_sync + X"03040506", -- 2 = dp_reserved[31:0] + X"00000102", -- 3 = dp_reserved[47:32] + X"00000000", -- 4 = udp_checksum + X"00000596", -- 5 = udp_total_length + X"00000000", -- 6 = udp_dst_port + X"00000000", -- 7 = udp_src_port -- readback is MM value, not the logic value + X"C0A80001", -- 8 = ip_dst_addr + X"C0A80009", -- 9 = ip_src_addr + X"00000000", -- 10 = ip_header_checksum + X"00000011", -- 11 = ip_protocol + X"0000007F", -- 12 = ip_time_to_live + X"00000000", -- 13 = ip_fragment_offset + X"00000002", -- 14 = ip_flags + X"00000000", -- 15 = ip_identification + X"000005AA", -- 16 = ip_total_length + X"00000000", -- 17 = ip_services + X"00000005", -- 18 = ip_header_length + X"00000004", -- 19 = ip_version + X"00000800", -- 20 = eth_type[15:0] + X"456789AB", -- 21 = eth_src_mac[31:0] -- readback is MM value, not the logic value + X"00000123", -- 22 = eth_src_mac[47:32] + X"214368AC", -- 23 = eth_dst_mac[31:0] + X"0000001B"); -- 24 = eth_dst_mac[47:32] + + -- RX: Corresponding storage of c_udp_offload_hdr_field_arr in MM register words + constant c_expected_rx_hdr_word_arr_default : t_slv_32_arr(0 to c_udp_offload_nof_hdr_words_default - 1) := ( -- word address + X"00000002", -- 0 = dp_bsn[31:0] -- dynamic value obtained from simulation + X"00000000", -- 1 = dp_bsn[c_bsn_w-1:32] + X"00000001", -- 2 = dp_sync -- dynamic value obtained from simulation + X"03040506", -- 3 = dp_reserved[31:0] + X"00000102", -- 4 = dp_reserved[47:32] + X"00000000", -- 5 = udp_checksum + X"00000596", -- 6 = udp_total_length + X"00000000", -- 7 = udp_dst_port + X"00000000", -- 8 = udp_src_port + X"C0A80001", -- 9 = ip_dst_addr + X"C0A80009", -- 10 = ip_src_addr + X"000074E8", -- 11 = ip_header_checksum + X"00000011", -- 12 = ip_protocol + X"0000007F", -- 13 = ip_time_to_live + X"00000000", -- 14 = ip_fragment_offset + X"00000002", -- 15 = ip_flags + X"00000000", -- 16 = ip_identification + X"000005AA", -- 17 = ip_total_length + X"00000000", -- 18 = ip_services + X"00000005", -- 19 = ip_header_length + X"00000004", -- 20 = ip_version + X"00000800", -- 21 = eth_type[15:0] + X"86080000", -- 22 = eth_src_mac[31:0] -- readback is the logic value x"00228608" & id_backplane = 0 & id_chip = 0 (c_NODE_ID = 0) + X"00000022", -- 23 = eth_src_mac[47:32] + X"214368AC", -- 24 = eth_dst_mac[31:0] + X"0000001B"); -- 25 = eth_dst_mac[47:32] + + constant c_expected_rx_hdr_word_arr_shortened : t_slv_32_arr(0 to c_udp_offload_nof_hdr_words_shortened - 1) := ( -- word address + X"00000002", -- 0 = dp_bsn[c_bsn_w-1:0] -- dynamic value obtained from simulation + X"00000001", -- 1 = dp_sync -- dynamic value obtained from simulation + X"03040506", -- 2 = dp_reserved[31:0] + X"00000102", -- 3 = dp_reserved[47:32] + X"00000000", -- 4 = udp_checksum + X"00000596", -- 5 = udp_total_length + X"00000000", -- 6 = udp_dst_port + X"00000000", -- 7 = udp_src_port + X"C0A80001", -- 8 = ip_dst_addr + X"C0A80009", -- 9 = ip_src_addr + X"000074E8", -- 10 = ip_header_checksum + X"00000011", -- 11 = ip_protocol + X"0000007F", -- 12 = ip_time_to_live + X"00000000", -- 13 = ip_fragment_offset + X"00000002", -- 14 = ip_flags + X"00000000", -- 15 = ip_identification + X"000005AA", -- 16 = ip_total_length + X"00000000", -- 17 = ip_services + X"00000005", -- 18 = ip_header_length + X"00000004", -- 19 = ip_version + X"00000800", -- 20 = eth_type[15:0] + X"86080000", -- 21 = eth_src_mac[31:0] -- readback is the logic value x"00228608" & id_backplane = 0 & id_chip = 0 (c_NODE_ID = 0) + X"00000022", -- 22 = eth_src_mac[47:32] + X"214368AC", -- 23 = eth_dst_mac[31:0] + X"0000001B"); -- 24 = eth_dst_mac[47:32] + + -- Override ('1') only the Ethernet fields so we can use MM defaults there + constant c_hdr_field_ovr_init : std_logic_vector(c_udp_offload_nof_hdr_fields - 1 downto 0) := "101" & "111111111111" & "1111" & "100"; + + constant c_NODE_ID : std_logic_vector(7 downto 0) := TO_UVEC(0, 8); + + signal id_backplane : std_logic_vector(c_byte_w - 1 downto 0); + signal id_chip : std_logic_vector(c_byte_w - 1 downto 0); + + signal dp_fifo_sc_src_in : t_dp_siso := c_dp_siso_rdy; + signal dp_fifo_sc_src_out : t_dp_sosi; + + signal dp_offload_tx_snk_in_arr : t_dp_sosi_arr(0 downto 0); + signal dp_offload_tx_snk_out_arr : t_dp_siso_arr(0 downto 0); + + signal tx_hdr_fields_in_arr : t_slv_1024_arr(0 downto 0); + signal tx_hdr_fields_out_arr : t_slv_1024_arr(0 downto 0); + + signal tx_hdr_word : std_logic_vector(c_word_w - 1 downto 0); + signal rx_hdr_word : std_logic_vector(c_word_w - 1 downto 0); + + signal reg_dp_offload_tx_hdr_dat_mosi : t_mem_mosi := c_mem_mosi_rst; + signal reg_dp_offload_tx_hdr_dat_miso : t_mem_miso; + + ----------------------------------------------------------------------------- + -- Link + ----------------------------------------------------------------------------- + + signal tx_offload_sosi_arr : t_dp_sosi_arr(0 downto 0); + signal tx_offload_siso_arr : t_dp_siso_arr(0 downto 0); + + signal link_offload_sosi_arr : t_dp_sosi_arr(0 downto 0); + signal link_offload_siso_arr : t_dp_siso_arr(0 downto 0); + + ----------------------------------------------------------------------------- + -- Rx offload + ----------------------------------------------------------------------------- + signal dp_offload_rx_src_out_arr : t_dp_sosi_arr(0 downto 0); + signal dp_offload_rx_src_in_arr : t_dp_siso_arr(0 downto 0); + + signal rx_hdr_fields_out_arr : t_slv_1024_arr(0 downto 0); + signal rx_hdr_fields_raw_arr : t_slv_1024_arr(0 downto 0); + + signal reg_dp_offload_rx_hdr_dat_mosi : t_mem_mosi := c_mem_mosi_rst; + signal reg_dp_offload_rx_hdr_dat_miso : t_mem_miso; + + ----------------------------------------------------------------------------- + -- Test + ----------------------------------------------------------------------------- + signal mm_clk : std_logic := '1'; + signal mm_rst : std_logic := '1'; + signal dp_clk : std_logic := '1'; + signal dp_rst : std_logic := '1'; + signal tb_end : std_logic := '0'; + + signal stimuli_src_in : t_dp_siso := c_dp_siso_rdy; + signal stimuli_src_out : t_dp_sosi; + signal stimuli_src_out_data : std_logic_vector(g_data_w - 1 downto 0); + + signal verify_snk_in_enable : t_dp_sosi_sl := c_dp_sosi_sl_rst; + signal last_snk_in : t_dp_sosi; + signal last_snk_in_evt : std_logic; + signal verify_last_snk_in_evt : t_dp_sosi_sl := c_dp_sosi_sl_rst; + + signal verify_snk_out : t_dp_siso := c_dp_siso_rdy; + signal verify_snk_in : t_dp_sosi; + signal verify_snk_in_data : std_logic_vector(g_data_w - 1 downto 0); + signal prev_verify_snk_in_data : std_logic_vector(g_data_w - 1 downto 0); + +begin + + ------------------------------------------------------------------------------ + -- Clock & reset + ------------------------------------------------------------------------------ + mm_clk <= (not mm_clk) or tb_end after c_mm_clk_period / 2; + mm_rst <= '1', '0' after c_mm_clk_period * 7; + dp_clk <= (not dp_clk) or tb_end after c_dp_clk_period / 2; + dp_rst <= '1', '0' after c_dp_clk_period * 7; + + ------------------------------------------------------------------------------ + -- DATA GENERATION + ------------------------------------------------------------------------------ + + u_dp_stream_stimuli : entity dp_lib.dp_stream_stimuli + generic map ( + g_instance_nr => 0, -- only one stream so choose index 0 + -- flow control + g_random_w => 15, -- use different random width for stimuli and for verify to have different random sequences + g_pulse_active => c_stimuli_pulse_active, + g_pulse_period => c_stimuli_pulse_period, + g_flow_control => g_flow_control_stimuli, -- always active, random or pulse flow control + -- initializations + g_sync_period => c_sync_period, + g_sync_offset => c_sync_offset, + g_data_init => c_data_init, + g_bsn_init => c_bsn_init, + -- specific + g_in_dat_w => g_data_w, + g_nof_repeat => c_nof_packets, + g_pkt_len => g_pkt_len, + g_pkt_gap => g_pkt_gap, + g_wait_last_evt => c_wait_last_evt + ) + port map ( + rst => dp_rst, + clk => dp_clk, + + -- Generate stimuli + src_in => stimuli_src_in, + src_out => stimuli_src_out, + + -- End of stimuli + last_snk_in => last_snk_in, -- expected verify_snk_in after end of stimuli + last_snk_in_evt => last_snk_in_evt, -- trigger verify to verify the last_snk_in + tb_end => tb_end -- signal end of tb as far as this dp_stream_stimuli is concerned + ); + + + ------------------------------------------------------------------------------ + -- DATA VERIFICATION + ------------------------------------------------------------------------------ + + -- Select fields that need to be verified + -- . during the test + verify_snk_in_enable.sync <= '1'; + verify_snk_in_enable.bsn <= '1'; + verify_snk_in_enable.data <= '1'; + verify_snk_in_enable.re <= '0'; + verify_snk_in_enable.im <= '0'; + verify_snk_in_enable.valid <= '1'; + verify_snk_in_enable.sop <= '1'; + verify_snk_in_enable.eop <= '1'; + verify_snk_in_enable.empty <= '0'; + verify_snk_in_enable.channel <= '0'; + verify_snk_in_enable.err <= '0'; + + -- . after the test + verify_last_snk_in_evt.sync <= last_snk_in_evt; + verify_last_snk_in_evt.bsn <= last_snk_in_evt; -- thanks to using rx_hdr_fields_raw_arr for bsn field + verify_last_snk_in_evt.data <= last_snk_in_evt; + verify_last_snk_in_evt.re <= '0'; + verify_last_snk_in_evt.im <= '0'; + verify_last_snk_in_evt.valid <= last_snk_in_evt; + verify_last_snk_in_evt.sop <= last_snk_in_evt; + verify_last_snk_in_evt.eop <= last_snk_in_evt; + verify_last_snk_in_evt.empty <= '0'; + verify_last_snk_in_evt.channel <= '0'; + verify_last_snk_in_evt.err <= '0'; + + u_dp_stream_verify : entity dp_lib.dp_stream_verify + generic map ( + g_instance_nr => 0, -- only one stream so choose index 0 + -- flow control + g_random_w => 14, -- use different random width for stimuli and for verify to have different random sequences + g_pulse_active => c_verify_pulse_active, + g_pulse_period => c_verify_pulse_period, + g_flow_control => g_flow_control_verify, -- always active, random or pulse flow control + -- initializations + g_sync_period => c_sync_period, + g_sync_offset => c_sync_offset, + g_snk_in_cnt_max => c_verify_snk_in_cnt_max, + g_snk_in_cnt_gap => c_verify_snk_in_cnt_gap, + -- specific + g_in_dat_w => g_data_w, + g_pkt_len => c_expected_pkt_len + ) + port map ( + rst => dp_rst, + clk => dp_clk, + + -- Verify data + snk_out => verify_snk_out, + snk_in => verify_snk_in, + + -- During stimuli + verify_snk_in_enable => verify_snk_in_enable, -- enable verify to verify that the verify_snk_in fields are incrementing + + -- End of stimuli + expected_snk_in => last_snk_in, -- expected verify_snk_in after end of stimuli + verify_expected_snk_in_evt => verify_last_snk_in_evt -- trigger verify to verify the last_snk_in + ); + + ------------------------------------------------------------------------------ + -- offload Tx + ------------------------------------------------------------------------------ + stimuli_src_in <= c_dp_siso_rdy; + + -- Use FIFO to handle backpressure just like in a network design. + u_dp_fifo_sc : entity dp_lib.dp_fifo_sc + generic map ( + g_data_w => g_data_w, + g_bsn_w => 64, + g_use_sync => true, + g_use_bsn => true, + g_fifo_size => 1024 + ) + port map ( + rst => dp_rst, + clk => dp_clk, + + snk_out => OPEN, -- stimuli_src_in + snk_in => stimuli_src_out, + + src_in => dp_fifo_sc_src_in, + src_out => dp_fifo_sc_src_out + ); + + dp_offload_tx_snk_in_arr(0) <= dp_fifo_sc_src_out; + dp_fifo_sc_src_in <= dp_offload_tx_snk_out_arr(0); + + -- Extract the chip and backplane numbers from c_NODE_ID + id_backplane <= RESIZE_UVEC(c_NODE_ID(7 downto 3), c_byte_w); + id_chip <= RESIZE_UVEC(c_NODE_ID(2 downto 0), c_byte_w); + + -- Wire the hardwired header fields to DP signals and c_NODE_ID + tx_hdr_fields_in_arr(0)(field_hi(c_udp_offload_hdr_field_arr, "eth_src_mac" ) downto field_lo(c_udp_offload_hdr_field_arr, "eth_src_mac" )) <= x"00228608" & id_backplane & id_chip; + tx_hdr_fields_in_arr(0)(field_hi(c_udp_offload_hdr_field_arr, "udp_src_port") downto field_lo(c_udp_offload_hdr_field_arr, "udp_src_port" )) <= x"D0" & c_NODE_ID; + tx_hdr_fields_in_arr(0)(field_hi(c_udp_offload_hdr_field_arr, "udp_dst_port") downto field_lo(c_udp_offload_hdr_field_arr, "udp_dst_port" )) <= x"D0" & c_NODE_ID; + tx_hdr_fields_in_arr(0)(field_hi(c_udp_offload_hdr_field_arr, "ip_src_addr" ) downto field_lo(c_udp_offload_hdr_field_arr, "ip_src_addr" )) <= x"0A63" & id_backplane & INCR_UVEC(id_chip, 1); + + tx_hdr_fields_in_arr(0)(field_hi(c_udp_offload_hdr_field_arr, "dp_sync" ) downto field_lo(c_udp_offload_hdr_field_arr, "dp_sync" )) <= slv(dp_offload_tx_snk_in_arr(0).sync); + tx_hdr_fields_in_arr(0)(field_hi(c_udp_offload_hdr_field_arr, "dp_bsn" ) downto field_lo(c_udp_offload_hdr_field_arr, "dp_bsn" )) <= dp_offload_tx_snk_in_arr(0).bsn(c_bsn_w - 1 downto 0); + + u_tx : entity dp_lib.dp_offload_tx_v3 + generic map ( + g_nof_streams => 1, + g_data_w => g_data_w, + g_symbol_w => g_symbol_w, + g_hdr_field_arr => c_udp_offload_hdr_field_arr, + g_hdr_field_sel => c_hdr_field_ovr_init + ) + port map ( + mm_rst => mm_rst, + mm_clk => mm_clk, + + dp_rst => dp_rst, + dp_clk => dp_clk, + + reg_hdr_dat_mosi => reg_dp_offload_tx_hdr_dat_mosi, + reg_hdr_dat_miso => reg_dp_offload_tx_hdr_dat_miso, + + snk_in_arr => dp_offload_tx_snk_in_arr, + snk_out_arr => dp_offload_tx_snk_out_arr, + + src_out_arr => tx_offload_sosi_arr, + src_in_arr => tx_offload_siso_arr, + + hdr_fields_in_arr => tx_hdr_fields_in_arr, + hdr_fields_out_arr => tx_hdr_fields_out_arr + ); + + ------------------------------------------------------------------------------ + -- DUT: IP header CRC checksum + ------------------------------------------------------------------------------ + u_dut : entity work.eth_ip_header_checksum + generic map ( + g_data_w => g_data_w, + g_hdr_field_arr => c_udp_offload_hdr_field_arr + ) + port map ( + rst => dp_rst, + clk => dp_clk, + + src_out => link_offload_sosi_arr(0), + snk_in => tx_offload_sosi_arr(0), + + src_in => link_offload_siso_arr(0), + snk_out => tx_offload_siso_arr(0), + + hdr_fields_slv_in => tx_hdr_fields_out_arr(0) + ); + + + p_rd_tx_hdr_words : process + variable v_word : std_logic_vector(c_word_w - 1 downto 0); + begin + proc_common_wait_until_hi_lo(dp_clk, tx_offload_sosi_arr(0).sync); + print_str("", g_print_en); + for I in 0 to c_udp_offload_nof_hdr_words - 1 loop + proc_mem_mm_bus_rd(I, mm_clk, reg_dp_offload_tx_hdr_dat_mosi); + proc_mem_mm_bus_rd_latency(c_mem_reg_rd_latency, mm_clk); + v_word := reg_dp_offload_tx_hdr_dat_miso.rddata(31 downto 0); + -- Log word in transcript window + print_str("tx_hdr_word(" & int_to_str(I) & ") = " & slv_to_hex(v_word), g_print_en); + -- View word in wave window + tx_hdr_word <= v_word; + -- Verify expected word + if c_use_shortened_header then + assert c_expected_tx_hdr_word_arr_shortened(I) = v_word report "Unexpected tx_hdr_word at address " & int_to_str(I) & ", expected " & slv_to_hex(c_expected_tx_hdr_word_arr_shortened(I)) severity ERROR; + else + assert c_expected_tx_hdr_word_arr_default(I) = v_word report "Unexpected tx_hdr_word at address " & int_to_str(I) & ", expected " & slv_to_hex(c_expected_tx_hdr_word_arr_default(I)) severity ERROR; + end if; + end loop; + print_str("", g_print_en); + wait; + end process; + + + ------------------------------------------------------------------------------ + -- offload Rx + ------------------------------------------------------------------------------ + u_rx : entity dp_lib.dp_offload_rx + generic map ( + g_nof_streams => 1, + g_data_w => g_data_w, + g_symbol_w => g_symbol_w, + g_hdr_field_arr => c_udp_offload_hdr_field_arr, + g_remove_crc => false, + g_crc_nof_words => 0 + ) + port map ( + mm_rst => mm_rst, + mm_clk => mm_clk, + + dp_rst => dp_rst, + dp_clk => dp_clk, + + reg_hdr_dat_mosi => reg_dp_offload_rx_hdr_dat_mosi, + reg_hdr_dat_miso => reg_dp_offload_rx_hdr_dat_miso, + + snk_in_arr => link_offload_sosi_arr, + snk_out_arr => link_offload_siso_arr, + + src_out_arr => dp_offload_rx_src_out_arr, + src_in_arr => dp_offload_rx_src_in_arr, + + hdr_fields_out_arr => rx_hdr_fields_out_arr, + hdr_fields_raw_arr => rx_hdr_fields_raw_arr + ); + + p_restore_sync_bsn : process(dp_offload_rx_src_out_arr, rx_hdr_fields_out_arr) + begin + verify_snk_in <= dp_offload_rx_src_out_arr(0); + verify_snk_in.sync <= sl(rx_hdr_fields_out_arr(0)(field_hi(c_udp_offload_hdr_field_arr, "dp_sync") downto field_lo(c_udp_offload_hdr_field_arr, "dp_sync" ))); + verify_snk_in.bsn <= RESIZE_UVEC(rx_hdr_fields_raw_arr(0)(field_hi(c_udp_offload_hdr_field_arr, "dp_bsn" ) downto field_lo(c_udp_offload_hdr_field_arr, "dp_bsn" )), c_dp_stream_bsn_w); + end process; + + dp_offload_rx_src_in_arr <= (others => c_dp_siso_rdy); + dp_offload_rx_src_in_arr(0) <= verify_snk_out; + + + p_rd_rx_hdr_words : process + variable v_word : std_logic_vector(c_word_w - 1 downto 0); + begin + proc_common_wait_until_hi_lo(dp_clk, verify_snk_in.sync); + -- Check first packet after sync with dp_sync = 1 + -- wait some latency until header fields of this sync packet are available via MM + proc_common_wait_some_cycles(dp_clk, c_hdr_len + 10); + print_str("", g_print_en); + for I in 0 to c_udp_offload_nof_hdr_words - 1 loop + proc_mem_mm_bus_rd(I, mm_clk, reg_dp_offload_rx_hdr_dat_mosi); + proc_mem_mm_bus_rd_latency(c_mem_reg_rd_latency, mm_clk); + v_word := reg_dp_offload_rx_hdr_dat_miso.rddata(31 downto 0); + -- Log word in transcript window + print_str("rx_hdr_word(" & int_to_str(I) & ") : " & slv_to_hex(v_word), g_print_en); + -- View word in wave window + rx_hdr_word <= v_word; + -- Verify expected word + if c_use_shortened_header then + assert v_word = c_expected_rx_hdr_word_arr_shortened(I) report "Unexpected rx_hdr_word at address " & int_to_str(I) & ", expected " & slv_to_hex(c_expected_rx_hdr_word_arr_shortened(I)) severity ERROR; + else + assert v_word = c_expected_rx_hdr_word_arr_default(I) report "Unexpected rx_hdr_word at address " & int_to_str(I) & ", expected " & slv_to_hex(c_expected_rx_hdr_word_arr_default(I)) severity ERROR; + end if; + end loop; + print_str("", g_print_en); + + -- Check dp_bsn and dp_sync of second packet after sync with dp_sync = 0 + proc_common_wait_until_hi_lo(dp_clk, verify_snk_in.sop); + -- wait some latency until header fields of this sync packet are available via MM + proc_common_wait_some_cycles(dp_clk, c_hdr_len + 5); + -- dp_bsn lo + proc_mem_mm_bus_rd(0, mm_clk, reg_dp_offload_rx_hdr_dat_mosi); + proc_mem_mm_bus_rd_latency(c_mem_reg_rd_latency, mm_clk); + v_word := reg_dp_offload_rx_hdr_dat_miso.rddata(31 downto 0); + rx_hdr_word <= v_word; -- View word in wave window + if c_use_shortened_header then + assert v_word = INCR_UVEC(c_expected_rx_hdr_word_arr_shortened(0), 1) report "Unexpected dp_bsn from MM" severity ERROR; + else + assert v_word = INCR_UVEC(c_expected_rx_hdr_word_arr_default(0), 1) report "Unexpected dp_bsn from MM" severity ERROR; + end if; + -- dp_sync + if c_use_shortened_header then + proc_mem_mm_bus_rd(1, mm_clk, reg_dp_offload_rx_hdr_dat_mosi); + else + proc_mem_mm_bus_rd(2, mm_clk, reg_dp_offload_rx_hdr_dat_mosi); + end if; + proc_mem_mm_bus_rd_latency(c_mem_reg_rd_latency, mm_clk); + v_word := reg_dp_offload_rx_hdr_dat_miso.rddata(31 downto 0); + rx_hdr_word <= v_word; -- View word in wave window + assert v_word = TO_UVEC(0, 32) report "Unexpected dp_sync from MM" severity ERROR; + + wait; + end process; + + ------------------------------------------------------------------------------ + -- Auxiliary + ------------------------------------------------------------------------------ + + -- Map to slv to ease monitoring in wave window + stimuli_src_out_data <= stimuli_src_out.data(g_data_w - 1 downto 0); + verify_snk_in_data <= verify_snk_in.data(g_data_w - 1 downto 0); + +end tb; diff --git a/libraries/io/eth/tb/vhdl/tb_tb_eth_ip_header_checksum.vhd b/libraries/io/eth/tb/vhdl/tb_tb_eth_ip_header_checksum.vhd new file mode 100644 index 0000000000000000000000000000000000000000..fe15112818c7b994e8c119845eb21cd15b2d2d7e --- /dev/null +++ b/libraries/io/eth/tb/vhdl/tb_tb_eth_ip_header_checksum.vhd @@ -0,0 +1,64 @@ +-- -------------------------------------------------------------------------- +-- Copyright 2023 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- -------------------------------------------------------------------------- +-- +-- Author: R. vd Walle +-- Purpose: Regression multi tb for eth_ip_header_checksum +-- Description: +-- Usage: +-- > as 4 +-- > run -all + +library IEEE, dp_lib; +use IEEE.std_logic_1164.all; +use dp_lib.tb_dp_pkg.all; -- for t_dp_flow_control_enum + +entity tb_tb_eth_ip_header_checksum is +end tb_tb_eth_ip_header_checksum; + + +architecture tb of tb_tb_eth_ip_header_checksum is + + signal tb_end : std_logic := '0'; -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end' + +begin + + -- -- general + -- g_flow_control_stimuli : t_dp_flow_control_enum := e_pulse; -- always e_active, e_random or e_pulse flow control + -- g_flow_control_verify : t_dp_flow_control_enum := e_active; -- always e_active, e_random or e_pulse flow control + -- g_print_en : BOOLEAN := TRUE; + -- -- specific + -- g_data_w : NATURAL := 64; + -- g_symbol_w : NATURAL := 16; + -- g_empty : NATURAL := 6; -- number of empty symbols in header when g_symbol_w < g_data_w, must be < c_nof_symbols_per_data + -- g_pkt_len : NATURAL := 240; + -- g_pkt_gap : NATURAL := 16 + + u_pls_act_data_w_256 : entity work.tb_eth_ip_header_checksum generic map (e_pulse, e_active, false, 256, 64, 0, 240, 16); + u_act_act_data_w_512_no_gap : entity work.tb_eth_ip_header_checksum generic map (e_active, e_active, false, 512, 8, 0, 240, 0); + u_pls_act_data_w_64_no_gap : entity work.tb_eth_ip_header_checksum generic map (e_pulse, e_active, false, 64, 64, 0, 240, 0); + u_rnd_act_data_w_32 : entity work.tb_eth_ip_header_checksum generic map (e_random, e_active, false, 32, 8, 0, 240, 16); + u_rnd_act_data_w_8 : entity work.tb_eth_ip_header_checksum generic map (e_random, e_active, false, 8, 8, 0, 240, 16); + --u_act_rnd_data_w : ENTITY work.tb_eth_ip_header_checksum GENERIC MAP (e_active, e_random, FALSE, 64, 64, 0, 240, 16); -- dp_offload_rx requires e_active + u_rnd_act_data_64_symbol_8_empty_1 : entity work.tb_eth_ip_header_checksum generic map (e_random, e_active, false, 64, 8, 1, 240, 16); + u_rnd_act_data_64_symbol_8_empty_6 : entity work.tb_eth_ip_header_checksum generic map (e_random, e_active, false, 64, 8, 6, 240, 16); + u_rnd_act_data_64_symbol_16 : entity work.tb_eth_ip_header_checksum generic map (e_random, e_active, false, 64, 16, 1, 240, 16); + u_rnd_act_data_64_symbol_32 : entity work.tb_eth_ip_header_checksum generic map (e_random, e_active, false, 64, 32, 1, 240, 16); + u_rnd_act_data_32_symbol_8 : entity work.tb_eth_ip_header_checksum generic map (e_random, e_active, false, 32, 8, 1, 240, 16); + u_rnd_act_data_32_symbol_16 : entity work.tb_eth_ip_header_checksum generic map (e_random, e_active, false, 32, 16, 1, 240, 16); + +end tb;