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;